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/components/workspace-setting/general/general.tsx b/packages/app/src/components/workspace-setting/general/general.tsx
index d5615a1656..f94e10a345 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 = () => {
@@ -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
: ''
}
>
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 ||
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]/[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();
}
}
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/dynamic-blocksuite.tsx b/packages/app/src/providers/app-state-provider/dynamic-blocksuite.tsx
index 0495063a98..8dd7d7d9b0 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 ee6cbc97f8..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
@@ -194,6 +171,12 @@ export const AppStateProvider = ({ children }: { children?: ReactNode }) => {
return;
}
const callback = async (user: AccessTokenMessage | null) => {
+ const isLogin = token.isLogin;
+ setState(state => ({
+ ...state,
+ user,
+ }));
+
const workspacesMeta = user
? await getWorkspaces().catch(() => {
return [];
@@ -202,9 +185,8 @@ export const AppStateProvider = ({ children }: { children?: ReactNode }) => {
setState(state => ({
...state,
- user: user,
workspacesMeta,
- synced: true,
+ synced: isLogin ? !!user : true,
}));
};
token.onChange(callback);