Browse Source

Merge pull request #326 from AykutSarac/refactor-codebase

Refactor codebase
Aykut Saraç 2 years ago
parent
commit
e4dc80f830

+ 1 - 1
package.json

@@ -9,7 +9,7 @@
     "build": "next build && next export",
     "start": "next start",
     "lint": "tsc && next lint",
-    "lint:fix": "prettier --write \"./**/*.{ts,tsx,json}\"",
+    "lint:fix": "prettier --write ./src & eslint --fix src",
     "deploy": "gh-pages -d out -t true"
   },
   "dependencies": {

BIN
public/assets/bunny.png


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

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

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

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

+ 7 - 11
src/components/Sidebar/index.tsx

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

+ 3 - 11
src/constants/globalStyle.ts

@@ -10,15 +10,10 @@ const monaSans = localFont({
 
 const GlobalStyle = createGlobalStyle`
   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-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-attachment: fixed;
     background-size: cover;
@@ -46,12 +41,9 @@ const GlobalStyle = createGlobalStyle`
     vertical-align: text-top;
   }
 
-  
   a {
-    text-decoration: none;
     color: unset;
-    padding: 0;
-    margin: 0;
+    text-decoration: none;
   }
 
   button {

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

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

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

@@ -126,6 +126,8 @@ export const StyledTitle = styled.h1`
   font-weight: 900;
   margin: 0;
   font-size: min(6vw, 86px);
+  color: white;
+  font-family: var(--mona-sans);
 
   @media only screen and (max-width: 768px) {
     font-size: 2.5rem;
@@ -329,6 +331,7 @@ export const StyledTabsWrapper = styled.div`
   gap: 10px;
   padding: 8px 8px;
   padding-bottom: 0;
+  height: 34px;
 
   pre {
     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-bottom: 0;
   margin-bottom: -2px;
-  padding: 8px 16px;
+  padding: 4px 8px;
   min-width: 80px;
   max-width: 150px;
   color: ${({ theme, active }) => (active ? theme.INTERACTIVE_ACTIVE : theme.INTERACTIVE_NORMAL)};
   overflow: hidden;
   white-space: nowrap;
   text-overflow: ellipsis;
+  font-size: 12px;
+  font-weight: 600;
 
   &: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 useModal from "src/store/useModal";
 import { shallow } from "zustand/shallow";
+import { PremiumModal } from "../Modals/PremiumModal";
 
 export const ModalController = () => {
   const setVisible = useModal(state => state.setVisible);
@@ -22,6 +23,7 @@ export const ModalController = () => {
     accountModal,
     loginModal,
     shareModal,
+    premiumModal,
   ] = useModal(
     state => [
       state.import,
@@ -32,6 +34,7 @@ export const ModalController = () => {
       state.account,
       state.login,
       state.share,
+      state.premium,
     ],
     shallow
   );
@@ -44,6 +47,7 @@ export const ModalController = () => {
       <SettingsModal opened={settingsModal} onClose={() => setVisible("settings")(false)} />
       <CloudModal opened={cloudModal} onClose={() => setVisible("cloud")(false)} />
       <AccountModal opened={accountModal} onClose={() => setVisible("account")(false)} />
+      <PremiumModal opened={premiumModal} onClose={() => setVisible("premium")(false)} />
       <LoginModal opened={loginModal} onClose={() => setVisible("login")(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 logout = useUser(state => state.logout);
 
-  const onImgFail = (e: React.SyntheticEvent<HTMLImageElement>) => {
-    e.currentTarget.setAttribute("src", `https://ui-avatars.com/api/?name=${user?.name}`);
-  };
-
   return (
     <Modal title="Account" opened={opened} onClose={onClose} centered>
       <StyledTitle>Hello, {user?.name}!</StyledTitle>
@@ -68,7 +64,15 @@ export const AccountModal: React.FC<ModalProps> = ({ opened, onClose }) => {
           <Grid.Col span={6}>
             <StyledContainer>
               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>
           </Grid.Col>
           <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 type { AppProps } from "next/app";
+import localFont from "next/font/local";
 import { MantineProvider } from "@mantine/core";
 import { init } from "@sentry/nextjs";
 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 { ModalController } from "src/containers/ModalController";
 import useStored from "src/store/useStored";
-import { ThemeProvider, useTheme } from "styled-components";
+import { ThemeProvider } from "styled-components";
 
 if (process.env.NODE_ENV !== "development") {
   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) {
-  const theme = useTheme();
   const [isReady, setReady] = React.useState(false);
   const lightmode = useStored(state => state.lightmode);
 
@@ -41,35 +48,46 @@ function JsonCrack({ Component, pageProps }: AppProps) {
     return (
       <QueryClientProvider client={queryClient}>
         <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} />
             <ModalController />
             <Toaster
@@ -86,8 +104,8 @@ function JsonCrack({ Component, pageProps }: AppProps) {
                 },
               }}
             />
-          </ThemeProvider>
-        </MantineProvider>
+          </MantineProvider>
+        </ThemeProvider>
       </QueryClientProvider>
     );
 }

+ 0 - 3
src/pages/editor.tsx

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

+ 2 - 2
src/store/useGraph.tsx

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

+ 1 - 0
src/store/useModal.tsx

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

+ 28 - 0
src/store/useUser.tsx

@@ -11,6 +11,7 @@ interface UserActions {
   checkSession: () => void;
   isPremium: () => boolean;
   tokenAuth: () => Promise<void>;
+  validatePremium: (cb: () => void) => void;
 }
 
 const initialStates = {
@@ -50,10 +51,29 @@ const useUser = create<UserStates & UserActions>()((set, get) => ({
     }
   },
   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();
 
     if (currentSession) {
       const dbUser = await altogic.auth.getUserFromDB();
+      console.log(dbUser);
 
       altogic.auth.setSession(currentSession);
       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;

+ 3 - 2
src/typings/altogic.ts

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