Browse Source

create altogic connections

AykutSarac 2 years ago
parent
commit
eb73508ab7

+ 3 - 1
.env.development

@@ -1 +1,3 @@
-NEXT_PUBLIC_BASE_URL=http://localhost:3000
+NEXT_PUBLIC_BASE_URL=http://localhost:3000
+NEXT_ALTOGIC_ENV_URL=https://815e-4e5a.c5-na.altogic.com/
+NEXT_ALTOGIC_CLIENT_KEY=f1e92022789f4ccf91273a345ab2bdf8

+ 1 - 1
README.md

@@ -35,7 +35,7 @@
 
 # JSON Crack (jsoncrack.com)
 
-JSON Crack is a tool that generates graph diagrams from JSON objects. These diagrams are much easier to navigate than the textual format and to make it even more convenient, the tool also allows you to search the nodes. Additionally, the generated diagrams can also be downloaded or clipboard as image.
+Looking for an easy and intuitive way to visualize and analyze your JSON data? Look no further than JSON Crack, the powerful and user-friendly JSON visualization tool. With its advanced features and sleek design, JSON Crack makes it easy to explore and understand even the most complex JSON structures. Whether you're a developer working on a large-scale project or a data enthusiast looking to uncover hidden insights, JSON Crack has the tools you need to unlock the full potential of your data. Try JSON Crack today and experience the power of data visualization like never before.
 
 You can use the web version at [jsoncrack.com](https://jsoncrack.com) or also run it locally as [Docker container](https://github.com/AykutSarac/jsoncrack.com#-docker).
 

+ 4 - 2
package.json

@@ -1,7 +1,7 @@
 {
   "name": "json-crack",
   "private": true,
-  "version": "v2.1.0",
+  "version": "v3.0.0",
   "author": "https://github.com/AykutSarac",
   "homepage": "https://jsoncrack.com",
   "scripts": {
@@ -16,12 +16,14 @@
     "@monaco-editor/react": "^4.4.6",
     "@react-oauth/google": "^0.4.0",
     "@sentry/nextjs": "^7.16.0",
+    "@tanstack/react-query": "^4.19.1",
     "allotment": "^1.17.0",
+    "altogic": "^2.3.8",
     "axios": "^1.1.3",
     "compress-json": "^2.1.2",
+    "dayjs": "^1.11.6",
     "html-to-image": "^1.10.8",
     "jsonc-parser": "^3.2.0",
-    "jwt-decode": "^3.1.2",
     "lodash.debounce": "^4.0.8",
     "lz-string": "^1.4.4",
     "next": "^12.3.1",

+ 2 - 2
src/components/Modal/index.tsx

@@ -20,7 +20,7 @@ export interface ModalProps {
   setVisible:
     | React.Dispatch<React.SetStateAction<boolean>>
     | ((visible: boolean) => void);
-  size?: "md" | "lg";
+  size?: "sm" | "md" | "lg";
 }
 
 const Header: ReactComponent = ({ children }) => {
@@ -54,7 +54,7 @@ const Modal: React.FC<React.PropsWithChildren<ModalProps>> & ModalTypes = ({
   children,
   visible,
   setVisible,
-  size = "md",
+  size = "sm",
 }) => {
   const onClick = (e: React.SyntheticEvent<HTMLDivElement>) => {
     if (e.currentTarget === e.target) {

+ 2 - 2
src/components/Modal/styles.tsx

@@ -22,9 +22,9 @@ export const ModalWrapper = styled.div`
   }
 `;
 
-export const ModalInnerWrapper = styled.div<{ size: "md" | "lg" }>`
+export const ModalInnerWrapper = styled.div<{ size: "sm" | "md" | "lg" }>`
   min-width: 440px;
-  max-width: ${({ size }) => (size === "md" ? "490px" : "90%")};
+  max-width: ${({ size }) => (size === "sm" ? "490px" : size === "md" ? "50%" : "90%")};
   width: fit-content;
   animation: ${appearAnimation} 220ms ease-in-out;
   line-height: 20px;

+ 1 - 1
src/components/Sidebar/index.tsx

@@ -256,7 +256,7 @@ export const Sidebar: React.FC = () => {
       </StyledTopWrapper>
       <StyledBottomWrapper>
         <Tooltip title="Account">
-          <StyledElement onClick={() => setVisible("login")(true)}>
+          <StyledElement onClick={() => setVisible("account")(true)}>
             <VscAccount />
           </StyledElement>
         </Tooltip>

+ 48 - 12
src/containers/Editor/BottomBar.tsx

@@ -1,13 +1,17 @@
 import React from "react";
 import { useRouter } from "next/router";
+import { useQuery } from "@tanstack/react-query";
+import toast from "react-hot-toast";
 import {
   AiOutlineCloudSync,
   AiOutlineCloudUpload,
   AiOutlineLink,
+  AiOutlineLock,
   AiOutlineUnlock,
 } from "react-icons/ai";
 import { VscAccount } from "react-icons/vsc";
-import { saveJson } from "src/services/db/json";
+import { altogic } from "src/services/altogic";
+import { getJson, saveJson, updateJson } from "src/services/db/json";
 import useConfig from "src/store/useConfig";
 import useGraph from "src/store/useGraph";
 import useModal from "src/store/useModal";
@@ -58,26 +62,58 @@ const StyledBottomBarItem = styled.button`
 `;
 
 export const BottomBar = () => {
-  const { replace, query } = useRouter();
+  const { isReady, replace, query } = useRouter();
+
+  const { data } = useQuery(
+    ["dbJson", query.json],
+    () => getJson(query.json as string),
+    {
+      enabled: isReady && !!query.json,
+    }
+  );
+
   const user = useUser(state => state.user);
   const setVisible = useModal(state => state.setVisible);
-  const getJson = useGraph(state => state.getJson);
+  const getJsonState = useGraph(state => state.getJson);
   const hasChanges = useConfig(state => state.hasChanges);
   const setConfig = useConfig(state => state.setConfig);
+  const [isPrivate, setIsPrivate] = React.useState(true);
+
+  React.useEffect(() => {
+    if (data) setIsPrivate(data.data.private);
+  }, [data]);
 
-  const handleSaveJson = async () => {
+  const handleSaveJson = React.useCallback(() => {
     if (hasChanges) {
-      await saveJson({ id: query.json, data: getJson() }).then(res => {
-        if (res.data._id) replace({ query: { json: res.data._id } });
-        setConfig("hasChanges", false);
-      });
+      toast.promise(
+        saveJson({ id: query.json, data: getJsonState() }).then(res => {
+          if (res.data._id) replace({ query: { json: res.data._id } });
+          setConfig("hasChanges", false);
+        }),
+        {
+          loading: "Saving JSON...",
+          success: "JSON saved to cloud",
+          error: "Failed to save JSON to cloud",
+        }
+      );
     }
+  }, [getJsonState, hasChanges, query.json, replace, setConfig]);
+
+  const handleLoginClick = () => {
+    if (user) return setVisible("account")(true);
+    altogic.auth.signInWithProvider("google");
+  };
+
+  const setPrivate = () => {
+    if (!query.json) return handleSaveJson();
+    updateJson(query.json as string, { private: !isPrivate });
+    setIsPrivate(!isPrivate);
   };
 
   return (
     <StyledBottomBar>
       <StyledLeft>
-        <StyledBottomBarItem onClick={() => setVisible("login")(true)}>
+        <StyledBottomBarItem onClick={handleLoginClick}>
           <VscAccount />
           {user ? user.name : "Login"}
         </StyledBottomBarItem>
@@ -85,9 +121,9 @@ export const BottomBar = () => {
           {hasChanges ? <AiOutlineCloudUpload /> : <AiOutlineCloudSync />}
           {hasChanges ? "Unsaved Changes" : "Saved"}
         </StyledBottomBarItem>
-        <StyledBottomBarItem>
-          <AiOutlineUnlock />
-          Public
+        <StyledBottomBarItem onClick={setPrivate}>
+          {isPrivate ? <AiOutlineLock /> : <AiOutlineUnlock />}
+          {isPrivate ? "Private" : "Public"}
         </StyledBottomBarItem>
         <StyledBottomBarItem onClick={() => setVisible("share")(true)}>
           <AiOutlineLink />

+ 1 - 1
src/containers/Editor/LiveEditor/GraphCanvas.tsx

@@ -20,7 +20,7 @@ export const GraphCanvas = ({ isWidget = false }: { isWidget?: boolean }) => {
     const hiddenItems = document.querySelectorAll(".hide");
     hiddenItems.forEach(item => item.classList.remove("hide"));
 
-    if (nodeList.length) {
+    if (nodeList.length > 1) {
       const selectedNodes = document.querySelectorAll(nodeList.join(","));
       const selectedEdges = document.querySelectorAll(edgeList.join(","));
 

+ 29 - 8
src/containers/Home/index.tsx

@@ -98,9 +98,10 @@ const FeaturesSection = () => (
       </Styles.StyledCardIcon>
       <Styles.StyledCardTitle>EASY-TO-USE</Styles.StyledCardTitle>
       <Styles.StyledCardDescription>
-        Don&apos;t even bother to update your schema to view your JSON into graphs;
-        directly paste, import or fetch! JSON Crack helps you to visualize without
-        any additional values and save your time.
+        We believe that powerful software doesn&apos;t have to be difficult to use.
+        That&apos;s why we&apos;ve designed our app to be as intuitive and
+        easy-to-use as possible. You can quickly and easily load your JSON data and
+        start exploring and analyzing it right away!
       </Styles.StyledCardDescription>
     </Styles.StyledSectionCard>
 
@@ -176,9 +177,20 @@ const GitHubSection = () => (
     <Styles.StyledSectionArea>
       <Styles.StyledSubTitle>Open Source Community</Styles.StyledSubTitle>
       <Styles.StyledMinorTitle>
-        Join the Open Source Community by suggesting new ideas, support by
-        contributing; implementing new features, fixing bugs and doing changes minor
-        to major!
+        At JSON Crack, we believe in the power of open source software and the
+        communities that support it. That&apos;s why we&apos;re proud to be part of
+        the open source community and to contribute to the development and growth of
+        open source tools and technologies.
+        <br />
+        <br /> As part of our commitment to the open source community, we&apos;ve
+        made our app freely available to anyone who wants to use it, and we welcome
+        contributions from anyone who&apos;s interested in helping to improve it.
+        Whether you&apos;re a developer, a data scientist, or just someone who&apos;s
+        passionate about open source, we&apos;d love to have you join our community
+        and help us make JSON Crack the best it can be.
+        <br />
+        <br /> So why not join us and become part of the JSON Crack open source
+        community today? We can&apos;t wait to see what we can accomplish together!
       </Styles.StyledMinorTitle>
       <Styles.StyledButton
         href="https://github.com/AykutSarac/jsoncrack.com"
@@ -196,8 +208,17 @@ const EmbedSection = () => (
     <Styles.StyledSectionArea>
       <Styles.StyledSubTitle>Embed Into Your Website</Styles.StyledSubTitle>
       <Styles.StyledMinorTitle>
-        Easily embed the JSON Crack graph into your website to showcase your
-        visitors, blog readers or anybody else!
+        JSON Crack provides users with the necessary code to embed the app into a
+        website easily using an iframe. This code can be easily copied and pasted
+        into the desired location on the website, allowing users to quickly and
+        easily integrate JSON Crack into existing workflows.
+        <br />
+        <br /> Once the app is embedded, users can use it to view and analyze JSON
+        data directly on the website. This can be useful for a variety of purposes,
+        such as quickly checking the structure of a JSON file or verifying the data
+        contained within it. JSON Crack&apos;s intuitive interface makes it easy to
+        navigate and understand even complex JSON data, making it a valuable tool for
+        anyone working with JSON.
       </Styles.StyledMinorTitle>
     </Styles.StyledSectionArea>
     <div>

+ 1 - 1
src/containers/Home/styles.tsx

@@ -146,7 +146,7 @@ export const StyledSubTitle = styled.h2`
 export const StyledMinorTitle = styled.p`
   color: #b4b4b4;
   text-align: center;
-  font-size: 1.25rem;
+  font-size: 1rem;
   margin: 0;
   letter-spacing: 1.2px;
 

+ 2 - 2
src/containers/ModalController/index.tsx

@@ -1,9 +1,9 @@
 import React from "react";
+import { AccountModal } from "src/containers/Modals/AccountModal";
 import { ClearModal } from "src/containers/Modals/ClearModal";
 import { CloudModal } from "src/containers/Modals/CloudModal";
 import { DownloadModal } from "src/containers/Modals/DownloadModal";
 import { ImportModal } from "src/containers/Modals/ImportModal";
-import { LoginModal } from "src/containers/Modals/LoginModal";
 import { SettingsModal } from "src/containers/Modals/SettingsModal";
 import { ShareModal } from "src/containers/Modals/ShareModal";
 import useModal from "src/store/useModal";
@@ -19,7 +19,7 @@ export const ModalController = () => {
       <DownloadModal visible={state.download} setVisible={setVisible("download")} />
       <SettingsModal visible={state.settings} setVisible={setVisible("settings")} />
       <CloudModal visible={state.cloud} setVisible={setVisible("cloud")} />
-      <LoginModal visible={state.login} setVisible={setVisible("login")} />
+      <AccountModal visible={state.account} setVisible={setVisible("account")} />
       <ShareModal visible={state.share} setVisible={setVisible("share")} />
     </>
   );

+ 4 - 47
src/containers/Modals/LoginModal/index.tsx → src/containers/Modals/AccountModal/index.tsx

@@ -1,6 +1,4 @@
 import React from "react";
-import { GoogleLogin } from "@react-oauth/google";
-import toast from "react-hot-toast";
 import { IoRocketSharp } from "react-icons/io5";
 import { Button } from "src/components/Button";
 import { Modal, ModalProps } from "src/components/Modal";
@@ -74,41 +72,6 @@ const StyledContainer = styled.div`
   }
 `;
 
-const LoginView: React.FC<Pick<ModalProps, "setVisible">> = ({ setVisible }) => {
-  const login = useUser(state => state.login);
-
-  return (
-    <>
-      <Modal.Header>Login to JSON Crack</Modal.Header>
-      <Modal.Content>
-        <StyledTitle>Join Now!</StyledTitle>
-        <StyledModalContent>
-          Login to JSON Crack to{" "}
-          <b>
-            save your JSON files, share links and directly embed into your websites
-          </b>{" "}
-          without JavaScript instantly!
-        </StyledModalContent>
-        <StyledLoginWrapper>
-          <GoogleLogin
-            auto_select
-            width="210"
-            onSuccess={credentialResponse => {
-              if (credentialResponse.credential) {
-                login(credentialResponse.credential);
-              }
-            }}
-            onError={() => {
-              toast.error("Unable to login to Google account!");
-            }}
-          />
-        </StyledLoginWrapper>
-      </Modal.Content>
-      <Modal.Controls setVisible={setVisible} />
-    </>
-  );
-};
-
 const AccountView: React.FC<Pick<ModalProps, "setVisible">> = ({ setVisible }) => {
   const user = useUser(state => state.user);
   const logout = useUser(state => state.logout);
@@ -117,12 +80,12 @@ const AccountView: React.FC<Pick<ModalProps, "setVisible">> = ({ setVisible }) =
     <>
       <Modal.Header>Account (FREE)</Modal.Header>
       <Modal.Content>
-        <StyledTitle>Hello, {user?.given_name}!</StyledTitle>
+        <StyledTitle>Hello, {user?.name}!</StyledTitle>
         <StyledAccountWrapper>
           <StyledAvatar
             width="80"
             height="80"
-            src={user?.picture}
+            src={user?.profilePicture}
             alt={user?.name}
           />
           <StyledContainer>
@@ -154,16 +117,10 @@ const AccountView: React.FC<Pick<ModalProps, "setVisible">> = ({ setVisible }) =
   );
 };
 
-export const LoginModal: React.FC<ModalProps> = ({ setVisible, visible }) => {
-  const isAuthenticated = useUser(state => state.isAuthenticated);
-
+export const AccountModal: React.FC<ModalProps> = ({ setVisible, visible }) => {
   return (
     <Modal visible={visible} setVisible={setVisible}>
-      {isAuthenticated ? (
-        <AccountView setVisible={setVisible} />
-      ) : (
-        <LoginView setVisible={setVisible} />
-      )}
+      <AccountView setVisible={setVisible} />
     </Modal>
   );
 };

+ 24 - 260
src/containers/Modals/CloudModal/index.tsx

@@ -1,13 +1,18 @@
 import React from "react";
+import { useQuery } from "@tanstack/react-query";
+import dayjs from "dayjs";
+import relativeTime from "dayjs/plugin/relativeTime";
 import { AiOutlinePlus } from "react-icons/ai";
 import { Modal, ModalProps } from "src/components/Modal";
+import { getAllJson } from "src/services/db/json";
 import styled from "styled-components";
 
+dayjs.extend(relativeTime);
+
 const StyledModalContent = styled.div`
   display: flex;
   flex-wrap: wrap;
   gap: 14px;
-  height: 70vh;
   overflow: auto;
 `;
 
@@ -55,9 +60,10 @@ interface GraphCardProsp {
 }
 
 const GraphCard: React.FC<{ data: GraphCardProsp }> = ({
-  data: { id = "#", details, preview, title },
+  data: { id = "", details, preview, title },
+  ...props
 }) => (
-  <StyledJsonCard href={`?id=${id}`}>
+  <StyledJsonCard href={`?json=${id}`} {...props}>
     <StyledImg width="200" height="100" src={preview}></StyledImg>
     <StyledInfo>
       <StyledTitle>{title}</StyledTitle>
@@ -84,268 +90,26 @@ const CreateCard: React.FC = () => (
 );
 
 export const CloudModal: React.FC<ModalProps> = ({ visible, setVisible }) => {
+  const { data, isLoading } = useQuery(["allJson"], () => getAllJson());
+
+  if (isLoading) return <div>loading</div>;
   return (
     <StyledModal size="lg" visible={visible} setVisible={setVisible}>
       <Modal.Header>Saved On The Cloud</Modal.Header>
       <Modal.Content>
         <StyledModalContent>
-          <GraphCard
-            data={{
-              id: "ffuds9fds97",
-              title: "Virtual Metric DB",
-              details: "3 days ago",
-              preview:
-                "https://blog.shevarezo.fr/uploads/posts/bulk/FNj3yQLp_visualiser-donnees-json-diagramme-json-crack_rotate3.png",
-            }}
-          />
-          <GraphCard
-            data={{
-              title: "Virtual Metric DB",
-              details: "3 days ago",
-              preview:
-                "https://blog.shevarezo.fr/uploads/posts/bulk/FNj3yQLp_visualiser-donnees-json-diagramme-json-crack_rotate3.png",
-            }}
-          />
-          <GraphCard
-            data={{
-              title: "Virtual Metric DB",
-              details: "3 days ago",
-              preview:
-                "https://blog.shevarezo.fr/uploads/posts/bulk/FNj3yQLp_visualiser-donnees-json-diagramme-json-crack_rotate3.png",
-            }}
-          />
-          <GraphCard
-            data={{
-              title: "Virtual Metric DB",
-              details: "3 days ago",
-              preview:
-                "https://blog.shevarezo.fr/uploads/posts/bulk/FNj3yQLp_visualiser-donnees-json-diagramme-json-crack_rotate3.png",
-            }}
-          />
-          <GraphCard
-            data={{
-              title: "Virtual Metric DB",
-              details: "3 days ago",
-              preview:
-                "https://blog.shevarezo.fr/uploads/posts/bulk/FNj3yQLp_visualiser-donnees-json-diagramme-json-crack_rotate3.png",
-            }}
-          />
-          <GraphCard
-            data={{
-              title: "Virtual Metric DB",
-              details: "3 days ago",
-              preview:
-                "https://blog.shevarezo.fr/uploads/posts/bulk/FNj3yQLp_visualiser-donnees-json-diagramme-json-crack_rotate3.png",
-            }}
-          />
-          <GraphCard
-            data={{
-              title: "Virtual Metric DB",
-              details: "3 days ago",
-              preview:
-                "https://blog.shevarezo.fr/uploads/posts/bulk/FNj3yQLp_visualiser-donnees-json-diagramme-json-crack_rotate3.png",
-            }}
-          />
-          <GraphCard
-            data={{
-              title: "Virtual Metric DB",
-              details: "3 days ago",
-              preview:
-                "https://blog.shevarezo.fr/uploads/posts/bulk/FNj3yQLp_visualiser-donnees-json-diagramme-json-crack_rotate3.png",
-            }}
-          />
-          <GraphCard
-            data={{
-              title: "Virtual Metric DB",
-              details: "3 days ago",
-              preview:
-                "https://blog.shevarezo.fr/uploads/posts/bulk/FNj3yQLp_visualiser-donnees-json-diagramme-json-crack_rotate3.png",
-            }}
-          />
-          <GraphCard
-            data={{
-              title: "Virtual Metric DB",
-              details: "3 days ago",
-              preview:
-                "https://blog.shevarezo.fr/uploads/posts/bulk/FNj3yQLp_visualiser-donnees-json-diagramme-json-crack_rotate3.png",
-            }}
-          />
-          <GraphCard
-            data={{
-              title: "Virtual Metric DB",
-              details: "3 days ago",
-              preview:
-                "https://blog.shevarezo.fr/uploads/posts/bulk/FNj3yQLp_visualiser-donnees-json-diagramme-json-crack_rotate3.png",
-            }}
-          />
-          <GraphCard
-            data={{
-              title: "Virtual Metric DB",
-              details: "3 days ago",
-              preview:
-                "https://blog.shevarezo.fr/uploads/posts/bulk/FNj3yQLp_visualiser-donnees-json-diagramme-json-crack_rotate3.png",
-            }}
-          />
-          <GraphCard
-            data={{
-              title: "Virtual Metric DB",
-              details: "3 days ago",
-              preview:
-                "https://blog.shevarezo.fr/uploads/posts/bulk/FNj3yQLp_visualiser-donnees-json-diagramme-json-crack_rotate3.png",
-            }}
-          />
-          <GraphCard
-            data={{
-              title: "Virtual Metric DB",
-              details: "3 days ago",
-              preview:
-                "https://blog.shevarezo.fr/uploads/posts/bulk/FNj3yQLp_visualiser-donnees-json-diagramme-json-crack_rotate3.png",
-            }}
-          />
-          <GraphCard
-            data={{
-              title: "Virtual Metric DB",
-              details: "3 days ago",
-              preview:
-                "https://blog.shevarezo.fr/uploads/posts/bulk/FNj3yQLp_visualiser-donnees-json-diagramme-json-crack_rotate3.png",
-            }}
-          />
-          <GraphCard
-            data={{
-              title: "Virtual Metric DB",
-              details: "3 days ago",
-              preview:
-                "https://blog.shevarezo.fr/uploads/posts/bulk/FNj3yQLp_visualiser-donnees-json-diagramme-json-crack_rotate3.png",
-            }}
-          />
-          <GraphCard
-            data={{
-              title: "Virtual Metric DB",
-              details: "3 days ago",
-              preview:
-                "https://blog.shevarezo.fr/uploads/posts/bulk/FNj3yQLp_visualiser-donnees-json-diagramme-json-crack_rotate3.png",
-            }}
-          />
-          <GraphCard
-            data={{
-              title: "Virtual Metric DB",
-              details: "3 days ago",
-              preview:
-                "https://blog.shevarezo.fr/uploads/posts/bulk/FNj3yQLp_visualiser-donnees-json-diagramme-json-crack_rotate3.png",
-            }}
-          />
-          <GraphCard
-            data={{
-              title: "Virtual Metric DB",
-              details: "3 days ago",
-              preview:
-                "https://blog.shevarezo.fr/uploads/posts/bulk/FNj3yQLp_visualiser-donnees-json-diagramme-json-crack_rotate3.png",
-            }}
-          />
-          <GraphCard
-            data={{
-              title: "Virtual Metric DB",
-              details: "3 days ago",
-              preview:
-                "https://blog.shevarezo.fr/uploads/posts/bulk/FNj3yQLp_visualiser-donnees-json-diagramme-json-crack_rotate3.png",
-            }}
-          />
-          <GraphCard
-            data={{
-              title: "Virtual Metric DB",
-              details: "3 days ago",
-              preview:
-                "https://blog.shevarezo.fr/uploads/posts/bulk/FNj3yQLp_visualiser-donnees-json-diagramme-json-crack_rotate3.png",
-            }}
-          />
-          <GraphCard
-            data={{
-              title: "Virtual Metric DB",
-              details: "3 days ago",
-              preview:
-                "https://blog.shevarezo.fr/uploads/posts/bulk/FNj3yQLp_visualiser-donnees-json-diagramme-json-crack_rotate3.png",
-            }}
-          />
-          <GraphCard
-            data={{
-              title: "Virtual Metric DB",
-              details: "3 days ago",
-              preview:
-                "https://blog.shevarezo.fr/uploads/posts/bulk/FNj3yQLp_visualiser-donnees-json-diagramme-json-crack_rotate3.png",
-            }}
-          />
-          <GraphCard
-            data={{
-              title: "Virtual Metric DB",
-              details: "3 days ago",
-              preview:
-                "https://blog.shevarezo.fr/uploads/posts/bulk/FNj3yQLp_visualiser-donnees-json-diagramme-json-crack_rotate3.png",
-            }}
-          />
-          <GraphCard
-            data={{
-              title: "Virtual Metric DB",
-              details: "3 days ago",
-              preview:
-                "https://blog.shevarezo.fr/uploads/posts/bulk/FNj3yQLp_visualiser-donnees-json-diagramme-json-crack_rotate3.png",
-            }}
-          />
-          <GraphCard
-            data={{
-              title: "Virtual Metric DB",
-              details: "3 days ago",
-              preview:
-                "https://blog.shevarezo.fr/uploads/posts/bulk/FNj3yQLp_visualiser-donnees-json-diagramme-json-crack_rotate3.png",
-            }}
-          />
-          <GraphCard
-            data={{
-              title: "Virtual Metric DB",
-              details: "3 days ago",
-              preview:
-                "https://blog.shevarezo.fr/uploads/posts/bulk/FNj3yQLp_visualiser-donnees-json-diagramme-json-crack_rotate3.png",
-            }}
-          />
-          <GraphCard
-            data={{
-              title: "Virtual Metric DB",
-              details: "3 days ago",
-              preview:
-                "https://blog.shevarezo.fr/uploads/posts/bulk/FNj3yQLp_visualiser-donnees-json-diagramme-json-crack_rotate3.png",
-            }}
-          />
-          <GraphCard
-            data={{
-              title: "Virtual Metric DB",
-              details: "3 days ago",
-              preview:
-                "https://blog.shevarezo.fr/uploads/posts/bulk/FNj3yQLp_visualiser-donnees-json-diagramme-json-crack_rotate3.png",
-            }}
-          />
-          <GraphCard
-            data={{
-              title: "Virtual Metric DB",
-              details: "3 days ago",
-              preview:
-                "https://blog.shevarezo.fr/uploads/posts/bulk/FNj3yQLp_visualiser-donnees-json-diagramme-json-crack_rotate3.png",
-            }}
-          />
-          <GraphCard
-            data={{
-              title: "Virtual Metric DB",
-              details: "3 days ago",
-              preview:
-                "https://blog.shevarezo.fr/uploads/posts/bulk/FNj3yQLp_visualiser-donnees-json-diagramme-json-crack_rotate3.png",
-            }}
-          />
-          <GraphCard
-            data={{
-              title: "Virtual Metric DB",
-              details: "3 days ago",
-              preview:
-                "https://blog.shevarezo.fr/uploads/posts/bulk/FNj3yQLp_visualiser-donnees-json-diagramme-json-crack_rotate3.png",
-            }}
-          />
+          {data?.data?.result?.map(json => (
+            <GraphCard
+              data={{
+                id: json._id,
+                title: json.name,
+                details: `Last modified ${dayjs(json.updatedAt).fromNow()}`,
+                preview:
+                  "https://blog.shevarezo.fr/uploads/posts/bulk/FNj3yQLp_visualiser-donnees-json-diagramme-json-crack_rotate3.png",
+              }}
+              key={json.id}
+            />
+          ))}
           <CreateCard />
         </StyledModalContent>
       </Modal.Content>

+ 28 - 4
src/containers/Modals/GoalsModal/index.tsx

@@ -36,13 +36,37 @@ export const GoalsModal = ({ visible, setVisible }) => {
   const { push } = useRouter();
 
   return (
-    <Modal visible={visible} setVisible={setVisible}>
+    <Modal visible={visible} setVisible={setVisible} size="md">
       <Modal.Header>Help JSON Crack&apos;s Goals</Modal.Header>
       <Modal.Content>
         <StyledTitle>OUR GOAL</StyledTitle>
-        <b>JSON Crack&apos;s Goal</b> is to keep the service completely free and open
-        source for everyone! For the contiunity of our service and keep the new
-        updates coming we need your support to make that possible ❤️
+        Dear user,
+        <br />
+        <br />
+        We are the team behind JSON Crack, an open-source JSON visualization app that
+        helps users better understand and work with complex JSON data. We are
+        passionate about making JSON data more accessible and user-friendly, and we
+        believe our app has the potential to make a real difference for developers
+        and data analysts alike.
+        <br />
+        <br />
+        However, developing and maintaining JSON Crack takes time and resources, and
+        we would love your support in helping us continue to improve the app and make
+        it even more useful for our users. As a sponsor, your support would help us
+        to continue to develop and maintain JSON Crack, and would allow us to add new
+        features and capabilities to the app.
+        <br />
+        <br />
+        We believe that JSON Crack has the potential to make a real difference for
+        developers and data analysts, and we would be grateful for your support in
+        helping us continue to improve the app. Thank you for considering sponsoring
+        JSON Crack.
+        <br />
+        <br />
+        Sincerely,
+        <br />
+        <br />
+        The JSON Crack team
         <ButtonsWrapper>
           <Button
             href="https://github.com/sponsors/AykutSarac"

+ 22 - 5
src/pages/Editor/index.tsx

@@ -1,13 +1,15 @@
 import React from "react";
 import Head from "next/head";
 import { useRouter } from "next/router";
+import { useQuery } from "@tanstack/react-query";
+import { decompressFromBase64 } from "lz-string";
 import { Sidebar } from "src/components/Sidebar";
 import { defaultJson } from "src/constants/data";
 import { BottomBar } from "src/containers/Editor/BottomBar";
 import Panes from "src/containers/Editor/Panes";
 import { getJson } from "src/services/db/json";
-import useConfig from "src/store/useConfig";
 import useGraph from "src/store/useGraph";
+import useUser from "src/store/useUser";
 import styled from "styled-components";
 
 export const StyledPageWrapper = styled.div`
@@ -31,16 +33,31 @@ export const StyledEditorWrapper = styled.div`
 
 const EditorPage: React.FC = () => {
   const { query, isReady } = useRouter();
+  const checkSession = useUser(state => state.checkSession);
+  const { data, isLoading } = useQuery(
+    ["dbJson", query.json],
+    () => getJson(query.json as string),
+    {
+      enabled: isReady && !!query.json,
+    }
+  );
+
   const setJson = useGraph(state => state.setJson);
-  const setConfig = useConfig(state => state.setConfig);
+  const setLoading = useGraph(state => state.setLoading);
+
+  React.useEffect(() => {
+    checkSession();
+  }, [checkSession]);
 
   React.useEffect(() => {
     if (isReady) {
-      if (typeof query.json === "string") {
-        getJson(query.json).then(res => res && setJson(res));
+      if (query.json) {
+        if (isLoading) return setLoading(true);
+        if (data?.data) setJson(decompressFromBase64(data.data.json) as string);
+        setLoading(false);
       } else setJson(defaultJson);
     }
-  }, [isReady, query.json, setConfig, setJson]);
+  }, [data, isLoading, isReady, query.json, setJson, setLoading]);
 
   return (
     <StyledEditorWrapper>

+ 1 - 1
src/pages/Widget/index.tsx

@@ -77,7 +77,7 @@ const WidgetPage = () => {
     const hiddenItems = document.querySelectorAll(".hide");
     hiddenItems.forEach(item => item.classList.remove("hide"));
 
-    if (nodeList.length) {
+    if (nodeList.length > 1) {
       const selectedNodes = document.querySelectorAll(nodeList.join(","));
       const selectedEdges = document.querySelectorAll(edgeList.join(","));
 

+ 15 - 11
src/pages/_app.tsx

@@ -1,14 +1,13 @@
 import React from "react";
 import type { AppProps } from "next/app";
-import { GoogleOAuthProvider } from "@react-oauth/google";
 import { init } from "@sentry/nextjs";
+import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
 import { Toaster } from "react-hot-toast";
 import { GoogleAnalytics } from "src/components/GoogleAnalytics";
 import GlobalStyle from "src/constants/globalStyle";
 import { darkTheme, lightTheme } from "src/constants/theme";
 import { ModalController } from "src/containers/ModalController";
 import useStored from "src/store/useStored";
-import useUser from "src/store/useUser";
 import { ThemeProvider } from "styled-components";
 
 if (process.env.NODE_ENV !== "development") {
@@ -18,21 +17,26 @@ if (process.env.NODE_ENV !== "development") {
   });
 }
 
+const queryClient = new QueryClient({
+  defaultOptions: {
+    queries: {
+      refetchOnWindowFocus: false,
+      retry: false,
+    },
+  },
+});
+
 function JsonCrack({ Component, pageProps }: AppProps) {
+  const [isReady, setReady] = React.useState(false);
   const lightmode = useStored(state => state.lightmode);
-  const [isRendered, setRendered] = React.useState(false);
-  const checkSession = useUser(state => state.checkSession);
 
   React.useEffect(() => {
-    setRendered(true);
+    setReady(true);
   }, []);
 
-  if (isRendered)
+  if (isReady)
     return (
-      <GoogleOAuthProvider
-        onScriptLoadSuccess={() => checkSession()}
-        clientId="34440253867-g7s6qhtaqe7lumj1vutctm49t8dhvcf9.apps.googleusercontent.com"
-      >
+      <QueryClientProvider client={queryClient}>
         <GoogleAnalytics />
         <ThemeProvider theme={lightmode ? lightTheme : darkTheme}>
           <GlobalStyle />
@@ -53,7 +57,7 @@ function JsonCrack({ Component, pageProps }: AppProps) {
           />
           <ModalController />
         </ThemeProvider>
-      </GoogleOAuthProvider>
+      </QueryClientProvider>
     );
 }
 

+ 14 - 0
src/services/altogic.ts

@@ -0,0 +1,14 @@
+import { createClient } from "altogic";
+
+let envUrl = process.env.NEXT_ALTOGIC_ENV_URL as string;
+let clientKey = process.env.NEXT_ALTOGIC_CLIENT_KEY as string;
+
+const altogic = createClient(
+  "https://815e-4e5a.c5-na.altogic.com/",
+  "f1e92022789f4ccf91273a345ab2bdf8",
+  {
+    signInRedirect: "/signin",
+  }
+);
+
+export { altogic };

+ 27 - 12
src/services/db/json.tsx

@@ -1,23 +1,38 @@
-import { compressToBase64, decompressFromBase64 } from "lz-string";
-import { HttpClient } from "src/api/interceptor";
+import { compressToBase64 } from "lz-string";
+import { altogic } from "../altogic";
 
-const saveJson = async ({ id, data }) => {
+type JSON = {
+  _id: string;
+  createdAt: Date;
+  updatedAt: Date;
+  name: string;
+  private: boolean;
+  json: string;
+};
+
+const saveJson = async ({ id, data }): Promise<{ data: { _id: string } }> => {
   const compressedData = compressToBase64(data);
 
   if (id) {
-    return await HttpClient.put<{ _id: string }>(`json/${id}`, {
-      data: compressedData,
+    return await altogic.endpoint.put(`json/${id}`, {
+      json: compressedData,
     });
   }
 
-  return await HttpClient.post<{ _id: string }>("json", {
-    data: compressedData,
+  return await altogic.endpoint.post("json", {
+    json: compressedData,
   });
 };
 
-const getJson = async (id: string) =>
-  await HttpClient.get(`json/${id}`).then(res =>
-    decompressFromBase64(res.data.data)
-  );
+const getAllJson = async (): Promise<{ data: JSON[] }> =>
+  await altogic.endpoint.get(`json`);
+
+const getJson = async (id: string): Promise<{ data: JSON }> =>
+  await altogic.endpoint.get(`json/${id}`);
+
+const updateJson = async (id: string, data: object) =>
+  await altogic.endpoint.put(`json/${id}`, {
+    ...data,
+  });
 
-export { saveJson, getJson };
+export { saveJson, getJson, getAllJson, updateJson };

+ 2 - 2
src/store/useModal.tsx

@@ -11,7 +11,7 @@ const initialStates = {
   download: false,
   goals: false,
   import: false,
-  login: false,
+  account: false,
   node: false,
   settings: false,
   share: false,
@@ -27,7 +27,7 @@ const useModal = create<ModalStates & ModalActions>()(set => ({
   ...initialStates,
   setVisible: modal => visible => {
     if (authModals.includes(modal) && !useUser.getState().isAuthenticated) {
-      return set({ login: true });
+      return set({ account: true });
     }
 
     set({ [modal]: visible });

+ 22 - 33
src/store/useUser.tsx

@@ -1,27 +1,20 @@
-import jwt_decode from "jwt-decode";
 import toast from "react-hot-toast";
-import { validateToken } from "src/services/google";
+import { altogic } from "src/services/altogic";
+import { AltogicAuth } from "src/typings/altogic";
 import create from "zustand";
 
-interface GoogleUser {
-  iss: string;
-  nbf: number;
-  aud: string;
-  sub: string;
+type User = {
+  _id: string;
+  provider: string;
+  providerUserId: string;
   email: string;
-  email_verified: boolean;
-  azp: string;
   name: string;
-  picture: string;
-  given_name: string;
-  family_name: string;
-  iat: number;
-  exp: number;
-  jti: string;
-}
-
+  profilePicture: string;
+  signUpAt: Date;
+  lastLoginAt: Date;
+};
 interface UserActions {
-  login: (credential: string) => void;
+  login: (response: AltogicAuth) => void;
   logout: () => void;
   setUser: (key: keyof typeof initialStates, value: any) => void;
   checkSession: () => void;
@@ -29,7 +22,7 @@ interface UserActions {
 
 const initialStates = {
   isAuthenticated: false,
-  user: null as GoogleUser | null,
+  user: null as User | null,
 };
 
 export type UserStates = typeof initialStates;
@@ -39,24 +32,20 @@ const useUser = create<UserStates & UserActions>()(set => ({
   setUser: (key, value) => set({ [key]: value }),
   logout: () => {
     localStorage.removeItem("auth_token");
-    set(initialStates);
     toast.success("Logged out.");
+    set(initialStates);
   },
-  login: credential => {
-    const googleUser = jwt_decode<GoogleUser>(credential);
-    localStorage.setItem("auth_token", credential);
-    set({ user: googleUser, isAuthenticated: true });
+  login: response => {
+    // localStorage.setItem("auth_token", response.session.token);
+    set({ user: response.user, isAuthenticated: true });
   },
-  checkSession: async () => {
-    const token = localStorage.getItem("auth_token");
+  checkSession: () => {
+    const currentSession = altogic.auth.getSession();
+    const currentUser = altogic.auth.getUser();
 
-    if (token) {
-      try {
-        const { data: user } = await validateToken<GoogleUser>(token);
-        set({ user, isAuthenticated: true });
-      } catch (error) {
-        set({ isAuthenticated: false });
-      }
+    if (currentSession) {
+      altogic.auth.setSession(currentSession);
+      set({ user: currentUser as any, isAuthenticated: true });
     }
   },
 }));

+ 46 - 0
src/typings/altogic.ts

@@ -0,0 +1,46 @@
+interface User {
+  _id: string;
+  provider: string;
+  providerUserId: string;
+  email: string;
+  name: string;
+  profilePicture: string;
+  signUpAt: Date;
+  lastLoginAt: Date;
+}
+
+interface Device {
+  family: string;
+  major: string;
+  minor: string;
+  patch: string;
+}
+
+interface Os {
+  family: string;
+  major: string;
+  minor: string;
+  patch: string;
+}
+
+interface UserAgent {
+  family: string;
+  major: string;
+  minor: string;
+  patch: string;
+  device: Device;
+  os: Os;
+}
+
+interface Session {
+  userId: string;
+  token: string;
+  creationDtm: Date;
+  userAgent: UserAgent;
+  accessGroupKeys: any[];
+}
+
+export interface AltogicAuth {
+  user: User;
+  session: Session;
+}

+ 85 - 8
yarn.lock

@@ -1468,6 +1468,11 @@
   dependencies:
     "@sentry/cli" "^1.74.4"
 
+"@socket.io/component-emitter@~3.1.0":
+  version "3.1.0"
+  resolved "https://registry.yarnpkg.com/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz#96116f2a912e0c02817345b3c10751069920d553"
+  integrity sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg==
+
 "@surma/rollup-plugin-off-main-thread@^2.2.3":
   version "2.2.3"
   resolved "https://registry.yarnpkg.com/@surma/rollup-plugin-off-main-thread/-/rollup-plugin-off-main-thread-2.2.3.tgz#ee34985952ca21558ab0d952f00298ad2190c053"
@@ -1485,6 +1490,19 @@
   dependencies:
     tslib "^2.4.0"
 
+"@tanstack/[email protected]":
+  version "4.19.1"
+  resolved "https://registry.yarnpkg.com/@tanstack/query-core/-/query-core-4.19.1.tgz#2e92d9e8a50884eb231c5beb4386e131ebe34306"
+  integrity sha512-Zp0aIose5C8skBzqbVFGk9HJsPtUhRVDVNWIqVzFbGQQgYSeLZMd3Sdb4+EnA5wl1J7X+bre2PJGnQg9x/zHOA==
+
+"@tanstack/react-query@^4.19.1":
+  version "4.19.1"
+  resolved "https://registry.yarnpkg.com/@tanstack/react-query/-/react-query-4.19.1.tgz#43356dd537127e76d75f5a2769eb23dafd9a3690"
+  integrity sha512-5dvHvmc0vrWI03AJugzvKfirxCyCLe+qawrWFCXdu8t7dklIhJ7D5ZhgTypv7mMtIpdHPcECtCiT/+V74wCn2A==
+  dependencies:
+    "@tanstack/query-core" "4.19.1"
+    use-sync-external-store "^1.2.0"
+
 "@testing-library/dom@^8.5.0":
   version "8.18.1"
   resolved "https://registry.yarnpkg.com/@testing-library/dom/-/dom-8.18.1.tgz#80f91be02bc171fe5a3a7003f88207be31ac2cf3"
@@ -1781,6 +1799,14 @@ allotment@^1.17.0:
     lodash.isequal "^4.5.0"
     use-resize-observer "^9.0.0"
 
+altogic@^2.3.8:
+  version "2.3.8"
+  resolved "https://registry.yarnpkg.com/altogic/-/altogic-2.3.8.tgz#475ca083faec97660d30b838bbddaeeb83da9072"
+  integrity sha512-KTRSPH/g490sJiIe0qfuJaMsidGFkYSAnPR93Hn9VQ9GyZ+0/KmMIPSV6ctRaCvk/fw06w56IgYQNZDf6VJyxg==
+  dependencies:
+    cross-fetch "^3.1.4"
+    socket.io-client "^4.5.1"
+
 ansi-regex@^2.0.0:
   version "2.1.1"
   resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df"
@@ -2217,6 +2243,13 @@ create-require@^1.1.0:
   resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333"
   integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==
 
+cross-fetch@^3.1.4:
+  version "3.1.5"
+  resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-3.1.5.tgz#e1389f44d9e7ba767907f7af8454787952ab534f"
+  integrity sha512-lvb1SBsI0Z7GDwmuid+mU3kWVBwTVUbe7S0H52yaaAdQOXq2YktTCZdlAcNKFzE6QtRz0snpw9bNiPeOIkkQvw==
+  dependencies:
+    node-fetch "2.6.7"
+
 cross-spawn@^7.0.2:
   version "7.0.3"
   resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6"
@@ -2267,7 +2300,12 @@ damerau-levenshtein@^1.0.8:
   resolved "https://registry.yarnpkg.com/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz#b43d286ccbd36bc5b2f7ed41caf2d0aba1f8a6e7"
   integrity sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==
 
-debug@4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.2, debug@^4.3.4:
+dayjs@^1.11.6:
+  version "1.11.6"
+  resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.6.tgz#2e79a226314ec3ec904e3ee1dd5a4f5e5b1c7afb"
+  integrity sha512-zZbY5giJAinCG+7AGaw0wIhNZ6J8AhWuSXKvuc1KAyMiRsvGQWqh4L+MomvhdAYjN+lqvVCMq1I41e3YHvXkyQ==
+
+debug@4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.2, debug@^4.3.4, debug@~4.3.1, debug@~4.3.2:
   version "4.3.4"
   resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865"
   integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==
@@ -2435,6 +2473,22 @@ emojis-list@^3.0.0:
   resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-3.0.0.tgz#5570662046ad29e2e916e71aae260abdff4f6a78"
   integrity sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==
 
+engine.io-client@~6.2.3:
+  version "6.2.3"
+  resolved "https://registry.yarnpkg.com/engine.io-client/-/engine.io-client-6.2.3.tgz#a8cbdab003162529db85e9de31575097f6d29458"
+  integrity sha512-aXPtgF1JS3RuuKcpSrBtimSjYvrbhKW9froICH4s0F3XQWLxsKNxqzG39nnvQZQnva4CMvUK63T7shevxRyYHw==
+  dependencies:
+    "@socket.io/component-emitter" "~3.1.0"
+    debug "~4.3.1"
+    engine.io-parser "~5.0.3"
+    ws "~8.2.3"
+    xmlhttprequest-ssl "~2.0.0"
+
+engine.io-parser@~5.0.3:
+  version "5.0.4"
+  resolved "https://registry.yarnpkg.com/engine.io-parser/-/engine.io-parser-5.0.4.tgz#0b13f704fa9271b3ec4f33112410d8f3f41d0fc0"
+  integrity sha512-+nVFp+5z1E3HcToEnO7ZIj3g+3k9389DvWtvJZz0T6/eOCPIyyxehFcedoYrZQrp0LgQbD9pPXhpMBKMd5QURg==
+
 enhanced-resolve@^5.10.0:
   version "5.10.0"
   resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.10.0.tgz#0dc579c3bb2a1032e357ac45b8f3a6f3ad4fb1e6"
@@ -3491,11 +3545,6 @@ jsonpointer@^5.0.0:
     array-includes "^3.1.5"
     object.assign "^4.1.3"
 
-jwt-decode@^3.1.2:
-  version "3.1.2"
-  resolved "https://registry.yarnpkg.com/jwt-decode/-/jwt-decode-3.1.2.tgz#3fb319f3675a2df0c2895c8f5e9fa4b67b04ed59"
-  integrity sha512-UfpWE/VZn0iP50d8cz9NrZLM9lSWhcJ+0Gt/nm4by88UL+J1SiKN8/5dkjMmbEzwL2CAe+67GsegCbIKtbp75A==
-
 kld-affine@^2.1.1:
   version "2.1.1"
   resolved "https://registry.yarnpkg.com/kld-affine/-/kld-affine-2.1.1.tgz#e28296585f305230eaad3b1e346d3ac5ca4c72b3"
@@ -3811,7 +3860,7 @@ next@^12.3.1:
     "@next/swc-win32-ia32-msvc" "12.3.1"
     "@next/swc-win32-x64-msvc" "12.3.1"
 
-node-fetch@^2.6.7:
+[email protected], node-fetch@^2.6.7:
   version "2.6.7"
   resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad"
   integrity sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==
@@ -4547,6 +4596,24 @@ slash@^3.0.0:
   resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634"
   integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==
 
+socket.io-client@^4.5.1:
+  version "4.5.4"
+  resolved "https://registry.yarnpkg.com/socket.io-client/-/socket.io-client-4.5.4.tgz#d3cde8a06a6250041ba7390f08d2468ccebc5ac9"
+  integrity sha512-ZpKteoA06RzkD32IbqILZ+Cnst4xewU7ZYK12aS1mzHftFFjpoMz69IuhP/nL25pJfao/amoPI527KnuhFm01g==
+  dependencies:
+    "@socket.io/component-emitter" "~3.1.0"
+    debug "~4.3.2"
+    engine.io-client "~6.2.3"
+    socket.io-parser "~4.2.1"
+
+socket.io-parser@~4.2.1:
+  version "4.2.1"
+  resolved "https://registry.yarnpkg.com/socket.io-parser/-/socket.io-parser-4.2.1.tgz#01c96efa11ded938dcb21cbe590c26af5eff65e5"
+  integrity sha512-V4GrkLy+HeF1F/en3SpUaM+7XxYXpuMUWLGde1kSSh5nQMN4hLrbPIkD+otwh6q9R6NOQBN4AMaOZ2zVjui82g==
+  dependencies:
+    "@socket.io/component-emitter" "~3.1.0"
+    debug "~4.3.1"
+
 source-list-map@^2.0.0:
   version "2.0.1"
   resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-2.0.1.tgz#3993bd873bfc48479cca9ea3a547835c7c154b34"
@@ -5031,7 +5098,7 @@ use-resize-observer@^9.0.0:
   dependencies:
     "@juggle/resize-observer" "^3.3.1"
 
[email protected]:
[email protected], use-sync-external-store@^1.2.0:
   version "1.2.0"
   resolved "https://registry.yarnpkg.com/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz#7dbefd6ef3fe4e767a0cf5d7287aacfb5846928a"
   integrity sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==
@@ -5285,6 +5352,16 @@ wrappy@1:
   resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
   integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==
 
+ws@~8.2.3:
+  version "8.2.3"
+  resolved "https://registry.yarnpkg.com/ws/-/ws-8.2.3.tgz#63a56456db1b04367d0b721a0b80cae6d8becbba"
+  integrity sha512-wBuoj1BDpC6ZQ1B7DWQBYVLphPWkm8i9Y0/3YdHjHKHiohOJ1ws+3OccDWtH+PoC9DZD5WOTrJvNbWvjS6JWaA==
+
+xmlhttprequest-ssl@~2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.0.0.tgz#91360c86b914e67f44dce769180027c0da618c67"
+  integrity sha512-QKxVRxiRACQcVuQEYFsI1hhkrMlrXHPegbbd1yn9UHOmRxY+si12nQYzri3vbzt8VdTTRviqcKxcyllFas5z2A==
+
 yallist@^4.0.0:
   version "4.0.0"
   resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72"