ソースを参照

feat: create account view after logged in

AykutSarac 2 年 前
コミット
a8df2c20aa

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

@@ -254,7 +254,7 @@ export const Sidebar: React.FC = () => {
           </StyledElement>
         </Tooltip>
 
-        <Tooltip className="desktop" title="View Saved JSON">
+        <Tooltip className="desktop" title="View Cloud">
           <StyledElement onClick={() => setVisible("cloud")(true)}>
             <VscCloud />
           </StyledElement>

+ 6 - 2
src/containers/Editor/BottomBar.tsx

@@ -58,7 +58,7 @@ export const BottomBar = () => {
   return (
     <StyledBottomBar>
       <StyledLeft>
-        <StyledBottomBarItem>
+        <StyledBottomBarItem onClick={() => setVisible("login")(true)}>
           <VscAccount />
           {user ? user.name : "Login"}
         </StyledBottomBarItem>
@@ -78,7 +78,11 @@ export const BottomBar = () => {
       <StyledRight>
         <StyledBottomBarItem>
           <VscHeart />
-          Support JSON Crack
+          <img
+            height="15"
+            src="https://cdn.discordapp.com/attachments/1016404295639388210/1022558676491440218/component_38.png"
+            alt="powered by buildable"
+          />
         </StyledBottomBarItem>
       </StyledRight>
     </StyledBottomBar>

+ 0 - 0
src/components/ModalController/index.tsx → src/containers/ModalController/index.tsx


+ 99 - 3
src/containers/Modals/LoginModal/index.tsx

@@ -1,6 +1,8 @@
 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";
 import useUser from "src/store/useUser";
 import styled from "styled-components";
@@ -37,11 +39,47 @@ const StyledLoginWrapper = styled.div`
   align-items: center;
 `;
 
-export const LoginModal: React.FC<ModalProps> = ({ setVisible, visible }) => {
+const StyledButton = styled(Button)``;
+
+const StyledAccountWrapper = styled.div`
+  display: flex;
+  flex-wrap: wrap;
+  gap: 20px;
+  background: ${({ theme }) => theme.BACKGROUND_TERTIARY};
+  padding: 16px;
+  border-radius: 6px;
+
+  button {
+    flex-basis: 100%;
+    background: #9036af;
+  }
+`;
+
+const StyledAvatar = styled.img`
+  border-radius: 100%;
+`;
+
+const StyledContainer = styled.div`
+  display: flex;
+  flex-direction: column;
+  gap: 8px;
+  padding: 12px 0;
+  font-size: 14px;
+  line-height: 16px;
+  font-weight: 600;
+  color: ${({ theme }) => theme.INTERACTIVE_NORMAL};
+
+  & > div {
+    font-weight: 400;
+    color: ${({ theme }) => theme.INTERACTIVE_ACTIVE};
+  }
+`;
+
+const LoginView: React.FC<Pick<ModalProps, "setVisible">> = ({ setVisible }) => {
   const login = useUser(state => state.login);
 
   return (
-    <Modal visible={visible} setVisible={setVisible}>
+    <>
       <Modal.Header>Login to JSON Crack</Modal.Header>
       <Modal.Content>
         <StyledTitle>Join Now!</StyledTitle>
@@ -59,7 +97,6 @@ export const LoginModal: React.FC<ModalProps> = ({ setVisible, visible }) => {
             onSuccess={credentialResponse => {
               if (credentialResponse.credential) {
                 login(credentialResponse.credential);
-                setVisible(false);
               }
             }}
             onError={() => {
@@ -69,6 +106,65 @@ export const LoginModal: React.FC<ModalProps> = ({ setVisible, visible }) => {
         </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);
+
+  return (
+    <>
+      <Modal.Header>Account (FREE)</Modal.Header>
+      <Modal.Content>
+        <StyledTitle>Hello, {user?.given_name}!</StyledTitle>
+        <StyledAccountWrapper>
+          <StyledAvatar
+            width="80"
+            height="80"
+            src={user?.picture}
+            alt={user?.name}
+          />
+          <StyledContainer>
+            USERNAME
+            <div>{user?.name}</div>
+          </StyledContainer>
+          <StyledContainer>
+            EMAIL
+            <div>{user?.email}</div>
+          </StyledContainer>
+          <Button block>
+            <IoRocketSharp />
+            UPDATE TO PREMIUM!
+          </Button>
+        </StyledAccountWrapper>
+      </Modal.Content>
+      <Modal.Controls setVisible={setVisible}>
+        <Button
+          status="DANGER"
+          onClick={() => {
+            logout();
+            setVisible(false);
+          }}
+        >
+          Log Out
+        </Button>
+      </Modal.Controls>
+    </>
+  );
+};
+
+export const LoginModal: React.FC<ModalProps> = ({ setVisible, visible }) => {
+  const isAuthenticated = useUser(state => state.isAuthenticated);
+
+  return (
+    <Modal visible={visible} setVisible={setVisible}>
+      {isAuthenticated ? (
+        <AccountView setVisible={setVisible} />
+      ) : (
+        <LoginView setVisible={setVisible} />
+      )}
     </Modal>
   );
 };

+ 1 - 1
src/pages/_app.tsx

@@ -6,9 +6,9 @@ import { init } from "@sentry/nextjs";
 import { decompress } from "compress-json";
 import { Toaster } from "react-hot-toast";
 import { GoogleAnalytics } from "src/components/GoogleAnalytics";
-import { ModalController } from "src/components/ModalController";
 import GlobalStyle from "src/constants/globalStyle";
 import { darkTheme, lightTheme } from "src/constants/theme";
+import { ModalController } from "src/containers/ModalController";
 import useConfig from "src/store/useConfig";
 import useStored from "src/store/useStored";
 import useUser from "src/store/useUser";

+ 1 - 4
src/store/useModal.tsx

@@ -1,6 +1,3 @@
-// TODO: handle all modals here
-// put all modals into a global place
-// display auth modal for unauthorized modal access actions
 import create from "zustand";
 import useUser from "./useUser";
 
@@ -29,7 +26,7 @@ export type ModalStates = typeof initialStates;
 const useModal = create<ModalStates & ModalActions>()(set => ({
   ...initialStates,
   setVisible: modal => visible => {
-    if (authModals.includes(modal) && !useUser.getState().isAuthorized) {
+    if (authModals.includes(modal) && !useUser.getState().isAuthenticated) {
       return set({ login: true });
     }
 

+ 15 - 8
src/store/useUser.tsx

@@ -1,4 +1,5 @@
 import jwt_decode from "jwt-decode";
+import toast from "react-hot-toast";
 import { validateToken } from "src/services/google";
 import create from "zustand";
 
@@ -21,12 +22,13 @@ interface GoogleUser {
 
 interface UserActions {
   login: (credential: string) => void;
+  logout: () => void;
   setUser: (key: keyof typeof initialStates, value: any) => void;
   checkSession: () => void;
 }
 
 const initialStates = {
-  isAuthorized: false,
+  isAuthenticated: false,
   user: null as GoogleUser | null,
 };
 
@@ -35,23 +37,28 @@ export type UserStates = typeof initialStates;
 const useUser = create<UserStates & UserActions>()(set => ({
   ...initialStates,
   setUser: (key, value) => set({ [key]: value }),
+  logout: () => {
+    localStorage.removeItem("auth_token");
+    set(initialStates);
+    toast.success("Logged out.");
+  },
+  login: credential => {
+    const googleUser = jwt_decode<GoogleUser>(credential);
+    localStorage.setItem("auth_token", credential);
+    set({ user: googleUser, isAuthenticated: true });
+  },
   checkSession: async () => {
     const token = localStorage.getItem("auth_token");
 
     if (token) {
       try {
         const { data: user } = await validateToken<GoogleUser>(token);
-        set({ user, isAuthorized: true });
+        set({ user, isAuthenticated: true });
       } catch (error) {
-        set({ isAuthorized: false });
+        set({ isAuthenticated: false });
       }
     }
   },
-  login: credential => {
-    const googleUser = jwt_decode<GoogleUser>(credential);
-    localStorage.setItem("auth_token", credential);
-    set({ user: googleUser, isAuthorized: true });
-  },
 }));
 
 export default useUser;