瀏覽代碼

refactor codebase

AykutSarac 2 年之前
父節點
當前提交
6eda021cbd

二進制
public/assets/bunny.png


+ 1 - 2
src/components/ErrorContainer/index.tsx

@@ -12,14 +12,13 @@ const StyledErrorExpand = styled.div<{ error: boolean }>`
   display: flex;
   display: flex;
   width: 100%;
   width: 100%;
   padding: 4px 16px;
   padding: 4px 16px;
-  height: 28px;
+  height: 36px;
   border-radius: 0;
   border-radius: 0;
   justify-content: space-between;
   justify-content: space-between;
   align-items: center;
   align-items: center;
   color: ${({ theme, error }) => (error ? theme.TEXT_DANGER : theme.TEXT_POSITIVE)};
   color: ${({ theme, error }) => (error ? theme.TEXT_DANGER : theme.TEXT_POSITIVE)};
   pointer-events: ${({ error }) => !error && "none"};
   pointer-events: ${({ error }) => !error && "none"};
   background: ${({ theme }) => theme.BACKGROUND_SECONDARY};
   background: ${({ theme }) => theme.BACKGROUND_SECONDARY};
-  box-shadow: 0 1px 0px ${({ theme }) => theme.BACKGROUND_TERTIARY};
 `;
 `;
 
 
 const StyledTitle = styled.span`
 const StyledTitle = styled.span`

+ 2 - 3
src/components/Graph/index.tsx

@@ -4,7 +4,6 @@ import { Canvas, Edge, EdgeProps, ElkRoot, NodeProps } from "reaflow";
 import { CustomNode } from "src/components/CustomNode";
 import { CustomNode } from "src/components/CustomNode";
 import useGraph from "src/store/useGraph";
 import useGraph from "src/store/useGraph";
 import useUser from "src/store/useUser";
 import useUser from "src/store/useUser";
-import { getNodePath } from "src/utils/getNodePath";
 import styled from "styled-components";
 import styled from "styled-components";
 import { Loading } from "../Loading";
 import { Loading } from "../Loading";
 import { ErrorView } from "./ErrorView";
 import { ErrorView } from "./ErrorView";
@@ -89,10 +88,10 @@ export const Graph = ({ isWidget = false, openNodeModal }: GraphProps) => {
   const handleNodeClick = React.useCallback(
   const handleNodeClick = React.useCallback(
     (_: React.MouseEvent<SVGElement>, data: NodeData) => {
     (_: React.MouseEvent<SVGElement>, data: NodeData) => {
       if (setSelectedNode)
       if (setSelectedNode)
-        setSelectedNode({ nodeData: data, path: getNodePath(nodes, edges, data.id) });
+        setSelectedNode({ nodeData: data });
       if (openNodeModal) openNodeModal();
       if (openNodeModal) openNodeModal();
     },
     },
-    [edges, nodes, openNodeModal, setSelectedNode]
+    [openNodeModal, setSelectedNode]
   );
   );
 
 
   const onInit = React.useCallback(
   const onInit = React.useCallback(

+ 6 - 8
src/components/Sidebar/index.tsx

@@ -26,6 +26,8 @@ const StyledSidebar = styled.div`
   background: ${({ theme }) => theme.BACKGROUND_TERTIARY};
   background: ${({ theme }) => theme.BACKGROUND_TERTIARY};
   padding: 4px;
   padding: 4px;
   border-right: 1px solid ${({ theme }) => theme.BACKGROUND_MODIFIER_ACCENT};
   border-right: 1px solid ${({ theme }) => theme.BACKGROUND_MODIFIER_ACCENT};
+  width: 50px;
+  text-align: center;
 
 
   @media only screen and (max-width: 768px) {
   @media only screen and (max-width: 768px) {
     flex-direction: row;
     flex-direction: row;
@@ -34,18 +36,14 @@ const StyledSidebar = styled.div`
 `;
 `;
 
 
 const StyledElement = styled.button`
 const StyledElement = styled.button`
-  position: relative;
-  display: flex;
-  justify-content: center;
-  text-align: center;
+  height: 45px;
   font-size: 24px;
   font-size: 24px;
   font-weight: 600;
   font-weight: 600;
-  width: fit-content;
-  color: ${({ theme }) => theme.SIDEBAR_ICONS};
   cursor: pointer;
   cursor: pointer;
+  color: ${({ theme }) => theme.SIDEBAR_ICONS};
+  width: 100%;
 
 
   svg {
   svg {
-    padding: 12px 8px;
     vertical-align: middle;
     vertical-align: middle;
   }
   }
 
 
@@ -114,8 +112,8 @@ const StyledBottomWrapper = styled.nav`
 
 
 const StyledLogo = styled.a`
 const StyledLogo = styled.a`
   color: ${({ theme }) => theme.FULL_WHITE};
   color: ${({ theme }) => theme.FULL_WHITE};
-  padding: 8px 4px;
   border-bottom: 1px solid ${({ theme }) => theme.BACKGROUND_MODIFIER_ACCENT};
   border-bottom: 1px solid ${({ theme }) => theme.BACKGROUND_MODIFIER_ACCENT};
+  width: 100%;
 
 
   @media only screen and (max-width: 768px) {
   @media only screen and (max-width: 768px) {
     border-bottom: 0;
     border-bottom: 0;

+ 3 - 11
src/constants/globalStyle.ts

@@ -10,15 +10,10 @@ const monaSans = localFont({
 
 
 const GlobalStyle = createGlobalStyle`
 const GlobalStyle = createGlobalStyle`
   html, body {
   html, body {
-    margin: 0;
-    padding: 0;
-    box-sizing: border-box;
-    color: ${({ theme }) => theme.FULL_WHITE};
-    font-family: var(--mona-sans);
+    color: ${({ theme }) => theme.FULL_WHITE} !important;
     font-weight: 400;
     font-weight: 400;
     font-size: 16px;
     font-size: 16px;
-    height: 100%;
-    background-color: #000000;
+    background-color: #000000 !important;
     background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='100%25' height='100%25' viewBox='0 0 800 800'%3E%3Cg fill-opacity='0.3'%3E%3Ccircle fill='%23000000' cx='400' cy='400' r='600'/%3E%3Ccircle fill='%23110718' cx='400' cy='400' r='500'/%3E%3Ccircle fill='%23220e30' cx='400' cy='400' r='400'/%3E%3Ccircle fill='%23331447' cx='400' cy='400' r='300'/%3E%3Ccircle fill='%23441b5f' cx='400' cy='400' r='200'/%3E%3Ccircle fill='%23552277' cx='400' cy='400' r='100'/%3E%3C/g%3E%3C/svg%3E");
     background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='100%25' height='100%25' viewBox='0 0 800 800'%3E%3Cg fill-opacity='0.3'%3E%3Ccircle fill='%23000000' cx='400' cy='400' r='600'/%3E%3Ccircle fill='%23110718' cx='400' cy='400' r='500'/%3E%3Ccircle fill='%23220e30' cx='400' cy='400' r='400'/%3E%3Ccircle fill='%23331447' cx='400' cy='400' r='300'/%3E%3Ccircle fill='%23441b5f' cx='400' cy='400' r='200'/%3E%3Ccircle fill='%23552277' cx='400' cy='400' r='100'/%3E%3C/g%3E%3C/svg%3E");
     background-attachment: fixed;
     background-attachment: fixed;
     background-size: cover;
     background-size: cover;
@@ -46,12 +41,9 @@ const GlobalStyle = createGlobalStyle`
     vertical-align: text-top;
     vertical-align: text-top;
   }
   }
 
 
-  
   a {
   a {
-    text-decoration: none;
     color: unset;
     color: unset;
-    padding: 0;
-    margin: 0;
+    text-decoration: none;
   }
   }
 
 
   button {
   button {

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

@@ -13,7 +13,7 @@ export const StyledTools = styled.div`
   align-items: center;
   align-items: center;
   gap: 4px;
   gap: 4px;
   flex-direction: row-reverse;
   flex-direction: row-reverse;
-  height: 28px;
+  height: 36px;
   padding: 4px 16px;
   padding: 4px 16px;
   background: ${({ theme }) => theme.BACKGROUND_PRIMARY};
   background: ${({ theme }) => theme.BACKGROUND_PRIMARY};
   color: ${({ theme }) => theme.SILVER};
   color: ${({ theme }) => theme.SILVER};

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

@@ -126,6 +126,8 @@ export const StyledTitle = styled.h1`
   font-weight: 900;
   font-weight: 900;
   margin: 0;
   margin: 0;
   font-size: min(6vw, 86px);
   font-size: min(6vw, 86px);
+  color: white;
+  font-family: var(--mona-sans);
 
 
   @media only screen and (max-width: 768px) {
   @media only screen and (max-width: 768px) {
     font-size: 2.5rem;
     font-size: 2.5rem;
@@ -329,6 +331,7 @@ export const StyledTabsWrapper = styled.div`
   gap: 10px;
   gap: 10px;
   padding: 8px 8px;
   padding: 8px 8px;
   padding-bottom: 0;
   padding-bottom: 0;
+  height: 34px;
 
 
   pre {
   pre {
     border-top: 2px solid ${({ theme }) => theme.PRIMARY};
     border-top: 2px solid ${({ theme }) => theme.PRIMARY};
@@ -341,13 +344,15 @@ export const StyledTab = styled.button<{ active?: boolean }>`
   border: 2px solid ${({ theme, active }) => (active ? theme.PRIMARY : "transparent")};
   border: 2px solid ${({ theme, active }) => (active ? theme.PRIMARY : "transparent")};
   border-bottom: 0;
   border-bottom: 0;
   margin-bottom: -2px;
   margin-bottom: -2px;
-  padding: 8px 16px;
+  padding: 4px 8px;
   min-width: 80px;
   min-width: 80px;
   max-width: 150px;
   max-width: 150px;
   color: ${({ theme, active }) => (active ? theme.INTERACTIVE_ACTIVE : theme.INTERACTIVE_NORMAL)};
   color: ${({ theme, active }) => (active ? theme.INTERACTIVE_ACTIVE : theme.INTERACTIVE_NORMAL)};
   overflow: hidden;
   overflow: hidden;
   white-space: nowrap;
   white-space: nowrap;
   text-overflow: ellipsis;
   text-overflow: ellipsis;
+  font-size: 12px;
+  font-weight: 600;
 
 
   &:hover {
   &:hover {
     color: ${({ theme, active }) => !active && theme.INTERACTIVE_HOVER};
     color: ${({ theme, active }) => !active && theme.INTERACTIVE_HOVER};

+ 4 - 0
src/containers/ModalController/index.tsx

@@ -9,6 +9,7 @@ import { SettingsModal } from "src/containers/Modals/SettingsModal";
 import { ShareModal } from "src/containers/Modals/ShareModal";
 import { ShareModal } from "src/containers/Modals/ShareModal";
 import useModal from "src/store/useModal";
 import useModal from "src/store/useModal";
 import { shallow } from "zustand/shallow";
 import { shallow } from "zustand/shallow";
+import { PremiumModal } from "../Modals/PremiumModal";
 
 
 export const ModalController = () => {
 export const ModalController = () => {
   const setVisible = useModal(state => state.setVisible);
   const setVisible = useModal(state => state.setVisible);
@@ -22,6 +23,7 @@ export const ModalController = () => {
     accountModal,
     accountModal,
     loginModal,
     loginModal,
     shareModal,
     shareModal,
+    premiumModal,
   ] = useModal(
   ] = useModal(
     state => [
     state => [
       state.import,
       state.import,
@@ -32,6 +34,7 @@ export const ModalController = () => {
       state.account,
       state.account,
       state.login,
       state.login,
       state.share,
       state.share,
+      state.premium,
     ],
     ],
     shallow
     shallow
   );
   );
@@ -44,6 +47,7 @@ export const ModalController = () => {
       <SettingsModal opened={settingsModal} onClose={() => setVisible("settings")(false)} />
       <SettingsModal opened={settingsModal} onClose={() => setVisible("settings")(false)} />
       <CloudModal opened={cloudModal} onClose={() => setVisible("cloud")(false)} />
       <CloudModal opened={cloudModal} onClose={() => setVisible("cloud")(false)} />
       <AccountModal opened={accountModal} onClose={() => setVisible("account")(false)} />
       <AccountModal opened={accountModal} onClose={() => setVisible("account")(false)} />
+      <PremiumModal opened={premiumModal} onClose={() => setVisible("premium")(false)} />
       <LoginModal opened={loginModal} onClose={() => setVisible("login")(false)} />
       <LoginModal opened={loginModal} onClose={() => setVisible("login")(false)} />
       <ShareModal opened={shareModal} onClose={() => setVisible("share")(false)} />
       <ShareModal opened={shareModal} onClose={() => setVisible("share")(false)} />
     </>
     </>

+ 9 - 5
src/containers/Modals/AccountModal/index.tsx

@@ -47,10 +47,6 @@ export const AccountModal: React.FC<ModalProps> = ({ opened, onClose }) => {
   const isPremium = useUser(state => state.isPremium());
   const isPremium = useUser(state => state.isPremium());
   const logout = useUser(state => state.logout);
   const logout = useUser(state => state.logout);
 
 
-  const onImgFail = (e: React.SyntheticEvent<HTMLImageElement>) => {
-    e.currentTarget.setAttribute("src", `https://ui-avatars.com/api/?name=${user?.name}`);
-  };
-
   return (
   return (
     <Modal title="Account" opened={opened} onClose={onClose} centered>
     <Modal title="Account" opened={opened} onClose={onClose} centered>
       <StyledTitle>Hello, {user?.name}!</StyledTitle>
       <StyledTitle>Hello, {user?.name}!</StyledTitle>
@@ -68,7 +64,15 @@ export const AccountModal: React.FC<ModalProps> = ({ opened, onClose }) => {
           <Grid.Col span={6}>
           <Grid.Col span={6}>
             <StyledContainer>
             <StyledContainer>
               ACCOUNT STATUS
               ACCOUNT STATUS
-              <div>{isPremium ? <Badge>Premium</Badge> : <Badge color="gray">Free</Badge>}</div>
+              <div>
+                {isPremium ? (
+                  <Badge>Premium</Badge>
+                ) : (
+                  <Badge variant="outline" color="gray">
+                    Free
+                  </Badge>
+                )}
+              </div>
             </StyledContainer>
             </StyledContainer>
           </Grid.Col>
           </Grid.Col>
           <Grid.Col span={6}>
           <Grid.Col span={6}>

+ 35 - 0
src/containers/Modals/PremiumModal/index.tsx

@@ -0,0 +1,35 @@
+import React from "react";
+import Link from "next/link";
+import { Modal, Group, Button, Divider, ModalProps, Title, Image } from "@mantine/core";
+import { IoRocketSharp } from "react-icons/io5";
+
+export const PremiumModal: React.FC<ModalProps> = ({ opened, onClose }) => {
+  return (
+    <Modal title="JSON Crack Premium" opened={opened} onClose={onClose} centered>
+      <Group py="sm">
+        <Title
+          variant="gradient"
+          order={3}
+          gradient={{ from: "yellow", to: "hotpink" }}
+          strikethrough
+          align="center"
+        >
+          Enhance your experience, unlock full benefits of JSON Crack!
+        </Title>
+        <Image mx="auto" src="assets/bunny.png" width={150} alt="bunny" />
+      </Group>
+      <Divider py="xs" />
+      <Group position="center">
+        <Link href="/pricing" target="_blank" rel="noreferrer">
+          <Button
+            variant="gradient"
+            gradient={{ from: "yellow", to: "hotpink" }}
+            leftIcon={<IoRocketSharp />}
+          >
+            UPGRADE TO PREMIUM!
+          </Button>
+        </Link>
+      </Group>
+    </Modal>
+  );
+};

+ 50 - 32
src/pages/_app.tsx

@@ -1,5 +1,6 @@
 import React from "react";
 import React from "react";
 import type { AppProps } from "next/app";
 import type { AppProps } from "next/app";
+import localFont from "next/font/local";
 import { MantineProvider } from "@mantine/core";
 import { MantineProvider } from "@mantine/core";
 import { init } from "@sentry/nextjs";
 import { init } from "@sentry/nextjs";
 import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
 import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
@@ -9,7 +10,7 @@ import GlobalStyle from "src/constants/globalStyle";
 import { darkTheme, lightTheme } from "src/constants/theme";
 import { darkTheme, lightTheme } from "src/constants/theme";
 import { ModalController } from "src/containers/ModalController";
 import { ModalController } from "src/containers/ModalController";
 import useStored from "src/store/useStored";
 import useStored from "src/store/useStored";
-import { ThemeProvider, useTheme } from "styled-components";
+import { ThemeProvider } from "styled-components";
 
 
 if (process.env.NODE_ENV !== "development") {
 if (process.env.NODE_ENV !== "development") {
   init({
   init({
@@ -28,8 +29,14 @@ const queryClient = new QueryClient({
   },
   },
 });
 });
 
 
+const monaSans = localFont({
+  src: "../pages/Mona-Sans.woff2",
+  variable: "--mona-sans",
+  display: "swap",
+  fallback: ["Arial, Helvetica, sans-serif", "Tahoma, Verdana, sans-serif"],
+});
+
 function JsonCrack({ Component, pageProps }: AppProps) {
 function JsonCrack({ Component, pageProps }: AppProps) {
-  const theme = useTheme();
   const [isReady, setReady] = React.useState(false);
   const [isReady, setReady] = React.useState(false);
   const lightmode = useStored(state => state.lightmode);
   const lightmode = useStored(state => state.lightmode);
 
 
@@ -41,35 +48,46 @@ function JsonCrack({ Component, pageProps }: AppProps) {
     return (
     return (
       <QueryClientProvider client={queryClient}>
       <QueryClientProvider client={queryClient}>
         <GoogleAnalytics />
         <GoogleAnalytics />
-        <MantineProvider
-          theme={{
-            colorScheme: lightmode ? "light" : "dark",
-            components: {
-              Divider: {
-                styles: () => ({
-                  root: {
-                    borderTopColor: "#4D4D4D !important",
-                  },
-                }),
-              },
-              Modal: {
-                styles: theme => ({
-                  title: {
-                    fontWeight: 700,
-                  },
-                  header: {
-                    backgroundColor: theme.colorScheme === "dark" ? "#36393E" : "#FFFFFF",
-                  },
-                  body: {
-                    backgroundColor: theme.colorScheme === "dark" ? "#36393E" : "#FFFFFF",
-                  },
-                }),
+        <ThemeProvider theme={lightmode ? lightTheme : darkTheme}>
+          <GlobalStyle />
+          <MantineProvider
+            withGlobalStyles
+            withNormalizeCSS
+            withCSSVariables
+            theme={{
+              colorScheme: lightmode ? "light" : "dark",
+              fontFamily: monaSans.style.fontFamily,
+              components: {
+                Divider: {
+                  styles: () => ({
+                    root: {
+                      borderTopColor: "#4D4D4D !important",
+                    },
+                  }),
+                },
+                Modal: {
+                  styles: theme => ({
+                    title: {
+                      fontWeight: 700,
+                    },
+                    header: {
+                      backgroundColor: theme.colorScheme === "dark" ? "#36393E" : "#FFFFFF",
+                    },
+                    body: {
+                      backgroundColor: theme.colorScheme === "dark" ? "#36393E" : "#FFFFFF",
+                    },
+                  }),
+                },
+                Button: {
+                  styles: theme => ({
+                    inner: {
+                      fontWeight: 700,
+                    },
+                  }),
+                },
               },
               },
-            },
-          }}
-        >
-          <ThemeProvider theme={lightmode ? lightTheme : darkTheme}>
-            <GlobalStyle />
+            }}
+          >
             <Component {...pageProps} />
             <Component {...pageProps} />
             <ModalController />
             <ModalController />
             <Toaster
             <Toaster
@@ -86,8 +104,8 @@ function JsonCrack({ Component, pageProps }: AppProps) {
                 },
                 },
               }}
               }}
             />
             />
-          </ThemeProvider>
-        </MantineProvider>
+          </MantineProvider>
+        </ThemeProvider>
       </QueryClientProvider>
       </QueryClientProvider>
     );
     );
 }
 }

+ 0 - 3
src/pages/editor.tsx

@@ -9,8 +9,6 @@ import useJson from "src/store/useJson";
 import useUser from "src/store/useUser";
 import useUser from "src/store/useUser";
 import styled from "styled-components";
 import styled from "styled-components";
 
 
-// import { AdTest } from "src/components/AdTest";
-
 export const StyledPageWrapper = styled.div`
 export const StyledPageWrapper = styled.div`
   display: flex;
   display: flex;
   flex-direction: row;
   flex-direction: row;
@@ -60,7 +58,6 @@ const EditorPage: React.FC = () => {
         </StyledEditorWrapper>
         </StyledEditorWrapper>
       </StyledPageWrapper>
       </StyledPageWrapper>
       <BottomBar />
       <BottomBar />
-      {/* <AdTest /> */}
     </StyledEditorWrapper>
     </StyledEditorWrapper>
   );
   );
 };
 };

+ 2 - 2
src/store/useGraph.tsx

@@ -38,12 +38,12 @@ interface GraphActions {
   zoomIn: () => void;
   zoomIn: () => void;
   zoomOut: () => void;
   zoomOut: () => void;
   centerView: () => void;
   centerView: () => void;
-  setSelectedNode: ({ nodeData, path }: { nodeData: NodeData; path: string }) => void;
+  setSelectedNode: ({ nodeData}: { nodeData: NodeData }) => void;
 }
 }
 
 
 const useGraph = create<Graph & GraphActions>((set, get) => ({
 const useGraph = create<Graph & GraphActions>((set, get) => ({
   ...initialStates,
   ...initialStates,
-  setSelectedNode: ({ nodeData, path }) => set({ selectedNode: nodeData, path }),
+  setSelectedNode: ({ nodeData}) => set({ selectedNode: nodeData}),
   setGraph: (data, options) => {
   setGraph: (data, options) => {
     const { nodes, edges } = parser(data ?? useJson.getState().json);
     const { nodes, edges } = parser(data ?? useJson.getState().json);
 
 

+ 11 - 0
src/store/useJson.tsx

@@ -5,11 +5,14 @@ import { defaultJson } from "src/constants/data";
 import { saveJson as saveJsonDB } from "src/services/db/json";
 import { saveJson as saveJsonDB } from "src/services/db/json";
 import useGraph from "src/store/useGraph";
 import useGraph from "src/store/useGraph";
 import { Json } from "src/typings/altogic";
 import { Json } from "src/typings/altogic";
+import { fixJSON } from "src/utils/repairJson";
 import { create } from "zustand";
 import { create } from "zustand";
+import useUser from "./useUser";
 
 
 interface JsonActions {
 interface JsonActions {
   setJson: (json: string) => void;
   setJson: (json: string) => void;
   getJson: () => string;
   getJson: () => string;
+  repairJSON: (jsonstring: string) => void;
   getHasChanges: () => boolean;
   getHasChanges: () => boolean;
   fetchJson: (jsonId: string | string[] | undefined) => void;
   fetchJson: (jsonId: string | string[] | undefined) => void;
   setError: (hasError: boolean) => void;
   setError: (hasError: boolean) => void;
@@ -39,6 +42,14 @@ const useJson = create<JsonStates & JsonActions>()((set, get) => ({
   ...initialStates,
   ...initialStates,
   getJson: () => get().json,
   getJson: () => get().json,
   getHasChanges: () => get().hasChanges,
   getHasChanges: () => get().hasChanges,
+  repairJSON: jsonstring => {
+    useUser.getState().validatePremium(() => {
+      const parsed = fixJSON(jsonstring);
+      console.log(parsed);
+
+      get().setJson(parsed);
+    });
+  },
   fetchJson: async jsonId => {
   fetchJson: async jsonId => {
     const isURL = new RegExp(
     const isURL = new RegExp(
       /^https?:\/\/(?:www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b(?:[-a-zA-Z0-9()@:%_\+.~#?&\/=]*)$/
       /^https?:\/\/(?:www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b(?:[-a-zA-Z0-9()@:%_\+.~#?&\/=]*)$/

+ 1 - 0
src/store/useModal.tsx

@@ -16,6 +16,7 @@ const initialStates = {
   settings: false,
   settings: false,
   share: false,
   share: false,
   login: false,
   login: false,
+  premium: false,
 };
 };
 
 
 type ModalType = keyof typeof initialStates;
 type ModalType = keyof typeof initialStates;

+ 28 - 0
src/store/useUser.tsx

@@ -11,6 +11,7 @@ interface UserActions {
   checkSession: () => void;
   checkSession: () => void;
   isPremium: () => boolean;
   isPremium: () => boolean;
   tokenAuth: () => Promise<void>;
   tokenAuth: () => Promise<void>;
+  validatePremium: (cb: () => void) => void;
 }
 }
 
 
 const initialStates = {
 const initialStates = {
@@ -50,10 +51,29 @@ const useUser = create<UserStates & UserActions>()((set, get) => ({
     }
     }
   },
   },
   checkSession: async () => {
   checkSession: async () => {
+    if (process.env.NODE_ENV === "development") {
+      return set({
+        user: {
+          _id: "0",
+          provider: "altogic",
+          providerUserId: "1",
+          email: "[email protected]",
+          name: "JSON Crack",
+          profilePicture: "",
+          signUpAt: "2022-12-04T11:07:32.000Z",
+          lastLoginAt: "2023-03-12T14:17:03.146Z",
+          type: 1,
+          updatedAt: "2022-12-30T10:56:29.772Z",
+        },
+        isAuthenticated: true,
+      });
+    }
+
     const currentSession = altogic.auth.getSession();
     const currentSession = altogic.auth.getSession();
 
 
     if (currentSession) {
     if (currentSession) {
       const dbUser = await altogic.auth.getUserFromDB();
       const dbUser = await altogic.auth.getUserFromDB();
+      console.log(dbUser);
 
 
       altogic.auth.setSession(currentSession);
       altogic.auth.setSession(currentSession);
       set({ user: dbUser.user as any, isAuthenticated: true });
       set({ user: dbUser.user as any, isAuthenticated: true });
@@ -66,6 +86,14 @@ const useUser = create<UserStates & UserActions>()((set, get) => ({
       }
       }
     }
     }
   },
   },
+  validatePremium: callback => {
+    if (get().isAuthenticated) {
+      if (!get().isPremium()) return useModal.getState().setVisible("premium")(true);
+      return callback();
+    } else {
+      return useModal.getState().setVisible("account")(true);
+    }
+  },
 }));
 }));
 
 
 export default useUser;
 export default useUser;

+ 3 - 2
src/typings/altogic.ts

@@ -5,8 +5,9 @@ export interface User {
   email: string;
   email: string;
   name: string;
   name: string;
   profilePicture: string;
   profilePicture: string;
-  signUpAt: Date;
-  lastLoginAt: Date;
+  signUpAt: string;
+  lastLoginAt: string;
+  updatedAt: string;
   type: 0 | 1;
   type: 0 | 1;
 }
 }
 
 

+ 73 - 0
src/utils/repairJson.ts

@@ -0,0 +1,73 @@
+import { toast } from "react-hot-toast";
+
+/**
+ * The ISC License
+ * Copyright (c) 2023 by Aykut Saraç
+ * Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies.
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+export function fixJSON(jsonString: string) {
+  try {
+
+    // Remove comments
+    jsonString = jsonString.replace(/(\/\/[^\n]*|\/\*[\s\S]*?\*\/)/g, "");
+
+    // Remove JSONP notation
+    jsonString = jsonString.replace(/^\s*callback\((.*)\)\s*$/, "$1");
+
+    // Remove escape characters from escaped strings
+    jsonString = jsonString.replace(/\\"/g, '"');
+
+    // Remove MongoDB data types
+    jsonString = jsonString.replace(/(ISODate|NumberLong)\s*\(\s*("[^"]+"|\d+)\s*\)/g, "$2");
+
+    // Replace Python constants with JSON equivalents
+    jsonString = jsonString.replace(/(?<=^|\s)(None|True|False)(?=$|\s|,|\]|\})/g, match =>
+      match.toLowerCase() === "none" ? "null" : match.toLowerCase()
+    );
+
+    // Concatenate strings
+    jsonString = jsonString.replace(
+      /"([^"\\]*(?:\\.[^"\\]*)*)"(\s*\+\s*"([^"\\]*(?:\\.[^"\\]*)*)")+("([^"\\]*(?:\\.[^"\\]*)*)"|$)/g,
+      (match, p1, _, p2, p3, p4) => `"${p1}${p2}${p3}${p4}"`
+    );
+
+    // Add missing quotes around keys, replace single quotes with double quotes, replace special quote characters and white space characters
+    jsonString = jsonString.replace(/(['"])?([a-zA-Z0-9_]+)(['"])?:/g, '"$2": ');
+    jsonString = jsonString.replace(/'/g, '"');
+    jsonString = jsonString.replace(/[“”]/g, '"');
+    jsonString = jsonString.replace(/\s/g, " ");
+
+    // Add missing commas, strip trailing commas, and add double-quoted property names
+    jsonString = jsonString.replace(/([,{\[}\]])\s*(['"])?([a-zA-Z0-9_]+)(['"])?\s*:/g, '$1"$3": ');
+    jsonString = jsonString.replace(/,(\s*[}\]])/g, "$1");
+
+    // Add missing closing brackets
+    const brackets = [];
+    jsonString.split("").forEach(char => {
+      if (char === "{" || char === "[") {
+        brackets.push(char as never);
+      } else if (char === "}" || char === "]") {
+        if (
+          brackets.length > 0 &&
+          ((char === "}" && brackets[brackets.length - 1] === "{") ||
+            (char === "]" && brackets[brackets.length - 1] === "["))
+        ) {
+          brackets.pop();
+        } else {
+          jsonString = jsonString.slice(0, -1);
+        }
+      }
+    });
+    while (brackets.length > 0) {
+      const lastBracket = brackets.pop();
+      jsonString += lastBracket === "{" ? "}" : "]";
+    }
+
+    // Parse and return the fixed JSON
+    return JSON.stringify(JSON.parse(jsonString), null, 2);
+  } catch (error) {
+    toast.error("Unable to fix this JSON!");
+    return jsonString;
+  }
+}