Explorar el Código

Fix/tauri login state (#1919)

* fix: redux serializableCheck

* chore: read login state

* fix: show loading page while checking user state

* chore: cell data parser
Nathan.fooo hace 2 años
padre
commit
205b0fc4a5

BIN
frontend/appflowy_tauri/src/appflowy_app/assets/launch_splash.jpg


+ 1 - 2
frontend/appflowy_tauri/src/appflowy_app/components/TestApiButton/TestAPI.tsx

@@ -1,5 +1,4 @@
 import React from 'react';
-import TestApiButton from './TestApiButton';
 import {
   TestCreateGrid,
   TestCreateNewField,
@@ -18,7 +17,7 @@ export const TestAPI = () => {
   return (
     <React.Fragment>
       <ul className='m-6, space-y-2'>
-        <TestApiButton></TestApiButton>
+        {/*<TestApiButton></TestApiButton>*/}
         <TestCreateGrid></TestCreateGrid>
         <TestCreateRow></TestCreateRow>
         <TestDeleteRow></TestDeleteRow>

+ 4 - 2
frontend/appflowy_tauri/src/appflowy_app/components/TestApiButton/TestGrid.tsx

@@ -126,8 +126,10 @@ export const TestCreateSelectOptionInCell = () => {
         );
         await cellController.subscribeChanged({
           onCellChanged: (value) => {
-            const option: SelectOptionCellDataPB = value.unwrap();
-            console.log(option);
+            if (value.some) {
+              const option: SelectOptionCellDataPB = value.unwrap();
+              console.log(option);
+            }
           },
         });
         const backendSvc = new SelectOptionCellBackendService(cellController.cellIdentifier);

+ 47 - 9
frontend/appflowy_tauri/src/appflowy_app/components/auth/ProtectedRoutes.tsx

@@ -1,16 +1,54 @@
-import { Navigate, Outlet, useLocation } from 'react-router-dom';
+import { Outlet } from 'react-router-dom';
 import { useAuth } from './auth.hooks';
 import { Screen } from '../layout/Screen';
+import { useEffect, useState } from 'react';
+import { GetStarted } from './GetStarted/GetStarted';
+import { AppflowyLogo } from '../_shared/svg/AppflowyLogo';
 
 export const ProtectedRoutes = () => {
-  const location = useLocation();
-  const { currentUser } = useAuth();
+  const { currentUser, checkUser } = useAuth();
+  const [isLoading, setIsLoading] = useState(true);
 
-  return currentUser.isAuthenticated ? (
-    <Screen>
-      <Outlet />
-    </Screen>
-  ) : (
-    <Navigate to='/auth/getStarted' replace state={{ from: location }} />
+  useEffect(() => {
+    void checkUser().then(async (result) => {
+      if (result.err) {
+        throw new Error(result.val.msg);
+      }
+
+      await new Promise(() =>
+        setTimeout(() => {
+          setIsLoading(false);
+        }, 1200)
+      );
+    });
+  }, []);
+
+  if (isLoading) {
+    // It's better to make a fading effect to disappear the loading page
+    return <StartLoading />;
+  } else {
+    return <SplashScreen isAuthenticated={currentUser.isAuthenticated} />;
+  }
+};
+
+const StartLoading = () => {
+  return (
+    <div className='flex h-screen w-full flex-col items-center justify-center'>
+      <div className='h-40 w-40 justify-center'>
+        <AppflowyLogo />
+      </div>
+    </div>
   );
 };
+
+const SplashScreen = ({ isAuthenticated }: { isAuthenticated: boolean }) => {
+  if (isAuthenticated) {
+    return (
+      <Screen>
+        <Outlet />
+      </Screen>
+    );
+  } else {
+    return <GetStarted></GetStarted>;
+  }
+};

+ 35 - 9
frontend/appflowy_tauri/src/appflowy_app/components/auth/auth.hooks.ts

@@ -1,20 +1,46 @@
 import { currentUserActions } from '../../stores/reducers/current-user/slice';
 import { useAppDispatch, useAppSelector } from '../../stores/store';
 import { UserProfilePB } from '../../../services/backend/events/flowy-user';
-import { AuthBackendService } from '../../stores/effects/user/user_bd_svc';
+import { AuthBackendService, UserBackendService } from '../../stores/effects/user/user_bd_svc';
 import { FolderEventReadCurrentWorkspace } from '../../../services/backend/events/flowy-folder';
 import { WorkspaceSettingPB } from '../../../services/backend/models/flowy-folder/workspace';
+import { Log } from '../../utils/log';
 
 export const useAuth = () => {
   const dispatch = useAppDispatch();
   const currentUser = useAppSelector((state) => state.currentUser);
   const authBackendService = new AuthBackendService();
 
+  async function checkUser() {
+    const result = await UserBackendService.checkUser();
+    if (result.ok) {
+      const userProfile = result.val;
+      const workspaceSetting = await _openWorkspace().then((r) => {
+        if (r.ok) {
+          return r.val;
+        } else {
+          return undefined;
+        }
+      });
+      dispatch(
+        currentUserActions.checkUser({
+          id: userProfile.id,
+          token: userProfile.token,
+          email: userProfile.email,
+          displayName: userProfile.name,
+          isAuthenticated: true,
+          workspaceSetting: workspaceSetting,
+        })
+      );
+    }
+    return result;
+  }
+
   async function register(email: string, password: string, name: string): Promise<UserProfilePB> {
     const authResult = await authBackendService.signUp({ email, password, name });
 
     if (authResult.ok) {
-      const { id, token } = authResult.val;
+      const userProfile = authResult.val;
       // Get the workspace setting after user registered. The workspace setting
       // contains the latest visiting view and the current workspace data.
       const openWorkspaceResult = await _openWorkspace();
@@ -22,10 +48,10 @@ export const useAuth = () => {
         const workspaceSetting: WorkspaceSettingPB = openWorkspaceResult.val;
         dispatch(
           currentUserActions.updateUser({
-            id: id,
-            token: token,
-            email,
-            displayName: name,
+            id: userProfile.id,
+            token: userProfile.token,
+            email: userProfile.email,
+            displayName: userProfile.name,
             isAuthenticated: true,
             workspaceSetting: workspaceSetting,
           })
@@ -33,7 +59,7 @@ export const useAuth = () => {
       }
       return authResult.val;
     } else {
-      console.error(authResult.val.msg);
+      Log.error(authResult.val.msg);
       throw new Error(authResult.val.msg);
     }
   }
@@ -53,7 +79,7 @@ export const useAuth = () => {
       );
       return result.val;
     } else {
-      console.error(result.val.msg);
+      Log.error(result.val.msg);
       throw new Error(result.val.msg);
     }
   }
@@ -67,5 +93,5 @@ export const useAuth = () => {
     return FolderEventReadCurrentWorkspace();
   }
 
-  return { currentUser, register, login, logout };
+  return { currentUser, checkUser, register, login, logout };
 };

+ 10 - 7
frontend/appflowy_tauri/src/appflowy_app/stores/effects/database/cell/cell_controller.ts

@@ -13,7 +13,7 @@ type Callbacks<T> = { onCellChanged: (value: Option<T>) => void; onFieldChanged?
 
 export class CellController<T, D> {
   private fieldBackendService: FieldBackendService;
-  private cellDataNotifier: CellDataNotifier<Option<T>>;
+  private cellDataNotifier: CellDataNotifier<T>;
   private cellObserver: CellObserver;
   private readonly cacheKey: CellCacheKey;
   private readonly fieldNotifier: DatabaseFieldObserver;
@@ -59,7 +59,7 @@ export class CellController<T, D> {
     this.subscribeCallbacks = callbacks;
     this.cellDataNotifier.observer.subscribe((cellData) => {
       if (cellData !== null) {
-        callbacks.onCellChanged(cellData);
+        callbacks.onCellChanged(Some(cellData));
       }
     });
   };
@@ -95,8 +95,11 @@ export class CellController<T, D> {
   private _loadCellData = () => {
     return this.cellDataLoader.loadData().then((result) => {
       if (result.ok) {
-        this.cellCache.insert(this.cacheKey, result.val);
-        this.cellDataNotifier.cellData = Some(result.val);
+        const cellData = result.val;
+        if (cellData.some) {
+          this.cellCache.insert(this.cacheKey, cellData.val);
+          this.cellDataNotifier.cellData = cellData;
+        }
       } else {
         this.cellCache.remove(this.cacheKey);
         this.cellDataNotifier.cellData = None;
@@ -110,12 +113,12 @@ export class CellController<T, D> {
   };
 }
 
-class CellDataNotifier<T> extends ChangeNotifier<T | null> {
+class CellDataNotifier<T> extends ChangeNotifier<T> {
   _cellData: Option<T>;
 
-  constructor(cellData: T) {
+  constructor(cellData: Option<T>) {
     super();
-    this._cellData = Some(cellData);
+    this._cellData = cellData;
   }
 
   set cellData(data: Option<T>) {

+ 5 - 0
frontend/appflowy_tauri/src/appflowy_app/stores/effects/user/user_bd_svc.ts

@@ -1,5 +1,6 @@
 import { nanoid } from '@reduxjs/toolkit';
 import {
+  UserEventCheckUser,
   UserEventGetUserProfile,
   UserEventSignIn,
   UserEventSignOut,
@@ -29,6 +30,10 @@ export class UserBackendService {
     return UserEventGetUserProfile();
   };
 
+  static checkUser = () => {
+    return UserEventCheckUser();
+  };
+
   updateUserProfile = (params: { name?: string; password?: string; email?: string; openAIKey?: string }) => {
     const payload = UpdateUserProfilePayloadPB.fromObject({ id: this.userId });
 

+ 3 - 4
frontend/appflowy_tauri/src/appflowy_app/stores/reducers/current-user/slice.ts

@@ -12,10 +12,6 @@ export interface ICurrentUser {
 }
 
 const initialState: ICurrentUser | null = {
-  id: nanoid(8),
-  displayName: 'Me 😃',
-  email: `${nanoid(4)}@gmail.com`,
-  token: nanoid(8),
   isAuthenticated: false,
 };
 
@@ -23,6 +19,9 @@ export const currentUserSlice = createSlice({
   name: 'currentUser',
   initialState: initialState,
   reducers: {
+    checkUser: (state, action: PayloadAction<ICurrentUser>) => {
+      return action.payload;
+    },
     updateUser: (state, action: PayloadAction<ICurrentUser>) => {
       return action.payload;
     },

+ 1 - 1
frontend/appflowy_tauri/src/appflowy_app/stores/store.ts

@@ -33,7 +33,7 @@ const store = configureStore({
     [workspaceSlice.name]: workspaceSlice.reducer,
     [errorSlice.name]: errorSlice.reducer,
   },
-  middleware: (gDM) => gDM().prepend(listenerMiddlewareInstance.middleware),
+  middleware: (gDM) => gDM({ serializableCheck: false }).prepend(listenerMiddlewareInstance.middleware),
 });
 
 export { store };