AykutSarac 2 anni fa
parent
commit
458723b2d6

+ 5 - 7
src/containers/Editor/LiveEditor/GraphCanvas.tsx

@@ -33,13 +33,11 @@ export const GraphCanvas = ({ isWidget = false }: { isWidget?: boolean }) => {
   return (
     <>
       <Graph openModal={openModal} setSelectedNode={setSelectedNode} isWidget={isWidget} />
-      {!isWidget && (
-        <NodeModal
-          selectedNode={selectedNode}
-          visible={isModalVisible}
-          closeModal={() => setModalVisible(false)}
-        />
-      )}
+      <NodeModal
+        selectedNode={selectedNode}
+        visible={isModalVisible}
+        closeModal={() => setModalVisible(false)}
+      />
     </>
   );
 };

+ 14 - 8
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 = () => {
         <Styles.StyledHighlightedText>instantly</Styles.StyledHighlightedText> into graphs.
       </Styles.StyledSubTitle>
 
-      <Styles.StyledButton rel="prefetch" href="/editor" link>
+      <Styles.StyledButton href="/editor" link>
         GO TO EDITOR
-        <AiOutlineRight strokeWidth="30px" />
+        <AiOutlineRight strokeWidth="80" />
       </Styles.StyledButton>
 
       <Styles.StyledButtonWrapper>
@@ -149,8 +149,9 @@ const GitHubSection = () => (
           Looking to understand or explore some JSON? Just paste or upload to visualize it as a
           graph with <a href="https://t.co/HlKSrhKryJ">https://t.co/HlKSrhKryJ</a> 😍 <br />
           <br />
-          Thanks to <a href="https://twitter.com/aykutsarach?ref_src=twsrc%5Etfw">@aykutsarach</a>!{" "}
-          <a href="https://t.co/0LyPUL8Ezz">pic.twitter.com/0LyPUL8Ezz</a>
+          Thanks to <a href="https://twitter.com/aykutsarach?ref_src=twsrc%5Etfw">
+            @aykutsarach
+          </a>! <a href="https://t.co/0LyPUL8Ezz">pic.twitter.com/0LyPUL8Ezz</a>
         </p>
         &mdash; GitHub (@github){" "}
         <a href="https://twitter.com/github/status/1519363257794015233?ref_src=twsrc%5Etfw">
@@ -207,16 +208,21 @@ const EmbedSection = () => (
     </Styles.StyledSectionArea>
     <div>
       <Styles.StyledIframge
-        src="https://jsoncrack.com/widget"
+        src={`${baseURL}/widget`}
         onLoad={e => {
           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",
                 },
               },
               "*"

+ 6 - 1
src/containers/Modals/ShareModal/index.tsx

@@ -39,6 +39,11 @@ export const ShareModal: React.FC<ModalProps> = ({ visible, setVisible }) => {
     setVisible(false);
   };
 
+  const onEmbedClick = () => {
+    push("/embed");
+    setVisible(false);
+  }
+
   return (
     <Modal visible={visible} setVisible={setVisible}>
       <Modal.Header>Create a Share Link</Modal.Header>
@@ -55,7 +60,7 @@ export const ShareModal: React.FC<ModalProps> = ({ visible, setVisible }) => {
         <StyledContainer>
           Embed into your website
           <StyledFlex>
-            <Button status="SUCCESS" onClick={() => push("/embed")} block>
+            <Button status="SUCCESS" onClick={onEmbedClick} block>
               Learn How to Embed
             </Button>
           </StyledFlex>

+ 1 - 1
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;
 `;
 

+ 20 - 77
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<any>(() => 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 (edgeList.length) {
-      const selectedEdges = document.querySelectorAll(edgeList.join(","));
-      selectedEdges.forEach(edge => edge.classList.add("hide"));
+    if (isReady) {
+      fetchJson(query.json);
+      if (!inIframe()) push("/");
     }
-
-    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 (
-      <StyledDeprecated>
-        <h1>⚠️ Deprecated ⚠️</h1>
-        <br />
-        <a href="https://jsoncrack.com/embed" target="_blank" rel="noreferrer">
-          https://jsoncrack.com/embed
-        </a>
-      </StyledDeprecated>
-    );
+  }, [setGraph, theme]);
 
   return (
     <ThemeProvider theme={theme === "dark" ? darkTheme : lightTheme}>
-      <Graph openModal={openModal} setSelectedNode={setSelectedNode} isWidget />
-      <NodeModal
-        selectedNode={selectedNode}
-        visible={isModalVisible}
-        closeModal={() => setModalVisible(false)}
-      />
+      <GraphCanvas isWidget />
       <StyledAttribute href={`${baseURL}/editor`} target="_blank" rel="noreferrer">
         jsoncrack.com
       </StyledAttribute>

+ 3 - 13
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<Graph>[]) => void;
   setLoading: (loading: boolean) => void;
   setDirection: (direction: CanvasDirection) => void;
   setZoomPanPinch: (ref: ReactZoomPanPinchRef) => void;
@@ -44,7 +43,7 @@ interface GraphActions {
 
 const useGraph = create<Graph & GraphActions>((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<Graph & GraphActions>((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(

+ 22 - 4
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<JsonStates & JsonActions>()((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<JsonStates & JsonActions>()((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);