diff --git a/package.json b/package.json index 827a9c2..9a1bc7e 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "json-visio", "private": true, - "version": "v1.9.0", + "version": "v1.9.2", "homepage": "https://jsonvisio.com", "scripts": { "dev": "next dev", @@ -15,6 +15,7 @@ "@monaco-editor/react": "^4.4.5", "@sentry/nextjs": "^7.1.1", "allotment": "^1.14.2", + "compress-json": "^2.0.1", "next": "^12.1.5", "next-transpile-modules": "^9.0.0", "parse-json": "^6.0.2", @@ -27,7 +28,8 @@ "react-zoom-pan-pinch": "^2.1.3", "reaflow": "^5.0.4", "save-html-as-image": "^1.7.1", - "styled-components": "^5.3.5" + "styled-components": "^5.3.5", + "usehooks-ts": "^2.5.2" }, "devDependencies": { "@testing-library/jest-dom": "^5.16.4", diff --git a/src/components/Modal/index.tsx b/src/components/Modal/index.tsx index ebcd98a..605a9a6 100644 --- a/src/components/Modal/index.tsx +++ b/src/components/Modal/index.tsx @@ -57,8 +57,10 @@ const Modal: React.FC> & ModalTypes = ({ } }; + if (!visible) return null; + return ( - + {children} ); diff --git a/src/components/Modal/styles.tsx b/src/components/Modal/styles.tsx index c19fb3b..9b09f01 100644 --- a/src/components/Modal/styles.tsx +++ b/src/components/Modal/styles.tsx @@ -5,17 +5,17 @@ const appearAnimation = keyframes` to { transform: scale(1); opacity: 1; }; `; -export const ModalWrapper = styled.div<{ visible: boolean }>` +export const ModalWrapper = styled.div` position: fixed; top: 0; left: 0; height: 100vh; width: 100%; - display: ${({ visible }) => (visible ? "flex" : "none")}; + display: flex; justify-content: center; align-items: center; background: rgba(0, 0, 0, 0.85); - z-index: 6; + z-index: 36; * { box-sizing: border-box; @@ -24,6 +24,7 @@ export const ModalWrapper = styled.div<{ visible: boolean }>` export const ModalInnerWrapper = styled.div` min-width: 440px; + max-width: 70%; width: fit-content; animation: ${appearAnimation} 220ms ease-in-out; `; diff --git a/src/components/Sidebar/index.tsx b/src/components/Sidebar/index.tsx index 3be7261..0564136 100644 --- a/src/components/Sidebar/index.tsx +++ b/src/components/Sidebar/index.tsx @@ -16,6 +16,7 @@ import { AiOutlineTwitter, AiOutlineSave, AiOutlineFileAdd, + AiOutlineLink, } from "react-icons/ai"; import { Tooltip } from "src/components/Tooltip"; @@ -24,6 +25,7 @@ import { useConfig } from "src/hocs/config"; import { useRouter } from "next/router"; import { ImportModal } from "src/containers/ImportModal"; import { ClearModal } from "src/containers/ClearModal"; +import { ShareModal } from "src/containers/ShareModal"; import { IoAlertCircleSharp } from "react-icons/io5"; const StyledSidebar = styled.div` @@ -138,6 +140,7 @@ export const Sidebar: React.FC = () => { const router = useRouter(); const [uploadVisible, setUploadVisible] = React.useState(false); const [clearVisible, setClearVisible] = React.useState(false); + const [shareVisible, setShareVisible] = React.useState(false); const handleSave = () => { const a = document.createElement("a"); @@ -197,16 +200,6 @@ export const Sidebar: React.FC = () => { {settings.expand ? : } - - setClearVisible(true)}> - - - - - - - - { /> + + + + + + + setClearVisible(true)}> + + + + + setShareVisible(true)}> + + + @@ -244,6 +252,7 @@ export const Sidebar: React.FC = () => { + ); }; diff --git a/src/containers/ImportModal/index.tsx b/src/containers/ImportModal/index.tsx index 2862cc3..595db45 100644 --- a/src/containers/ImportModal/index.tsx +++ b/src/containers/ImportModal/index.tsx @@ -15,7 +15,7 @@ const StyledInput = styled.input` border: none; border-radius: 4px; line-height: 32px; - padding: 16px; + padding: 12px 8px; width: 100%; margin-bottom: 10px; height: 30px; diff --git a/src/containers/JsonEditor/index.tsx b/src/containers/JsonEditor/index.tsx index 096e0c5..bd0b293 100644 --- a/src/containers/JsonEditor/index.tsx +++ b/src/containers/JsonEditor/index.tsx @@ -7,7 +7,6 @@ import { ConfigActionType } from "src/reducer/reducer"; import { useConfig } from "src/hocs/config"; import { Loading } from "src/components/Loading"; import { loader } from "@monaco-editor/react"; -import { CarbonAds } from "src/components/CarbonAds"; loader.config({ paths: { vs: "/monaco-editor/min/vs" } }); diff --git a/src/containers/ShareModal/index.tsx b/src/containers/ShareModal/index.tsx new file mode 100644 index 0000000..f8b32e3 --- /dev/null +++ b/src/containers/ShareModal/index.tsx @@ -0,0 +1,80 @@ +import React from "react"; +import toast from "react-hot-toast"; +import styled from "styled-components"; +import { useCopyToClipboard } from "usehooks-ts"; +import { Modal, ModalProps } from "src/components/Modal"; +import { Button } from "src/components/Button"; +import { BiErrorAlt } from "react-icons/bi"; +import { compress } from "compress-json"; +import { useConfig } from "src/hocs/config"; + +const StyledInput = styled.input` + background: ${({ theme }) => theme.BACKGROUND_TERTIARY}; + color: ${({ theme }) => theme.INTERACTIVE_NORMAL}; + outline: none; + border: none; + border-radius: 4px; + line-height: 32px; + padding: 12px 8px; + width: 100%; + margin-bottom: 10px; + height: 30px; +`; + +const StyledWarning = styled.p``; + +const StyledErrorWrapper = styled.div` + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + color: ${({ theme }) => theme.TEXT_DANGER}; + font-weight: 600; +`; + +export const ShareModal: React.FC = ({ visible, setVisible }) => { + const { json } = useConfig(); + const [url, setURL] = React.useState(""); + const [_, copy] = useCopyToClipboard(); + + React.useEffect(() => { + const jsonEncode = compress(JSON.parse(json)); + const jsonString = JSON.stringify(jsonEncode); + + setURL( + `https://jsonvisio.com/editor?json=${encodeURIComponent(jsonString)}` + ); + }, [json]); + + const handleShare = () => { + copy(url); + toast.success(`Link copied to clipboard.`); + setVisible(false); + }; + + return ( + + Create a Share Link + + {url.length > 5000 ? ( + + + + Link size exceeds 5000 characters, unable to generate link for + file of this size! + + + ) : ( + + )} + + + {url.length < 5000 && ( + + )} + + + ); +}; diff --git a/src/hocs/config.tsx b/src/hocs/config.tsx index 0879f57..93d39cc 100644 --- a/src/hocs/config.tsx +++ b/src/hocs/config.tsx @@ -6,6 +6,9 @@ import { useConfigReducer, } from "src/reducer/reducer"; import { ReactComponent, StorageConfig } from "src/typings/global"; +import { isValidJson } from "src/utils/isValidJson"; +import { useRouter } from "next/router"; +import { Compressed, decompress } from "compress-json"; export interface AppConfig { json: string; @@ -42,7 +45,23 @@ const WithConfig: ReactComponent = ({ children }) => { settings: states.settings, }; + const router = useRouter(); + const { json } = router.query; + React.useEffect(() => { + const jsonStored = localStorage.getItem("json"); + const isJsonValid = + typeof json === "string" && isValidJson(decodeURIComponent(json)); + + if (isJsonValid) { + const jsonDecoded = decompress(JSON.parse(isJsonValid)); + const jsonString = JSON.stringify(jsonDecoded); + + dispatch({ type: ConfigActionType.SET_JSON, payload: jsonString }); + } else if (jsonStored) { + dispatch({ type: ConfigActionType.SET_JSON, payload: jsonStored }); + } + const configStored = localStorage.getItem("config"); if (configStored) { @@ -53,7 +72,7 @@ const WithConfig: ReactComponent = ({ children }) => { } setRender(true); - }, [dispatch]); + }, [dispatch, json]); React.useEffect(() => { if (render) diff --git a/src/utils/isValidJson.ts b/src/utils/isValidJson.ts new file mode 100644 index 0000000..aeb6b97 --- /dev/null +++ b/src/utils/isValidJson.ts @@ -0,0 +1,8 @@ +export const isValidJson = (str: string) => { + try { + JSON.parse(str); + } catch (e) { + return false; + } + return str; +}; diff --git a/yarn.lock b/yarn.lock index 684c939..fca68fd 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2594,6 +2594,11 @@ commondir@^1.0.1: resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" integrity sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs= +compress-json@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/compress-json/-/compress-json-2.0.1.tgz#8460d79e1a5612fab747c20408f997d55f11bc48" + integrity sha512-iwGwSGvFd1eN0LQAbbjvBy/liXXKhJpF87u4Rmoqth66o+HqOCwJT3Ok7xY0qCAJhpo4XNH8A699nOUl2G90TQ== + concat-map@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" @@ -6612,6 +6617,11 @@ use-resize-observer@^9.0.0: dependencies: "@juggle/resize-observer" "^3.3.1" +usehooks-ts@^2.5.2: + version "2.6.0" + resolved "https://registry.yarnpkg.com/usehooks-ts/-/usehooks-ts-2.6.0.tgz#aebab367da2350a0bee1c3749bc6dd4bcce3eaae" + integrity sha512-Kj/4oc2nOxRDGTDb2v1ZulF7+tpeXFuqI6cUesM0Vic7TPPDlFORxKh4ivsYg+NTvX/YbM+lhqqkfFTiIt23eg== + util-deprecate@~1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"