diff --git a/apps/web/src/hooks/__tests__/index.spec.tsx b/apps/web/src/hooks/__tests__/index.spec.tsx index 6e477365c9..1e1fcbf11d 100644 --- a/apps/web/src/hooks/__tests__/index.spec.tsx +++ b/apps/web/src/hooks/__tests__/index.spec.tsx @@ -5,11 +5,7 @@ import 'fake-indexeddb/auto'; import assert from 'node:assert'; -import { - rootCurrentWorkspaceIdAtom, - rootWorkspacesMetadataAtom, -} from '@affine/workspace/atom'; -import type { LocalWorkspace } from '@affine/workspace/type'; +import { rootCurrentWorkspaceIdAtom } from '@affine/workspace/atom'; import { WorkspaceFlavour } from '@affine/workspace/type'; import type { PageBlockModel } from '@blocksuite/blocks'; import { __unstableSchemas, AffineSchemas } from '@blocksuite/blocks/models'; @@ -21,23 +17,17 @@ import { usePageMetaHelper, } from '@toeverything/hooks/use-block-suite-page-meta'; import { createStore, Provider } from 'jotai'; -import { useRouter } from 'next/router'; import routerMock from 'next-router-mock'; import { createDynamicRouteParser } from 'next-router-mock/dynamic-routes'; import type React from 'react'; import { beforeAll, beforeEach, describe, expect, test, vi } from 'vitest'; -import { currentWorkspaceIdAtom, workspacesAtom } from '../../atoms'; -import { LocalPlugin } from '../../plugins/local'; +import { workspacesAtom } from '../../atoms'; import { BlockSuiteWorkspace, WorkspaceSubPath } from '../../shared'; import { currentWorkspaceAtom, useCurrentWorkspace, } from '../current/use-current-workspace'; -import { - useRecentlyViewed, - useSyncRecentViewsWithRouter, -} from '../use-recent-views'; import { useAppHelper, useWorkspaces } from '../use-workspaces'; vi.mock( @@ -223,58 +213,3 @@ describe('useWorkspaces', () => { expect(firstWorkspace.blockSuiteWorkspace.meta.name).toBe('test'); }); }); - -describe('useRecentlyViewed', () => { - test('basic', async () => { - const { ProviderWrapper, store } = await getJotaiContext(); - const workspaceId = blockSuiteWorkspace.id; - const pageId = 'page0'; - store.set(rootWorkspacesMetadataAtom, [ - { - id: workspaceId, - flavour: WorkspaceFlavour.LOCAL, - }, - ]); - LocalPlugin.CRUD.get = vi.fn().mockResolvedValue({ - id: workspaceId, - flavour: WorkspaceFlavour.LOCAL, - blockSuiteWorkspace, - providers: [], - } satisfies LocalWorkspace); - store.set(currentWorkspaceIdAtom, blockSuiteWorkspace.id); - const workspace = await store.get(currentWorkspaceAtom); - expect(workspace?.id).toBe(blockSuiteWorkspace.id); - const currentHook = renderHook(() => useCurrentWorkspace(), { - wrapper: ProviderWrapper, - }); - expect(currentHook.result.current[0]?.id).toEqual(workspaceId); - await store.get(currentWorkspaceAtom); - const recentlyViewedHook = renderHook(() => useRecentlyViewed(), { - wrapper: ProviderWrapper, - }); - expect(recentlyViewedHook.result.current).toEqual([]); - const routerHook = renderHook(() => useRouter()); - await routerHook.result.current.push({ - pathname: '/workspace/[workspaceId]/[pageId]', - query: { - workspaceId, - pageId, - }, - }); - routerHook.rerender(); - const syncHook = renderHook( - router => useSyncRecentViewsWithRouter(router, blockSuiteWorkspace), - { - wrapper: ProviderWrapper, - initialProps: routerHook.result.current, - } - ); - syncHook.rerender(routerHook.result.current); - expect(recentlyViewedHook.result.current).toEqual([ - { - id: 'page0', - mode: 'page', - }, - ]); - }); -}); diff --git a/apps/web/src/hooks/__tests__/use-recent-views.spec.tsx b/apps/web/src/hooks/__tests__/use-recent-views.spec.tsx new file mode 100644 index 0000000000..44f9834e1d --- /dev/null +++ b/apps/web/src/hooks/__tests__/use-recent-views.spec.tsx @@ -0,0 +1,139 @@ +/** + * @vitest-environment happy-dom + */ +import { + rootCurrentWorkspaceIdAtom, + rootWorkspacesMetadataAtom, +} from '@affine/workspace/atom'; +import type { LocalWorkspace } from '@affine/workspace/type'; +import { WorkspaceFlavour } from '@affine/workspace/type'; +import { __unstableSchemas, AffineSchemas } from '@blocksuite/blocks/models'; +import type { Page } from '@blocksuite/store'; +import { assertExists } from '@blocksuite/store'; +import { renderHook } from '@testing-library/react'; +import { createStore, Provider } from 'jotai/index'; +import { useRouter } from 'next/router'; +import routerMock from 'next-router-mock'; +import { createDynamicRouteParser } from 'next-router-mock/dynamic-routes'; +import { beforeAll, beforeEach, describe, expect, test, vi } from 'vitest'; + +import { workspacesAtom } from '../../atoms'; +import { LocalPlugin } from '../../plugins/local'; +import { BlockSuiteWorkspace } from '../../shared'; +import { WorkspaceSubPath } from '../../shared'; +import { + currentWorkspaceAtom, + useCurrentWorkspace, +} from '../current/use-current-workspace'; +import { + useRecentlyViewed, + useSyncRecentViewsWithRouter, +} from '../use-recent-views'; + +let blockSuiteWorkspace: BlockSuiteWorkspace; +beforeAll(() => { + routerMock.useParser( + createDynamicRouteParser([ + `/workspace/[workspaceId/${WorkspaceSubPath.ALL}`, + `/workspace/[workspaceId/${WorkspaceSubPath.SETTING}`, + `/workspace/[workspaceId/${WorkspaceSubPath.TRASH}`, + `/workspace/[workspaceId/${WorkspaceSubPath.FAVORITE}`, + '/workspace/[workspaceId]/[pageId]', + ]) + ); +}); + +async function getJotaiContext() { + const store = createStore(); + const ProviderWrapper: React.FC = + function ProviderWrapper({ children }) { + return {children}; + }; + const workspaces = await store.get(workspacesAtom); + expect(workspaces.length).toBe(0); + return { + store, + ProviderWrapper, + initialWorkspaces: workspaces, + } as const; +} + +beforeEach(async () => { + blockSuiteWorkspace = new BlockSuiteWorkspace({ id: 'test' }) + .register(AffineSchemas) + .register(__unstableSchemas); + const initPage = (page: Page) => { + expect(page).not.toBeNull(); + assertExists(page); + const pageBlockId = page.addBlock('affine:page', { + title: new page.Text(''), + }); + const frameId = page.addBlock('affine:frame', {}, pageBlockId); + page.addBlock('affine:paragraph', {}, frameId); + }; + initPage( + blockSuiteWorkspace.createPage({ + id: 'page0', + }) + ); + initPage(blockSuiteWorkspace.createPage({ id: 'page1' })); + initPage(blockSuiteWorkspace.createPage({ id: 'page2' })); +}); + +describe('useRecentlyViewed', () => { + test('basic', async () => { + const { ProviderWrapper, store } = await getJotaiContext(); + const workspaceId = blockSuiteWorkspace.id; + const pageId = 'page0'; + store.set(rootWorkspacesMetadataAtom, [ + { + id: workspaceId, + flavour: WorkspaceFlavour.LOCAL, + }, + ]); + LocalPlugin.CRUD.get = vi.fn().mockResolvedValue({ + id: workspaceId, + flavour: WorkspaceFlavour.LOCAL, + blockSuiteWorkspace, + providers: [], + } satisfies LocalWorkspace); + store.set(rootCurrentWorkspaceIdAtom, blockSuiteWorkspace.id); + const workspace = await store.get(currentWorkspaceAtom); + expect(workspace?.id).toBe(blockSuiteWorkspace.id); + const currentHook = renderHook(() => useCurrentWorkspace(), { + wrapper: ProviderWrapper, + }); + expect(currentHook.result.current[0]?.id).toEqual(workspaceId); + store.set(rootCurrentWorkspaceIdAtom, blockSuiteWorkspace.id); + await store.get(currentWorkspaceAtom); + const recentlyViewedHook = renderHook(() => useRecentlyViewed(), { + wrapper: ProviderWrapper, + }); + expect(recentlyViewedHook.result.current).toEqual([]); + const routerHook = renderHook(() => useRouter(), { + wrapper: ProviderWrapper, + }); + await routerHook.result.current.push({ + pathname: '/workspace/[workspaceId]/[pageId]', + query: { + workspaceId, + pageId, + }, + }); + routerHook.rerender(); + const syncHook = renderHook( + router => useSyncRecentViewsWithRouter(router, blockSuiteWorkspace), + { + wrapper: ProviderWrapper, + initialProps: routerHook.result.current, + } + ); + syncHook.rerender(routerHook.result.current); + expect(recentlyViewedHook.result.current).toEqual([ + { + id: 'page0', + mode: 'page', + }, + ]); + }); +}); diff --git a/apps/web/src/hooks/affine/use-last-leave-workspace-id.ts b/apps/web/src/hooks/affine/use-last-leave-workspace-id.ts deleted file mode 100644 index 46dcc86ab5..0000000000 --- a/apps/web/src/hooks/affine/use-last-leave-workspace-id.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { useAtomValue } from 'jotai'; - -import { lastWorkspaceIdAtom } from '../current/use-current-workspace'; - -export function useLastWorkspaceId() { - return useAtomValue(lastWorkspaceIdAtom); -} diff --git a/apps/web/src/hooks/current/use-current-workspace.ts b/apps/web/src/hooks/current/use-current-workspace.ts index 4f7d1d6a10..adc9aee414 100644 --- a/apps/web/src/hooks/current/use-current-workspace.ts +++ b/apps/web/src/hooks/current/use-current-workspace.ts @@ -1,5 +1,4 @@ -import { useAtom, useAtomValue, useSetAtom } from 'jotai'; -import { atomWithStorage } from 'jotai/utils'; +import { useAtom, useAtomValue } from 'jotai'; import { useCallback } from 'react'; import { currentPageIdAtom, currentWorkspaceIdAtom } from '../../atoms'; @@ -11,11 +10,6 @@ import type { AllWorkspace } from '../../shared'; */ export const currentWorkspaceAtom = rootCurrentWorkspaceAtom; -export const lastWorkspaceIdAtom = atomWithStorage( - 'last_workspace_id', - null -); - export function useCurrentWorkspace(): [ AllWorkspace, (id: string | null) => void @@ -23,16 +17,17 @@ export function useCurrentWorkspace(): [ const currentWorkspace = useAtomValue(rootCurrentWorkspaceAtom); const [, setId] = useAtom(currentWorkspaceIdAtom); const [, setPageId] = useAtom(currentPageIdAtom); - const setLast = useSetAtom(lastWorkspaceIdAtom); return [ currentWorkspace, useCallback( (id: string | null) => { + if (typeof window !== 'undefined' && id) { + localStorage.setItem('last_workspace_id', id); + } setPageId(null); - setLast(id); setId(id); }, - [setId, setLast, setPageId] + [setId, setPageId] ), ]; } diff --git a/apps/web/src/hooks/use-router-and-workspace-with-page-id-defense.ts b/apps/web/src/hooks/use-router-and-workspace-with-page-id-defense.ts deleted file mode 100644 index d29f3e6470..0000000000 --- a/apps/web/src/hooks/use-router-and-workspace-with-page-id-defense.ts +++ /dev/null @@ -1,73 +0,0 @@ -import { DebugLogger } from '@affine/debug'; -import { rootCurrentPageIdAtom } from '@affine/workspace/atom'; -import { useAtom, useAtomValue } from 'jotai'; -import type { NextRouter } from 'next/router'; -import { useRef } from 'react'; - -import { rootCurrentWorkspaceAtom } from '../atoms/root'; -export const HALT_PROBLEM_TIMEOUT = 1000; - -const logger = new DebugLogger('useRouterWithWorkspaceIdDefense'); - -export function useRouterAndWorkspaceWithPageIdDefense(router: NextRouter) { - const currentWorkspace = useAtomValue(rootCurrentWorkspaceAtom); - const [currentPageId, setCurrentPageId] = useAtom(rootCurrentPageIdAtom); - const timeoutRef = useRef(null); - if (!router.isReady) { - return; - } - if (!timeoutRef.current) { - timeoutRef.current = setTimeout(() => { - if (currentPageId) { - const page = - currentWorkspace.blockSuiteWorkspace.getPage(currentPageId); - if (!page) { - const firstOne = - currentWorkspace.blockSuiteWorkspace.meta.pageMetas.at(0); - if (firstOne) { - logger.warn( - 'cannot find page', - currentPageId, - 'so redirect to', - firstOne.id - ); - setCurrentPageId(firstOne.id); - void router.push({ - pathname: '/workspace/[workspaceId]/[pageId]', - query: { - ...router.query, - workspaceId: currentWorkspace.id, - pageId: firstOne.id, - }, - }); - } - } - } - }, HALT_PROBLEM_TIMEOUT); - } - const { workspaceId, pageId } = router.query; - if (typeof pageId !== 'string') { - console.warn('pageId is not a string', pageId); - return; - } - if (typeof workspaceId !== 'string') { - console.warn('workspaceId is not a string', workspaceId); - return; - } - if (currentWorkspace?.id !== workspaceId) { - console.warn('workspaceId is not currentWorkspace', workspaceId); - return; - } - if (currentPageId !== pageId) { - console.log('set current page id', pageId); - setCurrentPageId(pageId); - void router.push({ - pathname: '/workspace/[workspaceId]/[pageId]', - query: { - ...router.query, - workspaceId, - pageId, - }, - }); - } -} diff --git a/apps/web/src/hooks/use-router-with-workspace-id-defense.ts b/apps/web/src/hooks/use-router-with-workspace-id-defense.ts deleted file mode 100644 index f66f19c040..0000000000 --- a/apps/web/src/hooks/use-router-with-workspace-id-defense.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { DebugLogger } from '@affine/debug'; -import { - rootCurrentPageIdAtom, - rootCurrentWorkspaceIdAtom, - rootWorkspacesMetadataAtom, -} from '@affine/workspace/atom'; -import { useAtom, useAtomValue, useSetAtom } from 'jotai'; -import type { NextRouter } from 'next/router'; -import { useMemo } from 'react'; - -const logger = new DebugLogger('useRouterWithWorkspaceIdDefense'); - -export function useRouterWithWorkspaceIdDefense(router: NextRouter) { - const metadata = useAtomValue(rootWorkspacesMetadataAtom); - const [currentWorkspaceId, setCurrentWorkspaceId] = useAtom( - rootCurrentWorkspaceIdAtom - ); - const setCurrentPageId = useSetAtom(rootCurrentPageIdAtom); - const exist = useMemo( - () => metadata.find(m => m.id === currentWorkspaceId), - [currentWorkspaceId, metadata] - ); - if (!router.isReady) { - return; - } - if (!currentWorkspaceId) { - return; - } - if (!exist) { - console.warn('workspace not exist, redirect to first one'); - // clean up - setCurrentWorkspaceId(null); - setCurrentPageId(null); - const firstOne = metadata.at(0); - if (!firstOne) { - throw new Error('no workspace'); - } - logger.debug('redirect to', firstOne.id); - void router.push({ - pathname: '/workspace/[workspaceId]/all', - query: { - ...router.query, - workspaceId: firstOne.id, - }, - }); - } -} diff --git a/apps/web/src/hooks/use-sync-router-with-current-page-id.ts b/apps/web/src/hooks/use-sync-router-with-current-page-id.ts deleted file mode 100644 index 8cf319281c..0000000000 --- a/apps/web/src/hooks/use-sync-router-with-current-page-id.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { rootCurrentPageIdAtom } from '@affine/workspace/atom'; -import { useAtom } from 'jotai'; -import type { NextRouter } from 'next/router'; - -export function useSyncRouterWithCurrentPageId(router: NextRouter) { - const [currentPageId, setCurrentPageId] = useAtom(rootCurrentPageIdAtom); - if (!router.isReady) { - return; - } - const pageId = router.query.pageId; - if (currentPageId === pageId) { - return; - } - if (typeof pageId === 'string') { - console.log('set page id', pageId); - setCurrentPageId(pageId); - } else if (pageId === undefined) { - console.log('cleanup page'); - setCurrentPageId(null); - } -} diff --git a/apps/web/src/hooks/use-sync-router-with-current-workspace-id.ts b/apps/web/src/hooks/use-sync-router-with-current-workspace-id.ts deleted file mode 100644 index 052bd16eca..0000000000 --- a/apps/web/src/hooks/use-sync-router-with-current-workspace-id.ts +++ /dev/null @@ -1,72 +0,0 @@ -import { DebugLogger } from '@affine/debug'; -import { - rootCurrentWorkspaceIdAtom, - rootWorkspacesMetadataAtom, -} from '@affine/workspace/atom'; -import { useAtom, useAtomValue } from 'jotai'; -import type { NextRouter } from 'next/router'; - -const logger = new DebugLogger('useSyncRouterWithCurrentWorkspaceId'); - -export function useSyncRouterWithCurrentWorkspaceId(router: NextRouter) { - const [currentWorkspaceId, setCurrentWorkspaceId] = useAtom( - rootCurrentWorkspaceIdAtom - ); - const metadata = useAtomValue(rootWorkspacesMetadataAtom); - if (!router.isReady) { - return; - } - const workspaceId = router.query.workspaceId; - if (typeof workspaceId !== 'string') { - return; - } - if (currentWorkspaceId === workspaceId) { - return; - } - if (currentWorkspaceId) { - if (currentWorkspaceId !== workspaceId) { - const target = metadata.find(workspace => workspace.id === workspaceId); - logger.debug('workspace not exist, redirect to current one'); - if (!target) { - // workspaceId is invalid, redirect to currentWorkspaceId - void router.push({ - pathname: router.pathname, - query: { - ...router.query, - workspaceId: currentWorkspaceId, - }, - }); - } - } - return; - } - const targetWorkspace = metadata.find( - workspace => workspace.id === workspaceId - ); - if (targetWorkspace) { - console.log('set workspace id', workspaceId); - setCurrentWorkspaceId(targetWorkspace.id); - logger.debug('redirect to', targetWorkspace.id); - void router.push({ - pathname: router.pathname, - query: { - ...router.query, - workspaceId: targetWorkspace.id, - }, - }); - } else { - const targetWorkspace = metadata.at(0); - if (targetWorkspace) { - console.log('set workspace id', workspaceId); - setCurrentWorkspaceId(targetWorkspace.id); - logger.debug('redirect to', targetWorkspace.id); - void router.push({ - pathname: router.pathname, - query: { - ...router.query, - workspaceId: targetWorkspace.id, - }, - }); - } - } -} diff --git a/apps/web/src/layouts/workspace-layout.tsx b/apps/web/src/layouts/workspace-layout.tsx index 3710f5e7d8..1d8315eabb 100644 --- a/apps/web/src/layouts/workspace-layout.tsx +++ b/apps/web/src/layouts/workspace-layout.tsx @@ -39,9 +39,6 @@ import { RootAppSidebar } from '../components/root-app-sidebar'; import { useCurrentWorkspace } from '../hooks/current/use-current-workspace'; import { useRouterHelper } from '../hooks/use-router-helper'; import { useRouterTitle } from '../hooks/use-router-title'; -import { useRouterWithWorkspaceIdDefense } from '../hooks/use-router-with-workspace-id-defense'; -import { useSyncRouterWithCurrentPageId } from '../hooks/use-sync-router-with-current-page-id'; -import { useSyncRouterWithCurrentWorkspaceId } from '../hooks/use-sync-router-with-current-workspace-id'; import { useWorkspaces } from '../hooks/use-workspaces'; import { WorkspaceAdapters } from '../plugins'; import { ModalProvider } from '../providers/modal-provider'; @@ -137,13 +134,21 @@ export const AllWorkspaceContext = ({ export const CurrentWorkspaceContext = ({ children, }: PropsWithChildren): ReactElement => { - const router = useRouter(); const workspaceId = useAtomValue(rootCurrentWorkspaceIdAtom); - useSyncRouterWithCurrentWorkspaceId(router); - useSyncRouterWithCurrentPageId(router); - useRouterWithWorkspaceIdDefense(router); const metadata = useAtomValue(rootWorkspacesMetadataAtom); const exist = metadata.find(m => m.id === workspaceId); + const router = useRouter(); + const push = router.push; + useEffect(() => { + const id = setTimeout(() => { + if (!exist) { + void push('/'); + } + }, 1000); + return () => { + clearTimeout(id); + }; + }, [push, exist]); if (!router.isReady) { return ; } diff --git a/apps/web/src/pages/index.tsx b/apps/web/src/pages/index.tsx index 40a72052b5..d1c0d9b1e6 100644 --- a/apps/web/src/pages/index.tsx +++ b/apps/web/src/pages/index.tsx @@ -4,7 +4,6 @@ import { useRouter } from 'next/router'; import { Suspense, useEffect } from 'react'; import { PageLoading } from '../components/pure/loading'; -import { useLastWorkspaceId } from '../hooks/affine/use-last-leave-workspace-id'; import { RouteLogic, useRouterHelper } from '../hooks/use-router-helper'; import { useAppHelper, useWorkspaces } from '../hooks/use-workspaces'; import { WorkspaceSubPath } from '../shared'; @@ -15,16 +14,15 @@ const IndexPageInner = () => { const router = useRouter(); const { jumpToPage, jumpToSubPath } = useRouterHelper(router); const workspaces = useWorkspaces(); - const lastWorkspaceId = useLastWorkspaceId(); const helper = useAppHelper(); useEffect(() => { if (!router.isReady) { return; } + const lastId = localStorage.getItem('last_workspace_id'); const targetWorkspace = - (lastWorkspaceId && - workspaces.find(({ id }) => id === lastWorkspaceId)) || + (lastId && workspaces.find(({ id }) => id === lastId)) || workspaces.at(0); if (targetWorkspace) { @@ -56,7 +54,7 @@ const IndexPageInner = () => { } else { console.warn('No target workspace. This should not happen in production'); } - }, [helper, jumpToPage, jumpToSubPath, lastWorkspaceId, router, workspaces]); + }, [helper, jumpToPage, jumpToSubPath, router, workspaces]); return ; }; diff --git a/apps/web/src/pages/workspace/[workspaceId]/[pageId].tsx b/apps/web/src/pages/workspace/[workspaceId]/[pageId].tsx index f94fb638f5..e1a0b6cff8 100644 --- a/apps/web/src/pages/workspace/[workspaceId]/[pageId].tsx +++ b/apps/web/src/pages/workspace/[workspaceId]/[pageId].tsx @@ -20,7 +20,6 @@ import { useReferenceLinkEffect } from '../../../hooks/affine/use-reference-link import { useCurrentWorkspace } from '../../../hooks/current/use-current-workspace'; import { usePinboardHandler } from '../../../hooks/use-pinboard-handler'; import { useSyncRecentViewsWithRouter } from '../../../hooks/use-recent-views'; -import { useRouterAndWorkspaceWithPageIdDefense } from '../../../hooks/use-router-and-workspace-with-page-id-defense'; import { useRouterHelper } from '../../../hooks/use-router-helper'; import { WorkspaceLayout } from '../../../layouts/workspace-layout'; import { WorkspaceAdapters } from '../../../plugins'; @@ -110,7 +109,6 @@ const WorkspaceDetailPage: NextPageWithLayout = () => { const router = useRouter(); const currentWorkspace = useAtomValue(rootCurrentWorkspaceAtom); const currentPageId = useAtomValue(rootCurrentPageIdAtom); - useRouterAndWorkspaceWithPageIdDefense(router); const page = useBlockSuiteWorkspacePage( currentWorkspace.blockSuiteWorkspace, currentPageId diff --git a/apps/web/src/pages/workspace/[workspaceId]/all.tsx b/apps/web/src/pages/workspace/[workspaceId]/all.tsx index 63dc7b7e54..307f96d88a 100644 --- a/apps/web/src/pages/workspace/[workspaceId]/all.tsx +++ b/apps/web/src/pages/workspace/[workspaceId]/all.tsx @@ -14,7 +14,6 @@ import { PageLoading } from '../../../components/pure/loading'; import { WorkspaceTitle } from '../../../components/pure/workspace-title'; import { useCurrentWorkspace } from '../../../hooks/current/use-current-workspace'; import { useRouterHelper } from '../../../hooks/use-router-helper'; -import { useSyncRouterWithCurrentWorkspaceId } from '../../../hooks/use-sync-router-with-current-workspace-id'; import { WorkspaceLayout } from '../../../layouts/workspace-layout'; import { WorkspaceAdapters } from '../../../plugins'; import type { NextPageWithLayout } from '../../../shared'; @@ -24,7 +23,6 @@ const AllPage: NextPageWithLayout = () => { const { jumpToPage } = useRouterHelper(router); const [currentWorkspace] = useCurrentWorkspace(); const t = useAFFiNEI18N(); - useSyncRouterWithCurrentWorkspaceId(router); const onClickPage = useCallback( (pageId: string, newTab?: boolean) => { assertExists(currentWorkspace); diff --git a/apps/web/src/pages/workspace/[workspaceId]/favorite.tsx b/apps/web/src/pages/workspace/[workspaceId]/favorite.tsx index cf26f1746a..2efcf7ae2b 100644 --- a/apps/web/src/pages/workspace/[workspaceId]/favorite.tsx +++ b/apps/web/src/pages/workspace/[workspaceId]/favorite.tsx @@ -10,7 +10,6 @@ import { PageLoading } from '../../../components/pure/loading'; import { WorkspaceTitle } from '../../../components/pure/workspace-title'; import { useCurrentWorkspace } from '../../../hooks/current/use-current-workspace'; import { useRouterHelper } from '../../../hooks/use-router-helper'; -import { useSyncRouterWithCurrentWorkspaceId } from '../../../hooks/use-sync-router-with-current-workspace-id'; import { WorkspaceLayout } from '../../../layouts/workspace-layout'; import type { NextPageWithLayout } from '../../../shared'; @@ -19,7 +18,6 @@ const FavouritePage: NextPageWithLayout = () => { const { jumpToPage } = useRouterHelper(router); const [currentWorkspace] = useCurrentWorkspace(); const t = useAFFiNEI18N(); - useSyncRouterWithCurrentWorkspaceId(router); const onClickPage = useCallback( (pageId: string, newTab?: boolean) => { assertExists(currentWorkspace); diff --git a/apps/web/src/pages/workspace/[workspaceId]/setting.tsx b/apps/web/src/pages/workspace/[workspaceId]/setting.tsx index c770c2d689..b86a2f9eca 100644 --- a/apps/web/src/pages/workspace/[workspaceId]/setting.tsx +++ b/apps/web/src/pages/workspace/[workspaceId]/setting.tsx @@ -20,7 +20,6 @@ import { PageLoading } from '../../../components/pure/loading'; import { WorkspaceTitle } from '../../../components/pure/workspace-title'; import { useCurrentWorkspace } from '../../../hooks/current/use-current-workspace'; import { useOnTransformWorkspace } from '../../../hooks/root/use-on-transform-workspace'; -import { useSyncRouterWithCurrentWorkspaceId } from '../../../hooks/use-sync-router-with-current-workspace-id'; import { useAppHelper } from '../../../hooks/use-workspaces'; import { WorkspaceLayout } from '../../../layouts/workspace-layout'; import { WorkspaceAdapters } from '../../../plugins'; @@ -84,7 +83,6 @@ const SettingPage: NextPageWithLayout = () => { const workspaceIds = useAtomValue(rootWorkspacesMetadataAtom); const [currentWorkspace] = useCurrentWorkspace(); const t = useAFFiNEI18N(); - useSyncRouterWithCurrentWorkspaceId(router); const [currentTab, setCurrentTab] = useAtom(settingPanelAtom); useEffect(() => {}); const onChangeTab = useCallback( diff --git a/apps/web/src/pages/workspace/[workspaceId]/shared.tsx b/apps/web/src/pages/workspace/[workspaceId]/shared.tsx index 8a6bd3a3db..779ecc3ff7 100644 --- a/apps/web/src/pages/workspace/[workspaceId]/shared.tsx +++ b/apps/web/src/pages/workspace/[workspaceId]/shared.tsx @@ -10,7 +10,6 @@ import { PageLoading } from '../../../components/pure/loading'; import { WorkspaceTitle } from '../../../components/pure/workspace-title'; import { useCurrentWorkspace } from '../../../hooks/current/use-current-workspace'; import { useRouterHelper } from '../../../hooks/use-router-helper'; -import { useSyncRouterWithCurrentWorkspaceId } from '../../../hooks/use-sync-router-with-current-workspace-id'; import { WorkspaceLayout } from '../../../layouts/workspace-layout'; import type { NextPageWithLayout } from '../../../shared'; @@ -19,7 +18,6 @@ const SharedPages: NextPageWithLayout = () => { const { jumpToPage } = useRouterHelper(router); const [currentWorkspace] = useCurrentWorkspace(); const t = useAFFiNEI18N(); - useSyncRouterWithCurrentWorkspaceId(router); const onClickPage = useCallback( (pageId: string, newTab?: boolean) => { assertExists(currentWorkspace); diff --git a/apps/web/src/pages/workspace/[workspaceId]/trash.tsx b/apps/web/src/pages/workspace/[workspaceId]/trash.tsx index 5e2a3a487c..1fd4efd3be 100644 --- a/apps/web/src/pages/workspace/[workspaceId]/trash.tsx +++ b/apps/web/src/pages/workspace/[workspaceId]/trash.tsx @@ -10,7 +10,6 @@ import { PageLoading } from '../../../components/pure/loading'; import { WorkspaceTitle } from '../../../components/pure/workspace-title'; import { useCurrentWorkspace } from '../../../hooks/current/use-current-workspace'; import { useRouterHelper } from '../../../hooks/use-router-helper'; -import { useSyncRouterWithCurrentWorkspaceId } from '../../../hooks/use-sync-router-with-current-workspace-id'; import { WorkspaceLayout } from '../../../layouts/workspace-layout'; import type { NextPageWithLayout } from '../../../shared'; @@ -19,7 +18,6 @@ const TrashPage: NextPageWithLayout = () => { const { jumpToPage } = useRouterHelper(router); const [currentWorkspace] = useCurrentWorkspace(); const t = useAFFiNEI18N(); - useSyncRouterWithCurrentWorkspaceId(router); const onClickPage = useCallback( (pageId: string, newTab?: boolean) => { assertExists(currentWorkspace); diff --git a/apps/web/src/providers/modal-provider.tsx b/apps/web/src/providers/modal-provider.tsx index 70d8170184..9569172829 100644 --- a/apps/web/src/providers/modal-provider.tsx +++ b/apps/web/src/providers/modal-provider.tsx @@ -152,7 +152,7 @@ export function Modals() { setOpenCreateWorkspaceModal(false); setOpenWorkspacesModal(false); setCurrentWorkspace(id); - return jumpToSubPath(id, WorkspaceSubPath.SETTING); + return jumpToSubPath(id, WorkspaceSubPath.ALL); }, [ jumpToSubPath, diff --git a/packages/workspace/package.json b/packages/workspace/package.json index ff1456c62d..4de43459cc 100644 --- a/packages/workspace/package.json +++ b/packages/workspace/package.json @@ -37,6 +37,7 @@ }, "devDependencies": { "@types/ws": "^8.5.4", + "next": "^13.4.1", "ws": "^8.13.0" }, "version": "0.6.0-canary.0" diff --git a/packages/workspace/src/atom.ts b/packages/workspace/src/atom.ts index 405efe7039..fd5a248b38 100644 --- a/packages/workspace/src/atom.ts +++ b/packages/workspace/src/atom.ts @@ -1,6 +1,7 @@ import type { EditorContainer } from '@blocksuite/editor'; import { atom, createStore } from 'jotai'; import { atomWithStorage, createJSONStorage } from 'jotai/utils'; +import Router from 'next/router'; import type { WorkspaceFlavour } from './type'; @@ -32,12 +33,49 @@ export const rootCurrentWorkspaceIdAtom = atomWithStorage( null, createJSONStorage(() => sessionStorage) ); + +rootCurrentWorkspaceIdAtom.onMount = set => { + if (typeof window !== 'undefined') { + const callback = (url: string) => { + const value = url.split('/')[2]; + if (value) { + set(value); + } else { + set(null); + } + }; + callback(window.location.pathname); + Router.events.on('routeChangeStart', callback); + return () => { + Router.events.off('routeChangeStart', callback); + }; + } +}; + export const rootCurrentPageIdAtom = atomWithStorage( 'root-current-page-id', null, createJSONStorage(() => sessionStorage) ); +rootCurrentPageIdAtom.onMount = set => { + if (typeof window !== 'undefined') { + const callback = (url: string) => { + const value = url.split('/')[3]; + if (value) { + set(value); + } else { + set(null); + } + }; + callback(window.location.pathname); + Router.events.on('routeChangeStart', callback); + return () => { + Router.events.off('routeChangeStart', callback); + }; + } +}; + // current editor atom, each app should have only one editor in the same time export const rootCurrentEditorAtom = atom | null>( null diff --git a/tests/parallels/affine/affine-public-single-page.spec.ts b/tests/parallels/affine/affine-public-single-page.spec.ts index 94bf451608..418e83ce35 100644 --- a/tests/parallels/affine/affine-public-single-page.spec.ts +++ b/tests/parallels/affine/affine-public-single-page.spec.ts @@ -36,9 +36,6 @@ test('public single page', async ({ page, browser }) => { ); await page.getByTestId('confirm-enable-cloud-button').click(); await promise; - const newPage2Url = page.url().split('/'); - newPage2Url[newPage2Url.length - 1] = page2Id as string; - await page.goto(newPage2Url.join('/')); await page.waitForSelector('v-line'); const currentTitle = await page .locator('[data-block-is-title="true"]') diff --git a/tests/parallels/affine/affine-workspace.spec.ts b/tests/parallels/affine/affine-workspace.spec.ts index 044e384acf..2fc6be2ee8 100644 --- a/tests/parallels/affine/affine-workspace.spec.ts +++ b/tests/parallels/affine/affine-workspace.spec.ts @@ -46,9 +46,10 @@ test('should enable affine workspace successfully', async ({ page }) => { await page.locator('[data-block-is-title="true"]').type('Hello, world!', { delay: 50, }); + await page.waitForTimeout(1000); await assertCurrentWorkspaceFlavour('affine', page); await openWorkspaceListModal(page); await page.getByTestId('workspace-list-modal-sign-out').click(); - await page.waitForTimeout(1000); + await waitMarkdownImported(page); await assertCurrentWorkspaceFlavour('local', page); }); diff --git a/tests/parallels/open-affine.spec.ts b/tests/parallels/open-affine.spec.ts index 9aa111e5e0..ee07421a3c 100644 --- a/tests/parallels/open-affine.spec.ts +++ b/tests/parallels/open-affine.spec.ts @@ -9,9 +9,7 @@ test('Open last workspace when back to affine', async ({ page }) => { await openHomePage(page); await waitMarkdownImported(page); await createWorkspace({ name: 'New Workspace 2' }, page); - // FIXME: can not get when the new workspace is surely created, hack a timeout to wait - // waiting for page loading end - await page.waitForTimeout(3000); + await waitMarkdownImported(page); // show workspace list await page.getByTestId('workspace-name').click(); diff --git a/tests/parallels/router.spec.ts b/tests/parallels/router.spec.ts index f9c195d164..5d66b4aea4 100644 --- a/tests/parallels/router.spec.ts +++ b/tests/parallels/router.spec.ts @@ -3,7 +3,6 @@ import { expect } from '@playwright/test'; import { openHomePage, webUrl } from '../libs/load-page'; import { waitMarkdownImported } from '../libs/page-logic'; -import { clickSideBarAllPageButton } from '../libs/sidebar'; test('goto not found page', async ({ page }) => { await openHomePage(page); @@ -17,9 +16,8 @@ test('goto not found page', async ({ page }) => { test('goto not found workspace', async ({ page }) => { await openHomePage(page); await waitMarkdownImported(page); - await clickSideBarAllPageButton(page); const currentUrl = page.url(); await page.goto(new URL('/workspace/invalid/all', webUrl).toString()); - await clickSideBarAllPageButton(page); + await page.waitForSelector('v-line'); expect(page.url()).toEqual(currentUrl); }); diff --git a/yarn.lock b/yarn.lock index 11e88b803a..cb7da87021 100644 --- a/yarn.lock +++ b/yarn.lock @@ -379,6 +379,7 @@ __metadata: js-base64: ^3.7.5 ky: ^0.33.3 lib0: ^0.2.74 + next: ^13.4.1 react: ^18.2.0 react-dom: ^18.2.0 ws: ^8.13.0