From 458723b2d66004414afdc749deca1c1d7a0cdfea Mon Sep 17 00:00:00 2001 From: AykutSarac Date: Thu, 22 Dec 2022 22:13:40 +0300 Subject: [PATCH] improve embeds --- .../Editor/LiveEditor/GraphCanvas.tsx | 12 +-- src/containers/Home/index.tsx | 22 +++-- src/containers/Modals/ShareModal/index.tsx | 7 +- src/pages/_error.tsx | 2 +- src/pages/widget.tsx | 97 ++++--------------- src/store/useGraph.tsx | 16 +-- src/store/useJson.tsx | 26 ++++- 7 files changed, 71 insertions(+), 111 deletions(-) diff --git a/src/containers/Editor/LiveEditor/GraphCanvas.tsx b/src/containers/Editor/LiveEditor/GraphCanvas.tsx index 18939f9..7c04662 100644 --- a/src/containers/Editor/LiveEditor/GraphCanvas.tsx +++ b/src/containers/Editor/LiveEditor/GraphCanvas.tsx @@ -33,13 +33,11 @@ export const GraphCanvas = ({ isWidget = false }: { isWidget?: boolean }) => { return ( <> - {!isWidget && ( - setModalVisible(false)} - /> - )} + setModalVisible(false)} + /> ); }; diff --git a/src/containers/Home/index.tsx b/src/containers/Home/index.tsx index d360720..af3d0ff 100644 --- a/src/containers/Home/index.tsx +++ b/src/containers/Home/index.tsx @@ -15,7 +15,7 @@ import { CarbonAds } from "src/components/CarbonAds"; import { Producthunt } from "src/components/Producthunt"; import { Sponsors } from "src/components/Sponsors"; import { SupportButton } from "src/components/SupportButton"; -import { defaultJson } from "src/constants/data"; +import { baseURL } from "src/constants/data"; import { GoalsModal } from "src/containers/Modals/GoalsModal"; import pkg from "../../../package.json"; import * as Styles from "./styles"; @@ -52,9 +52,9 @@ const HeroSection = () => { instantly into graphs. - + GO TO EDITOR - + @@ -149,8 +149,9 @@ const GitHubSection = () => ( Looking to understand or explore some JSON? Just paste or upload to visualize it as a graph with https://t.co/HlKSrhKryJ 😍

- Thanks to @aykutsarach!{" "} - pic.twitter.com/0LyPUL8Ezz + Thanks to + @aykutsarach + ! pic.twitter.com/0LyPUL8Ezz

— GitHub (@github){" "} @@ -207,16 +208,21 @@ const EmbedSection = () => (
{ const frame = e.currentTarget.contentWindow; setTimeout(() => { frame?.postMessage( { - json: defaultJson, + json: JSON.stringify({ + "random images": [ + "https://random.imagecdn.app/50/50?.png", + "https://random.imagecdn.app/80/80?.png", + "https://random.imagecdn.app/100/100?.png", + ], + }), options: { theme: "dark", - direction: "DOWN", }, }, "*" diff --git a/src/containers/Modals/ShareModal/index.tsx b/src/containers/Modals/ShareModal/index.tsx index 45747e2..fcbbf3a 100644 --- a/src/containers/Modals/ShareModal/index.tsx +++ b/src/containers/Modals/ShareModal/index.tsx @@ -39,6 +39,11 @@ export const ShareModal: React.FC = ({ visible, setVisible }) => { setVisible(false); }; + const onEmbedClick = () => { + push("/embed"); + setVisible(false); + } + return ( Create a Share Link @@ -55,7 +60,7 @@ export const ShareModal: React.FC = ({ visible, setVisible }) => { Embed into your website - diff --git a/src/pages/_error.tsx b/src/pages/_error.tsx index 8d64f25..7cad8aa 100644 --- a/src/pages/_error.tsx +++ b/src/pages/_error.tsx @@ -15,7 +15,7 @@ const StyledNotFound = styled.div` const StyledMessage = styled.h4` color: ${({ theme }) => theme.FULL_WHITE}; font-size: 25px; - font-weight: 600; + font-weight: 800; margin: 10px 0; `; diff --git a/src/pages/widget.tsx b/src/pages/widget.tsx index 3af4d14..e3080af 100644 --- a/src/pages/widget.tsx +++ b/src/pages/widget.tsx @@ -4,14 +4,16 @@ import { useRouter } from "next/router"; import toast from "react-hot-toast"; import { baseURL } from "src/constants/data"; import { darkTheme, lightTheme } from "src/constants/theme"; -import { NodeModal } from "src/containers/Modals/NodeModal"; import useGraph from "src/store/useGraph"; -import { parser } from "src/utils/jsonParser"; +import useJson from "src/store/useJson"; import styled, { ThemeProvider } from "styled-components"; -const Graph = dynamic(() => import("src/components/Graph").then(c => c.Graph), { - ssr: false, -}); +const GraphCanvas = dynamic( + () => import("src/containers/Editor/LiveEditor/GraphCanvas").then(c => c.GraphCanvas), + { + ssr: false, + } +); const StyledAttribute = styled.a` position: fixed; @@ -45,71 +47,28 @@ interface EmbedMessage { }; } -const StyledDeprecated = styled.div` - display: flex; - flex-direction: column; - justify-content: center; - align-items: center; - width: 100%; - height: 100vh; - - a { - text-decoration: underline; - } -`; - const WidgetPage = () => { - const { query, push } = useRouter(); - - const [isModalVisible, setModalVisible] = React.useState(false); - const [selectedNode, setSelectedNode] = React.useState<[string, string][]>([]); + const { query, push, isReady } = useRouter(); const [theme, setTheme] = React.useState("dark"); - - const collapsedNodes = useGraph(state => state.collapsedNodes); - const collapsedEdges = useGraph(state => state.collapsedEdges); - const loading = useGraph(state => state.loading); - const setNodeEdges = useGraph(state => state.setNodeEdges); - const setDirection = useGraph(state => state.setDirection); - - const openModal = React.useCallback(() => setModalVisible(true), []); + const fetchJson = useJson(state => state.fetchJson); + const setGraph = useGraph(state => state.setGraph); React.useEffect(() => { - const nodeList = collapsedNodes.map(id => `[id$="node-${id}"]`); - const edgeList = collapsedEdges.map(id => `[class$="edge-${id}"]`); - - const hiddenItems = document.querySelectorAll(".hide"); - hiddenItems.forEach(item => item.classList.remove("hide")); - - if (nodeList.length) { - const selectedNodes = document.querySelectorAll(nodeList.join(",")); - selectedNodes.forEach(node => node.classList.add("hide")); + if (isReady) { + fetchJson(query.json); + if (!inIframe()) push("/"); } - - if (edgeList.length) { - const selectedEdges = document.querySelectorAll(edgeList.join(",")); - selectedEdges.forEach(edge => edge.classList.add("hide")); - } - - if (!inIframe()) push("/"); - }, [collapsedNodes, collapsedEdges, loading, push]); + }, [fetchJson, isReady, push, query.json]); React.useEffect(() => { const handler = (event: EmbedMessage) => { try { if (!event.data?.json) return; + if (event.data?.options?.theme === "light" || event.data?.options?.theme === "dark") { + setTheme(event.data.options.theme); + } - const { nodes, edges } = parser(event.data.json); - - const options = { - direction: "RIGHT", - theme, - ...event.data.options, - }; - - setDirection(options.direction); - if (options.theme === "light" || options.theme === "dark") setTheme(options.theme); - - setNodeEdges(nodes, edges); + setGraph(event.data.json, event.data.options); } catch (error) { console.error(error); toast.error("Invalid JSON!"); @@ -118,27 +77,11 @@ const WidgetPage = () => { window.addEventListener("message", handler); return () => window.removeEventListener("message", handler); - }, [setDirection, setNodeEdges, theme]); - - if (query.json) - return ( - -

⚠️ Deprecated ⚠️

-
-
- https://jsoncrack.com/embed - - - ); + }, [setGraph, theme]); return ( - - setModalVisible(false)} - /> + jsoncrack.com diff --git a/src/store/useGraph.tsx b/src/store/useGraph.tsx index c119a27..0eaf31f 100644 --- a/src/store/useGraph.tsx +++ b/src/store/useGraph.tsx @@ -25,8 +25,7 @@ const initialStates = { export type Graph = typeof initialStates; interface GraphActions { - setGraph: (json?: string) => void; - setNodeEdges: (nodes: NodeData[], edges: EdgeData[]) => void; + setGraph: (json?: string, options?: Partial[]) => void; setLoading: (loading: boolean) => void; setDirection: (direction: CanvasDirection) => void; setZoomPanPinch: (ref: ReactZoomPanPinchRef) => void; @@ -44,7 +43,7 @@ interface GraphActions { const useGraph = create((set, get) => ({ ...initialStates, - setGraph: (data?: string) => { + setGraph: (data, options) => { const { nodes, edges } = parser(data ?? useJson.getState().json); set({ @@ -55,19 +54,10 @@ const useGraph = create((set, get) => ({ collapsedEdges: [], graphCollapsed: false, loading: true, + ...options, }); }, setDirection: direction => set({ direction }), - setNodeEdges: (nodes, edges) => - set({ - nodes, - edges, - collapsedParents: [], - collapsedNodes: [], - collapsedEdges: [], - graphCollapsed: false, - loading: true, - }), setLoading: loading => set({ loading }), expandNodes: nodeId => { const [childrenNodes, matchingNodes] = getOutgoers( diff --git a/src/store/useJson.tsx b/src/store/useJson.tsx index 5c83058..c99a38a 100644 --- a/src/store/useJson.tsx +++ b/src/store/useJson.tsx @@ -1,4 +1,5 @@ import { decompressFromBase64 } from "lz-string"; +import toast from "react-hot-toast"; import { altogic } from "src/api/altogic"; import { defaultJson } from "src/constants/data"; import useGraph from "src/store/useGraph"; @@ -35,7 +36,24 @@ const useJson = create()((set, get) => ({ ...initialStates, getJson: () => get().json, fetchJson: async jsonId => { - if (jsonId) { + const isURL = new RegExp( + /^https?:\/\/(?:www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b(?:[-a-zA-Z0-9()@:%_\+.~#?&\/=]*)$/ + ); + + if (typeof jsonId === "string" && isURL.test(jsonId)) { + try { + const res = await fetch(jsonId); + const json = await res.json(); + const jsonStr = JSON.stringify(json, null, 2); + + useGraph.getState().setGraph(jsonStr); + set({ json: jsonStr, loading: false }); + } catch (error) { + useGraph.getState().setGraph(defaultJson); + set({ json: defaultJson, loading: false }); + toast.error('Failed to fetch JSON from URL!'); + } + } else if (jsonId) { const { data, errors } = await altogic.endpoint.get(`json/${jsonId}`); if (!errors) { @@ -49,10 +67,10 @@ const useJson = create()((set, get) => ({ }); } } + } else { + useGraph.getState().setGraph(defaultJson); + set({ json: defaultJson, loading: false }); } - - useGraph.getState().setGraph(defaultJson); - set({ json: defaultJson, loading: false }); }, setJson: json => { useGraph.getState().setGraph(json);