diff --git a/packages/app/src/components/header/EditorHeader.tsx b/packages/app/src/components/header/EditorHeader.tsx index b343179496..76bdb9c88b 100644 --- a/packages/app/src/components/header/EditorHeader.tsx +++ b/packages/app/src/components/header/EditorHeader.tsx @@ -6,7 +6,7 @@ import { StyledTitleWrapper, } from './styles'; import { Content } from '@/ui/layout'; -import { useAppState } from '@/providers/app-state-provider/context'; +import { useAppState } from '@/providers/app-state-provider'; import EditorModeSwitch from '@/components/editor-mode-switch'; import QuickSearchButton from './QuickSearchButton'; import Header from './Header'; diff --git a/packages/app/src/components/header/header-right-items/SyncUser.tsx b/packages/app/src/components/header/header-right-items/SyncUser.tsx index 86c85926bf..60af1b461a 100644 --- a/packages/app/src/components/header/header-right-items/SyncUser.tsx +++ b/packages/app/src/components/header/header-right-items/SyncUser.tsx @@ -1,6 +1,6 @@ import { CloudUnsyncedIcon, CloudInsyncIcon } from '@blocksuite/icons'; import { useModal } from '@/providers/GlobalModalProvider'; -import { useAppState } from '@/providers/app-state-provider/context'; +import { useAppState } from '@/providers/app-state-provider'; import { IconButton } from '@/ui/button'; export const SyncUser = () => { diff --git a/packages/app/src/components/import/index.tsx b/packages/app/src/components/import/index.tsx index 96a383dad2..e296a7a0b9 100644 --- a/packages/app/src/components/import/index.tsx +++ b/packages/app/src/components/import/index.tsx @@ -4,7 +4,7 @@ import { Button } from '@/ui/button'; import { Wrapper, Content } from '@/ui/layout'; import Loading from '@/components/loading'; import { usePageHelper } from '@/hooks/use-page-helper'; -import { useAppState } from '@/providers/app-state-provider/context'; +import { useAppState } from '@/providers/app-state-provider'; import { useEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; // import { Tooltip } from '@/ui/tooltip'; diff --git a/packages/app/src/components/page-list/index.tsx b/packages/app/src/components/page-list/index.tsx index 2d5f13ed58..429ee6bbb9 100644 --- a/packages/app/src/components/page-list/index.tsx +++ b/packages/app/src/components/page-list/index.tsx @@ -20,7 +20,7 @@ import DateCell from '@/components/page-list/DateCell'; import { IconButton } from '@/ui/button'; import { Tooltip } from '@/ui/tooltip'; import { useRouter } from 'next/router'; -import { useAppState } from '@/providers/app-state-provider/context'; +import { useAppState } from '@/providers/app-state-provider'; import { toast } from '@/ui/toast'; import { usePageHelper } from '@/hooks/use-page-helper'; import { useTheme } from '@/providers/ThemeProvider'; diff --git a/packages/app/src/components/quick-search/Input.tsx b/packages/app/src/components/quick-search/Input.tsx index a3897675fb..69d47a16a1 100644 --- a/packages/app/src/components/quick-search/Input.tsx +++ b/packages/app/src/components/quick-search/Input.tsx @@ -17,11 +17,10 @@ export const Input = (props: { const [isComposition, setIsComposition] = useState(false); const [inputValue, setInputValue] = useState(''); const inputRef = useRef(null); - const { currentWorkspaceId, workspacesMeta, currentWorkspace } = - useAppState(); - const isPublic = workspacesMeta.find( + const { currentWorkspaceId, workspaceList, currentWorkspace } = useAppState(); + const isPublish = workspaceList.find( meta => String(meta.id) === String(currentWorkspaceId) - )?.public; + )?.isPublish; useEffect(() => { inputRef.current?.addEventListener( 'blur', @@ -80,7 +79,7 @@ export const Input = (props: { } }} placeholder={ - isPublic + isPublish ? `Search in ${currentWorkspace?.meta.name}` : 'Quick Search...' } diff --git a/packages/app/src/components/quick-search/index.tsx b/packages/app/src/components/quick-search/index.tsx index b08f69196e..87eeadf09b 100644 --- a/packages/app/src/components/quick-search/index.tsx +++ b/packages/app/src/components/quick-search/index.tsx @@ -22,16 +22,17 @@ const isMac = () => { return getUaHelper().isMacOs; }; export const QuickSearch = ({ open, onClose }: TransitionsModalProps) => { + const { currentWorkspaceId, workspaceList } = useAppState(); + const [query, setQuery] = useState(''); const [loading, setLoading] = useState(true); const [showCreatePage, setShowCreatePage] = useState(true); const { triggerQuickSearchModal } = useModal(); - const { currentWorkspaceId, workspacesMeta } = useAppState(); - const currentWorkspace = workspacesMeta.find( + const currentWorkspace = workspaceList.find( meta => String(meta.id) === String(currentWorkspaceId) ); - const isPublic = currentWorkspace?.public; + const isPublish = currentWorkspace?.isPublish; // Add ‘⌘+K’ shortcut keys as switches useEffect(() => { @@ -98,7 +99,7 @@ export const QuickSearch = ({ open, onClose }: TransitionsModalProps) => { setShowCreatePage={setShowCreatePage} /> - {isPublic ? ( + {isPublish ? ( <> ) : showCreatePage ? ( <> diff --git a/packages/app/src/components/workspace-slider-bar/WorkspaceSelector/SelectorPopperContent.tsx b/packages/app/src/components/workspace-slider-bar/WorkspaceSelector/SelectorPopperContent.tsx index f8bca4787d..208f3a76d7 100644 --- a/packages/app/src/components/workspace-slider-bar/WorkspaceSelector/SelectorPopperContent.tsx +++ b/packages/app/src/components/workspace-slider-bar/WorkspaceSelector/SelectorPopperContent.tsx @@ -1,7 +1,7 @@ import { InformationIcon, LogOutIcon } from '@blocksuite/icons'; import { styled } from '@/styles'; import { Divider } from '@/ui/divider'; -import { useAppState } from '@/providers/app-state-provider/context'; +import { useAppState } from '@/providers/app-state-provider'; import { SelectorPopperContainer } from './styles'; import { PrivateWorkspaceItem, diff --git a/packages/app/src/components/workspace-slider-bar/WorkspaceSelector/WorkspaceItem/PrivateWorkspaceItem.tsx b/packages/app/src/components/workspace-slider-bar/WorkspaceSelector/WorkspaceItem/PrivateWorkspaceItem.tsx index 1301200f79..ea5ae4f7b2 100644 --- a/packages/app/src/components/workspace-slider-bar/WorkspaceSelector/WorkspaceItem/PrivateWorkspaceItem.tsx +++ b/packages/app/src/components/workspace-slider-bar/WorkspaceSelector/WorkspaceItem/PrivateWorkspaceItem.tsx @@ -1,5 +1,5 @@ import { styled } from '@/styles'; -import { useAppState } from '@/providers/app-state-provider/context'; +import { useAppState } from '@/providers/app-state-provider'; import { WorkspaceItemAvatar, PrivateWorkspaceWrapper, diff --git a/packages/app/src/components/workspace-slider-bar/index.tsx b/packages/app/src/components/workspace-slider-bar/index.tsx index 333dea3e6e..7640b35ce0 100644 --- a/packages/app/src/components/workspace-slider-bar/index.tsx +++ b/packages/app/src/components/workspace-slider-bar/index.tsx @@ -26,7 +26,7 @@ import { import Link from 'next/link'; import { Tooltip } from '@/ui/tooltip'; import { useModal } from '@/providers/GlobalModalProvider'; -import { useAppState } from '@/providers/app-state-provider/context'; +import { useAppState } from '@/providers/app-state-provider'; import { IconButton } from '@/ui/button'; import useLocalStorage from '@/hooks/use-local-storage'; import usePageMetaList from '@/hooks/use-page-meta-list'; diff --git a/packages/app/src/hooks/use-ensure-workspace.ts b/packages/app/src/hooks/use-ensure-workspace.ts index d20c63b05f..9412379008 100644 --- a/packages/app/src/hooks/use-ensure-workspace.ts +++ b/packages/app/src/hooks/use-ensure-workspace.ts @@ -7,22 +7,18 @@ const defaultOutLineWorkspaceId = 'affine'; // Cause it not just ensure workspace loaded, but also have router change. export const useEnsureWorkspace = () => { const [workspaceLoaded, setWorkspaceLoaded] = useState(false); - const { workspacesMeta, loadWorkspace, synced, user } = useAppState(); + const { workspaceList, loadWorkspace, user } = useAppState(); const router = useRouter(); // const defaultOutLineWorkspaceId = '99ce7eb7'; // console.log(defaultOutLineWorkspaceId); useEffect(() => { - if (!synced) { - setWorkspaceLoaded(false); - return; - } // If router.query.workspaceId is not in workspace list, jump to 404 page - // If workspacesMeta is empty, we need to create a default workspace but not jump to 404 + // If workspaceList is empty, we need to create a default workspace but not jump to 404 if ( - workspacesMeta.length && + workspaceList.length && router.query.workspaceId && - workspacesMeta.findIndex( + workspaceList.findIndex( meta => meta.id.toString() === router.query.workspaceId ) === -1 ) { @@ -40,13 +36,13 @@ export const useEnsureWorkspace = () => { // } const workspaceId = user - ? (router.query.workspaceId as string) || workspacesMeta[0]?.id + ? (router.query.workspaceId as string) || workspaceList[0]?.id : (router.query.workspaceId as string) || defaultOutLineWorkspaceId; loadWorkspace(workspaceId).finally(() => { setWorkspaceLoaded(true); }); - }, [loadWorkspace, router, synced, user, workspacesMeta]); + }, [loadWorkspace, router, user, workspaceList]); return { workspaceLoaded, diff --git a/packages/app/src/hooks/use-init-workspace.ts b/packages/app/src/hooks/use-init-workspace.ts index 1866e69b4c..4f916365c2 100644 --- a/packages/app/src/hooks/use-init-workspace.ts +++ b/packages/app/src/hooks/use-init-workspace.ts @@ -1,5 +1,5 @@ import { useRouter } from 'next/router'; -import { useAppState } from '@/providers/app-state-provider/context'; +import { useAppState } from '@/providers/app-state-provider'; import { useEffect, useRef, useState } from 'react'; export const useInitWorkspace = (disabled?: boolean) => { @@ -8,15 +8,11 @@ export const useInitWorkspace = (disabled?: boolean) => { const defaultOutLineWorkspaceId = useRef(new Date().getTime().toString()); const router = useRouter(); - const { - workspacesMeta, - loadWorkspace, - currentWorkspace, - currentWorkspaceId, - } = useAppState(); + const { workspaceList, loadWorkspace, currentWorkspace, currentWorkspaceId } = + useAppState(); const workspaceId = (router.query.workspaceId as string) || - workspacesMeta?.[0]?.id || + workspaceList?.[0]?.id || defaultOutLineWorkspaceId.current; useEffect(() => { diff --git a/packages/app/src/pages/_app.tsx b/packages/app/src/pages/_app.tsx index da94a4eede..a8a4ecb762 100644 --- a/packages/app/src/pages/_app.tsx +++ b/packages/app/src/pages/_app.tsx @@ -10,9 +10,11 @@ import '../utils/print-build-info'; import ProviderComposer from '@/components/provider-composer'; import type { PropsWithChildren, ReactElement, ReactNode } from 'react'; import type { NextPage } from 'next'; -import { AppStateProvider } from '@/providers/app-state-provider/Provider'; +import { AppStateProvider } from '@/providers/app-state-provider'; import ConfirmProvider from '@/providers/ConfirmProvider'; import { ModalProvider } from '@/providers/GlobalModalProvider'; +// import AppStateProvider2 from '@/providers/app-state-provider2/provider'; + import { useRouter } from 'next/router'; import { useEffect } from 'react'; import { useAppState } from '@/providers/app-state-provider'; @@ -56,6 +58,7 @@ const App = ({ Component, pageProps }: AppPropsWithLayout) => { , , , + // , , , ]} @@ -67,9 +70,8 @@ const App = ({ Component, pageProps }: AppPropsWithLayout) => { }; const AppDefender = ({ children }: PropsWithChildren) => { - const router = useRouter(); - const { synced } = useAppState(); + const router = useRouter(); useEffect(() => { if (router.pathname === '/') { diff --git a/packages/app/src/pages/playground/templates.tsx b/packages/app/src/pages/playground/templates.tsx index a83d25e516..3dc825b1e9 100644 --- a/packages/app/src/pages/playground/templates.tsx +++ b/packages/app/src/pages/playground/templates.tsx @@ -4,7 +4,7 @@ import exampleMarkdown1 from '@/templates/Welcome-to-the-AFFiNE-Alpha.md'; import exampleMarkdown2 from '@/templates/AFFiNE-Docs.md'; import { usePageHelper } from '@/hooks/use-page-helper'; -import { useAppState } from '@/providers/app-state-provider/context'; +import { useAppState } from '@/providers/app-state-provider'; import { Button } from '@/ui/button'; interface Template { name: string; diff --git a/packages/app/src/pages/workspace/[workspaceId]/[pageId].tsx b/packages/app/src/pages/workspace/[workspaceId]/[pageId].tsx index 05c2c17eba..eba930db91 100644 --- a/packages/app/src/pages/workspace/[workspaceId]/[pageId].tsx +++ b/packages/app/src/pages/workspace/[workspaceId]/[pageId].tsx @@ -10,7 +10,7 @@ import { styled } from '@/styles'; import { EditorHeader } from '@/components/header'; import EdgelessToolbar from '@/components/edgeless-toolbar'; import MobileModal from '@/components/mobile-modal'; -import { useAppState } from '@/providers/app-state-provider/context'; +import { useAppState } from '@/providers/app-state-provider'; import exampleMarkdown from '@/templates/Welcome-to-AFFiNE-Alpha-v2.0.md'; import type { NextPageWithLayout } from '../..//_app'; import WorkspaceLayout from '@/components/workspace-layout'; diff --git a/packages/app/src/pages/workspace/[workspaceId]/index.tsx b/packages/app/src/pages/workspace/[workspaceId]/index.tsx index 39a1ad121b..d502f57980 100644 --- a/packages/app/src/pages/workspace/[workspaceId]/index.tsx +++ b/packages/app/src/pages/workspace/[workspaceId]/index.tsx @@ -1,6 +1,6 @@ import { useEffect } from 'react'; import { useRouter } from 'next/router'; -import { useAppState } from '@/providers/app-state-provider/context'; +import { useAppState } from '@/providers/app-state-provider'; import useEnsureWorkspace from '@/hooks/use-ensure-workspace'; import { PageLoading } from '@/components/loading'; import usePageHelper from '@/hooks/use-page-helper'; diff --git a/packages/app/src/providers/GlobalModalProvider.tsx b/packages/app/src/providers/GlobalModalProvider.tsx index 76a0365dfc..5f72397ff9 100644 --- a/packages/app/src/providers/GlobalModalProvider.tsx +++ b/packages/app/src/providers/GlobalModalProvider.tsx @@ -97,12 +97,12 @@ export const ModalProvider = ({ triggerHandler('shortcuts', false); }} > - { - triggerHandler('quickSearch', false); - }} - > + {/* {*/} + {/* triggerHandler('quickSearch', false);*/} + {/* }}*/} + {/*>*/} { diff --git a/packages/app/src/providers/app-state-provider/DynamicBlocksuite.tsx b/packages/app/src/providers/app-state-provider/DynamicBlocksuite.tsx index 4b7b876545..611f316a4f 100644 --- a/packages/app/src/providers/app-state-provider/DynamicBlocksuite.tsx +++ b/packages/app/src/providers/app-state-provider/DynamicBlocksuite.tsx @@ -2,11 +2,11 @@ import { useEffect } from 'react'; import type { Page } from '@blocksuite/store'; import '@blocksuite/blocks'; import { EditorContainer } from '@blocksuite/editor'; -import type { CreateEditorHandler } from './context'; +import { CreateEditorHandler } from './interface'; -interface Props { +type Props = { setCreateEditorHandler: (handler: CreateEditorHandler) => void; -} +}; const DynamicBlocksuite = ({ setCreateEditorHandler }: Props) => { useEffect(() => { diff --git a/packages/app/src/providers/app-state-provider/Provider.tsx b/packages/app/src/providers/app-state-provider/Provider.tsx index f104e9be3e..50ef656fb6 100644 --- a/packages/app/src/providers/app-state-provider/Provider.tsx +++ b/packages/app/src/providers/app-state-provider/Provider.tsx @@ -1,46 +1,30 @@ -import { useMemo, useState, useCallback, useRef } from 'react'; -import type { ReactNode } from 'react'; -import dynamic from 'next/dynamic'; +import { + createContext, + useCallback, + useContext, + useEffect, + useState, +} from 'react'; +import type { PropsWithChildren } from 'react'; import { getDataCenter } from '@affine/datacenter'; -import { AppState, AppStateContext } from './context'; -import type { - AppStateValue, - CreateEditorHandler, - LoadWorkspaceHandler, -} from './context'; -import { Page } from '@blocksuite/store'; -import { EditorContainer } from '@blocksuite/editor'; +import { AppStateContext, AppStateValue } from './interface'; +import { createDefaultWorkspace } from './utils'; + +type AppStateContextProps = PropsWithChildren>; +import dynamic from 'next/dynamic'; +import { CreateEditorHandler } from '@/providers/app-state-provider3'; + const DynamicBlocksuite = dynamic(() => import('./DynamicBlocksuite'), { ssr: false, }); +export const AppState = createContext({} as AppStateContext); -const loadWorkspaceHandler: LoadWorkspaceHandler = async workspaceId => { - if (workspaceId) { - const dc = await getDataCenter(); - return dc.load(workspaceId, { providerId: 'affine' }); - } else { - return null; - } -}; -export const AppStateProvider = ({ children }: { children?: ReactNode }) => { - const refreshWorkspacesMeta = async () => { - const dc = await getDataCenter(); - const workspacesMeta = await dc.apis.getWorkspaces().catch(() => { - return []; - }); - setState(state => ({ ...state, workspacesMeta })); - }; +export const useAppState = () => useContext(AppState); - const [state, setState] = useState({ - user: null, - workspacesMeta: [], - currentWorkspaceId: '', - currentWorkspace: null, - currentPage: null, - editor: null, - refreshWorkspacesMeta, - synced: true, - }); +export const AppStateProvider = ({ + children, +}: PropsWithChildren) => { + const [appState, setAppState] = useState({} as AppStateValue); const [createEditorHandler, _setCreateEditorHandler] = useState(); @@ -52,54 +36,84 @@ export const AppStateProvider = ({ children }: { children?: ReactNode }) => { [_setCreateEditorHandler] ); - const loadWorkspace = useRef(() => - Promise.resolve(null) - ); - loadWorkspace.current = async (workspaceId: string) => { - if (state.currentWorkspaceId === workspaceId) { - return state.currentWorkspace; + useEffect(() => { + const init = async () => { + const dataCenter = await getDataCenter(); + + if (dataCenter.workspaces.length === 0) { + await createDefaultWorkspace(dataCenter); + } + + const currentWorkspace = await dataCenter.loadWorkspace( + dataCenter.workspaces[0].id + ); + + setAppState({ + dataCenter, + user: await dataCenter.getUserInfo(), + workspaceList: dataCenter.workspaces, + currentWorkspaceId: dataCenter.workspaces[0].id, + currentWorkspace, + pageList: currentWorkspace.meta.pageMetas, + currentPage: null, + editor: null, + synced: true, + }); + }; + + init(); + }, []); + + useEffect(() => { + if (!appState?.currentWorkspace) { + return; } - const workspace = - (await loadWorkspaceHandler(workspaceId, state.user)) || null; + const currentWorkspace = appState.currentWorkspace; + const dispose = currentWorkspace.meta.pagesUpdated.on(() => { + setAppState({ + ...appState, + pageList: currentWorkspace.meta.pageMetas, + }); + }).dispose; + return () => { + dispose(); + }; + }, [appState]); - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-expect-error - window.workspace = workspace; - // FIXME: there needs some method to destroy websocket. - // Or we need a manager to manage websocket. - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-expect-error - state.currentWorkspace?.__ws__?.destroy(); - - setState(state => ({ - ...state, - currentWorkspace: workspace, - currentWorkspaceId: workspaceId, - })); - return workspace; - }; - - const loadPage = useRef(() => - Promise.resolve(null) - ); - loadPage.current = async (pageId: string) => { - const { currentWorkspace, currentPage } = state; + const loadPage = (pageId: string) => { + const { currentWorkspace, currentPage } = appState; if (pageId === currentPage?.id) { - return currentPage; + return; } - const page = (pageId ? currentWorkspace?.getPage(pageId) : null) || null; - setState(state => ({ ...state, currentPage: page })); - return page; + const page = currentWorkspace?.getPage(pageId) || null; + setAppState({ + ...appState, + currentPage: page, + }); }; - const createEditor = useRef< - ((page: Page) => EditorContainer | null) | undefined - >(); - createEditor.current = () => { - const { currentPage, currentWorkspace } = state; + const loadWorkspace = async (workspaceId: string) => { + const { dataCenter, workspaceList, currentWorkspaceId } = appState; + if (!workspaceList.find(v => v.id === workspaceId)) { + return; + } + if (workspaceId === currentWorkspaceId) { + return; + } + + setAppState({ + ...appState, + currentWorkspace: await dataCenter.loadWorkspace(workspaceId), + currentWorkspaceId: workspaceId, + }); + }; + + const createEditor = () => { + const { currentPage, currentWorkspace } = appState; if (!currentPage || !currentWorkspace) { return null; } + const editor = createEditorHandler?.(currentPage) || null; if (editor) { @@ -117,28 +131,28 @@ export const AppStateProvider = ({ children }: { children?: ReactNode }) => { return editor; }; - const setEditor = useRef<(editor: AppStateValue['editor']) => void>(); - - setEditor.current = (editor: AppStateValue['editor']) => { - setState(state => ({ ...state, editor })); + const setEditor = (editor: AppStateValue['editor']) => { + setAppState({ + ...appState, + editor, + }); }; - const context = useMemo( - () => ({ - ...state, - setState, - createEditor, - setEditor, - loadWorkspace: loadWorkspace.current, - loadPage: loadPage.current, - }), - [state, setState, loadPage, loadWorkspace] - ); - return ( - + + {children} ); }; + +export default AppStateProvider; diff --git a/packages/app/src/providers/app-state-provider/index.ts b/packages/app/src/providers/app-state-provider/index.ts index 88c38fd7cf..505094109d 100644 --- a/packages/app/src/providers/app-state-provider/index.ts +++ b/packages/app/src/providers/app-state-provider/index.ts @@ -1,2 +1 @@ -export * from './context'; -export * from './interface'; +export * from './Provider'; diff --git a/packages/app/src/providers/app-state-provider/interface.ts b/packages/app/src/providers/app-state-provider/interface.ts index 19b028703d..cd789a84da 100644 --- a/packages/app/src/providers/app-state-provider/interface.ts +++ b/packages/app/src/providers/app-state-provider/interface.ts @@ -1,17 +1,31 @@ -import { PageMeta as OriginalPageMeta } from '@blocksuite/store/dist/workspace/workspace'; +import { DataCenter, User, Workspace } from '@affine/datacenter'; +import type { EditorContainer } from '@blocksuite/editor'; -// export type PageMeta = { -// favorite: boolean; -// trash: boolean; -// trashDate: number | void; -// updatedDate: number | void; -// mode: EditorContainer['mode']; -// } & OriginalPageMeta; +import type { + Page as StorePage, + Workspace as StoreWorkspace, + PageMeta, +} from '@blocksuite/store'; -export interface PageMeta extends OriginalPageMeta { - favorite: boolean; - trash: boolean; - trashDate: number; - updatedDate: number; - mode: 'edgeless' | 'page'; -} +export type AppStateValue = { + dataCenter: DataCenter; + user: User | null; + workspaceList: Workspace[]; + currentWorkspace: StoreWorkspace; + currentWorkspaceId: string; + pageList: PageMeta[]; + currentPage: StorePage | null; + editor?: EditorContainer | null; + synced: boolean; +}; + +export type AppStateFunction = { + createEditor: (page: StorePage) => EditorContainer | null; + setEditor: (page: EditorContainer) => void; + loadWorkspace: (workspaceId: string) => Promise; + loadPage: (pageId: string) => void; +}; + +export type AppStateContext = AppStateValue & AppStateFunction; + +export type CreateEditorHandler = (page: StorePage) => EditorContainer | null; diff --git a/packages/app/src/providers/app-state-provider/utils.ts b/packages/app/src/providers/app-state-provider/utils.ts new file mode 100644 index 0000000000..066ef7a2c8 --- /dev/null +++ b/packages/app/src/providers/app-state-provider/utils.ts @@ -0,0 +1,10 @@ +import { DataCenter } from '@affine/datacenter'; + +const DEFAULT_WORKSPACE_NAME = 'affine'; + +export const createDefaultWorkspace = async (dataCenter: DataCenter) => { + return dataCenter.createWorkspace({ + avatar: 'test', + name: DEFAULT_WORKSPACE_NAME, + }); +}; diff --git a/packages/app/src/providers/app-state-provider3/DynamicBlocksuite.tsx b/packages/app/src/providers/app-state-provider3/DynamicBlocksuite.tsx new file mode 100644 index 0000000000..4b7b876545 --- /dev/null +++ b/packages/app/src/providers/app-state-provider3/DynamicBlocksuite.tsx @@ -0,0 +1,25 @@ +import { useEffect } from 'react'; +import type { Page } from '@blocksuite/store'; +import '@blocksuite/blocks'; +import { EditorContainer } from '@blocksuite/editor'; +import type { CreateEditorHandler } from './context'; + +interface Props { + setCreateEditorHandler: (handler: CreateEditorHandler) => void; +} + +const DynamicBlocksuite = ({ setCreateEditorHandler }: Props) => { + useEffect(() => { + const createEditorHandler: CreateEditorHandler = (page: Page) => { + const editor = new EditorContainer(); + editor.page = page; + return editor; + }; + + setCreateEditorHandler(createEditorHandler); + }, [setCreateEditorHandler]); + + return <>; +}; + +export default DynamicBlocksuite; diff --git a/packages/app/src/providers/app-state-provider3/Provider.tsx b/packages/app/src/providers/app-state-provider3/Provider.tsx new file mode 100644 index 0000000000..f104e9be3e --- /dev/null +++ b/packages/app/src/providers/app-state-provider3/Provider.tsx @@ -0,0 +1,144 @@ +import { useMemo, useState, useCallback, useRef } from 'react'; +import type { ReactNode } from 'react'; +import dynamic from 'next/dynamic'; +import { getDataCenter } from '@affine/datacenter'; +import { AppState, AppStateContext } from './context'; +import type { + AppStateValue, + CreateEditorHandler, + LoadWorkspaceHandler, +} from './context'; +import { Page } from '@blocksuite/store'; +import { EditorContainer } from '@blocksuite/editor'; +const DynamicBlocksuite = dynamic(() => import('./DynamicBlocksuite'), { + ssr: false, +}); + +const loadWorkspaceHandler: LoadWorkspaceHandler = async workspaceId => { + if (workspaceId) { + const dc = await getDataCenter(); + return dc.load(workspaceId, { providerId: 'affine' }); + } else { + return null; + } +}; +export const AppStateProvider = ({ children }: { children?: ReactNode }) => { + const refreshWorkspacesMeta = async () => { + const dc = await getDataCenter(); + const workspacesMeta = await dc.apis.getWorkspaces().catch(() => { + return []; + }); + setState(state => ({ ...state, workspacesMeta })); + }; + + const [state, setState] = useState({ + user: null, + workspacesMeta: [], + currentWorkspaceId: '', + currentWorkspace: null, + currentPage: null, + editor: null, + refreshWorkspacesMeta, + synced: true, + }); + + const [createEditorHandler, _setCreateEditorHandler] = + useState(); + + const setCreateEditorHandler = useCallback( + (handler: CreateEditorHandler) => { + _setCreateEditorHandler(() => handler); + }, + [_setCreateEditorHandler] + ); + + const loadWorkspace = useRef(() => + Promise.resolve(null) + ); + loadWorkspace.current = async (workspaceId: string) => { + if (state.currentWorkspaceId === workspaceId) { + return state.currentWorkspace; + } + const workspace = + (await loadWorkspaceHandler(workspaceId, state.user)) || null; + + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-expect-error + window.workspace = workspace; + // FIXME: there needs some method to destroy websocket. + // Or we need a manager to manage websocket. + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-expect-error + state.currentWorkspace?.__ws__?.destroy(); + + setState(state => ({ + ...state, + currentWorkspace: workspace, + currentWorkspaceId: workspaceId, + })); + return workspace; + }; + + const loadPage = useRef(() => + Promise.resolve(null) + ); + loadPage.current = async (pageId: string) => { + const { currentWorkspace, currentPage } = state; + if (pageId === currentPage?.id) { + return currentPage; + } + const page = (pageId ? currentWorkspace?.getPage(pageId) : null) || null; + setState(state => ({ ...state, currentPage: page })); + return page; + }; + + const createEditor = useRef< + ((page: Page) => EditorContainer | null) | undefined + >(); + createEditor.current = () => { + const { currentPage, currentWorkspace } = state; + if (!currentPage || !currentWorkspace) { + return null; + } + const editor = createEditorHandler?.(currentPage) || null; + + if (editor) { + const pageMeta = currentWorkspace.meta.pageMetas.find( + p => p.id === currentPage.id + ); + if (pageMeta?.mode) { + editor.mode = pageMeta.mode as 'page' | 'edgeless' | undefined; + } + if (pageMeta?.trash) { + editor.readonly = true; + } + } + + return editor; + }; + + const setEditor = useRef<(editor: AppStateValue['editor']) => void>(); + + setEditor.current = (editor: AppStateValue['editor']) => { + setState(state => ({ ...state, editor })); + }; + + const context = useMemo( + () => ({ + ...state, + setState, + createEditor, + setEditor, + loadWorkspace: loadWorkspace.current, + loadPage: loadPage.current, + }), + [state, setState, loadPage, loadWorkspace] + ); + + return ( + + + {children} + + ); +}; diff --git a/packages/app/src/providers/app-state-provider/context.ts b/packages/app/src/providers/app-state-provider3/context.ts similarity index 100% rename from packages/app/src/providers/app-state-provider/context.ts rename to packages/app/src/providers/app-state-provider3/context.ts diff --git a/packages/app/src/providers/app-state-provider3/index.ts b/packages/app/src/providers/app-state-provider3/index.ts new file mode 100644 index 0000000000..88c38fd7cf --- /dev/null +++ b/packages/app/src/providers/app-state-provider3/index.ts @@ -0,0 +1,2 @@ +export * from './context'; +export * from './interface'; diff --git a/packages/app/src/providers/app-state-provider3/interface.ts b/packages/app/src/providers/app-state-provider3/interface.ts new file mode 100644 index 0000000000..19b028703d --- /dev/null +++ b/packages/app/src/providers/app-state-provider3/interface.ts @@ -0,0 +1,17 @@ +import { PageMeta as OriginalPageMeta } from '@blocksuite/store/dist/workspace/workspace'; + +// export type PageMeta = { +// favorite: boolean; +// trash: boolean; +// trashDate: number | void; +// updatedDate: number | void; +// mode: EditorContainer['mode']; +// } & OriginalPageMeta; + +export interface PageMeta extends OriginalPageMeta { + favorite: boolean; + trash: boolean; + trashDate: number; + updatedDate: number; + mode: 'edgeless' | 'page'; +} diff --git a/packages/data-center/src/index.ts b/packages/data-center/src/index.ts index 6fbb0e45db..297b1cf0c1 100644 --- a/packages/data-center/src/index.ts +++ b/packages/data-center/src/index.ts @@ -26,5 +26,6 @@ const _initializeDataCenter = () => { export const getDataCenter = _initializeDataCenter(); export type { AccessTokenMessage } from './provider/affine/apis'; -export type { Workspace } from './types'; +export * from './types'; +export { DataCenter } from './datacenter'; export { getLogger } from './logger'; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 37654527ef..40aca469e1 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -151,7 +151,7 @@ importers: y-protocols: ^1.0.5 yjs: ^13.5.44 dependencies: - '@blocksuite/blocks': 0.3.1-20230109032243-37ad3ba_yjs@13.5.44 + '@blocksuite/blocks': 0.3.1_yjs@13.5.44 '@blocksuite/store': 0.3.1-20230109032243-37ad3ba_yjs@13.5.44 debug: 4.3.4 encoding: 0.1.13 @@ -1491,8 +1491,8 @@ packages: resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==} dev: true - /@blocksuite/blocks/0.3.1-20230106060050-1aad55d_yjs@13.5.44: - resolution: {integrity: sha512-qRNXmhjw+GAGsV1mI2XXPxYTlHfsFHv9ttTCNQ6IIcxvc5Hh6lWmdwVibxvlpYUkgEc1zv3/GxOEsR/ngpZXzQ==} + /@blocksuite/blocks/0.3.1-20230109032243-37ad3ba_yjs@13.5.44: + resolution: {integrity: sha512-UTlbk0Is7TMRBbvUyM2nivbqM/TLwRj1qArMYbOmvDGUNYadWo68cTwv/Ej2WwiKn22q4/4JHryGsv3gTCRz1Q==} dependencies: '@blocksuite/phasor': 0.3.1-20230109032243-37ad3ba_yjs@13.5.44 '@blocksuite/store': 0.3.1-20230109032243-37ad3ba_yjs@13.5.44 @@ -1511,6 +1511,25 @@ packages: - yjs dev: false + /@blocksuite/blocks/0.3.1_yjs@13.5.44: + resolution: {integrity: sha512-b0dGz2MG4yIgngJPRumaMY58wAsd2FEVSZl3tpCXlagK9I0HD165Bq70PxcaRHVjBSV1Gf29ZVHUF6BVTYogPw==} + dependencies: + '@blocksuite/store': 0.3.1_yjs@13.5.44 + '@tldraw/intersect': 1.8.0 + autosize: 5.0.2 + highlight.js: 11.7.0 + hotkeys-js: 3.10.1 + lit: 2.5.0 + perfect-freehand: 1.2.0 + quill: 1.3.7 + quill-cursors: 4.0.0 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + - yjs + dev: false + /@blocksuite/editor/0.3.1-20230109032243-37ad3ba_yjs@13.5.44: resolution: {integrity: sha512-bYbMn4EL/od+xP4K3u2kJT08kJBpK6H7b4cbRb9No3SUwgNHvvVNxia/QH1AQXyKaZQj/DHFgVxrw9GKo2GIPA==} dependencies: @@ -1565,6 +1584,27 @@ packages: - utf-8-validate dev: false + /@blocksuite/store/0.3.1_yjs@13.5.44: + resolution: {integrity: sha512-kynVTDfNCSChz2JI2rtGHxRIV2YrLzvAgVajcbfDVCuXKG0siBoEjLasG1a0kvevbvW/FabrNAj+xaIplklioA==} + peerDependencies: + yjs: ^13 + dependencies: + '@types/flexsearch': 0.7.3 + '@types/quill': 1.3.10 + buffer: 6.0.3 + flexsearch: 0.7.21 + idb-keyval: 6.2.0 + ky: 0.33.1 + lib0: 0.2.58 + y-protocols: 1.0.5 + y-webrtc: 10.2.3 + yjs: 13.5.44 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + dev: false + /@changesets/apply-release-plan/6.1.3: resolution: {integrity: sha512-ECDNeoc3nfeAe1jqJb5aFQX7CqzQhD2klXRez2JDb/aVpGUbX673HgKrnrgJRuQR/9f2TtLoYIzrGB9qwD77mg==} dependencies: