Fix/tauri login state (#1919)

* fix: redux serializableCheck

* chore: read login state

* fix: show loading page while checking user state

* chore: cell data parser
This commit is contained in:
Nathan.fooo 2023-03-04 11:03:16 +08:00 committed by GitHub
parent 3f0d3d802a
commit 205b0fc4a5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 106 additions and 34 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

View File

@ -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>

View File

@ -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);

View File

@ -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>;
}
};

View File

@ -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 };
};

View File

@ -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>) {

View File

@ -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 });

View File

@ -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;
},

View File

@ -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 };