feat: add page setting atom (#2725)

(cherry picked from commit 9f129075dd)
This commit is contained in:
Himself65
2023-06-09 00:58:46 +08:00
committed by Alex Yang
parent 2ca5ad6509
commit c3bfc16d27
14 changed files with 119 additions and 275 deletions
@@ -1,137 +0,0 @@
/**
* @vitest-environment happy-dom
*/
import type { LocalWorkspace } from '@affine/env/workspace';
import { WorkspaceFlavour, WorkspaceSubPath } from '@affine/env/workspace';
import {
rootCurrentWorkspaceIdAtom,
rootWorkspacesMetadataAtom,
} from '@affine/workspace/atom';
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';
import { useRouter } from 'next/router';
import routerMock from 'next-router-mock';
import { createDynamicRouteParser } from 'next-router-mock/dynamic-routes';
import type { FC, PropsWithChildren } from 'react';
import { beforeAll, beforeEach, describe, expect, test, vi } from 'vitest';
import { LocalAdapter } from '../../adapters/local';
import { workspacesAtom } from '../../atoms';
import { rootCurrentWorkspaceAtom } from '../../atoms/root';
import { BlockSuiteWorkspace } from '../../shared';
import { 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]/[pageId]',
])
);
});
async function getJotaiContext() {
const store = createStore();
const ProviderWrapper: FC<PropsWithChildren> = function ProviderWrapper({
children,
}) {
return <Provider store={store}>{children}</Provider>;
};
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,
},
]);
LocalAdapter.CRUD.get = vi.fn().mockResolvedValue({
id: workspaceId,
flavour: WorkspaceFlavour.LOCAL,
blockSuiteWorkspace,
providers: [],
} satisfies LocalWorkspace);
store.set(rootCurrentWorkspaceIdAtom, blockSuiteWorkspace.id);
const workspace = await store.get(rootCurrentWorkspaceAtom);
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(rootCurrentWorkspaceAtom);
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',
},
]);
});
});
@@ -1,15 +1,15 @@
import { rootCurrentPageIdAtom } from '@affine/workspace/atom';
import { atom, useAtomValue } from 'jotai';
import { workspacePreferredModeAtom } from '../../atoms';
import { pageSettingFamily } from '../../atoms';
const currentModeAtom = atom<'page' | 'edgeless'>(get => {
const pageId = get(rootCurrentPageIdAtom);
const record = get(workspacePreferredModeAtom);
if (pageId) return record[pageId] ?? 'page';
else {
// fixme(himself65): pageId should not be null
if (!pageId) {
return 'page';
}
return get(pageSettingFamily(pageId))?.mode ?? 'page';
});
export const useCurrentMode = () => {
-62
View File
@@ -1,62 +0,0 @@
import type { Page, Workspace } from '@blocksuite/store';
import { useBlockSuitePageMeta } from '@toeverything/hooks/use-block-suite-page-meta';
import { useAtom, useAtomValue, useSetAtom } from 'jotai';
import type { NextRouter } from 'next/router';
import { useEffect } from 'react';
import {
workspacePreferredModeAtom,
workspaceRecentViewsAtom,
workspaceRecentViresWriteAtom,
} from '../atoms';
import { useCurrentWorkspace } from './current/use-current-workspace';
export const useWorkspacePreferredMode = () => {
const [record, setPreferred] = useAtom(workspacePreferredModeAtom);
return {
getPreferredMode: (pageId: Page['id']) => record[pageId] ?? 'page',
setPreferredMode: (pageId: Page['id'], mode: 'page' | 'edgeless') => {
setPreferred(record => ({
...record,
[pageId]: mode,
}));
},
};
};
export function useRecentlyViewed() {
const [workspace] = useCurrentWorkspace();
const workspaceId = workspace?.id || null;
const recentlyViewed = useAtomValue(workspaceRecentViewsAtom);
if (!workspaceId) return [];
return recentlyViewed[workspaceId] ?? [];
}
export function useSyncRecentViewsWithRouter(
router: NextRouter,
blockSuiteWorkspace: Workspace
) {
const workspaceId = blockSuiteWorkspace.id;
const pageId = router.query.pageId;
const set = useSetAtom(workspaceRecentViresWriteAtom);
const meta = useBlockSuitePageMeta(blockSuiteWorkspace).find(
meta => meta.id === pageId
);
const { getPreferredMode } = useWorkspacePreferredMode();
const currentMode =
typeof pageId === 'string' ? getPreferredMode(pageId) : 'page';
useEffect(() => {
if (!workspaceId) return;
if (pageId && meta) {
set(workspaceId, {
id: pageId as string,
/**
* @deprecated No necessary update `mode` at here, use `mode` from {@link useWorkspacePreferredMode} directly.
*/
mode: currentMode,
});
}
}, [pageId, meta, workspaceId, set, currentMode]);
}