refactor: use suspense in AppDefender (#1020)

This commit is contained in:
Himself65
2023-02-15 02:51:44 -06:00
committed by GitHub
parent eb1d4fe1f6
commit 34a3a99d62
6 changed files with 44 additions and 47 deletions

View File

@@ -16,14 +16,13 @@ import { ModalProvider } from '@/store/globalModal';
// import AppStateProvider2 from '@/providers/app-state-provider2/provider'; // import AppStateProvider2 from '@/providers/app-state-provider2/provider';
import { useRouter } from 'next/router'; import { useRouter } from 'next/router';
import { useEffect } from 'react'; import { Suspense, useEffect } from 'react';
import { useAppState } from '@/providers/app-state-provider';
import { PageLoading } from '@/components/loading'; import { PageLoading } from '@/components/loading';
import Head from 'next/head'; import Head from 'next/head';
import '@affine/i18n'; import '@affine/i18n';
import { useTranslation } from '@affine/i18n'; import { useTranslation } from '@affine/i18n';
import React from 'react'; import React from 'react';
import { GlobalAppProvider } from '@/store/app'; import { DataCenterLoader, GlobalAppProvider } from '@/store/app';
const ThemeProvider = dynamic(() => import('@/providers/ThemeProvider'), { const ThemeProvider = dynamic(() => import('@/providers/ThemeProvider'), {
ssr: false, ssr: false,
@@ -80,7 +79,13 @@ const App = ({ Component, pageProps }: AppPropsWithLayout) => {
{NoNeedAppStatePageList.includes(router.route) ? ( {NoNeedAppStatePageList.includes(router.route) ? (
getLayout(<Component {...pageProps} />) getLayout(<Component {...pageProps} />)
) : ( ) : (
<AppDefender>{getLayout(<Component {...pageProps} />)}</AppDefender> <Suspense fallback={<PageLoading />}>
{/* we should put this before every component in case of they read a null value */}
<DataCenterLoader />
<AppDefender>
{getLayout(<Component {...pageProps} />)}
</AppDefender>
</Suspense>
)} )}
</ProviderComposer> </ProviderComposer>
</GlobalAppProvider> </GlobalAppProvider>
@@ -90,15 +95,13 @@ const App = ({ Component, pageProps }: AppPropsWithLayout) => {
const AppDefender = ({ children }: PropsWithChildren) => { const AppDefender = ({ children }: PropsWithChildren) => {
const router = useRouter(); const router = useRouter();
const { synced } = useAppState();
useEffect(() => { useEffect(() => {
if (['/index.html', '/'].includes(router.asPath)) { if (['/index.html', '/'].includes(router.asPath)) {
router.replace('/workspace'); router.replace('/workspace');
} }
}, [router]); }, [router]);
return <div>{synced ? children : <PageLoading />}</div>; return <>{children}</>;
}; };
export default App; export default App;

View File

@@ -45,21 +45,14 @@ export const AppStateProvider = ({
const onceRef = useRef(true); const onceRef = useRef(true);
const dataCenter = useGlobalState(store => store.dataCenter); const dataCenter = useGlobalState(store => store.dataCenter);
useEffect(() => { if (onceRef.current && dataCenter) {
if (dataCenter !== null) { setAppState({
if (onceRef.current) { workspaceList: dataCenter.workspaces,
setAppState({ currentWorkspace: null,
workspaceList: dataCenter.workspaces, pageList: [],
currentWorkspace: null, });
pageList: [], onceRef.current = false;
synced: true, }
});
onceRef.current = false;
} else {
console.warn('dataCenter Effect called twice. Please fix this ASAP');
}
}
}, [dataCenter]);
useEffect(() => { useEffect(() => {
// FIXME: onWorkspacesChange should have dispose function // FIXME: onWorkspacesChange should have dispose function

View File

@@ -18,7 +18,6 @@ export type AppStateValue = {
workspaceList: WorkspaceUnit[]; workspaceList: WorkspaceUnit[];
currentWorkspace: WorkspaceUnit | null; currentWorkspace: WorkspaceUnit | null;
pageList: PageMeta[]; pageList: PageMeta[];
synced: boolean;
blobDataSynced?: boolean; blobDataSynced?: boolean;
}; };

View File

@@ -1,5 +1,5 @@
import type React from 'react'; import type React from 'react';
import { createContext, useContext, useEffect, useMemo, useRef } from 'react'; import { createContext, useContext, useMemo } from 'react';
import { createStore, StateCreator, useStore } from 'zustand'; import { createStore, StateCreator, useStore } from 'zustand';
import { combine, subscribeWithSelector } from 'zustand/middleware'; import { combine, subscribeWithSelector } from 'zustand/middleware';
import type { UseBoundStore } from 'zustand/react'; import type { UseBoundStore } from 'zustand/react';
@@ -27,6 +27,7 @@ export type GlobalActionsCreator<Actions, Store = GlobalState> = StateCreator<
export interface GlobalState extends BlockSuiteState, UserState { export interface GlobalState extends BlockSuiteState, UserState {
readonly dataCenter: DataCenter; readonly dataCenter: DataCenter;
readonly dataCenterPromise: Promise<DataCenter>;
} }
export interface GlobalActions extends BlockSuiteActions, UserActions {} export interface GlobalActions extends BlockSuiteActions, UserActions {}
@@ -40,6 +41,8 @@ const create = () =>
...createUserState(), ...createUserState(),
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
dataCenter: null!, dataCenter: null!,
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
dataCenterPromise: null!,
}, },
/* deepscan-disable TOO_MANY_ARGS */ /* deepscan-disable TOO_MANY_ARGS */
(set, get, api) => ({ (set, get, api) => ({
@@ -71,27 +74,25 @@ export const useGlobalState: UseBoundStore<Store> = ((
// eslint-disable-next-line @typescript-eslint/no-explicit-any // eslint-disable-next-line @typescript-eslint/no-explicit-any
}) as any; }) as any;
function DataCenterSideEffect() { export function DataCenterLoader() {
const onceRef = useRef(true); const dataCenter = useGlobalState(store => store.dataCenter);
const dataCenterPromise = useGlobalState(store => store.dataCenterPromise);
const api = useGlobalStateApi(); const api = useGlobalStateApi();
useEffect(() => { if (!dataCenter && !dataCenterPromise) {
async function init() { const promise = getDataCenter();
const dataCenterPromise = getDataCenter(); api.setState({ dataCenterPromise: promise });
dataCenterPromise.then(async dataCenter => { promise.then(async dataCenter => {
// Ensure datacenter has at least one workspace // Ensure datacenter has at least one workspace
if (dataCenter.workspaces.length === 0) { if (dataCenter.workspaces.length === 0) {
await createDefaultWorkspace(dataCenter); await createDefaultWorkspace(dataCenter);
} }
api.setState({ dataCenter }); api.setState({ dataCenter });
}); });
} throw promise;
if (onceRef.current) { }
onceRef.current = false; if (!dataCenter) {
init().then(() => { throw dataCenterPromise;
console.log('datacenter init success'); }
});
}
}, [api]);
return null; return null;
} }
@@ -99,7 +100,6 @@ export const GlobalAppProvider: React.FC<React.PropsWithChildren> =
function ModelProvider({ children }) { function ModelProvider({ children }) {
return ( return (
<GlobalStateContext.Provider value={useMemo(() => create(), [])}> <GlobalStateContext.Provider value={useMemo(() => create(), [])}>
<DataCenterSideEffect />
{children} {children}
</GlobalStateContext.Provider> </GlobalStateContext.Provider>
); );

View File

@@ -29,8 +29,9 @@ export const createWorkspaceUnit = async (params: WorkspaceUnitCtorParams) => {
await setDefaultAvatar(blocksuiteWorkspace); await setDefaultAvatar(blocksuiteWorkspace);
workspaceUnit.update({ avatar: blocksuiteWorkspace.meta.avatar }); workspaceUnit.update({ avatar: blocksuiteWorkspace.meta.avatar });
} }
if (typeof window !== 'undefined') {
await writeUpdatesToLocal(blocksuiteWorkspace); await writeUpdatesToLocal(blocksuiteWorkspace);
}
workspaceUnit.setBlocksuiteWorkspace(blocksuiteWorkspace); workspaceUnit.setBlocksuiteWorkspace(blocksuiteWorkspace);

View File

@@ -11,6 +11,7 @@ export const createBlocksuiteWorkspace = (
return new BlocksuiteWorkspace({ return new BlocksuiteWorkspace({
room: workspaceId, room: workspaceId,
defaultFlags: { enable_slash_menu: true }, defaultFlags: { enable_slash_menu: true },
isSSR: typeof window === 'undefined',
...workspaceOption, ...workspaceOption,
}) })
.register(builtInSchemas) .register(builtInSchemas)