refactor: init hook useRouterTargetWorkspace (#1127)

This commit is contained in:
Himself65
2023-02-20 01:37:18 -06:00
committed by GitHub
parent 54963842ed
commit e583725cd2
9 changed files with 191 additions and 211 deletions

View File

@@ -1,18 +1,14 @@
import { useGlobalState } from '@affine/store';
import { useRouter } from 'next/router'; import { useRouter } from 'next/router';
import { PropsWithChildren } from 'react'; import { PropsWithChildren, useEffect } from 'react';
import HelpIsland from '@/components/help-island'; import HelpIsland from '@/components/help-island';
import { WorkSpaceSliderBar } from '@/components/workspace-slider-bar'; import { WorkSpaceSliderBar } from '@/components/workspace-slider-bar';
import useEnsureWorkspace from '@/hooks/use-ensure-workspace'; import { useRouterTargetWorkspace } from '@/hooks/use-router-target-workspace';
import { PageLoading } from '../loading'; import { PageLoading } from '../loading';
import { StyledPage, StyledToolWrapper, StyledWrapper } from './styles'; import { StyledPage, StyledToolWrapper, StyledWrapper } from './styles';
export const WorkspaceDefender = ({ children }: PropsWithChildren) => {
const { workspaceLoaded } = useEnsureWorkspace();
return <>{workspaceLoaded ? children : <PageLoading />}</>;
};
export const WorkspaceLayout = ({ children }: PropsWithChildren) => { export const WorkspaceLayout = ({ children }: PropsWithChildren) => {
const router = useRouter(); const router = useRouter();
@@ -35,10 +31,22 @@ export const WorkspaceLayout = ({ children }: PropsWithChildren) => {
}; };
export const Layout = ({ children }: PropsWithChildren) => { export const Layout = ({ children }: PropsWithChildren) => {
return ( const { targetWorkspace, exist } = useRouterTargetWorkspace();
<WorkspaceDefender> const router = useRouter();
<WorkspaceLayout>{children}</WorkspaceLayout> const loadWorkspace = useGlobalState(store => store.loadWorkspace);
</WorkspaceDefender> useEffect(() => {
); if (!exist) {
router.replace('/404');
}
}, [exist, router]);
useEffect(() => {
if (exist && targetWorkspace) {
loadWorkspace(targetWorkspace.id);
}
}, [exist, loadWorkspace, targetWorkspace]);
if (!targetWorkspace) {
return <PageLoading />;
}
return <WorkspaceLayout>{children}</WorkspaceLayout>;
}; };
export default Layout; export default Layout;

View File

@@ -1,73 +0,0 @@
import { assertEquals } from '@blocksuite/global/utils';
import { useRouter } from 'next/router';
import { useCallback, useEffect, useState } from 'react';
import { useDataCenter, useGlobalState } from '@/store/app';
// todo: refactor with suspense mode
// It is a fully effective hook
// Cause it not just ensure workspace loaded, but also have router change.
export const useEnsureWorkspace = () => {
const dataCenter = useDataCenter();
const currentWorkspace = useGlobalState(
useCallback(store => store.currentDataCenterWorkspace, [])
);
const loadWorkspace = useGlobalState(
useCallback(store => store.loadWorkspace, [])
);
const router = useRouter();
const [currentWorkspaceId, setCurrentWorkspaceId] = useState<string | null>(
typeof router.query.workspaceId === 'string'
? router.query.workspaceId
: null
);
// const defaultOutLineWorkspaceId = '99ce7eb7';
// console.log(defaultOutLineWorkspaceId);
useEffect(() => {
const abortController = new AbortController();
const workspaceId =
(router.query.workspaceId as string) || dataCenter.workspaces[0]?.id;
// If router.query.workspaceId is not in workspace list, jump to 404 page
// If workspaceList is empty, we need to create a default workspace but not jump to 404
if (
workspaceId &&
dataCenter.workspaces.length &&
dataCenter.workspaces.findIndex(
meta => meta.id.toString() === 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;
// }
loadWorkspace(workspaceId, abortController.signal).then(unit => {
if (!abortController.signal.aborted && unit) {
setCurrentWorkspaceId(unit.id);
assertEquals(unit.id, workspaceId);
}
});
return () => {
abortController.abort();
};
}, [dataCenter, loadWorkspace, router]);
return {
workspaceLoaded: currentWorkspace?.id === currentWorkspaceId,
activeWorkspaceId: currentWorkspace?.id ?? router.query.workspaceId,
};
};
export default useEnsureWorkspace;

View File

@@ -5,7 +5,7 @@ import { uuidv4, Workspace } from '@blocksuite/store';
// eslint-disable-next-line @typescript-eslint/no-restricted-imports // eslint-disable-next-line @typescript-eslint/no-restricted-imports
import type { QueryContent } from '@blocksuite/store/dist/workspace/search'; import type { QueryContent } from '@blocksuite/store/dist/workspace/search';
import { useRouter } from 'next/router'; import { useRouter } from 'next/router';
import { useCallback } from 'react'; import { useCallback, useMemo } from 'react';
import { useChangePageMeta } from '@/hooks/use-change-page-meta'; import { useChangePageMeta } from '@/hooks/use-change-page-meta';
import { useGlobalState } from '@/store/app'; import { useGlobalState } from '@/store/app';
@@ -48,104 +48,107 @@ export const usePageHelper = (): EditorHandlers => {
useCallback(store => store.currentDataCenterWorkspace, []) useCallback(store => store.currentDataCenterWorkspace, [])
); );
return { return useMemo(
createPage: ({ () => ({
pageId = uuidv4().replaceAll('-', ''), createPage: ({
title = '', pageId = uuidv4().replaceAll('-', ''),
} = {}) => { title = '',
return new Promise(resolve => { } = {}) => {
if (!currentWorkspace) { return new Promise(resolve => {
return resolve(null); if (!currentWorkspace) {
} return resolve(null);
currentWorkspace.blocksuiteWorkspace?.createPage(pageId);
currentWorkspace.blocksuiteWorkspace?.signals.pageAdded.once(
addedPageId => {
currentWorkspace.blocksuiteWorkspace?.setPageMeta(addedPageId, {
title,
});
resolve(addedPageId);
} }
); currentWorkspace.blocksuiteWorkspace?.createPage(pageId);
}); currentWorkspace.blocksuiteWorkspace?.signals.pageAdded.once(
}, addedPageId => {
toggleFavoritePage: async pageId => { currentWorkspace.blocksuiteWorkspace?.setPageMeta(addedPageId, {
const pageMeta = getPageMeta(currentWorkspace, pageId); title,
if (!pageMeta) { });
return Promise.reject('No page'); resolve(addedPageId);
} }
const favorite = !pageMeta.favorite; );
changePageMeta(pageMeta.id, { });
favorite, },
}); toggleFavoritePage: async pageId => {
return favorite; const pageMeta = getPageMeta(currentWorkspace, pageId);
}, if (!pageMeta) {
toggleDeletePage: async pageId => { return Promise.reject('No page');
const pageMeta = getPageMeta(currentWorkspace, pageId);
if (!pageMeta) {
return Promise.reject('No page');
}
const trash = !pageMeta.trash;
changePageMeta(pageMeta.id, {
trash,
trashDate: +new Date(),
});
return trash;
},
search: (query: QueryContent, workspace?: Workspace) => {
if (workspace) {
return workspace.search(query);
}
if (currentWorkspace) {
if (currentWorkspace.blocksuiteWorkspace) {
return currentWorkspace.blocksuiteWorkspace.search(query);
} }
} const favorite = !pageMeta.favorite;
return new Map(); changePageMeta(pageMeta.id, {
}, favorite,
changePageMode: async (pageId, mode) => { });
const pageMeta = getPageMeta(currentWorkspace, pageId); return favorite;
if (!pageMeta) { },
return Promise.reject('No page'); toggleDeletePage: async pageId => {
} const pageMeta = getPageMeta(currentWorkspace, pageId);
editor?.setAttribute('mode', mode as string); if (!pageMeta) {
return Promise.reject('No page');
}
const trash = !pageMeta.trash;
changePageMeta(pageMeta.id, { changePageMeta(pageMeta.id, {
mode, trash,
}); trashDate: +new Date(),
return mode; });
}, return trash;
permanentlyDeletePage: pageId => { },
// TODO: workspace.meta.removePage or workspace.removePage? search: (query: QueryContent, workspace?: Workspace) => {
if (workspace) {
return workspace.search(query);
}
if (currentWorkspace) {
if (currentWorkspace.blocksuiteWorkspace) {
return currentWorkspace.blocksuiteWorkspace.search(query);
}
}
return new Map();
},
changePageMode: async (pageId, mode) => {
const pageMeta = getPageMeta(currentWorkspace, pageId);
if (!pageMeta) {
return Promise.reject('No page');
}
currentWorkspace!.blocksuiteWorkspace?.meta.removePage(pageId); editor?.setAttribute('mode', mode as string);
},
openPage: (pageId, query = {}, newTab = false) => {
pageId = pageId.replace('space:', '');
if (newTab) { changePageMeta(pageMeta.id, {
window.open(`/workspace/${currentWorkspace?.id}/${pageId}`, '_blank'); mode,
return Promise.resolve(true); });
} return mode;
return router.push({ },
pathname: `/workspace/${currentWorkspace?.id}/${pageId}`, permanentlyDeletePage: pageId => {
query, // TODO: workspace.meta.removePage or workspace.removePage?
});
},
getPageMeta: pageId => {
if (!currentWorkspace) {
return null;
}
return ( currentWorkspace!.blocksuiteWorkspace?.meta.removePage(pageId);
(currentWorkspace.blocksuiteWorkspace?.meta.pageMetas.find( },
page => page.id === pageId openPage: (pageId, query = {}, newTab = false) => {
) as PageMeta) || null pageId = pageId.replace('space:', '');
);
}, if (newTab) {
}; window.open(`/workspace/${currentWorkspace?.id}/${pageId}`, '_blank');
return Promise.resolve(true);
}
return router.push({
pathname: `/workspace/${currentWorkspace?.id}/${pageId}`,
query,
});
},
getPageMeta: pageId => {
if (!currentWorkspace) {
return null;
}
return (
(currentWorkspace.blocksuiteWorkspace?.meta.pageMetas.find(
page => page.id === pageId
) as PageMeta) || null
);
},
}),
[changePageMeta, currentWorkspace, editor, router]
);
}; };
export default usePageHelper; export default usePageHelper;

View File

@@ -0,0 +1,26 @@
import { useDataCenter, useDataCenterWorkspace } from '@affine/store';
import { useRouter } from 'next/router';
import { useMemo } from 'react';
export function useRouterTargetWorkspace() {
const router = useRouter();
const dataCenter = useDataCenter();
const workspaceId =
typeof router.query.workspaceId === 'string'
? router.query.workspaceId
: dataCenter.workspaces.at(0)?.id ?? null;
const targetWorkspace = useDataCenterWorkspace(workspaceId);
const notExist = useMemo(
() =>
workspaceId &&
dataCenter.workspaces.length &&
dataCenter.workspaces.findIndex(
meta => meta.id.toString() === workspaceId
) === -1,
[dataCenter.workspaces, workspaceId]
);
return {
targetWorkspace,
exist: !notExist,
};
}

View File

@@ -1,44 +1,48 @@
import { useRouter } from 'next/router'; import { useRouter } from 'next/router';
import { useCallback, useEffect } from 'react'; import { useEffect } from 'react';
import { PageLoading } from '@/components/loading'; import { PageLoading } from '@/components/loading';
import useEnsureWorkspace from '@/hooks/use-ensure-workspace';
import usePageHelper from '@/hooks/use-page-helper'; import usePageHelper from '@/hooks/use-page-helper';
import { useGlobalState } from '@/store/app'; import { useRouterTargetWorkspace } from '@/hooks/use-router-target-workspace';
const WorkspaceIndex = () => { const WorkspaceIndex = () => {
const router = useRouter(); const router = useRouter();
const currentWorkspace = useGlobalState( const { targetWorkspace, exist } = useRouterTargetWorkspace();
useCallback(store => store.currentDataCenterWorkspace, [])
);
const { createPage } = usePageHelper(); const { createPage } = usePageHelper();
const { workspaceLoaded, activeWorkspaceId } = useEnsureWorkspace();
useEffect(() => { useEffect(() => {
if (!exist) {
router.push('/404');
return;
}
const abortController = new AbortController();
const initPage = async () => { const initPage = async () => {
if (!workspaceLoaded) { if (abortController.signal.aborted) {
return;
}
if (!targetWorkspace) {
return; return;
} }
const savedPageId = const savedPageId =
currentWorkspace?.blocksuiteWorkspace?.meta.pageMetas.find( targetWorkspace.blocksuiteWorkspace?.meta.pageMetas.find(
meta => !meta.trash meta => !meta.trash
)?.id; )?.id;
if (savedPageId) { if (savedPageId) {
router.replace(`/workspace/${activeWorkspaceId}/${savedPageId}`); router.replace(`/workspace/${targetWorkspace.id}/${savedPageId}`);
return; return;
} else {
const pageId = await createPage();
if (abortController.signal.aborted) {
return;
}
router.replace(`/workspace/${targetWorkspace.id}/${pageId}`);
} }
const pageId = await createPage();
router.replace(`/workspace/${activeWorkspaceId}/${pageId}`);
}; };
initPage(); initPage();
}, [ return () => {
currentWorkspace, abortController.abort();
createPage, };
router, }, [targetWorkspace, createPage, router, exist]);
workspaceLoaded,
activeWorkspaceId,
]);
return <PageLoading />; return <PageLoading />;
}; };

View File

@@ -1,23 +1,19 @@
import { useRouter } from 'next/router'; import { useRouter } from 'next/router';
import { useCallback, useEffect } from 'react'; import { useEffect } from 'react';
import { PageLoading } from '@/components/loading'; import { PageLoading } from '@/components/loading';
import useEnsureWorkspace from '@/hooks/use-ensure-workspace'; import { useRouterTargetWorkspace } from '@/hooks/use-router-target-workspace';
import { useGlobalState } from '@/store/app';
export const WorkspaceIndex = () => { export const WorkspaceIndex = () => {
const router = useRouter(); const router = useRouter();
const currentWorkspace = useGlobalState( const { targetWorkspace, exist } = useRouterTargetWorkspace();
useCallback(store => store.currentDataCenterWorkspace, [])
);
const { workspaceLoaded } = useEnsureWorkspace();
useEffect(() => { useEffect(() => {
if (workspaceLoaded) { if (!exist) {
router.push(`/workspace/${currentWorkspace?.id}`); router.push('/404');
} else if (targetWorkspace) {
router.push(`/workspace/${targetWorkspace.id}`);
} }
}, [currentWorkspace, router, workspaceLoaded]); }, [targetWorkspace, exist, router]);
return <PageLoading />; return <PageLoading />;
}; };

View File

@@ -116,6 +116,15 @@ export function useDataCenter() {
return data as DataCenter; return data as DataCenter;
} }
export function useDataCenterWorkspace(
workspaceId: string | null
): WorkspaceUnit | null {
const { data } = useSWR<WorkspaceUnit | null>(['datacenter', workspaceId], {
fallbackData: null,
});
return data ?? null;
}
export function DataCenterPreloader({ children }: React.PropsWithChildren) { export function DataCenterPreloader({ children }: React.PropsWithChildren) {
const api = useGlobalStateApi(); const api = useGlobalStateApi();
//# region effect for updating workspace page list //# region effect for updating workspace page list

View File

@@ -84,7 +84,7 @@ 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;
export type DataKey = ['datacenter', string] | ['datacenter']; export type DataKey = ['datacenter', string | null] | ['datacenter'];
const swrFetcher = async (keys: DataKey) => { const swrFetcher = async (keys: DataKey) => {
assertEquals(keys[0], 'datacenter'); assertEquals(keys[0], 'datacenter');
@@ -96,6 +96,9 @@ const swrFetcher = async (keys: DataKey) => {
return dataCenter; return dataCenter;
}); });
} else { } else {
if (keys[1] === null) {
return null;
}
const dataCenter = await dataCenterPromise; const dataCenter = await dataCenterPromise;
return dataCenter.loadWorkspace(keys[1]); return dataCenter.loadWorkspace(keys[1]);
} }

View File

@@ -1,4 +1,8 @@
export * from './app'; export * from './app';
export type { PageMeta } from './app/datacenter'; export type { PageMeta } from './app/datacenter';
export { createDefaultWorkspace, DataCenterPreloader } from './app/datacenter'; export { createDefaultWorkspace, DataCenterPreloader } from './app/datacenter';
export { dataCenterPromise, useDataCenter } from './app/datacenter'; export {
dataCenterPromise,
useDataCenter,
useDataCenterWorkspace,
} from './app/datacenter';