From aa58bbcd1bb7b8ddc45c459955ebd05828218cb1 Mon Sep 17 00:00:00 2001 From: MingLiang Wang Date: Fri, 23 Dec 2022 18:58:15 +0800 Subject: [PATCH 1/5] feat: add email avatar --- .../workspace-setting/general/general.tsx | 2 +- .../WorkspaceSelector/SelectorPopperContent.tsx | 8 +++++--- .../WorkspaceSelector/WorkspaceSelector.tsx | 14 ++++++++++---- 3 files changed, 16 insertions(+), 8 deletions(-) diff --git a/packages/app/src/components/workspace-setting/general/general.tsx b/packages/app/src/components/workspace-setting/general/general.tsx index d5615a1656..98c1dcb997 100644 --- a/packages/app/src/components/workspace-setting/general/general.tsx +++ b/packages/app/src/components/workspace-setting/general/general.tsx @@ -57,7 +57,7 @@ export const GeneralPage = ({ meta => meta.id === workspace.id ); const nextWorkSpaceId = - currentWorkspaceIndex > workspacesMeta.length - 1 + currentWorkspaceIndex === workspacesMeta.length - 1 ? workspacesMeta[currentWorkspaceIndex - 1]?.id : workspacesMeta[currentWorkspaceIndex + 1]?.id; const handleClickDelete = () => { 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 87c44070a6..6f6916c44e 100644 --- a/packages/app/src/components/workspace-slider-bar/WorkspaceSelector/SelectorPopperContent.tsx +++ b/packages/app/src/components/workspace-slider-bar/WorkspaceSelector/SelectorPopperContent.tsx @@ -109,9 +109,11 @@ export const SelectorPopperContent = ({ key={workspace.id} id={workspace.id} icon={ - workspaces[workspace.id]?.meta.avatar - ? 'api/blob/' + workspaces[workspace.id]?.meta.avatar - : '' + (workspaces[workspace.id]?.meta.avatar && + `/api/blob/${workspaces[workspace.id]?.meta.avatar}`) || + (workspace.type === WorkspaceType.Private + ? user.avatar_url + : `loading...`) } onClick={handleClickSettingWorkspace} name={ diff --git a/packages/app/src/components/workspace-slider-bar/WorkspaceSelector/WorkspaceSelector.tsx b/packages/app/src/components/workspace-slider-bar/WorkspaceSelector/WorkspaceSelector.tsx index d97eb8ceeb..f7967febc6 100644 --- a/packages/app/src/components/workspace-slider-bar/WorkspaceSelector/WorkspaceSelector.tsx +++ b/packages/app/src/components/workspace-slider-bar/WorkspaceSelector/WorkspaceSelector.tsx @@ -24,12 +24,18 @@ export const WorkspaceSelector = () => { - + {workspaceMeta?.type === WorkspaceType.Private && user ? ( + user?.name[0] + ) : ( + + )} {currentWorkspace?.meta.name || From c58dd6f403257054e8c18ab10df58d21fa6ef4fb Mon Sep 17 00:00:00 2001 From: QiShaoXuan Date: Fri, 23 Dec 2022 19:04:42 +0800 Subject: [PATCH 2/5] feat: modify router logic, can not create workspace by router query --- .../src/components/workspace-layout/index.tsx | 9 ++-- .../app/src/hooks/use-ensure-workspace.ts | 54 +++++++++++++++++++ .../pages/workspace/[workspaceId]/index.tsx | 18 ++++--- packages/app/src/pages/workspace/index.tsx | 12 +++-- .../providers/app-state-provider/provider.tsx | 3 +- 5 files changed, 78 insertions(+), 18 deletions(-) create mode 100644 packages/app/src/hooks/use-ensure-workspace.ts diff --git a/packages/app/src/components/workspace-layout/index.tsx b/packages/app/src/components/workspace-layout/index.tsx index 46aa3701e7..3da85a5ad4 100644 --- a/packages/app/src/components/workspace-layout/index.tsx +++ b/packages/app/src/components/workspace-layout/index.tsx @@ -3,14 +3,11 @@ import { WorkSpaceSliderBar } from '@/components/workspace-slider-bar'; import { useRouter } from 'next/router'; import { StyledPage, StyledWrapper } from './styles'; import { PropsWithChildren } from 'react'; -import { useAppState } from '@/providers/app-state-provider'; -import { useInitWorkspace } from '@/hooks/use-init-workspace'; +import useEnsureWorkspace from '@/hooks/use-ensure-workspace'; export const WorkspaceDefender = ({ children }: PropsWithChildren) => { - const { synced } = useAppState(); - const { loading } = useInitWorkspace(!synced); - - return <>{!loading ? children : null}; + const { workspaceLoaded } = useEnsureWorkspace(); + return <>{workspaceLoaded ? children : null}; }; export const WorkspaceLayout = ({ children }: PropsWithChildren) => { diff --git a/packages/app/src/hooks/use-ensure-workspace.ts b/packages/app/src/hooks/use-ensure-workspace.ts new file mode 100644 index 0000000000..60009d8298 --- /dev/null +++ b/packages/app/src/hooks/use-ensure-workspace.ts @@ -0,0 +1,54 @@ +import { useState, useEffect } from 'react'; +import { useAppState } from '@/providers/app-state-provider'; +import { useRouter } from 'next/router'; + +// It is a fully effective hook +// 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 router = useRouter(); + const defaultOutLineWorkspaceId = '112233'; + + 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 ( + workspacesMeta.length && + router.query.workspaceId && + workspacesMeta.findIndex( + meta => meta.id.toString() === router.query.workspaceId + ) === -1 + ) { + router.push('/404'); + return; + } + // If user is not login and input a custom workspaceId, jump to 404 page + if ( + !user && + router.query.workspaceId && + router.query.workspaceId !== defaultOutLineWorkspaceId + ) { + router.push('/404'); + return; + } + + const workspaceId = user + ? (router.query.workspaceId as string) || workspacesMeta?.[0]?.id + : defaultOutLineWorkspaceId; + + loadWorkspace(workspaceId).finally(() => { + setWorkspaceLoaded(true); + }); + }, [loadWorkspace, router, synced, user, workspacesMeta]); + + return { + workspaceLoaded, + }; +}; + +export default useEnsureWorkspace; diff --git a/packages/app/src/pages/workspace/[workspaceId]/index.tsx b/packages/app/src/pages/workspace/[workspaceId]/index.tsx index 2b3bc8f8bd..c7b158b2f9 100644 --- a/packages/app/src/pages/workspace/[workspaceId]/index.tsx +++ b/packages/app/src/pages/workspace/[workspaceId]/index.tsx @@ -1,19 +1,19 @@ import { useEffect } from 'react'; import { useRouter } from 'next/router'; import { useAppState } from '@/providers/app-state-provider/context'; -import { useInitWorkspace } from '@/hooks/use-init-workspace'; +import useEnsureWorkspace from '@/hooks/use-ensure-workspace'; const WorkspaceIndex = () => { const router = useRouter(); - const { createPage, currentWorkspaceId } = useAppState(); - const { workspace } = useInitWorkspace(); + const { createPage, currentWorkspaceId, currentWorkspace } = useAppState(); + const { workspaceLoaded } = useEnsureWorkspace(); useEffect(() => { const initPage = async () => { - if (!workspace) { + if (!workspaceLoaded) { return; } - const savedPageId = workspace.meta.pageMetas[0]?.id; + const savedPageId = currentWorkspace?.meta.pageMetas[0]?.id; if (savedPageId) { router.replace(`/workspace/${currentWorkspaceId}/${savedPageId}`); return; @@ -23,7 +23,13 @@ const WorkspaceIndex = () => { router.replace(`/workspace/${currentWorkspaceId}/${pageId}`); }; initPage(); - }, [workspace, currentWorkspaceId, createPage, router]); + }, [ + currentWorkspace, + currentWorkspaceId, + createPage, + router, + workspaceLoaded, + ]); return <>; }; diff --git a/packages/app/src/pages/workspace/index.tsx b/packages/app/src/pages/workspace/index.tsx index 244c91763a..d81e348517 100644 --- a/packages/app/src/pages/workspace/index.tsx +++ b/packages/app/src/pages/workspace/index.tsx @@ -1,16 +1,18 @@ import { useEffect } from 'react'; import { useRouter } from 'next/router'; -import { useInitWorkspace } from '@/hooks/use-init-workspace'; +import { useAppState } from '@/providers/app-state-provider'; +import useEnsureWorkspace from '@/hooks/use-ensure-workspace'; export const WorkspaceIndex = () => { const router = useRouter(); - const { workspaceId, workspace } = useInitWorkspace(); + const { currentWorkspaceId } = useAppState(); + const { workspaceLoaded } = useEnsureWorkspace(); useEffect(() => { - if (workspace) { - router.push(`/workspace/${workspaceId}`); + if (workspaceLoaded) { + router.push(`/workspace/${currentWorkspaceId}`); } - }, [router, workspace, workspaceId]); + }, [currentWorkspaceId, router, workspaceLoaded]); return <>; }; diff --git a/packages/app/src/providers/app-state-provider/provider.tsx b/packages/app/src/providers/app-state-provider/provider.tsx index ee6cbc97f8..6a344fa61f 100644 --- a/packages/app/src/providers/app-state-provider/provider.tsx +++ b/packages/app/src/providers/app-state-provider/provider.tsx @@ -194,6 +194,7 @@ export const AppStateProvider = ({ children }: { children?: ReactNode }) => { return; } const callback = async (user: AccessTokenMessage | null) => { + const isLogin = token.isLogin; const workspacesMeta = user ? await getWorkspaces().catch(() => { return []; @@ -204,7 +205,7 @@ export const AppStateProvider = ({ children }: { children?: ReactNode }) => { ...state, user: user, workspacesMeta, - synced: true, + synced: isLogin ? !!user : true, })); }; token.onChange(callback); From e50919d106cb95f71bbbfae469a780288dc17594 Mon Sep 17 00:00:00 2001 From: MingLiang Wang Date: Fri, 23 Dec 2022 19:22:06 +0800 Subject: [PATCH 3/5] feat: avatar --- .../app/src/components/workspace-setting/general/general.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/app/src/components/workspace-setting/general/general.tsx b/packages/app/src/components/workspace-setting/general/general.tsx index 98c1dcb997..f94e10a345 100644 --- a/packages/app/src/components/workspace-setting/general/general.tsx +++ b/packages/app/src/components/workspace-setting/general/general.tsx @@ -90,7 +90,7 @@ export const GeneralPage = ({ alt="workspace avatar" src={ workspaces[workspace.id]?.meta.avatar - ? 'api/blob/' + workspaces[workspace.id]?.meta.avatar + ? '/api/blob/' + workspaces[workspace.id]?.meta.avatar : '' } > From 235ea0407890658d1cff0f33d16c9cdcefd83f6d Mon Sep 17 00:00:00 2001 From: MingLiang Wang Date: Fri, 23 Dec 2022 19:28:06 +0800 Subject: [PATCH 4/5] feat: catch 401 error --- .../app-state-provider/dynamic-blocksuite.tsx | 29 +++++++++------- .../providers/app-state-provider/provider.tsx | 33 ++++--------------- 2 files changed, 24 insertions(+), 38 deletions(-) diff --git a/packages/app/src/providers/app-state-provider/dynamic-blocksuite.tsx b/packages/app/src/providers/app-state-provider/dynamic-blocksuite.tsx index 8315603738..c5e5e2b2b5 100644 --- a/packages/app/src/providers/app-state-provider/dynamic-blocksuite.tsx +++ b/packages/app/src/providers/app-state-provider/dynamic-blocksuite.tsx @@ -27,8 +27,7 @@ const DynamicBlocksuite = ({ useEffect(() => { const openWorkspace: LoadWorkspaceHandler = ( workspaceId: string, - websocket = false, - user + websocket = false ) => // eslint-disable-next-line no-async-promise-executor new Promise(async resolve => { @@ -64,16 +63,22 @@ const DynamicBlocksuite = ({ const indexDBProvider = workspace.providers.find( p => p instanceof IndexedDBDocProvider ); - if (user) { - const updates = await downloadWorkspace({ workspaceId }); - updates && - StoreWorkspace.Y.applyUpdate( - workspace.doc, - new Uint8Array(updates) - ); - // if after update, the space:meta is empty, then we need to get map with doc - workspace.doc.getMap('space:meta'); - } + await new Promise(resolve => { + downloadWorkspace({ workspaceId }) + .then(async updates => { + updates && + StoreWorkspace.Y.applyUpdate( + workspace.doc, + new Uint8Array(updates) + ); + // if after update, the space:meta is empty, then we need to get map with doc + workspace.doc.getMap('space:meta'); + resolve(undefined); + }) + .catch(() => { + resolve(undefined); + }); + }); if (indexDBProvider) { (indexDBProvider as IndexedDBDocProvider).on('synced', async () => { resolve(workspace); diff --git a/packages/app/src/providers/app-state-provider/provider.tsx b/packages/app/src/providers/app-state-provider/provider.tsx index 6a344fa61f..52acf87ffc 100644 --- a/packages/app/src/providers/app-state-provider/provider.tsx +++ b/packages/app/src/providers/app-state-provider/provider.tsx @@ -41,31 +41,11 @@ export const AppStateProvider = ({ children }: { children?: ReactNode }) => { workspaces: {}, }); - const refreshWorkspacesInfo = async ( - workspaces: AppStateValue['workspaces'] - ) => { - return Promise.all( - Object.entries(workspaces).map(async ([workspaceId, workspace]) => { - if (workspace) { - const updates = await downloadWorkspace({ workspaceId }); - updates && - StoreWorkspace.Y.applyUpdate( - workspace.doc, - new Uint8Array(updates) - ); - // if after update, the space:meta is empty, then we need to get map with doc - workspace.doc.getMap('space:meta'); - } - }) - ); - }; - useEffect(() => { (async () => { const workspacesList = await Promise.all( state.workspacesMeta.map(async ({ id }) => { - const workspace = - (await loadWorkspaceHandler?.(id, false, state.user)) || null; + const workspace = (await loadWorkspaceHandler?.(id, false)) || null; return { id, workspace }; }) ); @@ -73,7 +53,6 @@ export const AppStateProvider = ({ children }: { children?: ReactNode }) => { workspacesList.forEach(({ id, workspace }) => { workspaces[id] = workspace; }); - await refreshWorkspacesInfo(workspaces); setState(state => ({ ...state, workspaces, @@ -108,9 +87,7 @@ export const AppStateProvider = ({ children }: { children?: ReactNode }) => { if (state.currentWorkspaceId === workspaceId) { return state.currentWorkspace; } - - const workspace = - (await loadWorkspaceHandler?.(workspaceId, true, state.user)) || null; + const workspace = (await loadWorkspaceHandler?.(workspaceId, true)) || null; // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-expect-error @@ -195,6 +172,11 @@ export const AppStateProvider = ({ children }: { children?: ReactNode }) => { } const callback = async (user: AccessTokenMessage | null) => { const isLogin = token.isLogin; + setState(state => ({ + ...state, + user, + })); + const workspacesMeta = user ? await getWorkspaces().catch(() => { return []; @@ -203,7 +185,6 @@ export const AppStateProvider = ({ children }: { children?: ReactNode }) => { setState(state => ({ ...state, - user: user, workspacesMeta, synced: isLogin ? !!user : true, })); From 790a84d742279c8f29826365f0955d1f764e29c3 Mon Sep 17 00:00:00 2001 From: QiShaoXuan Date: Fri, 23 Dec 2022 19:29:17 +0800 Subject: [PATCH 5/5] feat: just first page in workspace init an introduce markdown --- .../src/pages/workspace/[workspaceId]/[pageId].tsx | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/packages/app/src/pages/workspace/[workspaceId]/[pageId].tsx b/packages/app/src/pages/workspace/[workspaceId]/[pageId].tsx index 7e7fb325ec..4b5b5f30fe 100644 --- a/packages/app/src/pages/workspace/[workspaceId]/[pageId].tsx +++ b/packages/app/src/pages/workspace/[workspaceId]/[pageId].tsx @@ -40,7 +40,8 @@ const Page: NextPageWithLayout = () => { editorContainer.current?.appendChild(editor); setEditor?.current?.(editor); if (currentPage!.isEmpty) { - const title = 'Welcome to the AFFiNE Alpha'; + const isFirstPage = currentWorkspace?.meta.pageMetas.length === 1; + const title = isFirstPage ? 'Welcome to the AFFiNE Alpha' : ''; const pageId = currentPage!.addBlock({ flavour: 'affine:page', title, @@ -49,9 +50,12 @@ const Page: NextPageWithLayout = () => { { flavour: 'affine:group' }, pageId ); - editor.clipboard.importMarkdown(exampleMarkdown, `${groupId}`); - - currentWorkspace!.setPageMeta(currentPage!.id, { title }); + currentPage!.addBlock({ flavour: 'affine:group' }, pageId); + // If this is a first page in workspace, init an introduction markdown + if (isFirstPage) { + editor.clipboard.importMarkdown(exampleMarkdown, `${groupId}`); + currentWorkspace!.setPageMeta(currentPage!.id, { title }); + } currentPage!.resetHistory(); } }