import { createContext, useContext, useEffect, useState, useRef } from 'react'; import type { PropsWithChildren } from 'react'; import { getDataCenter } from '@affine/datacenter'; import { AppStateContext, AppStateFunction, AppStateValue, PageMeta, } from './interface'; import { createDefaultWorkspace } from './utils'; type AppStateContextProps = PropsWithChildren>; export const AppState = createContext({} as AppStateContext); export const useAppState = () => useContext(AppState); export const AppStateProvider = ({ children, }: PropsWithChildren) => { const [appState, setAppState] = useState({} as AppStateValue); useEffect(() => { const init = async () => { const dataCenter = await getDataCenter(); if (dataCenter.workspaces.length === 0) { await createDefaultWorkspace(dataCenter); } let currentWorkspace = appState.currentWorkspace; if (!currentWorkspace) { currentWorkspace = await dataCenter.loadWorkspace( dataCenter.workspaces[0].id ); } const currentMetaWorkSpace = dataCenter.workspaces.find(item => { return item.id === currentWorkspace.room; }); setAppState({ dataCenter, user: (await dataCenter.getUserInfo()) || null, workspaceList: dataCenter.workspaces, currentWorkspaceId: dataCenter.workspaces[0].id, currentWorkspace, pageList: currentWorkspace.meta.pageMetas as PageMeta[], currentPage: null, editor: null, synced: true, currentMetaWorkSpace: currentMetaWorkSpace ?? null, }); }; init(); }); useEffect(() => { if (!appState?.currentWorkspace) { return; } const currentWorkspace = appState.currentWorkspace; const dispose = currentWorkspace.meta.pagesUpdated.on(() => { setAppState({ ...appState, pageList: currentWorkspace.meta.pageMetas as PageMeta[], }); }).dispose; return () => { dispose(); }; }, [appState]); useEffect(() => { const { dataCenter } = appState; // FIXME: onWorkspacesChange should have dispose function dataCenter?.onWorkspacesChange(() => { setAppState({ ...appState, workspaceList: dataCenter.workspaces, }); }); }, [appState]); const loadPage = useRef(); loadPage.current = (pageId: string) => { const { currentWorkspace, currentPage } = appState; if (pageId === currentPage?.id) { return; } const page = currentWorkspace?.getPage(pageId) || null; setAppState({ ...appState, currentPage: page, }); }; const loadWorkspace = useRef(); loadWorkspace.current = async (workspaceId: string) => { const { dataCenter, workspaceList, currentWorkspaceId, currentWorkspace } = appState; if (!workspaceList.find(v => v.id === workspaceId)) { return null; } if (workspaceId === currentWorkspaceId) { return currentWorkspace; } const workspace = await dataCenter.loadWorkspace(workspaceId); const currentMetaWorkSpace = dataCenter.workspaces.find(item => { return item.id === workspace.room; }); setAppState({ ...appState, currentWorkspace: workspace, currentWorkspaceId: workspaceId, currentMetaWorkSpace: currentMetaWorkSpace ?? null, }); return workspace; }; const setEditor: AppStateFunction['setEditor'] = useRef() as AppStateFunction['setEditor']; setEditor.current = editor => { setAppState({ ...appState, editor, }); }; return ( {children} ); };