feat: add page setting atom (#2725)

This commit is contained in:
Himself65
2023-06-09 00:58:46 +08:00
committed by GitHub
parent 935b4f847c
commit 9f129075dd
14 changed files with 119 additions and 275 deletions
+61 -26
View File
@@ -2,9 +2,8 @@ import { DebugLogger } from '@affine/debug';
import { WorkspaceFlavour } from '@affine/env/workspace'; import { WorkspaceFlavour } from '@affine/env/workspace';
import type { RootWorkspaceMetadata } from '@affine/workspace/atom'; import type { RootWorkspaceMetadata } from '@affine/workspace/atom';
import { rootWorkspacesMetadataAtom } from '@affine/workspace/atom'; import { rootWorkspacesMetadataAtom } from '@affine/workspace/atom';
import type { Page } from '@blocksuite/store';
import { atom } from 'jotai'; import { atom } from 'jotai';
import { atomWithStorage } from 'jotai/utils'; import { atomFamily, atomWithStorage } from 'jotai/utils';
import { WorkspaceAdapters } from '../adapters/workspace'; import { WorkspaceAdapters } from '../adapters/workspace';
import type { CreateWorkspaceMode } from '../components/affine/create-workspace-modal'; import type { CreateWorkspaceMode } from '../components/affine/create-workspace-modal';
@@ -96,34 +95,70 @@ export const workspaceRecentViewsAtom = atomWithStorage<WorkspaceRecentViews>(
{} {}
); );
export type PreferredModeRecord = Record<Page['id'], 'page' | 'edgeless'>; type PageMode = 'page' | 'edgeless';
/** type PageLocalSetting = {
* @deprecated Use `useWorkspacePreferredMode` instead. mode: PageMode;
*/ };
export const workspacePreferredModeAtom = atomWithStorage<PreferredModeRecord>(
'preferredMode', type PartialPageLocalSettingWithPageId = Partial<PageLocalSetting> & {
{} id: string;
};
const pageSettingsBaseAtom = atomWithStorage(
'pageSettings',
{} as Record<string, PageLocalSetting>
); );
export const workspaceRecentViresWriteAtom = atom<null, [string, View], View[]>( // readonly atom by design
null, export const pageSettingsAtom = atom(get => get(pageSettingsBaseAtom));
(get, set, id, value) => {
const record = get(workspaceRecentViewsAtom);
if (Array.isArray(record[id])) {
const idx = record[id].findIndex(view => view.id === value.id);
if (idx !== -1) {
record[id].splice(idx, 1);
}
record[id] = [value, ...record[id]];
} else {
record[id] = [value];
}
record[id] = record[id].slice(0, 3); const recentPageSettingsBaseAtom = atomWithStorage<string[]>(
set(workspaceRecentViewsAtom, { ...record }); 'recentPageSettings',
return record[id]; []
);
export const recentPageSettingsAtom = atom<PartialPageLocalSettingWithPageId[]>(
get => {
const recentPageIDs = get(recentPageSettingsBaseAtom);
const pageSettings = get(pageSettingsAtom);
return recentPageIDs.map(id => ({
...pageSettings[id],
id,
}));
}
);
export const pageSettingFamily = atomFamily((pageId: string) =>
atom(
get => get(pageSettingsBaseAtom)[pageId],
(
get,
set,
patch:
| Partial<PageLocalSetting>
| ((prevSetting: PageLocalSetting | undefined) => void)
) => {
set(recentPageSettingsBaseAtom, ids => {
// pick 3 recent page ids
return [...new Set([pageId, ...ids]).values()].slice(0, 3);
});
set(pageSettingsBaseAtom, settings => ({
...settings,
[pageId]: {
...settings[pageId],
...(typeof patch === 'function' ? patch(settings[pageId]) : patch),
},
}));
}
)
);
export const setPageModeAtom = atom(
void 0,
(get, set, pageId: string, mode: PageMode) => {
set(pageSettingFamily(pageId), { mode });
} }
); );
export type PageModeOption = 'all' | 'page' | 'edgeless'; export type PageModeOption = 'all' | 'page' | 'edgeless';
export const pageModeSelectAtom = atom<PageModeOption>('all'); export const allPageModeSelectAtom = atom<PageModeOption>('all');
@@ -17,7 +17,7 @@ import { useAtom } from 'jotai';
import type React from 'react'; import type React from 'react';
import { useMemo } from 'react'; import { useMemo } from 'react';
import { pageModeSelectAtom } from '../../../atoms'; import { allPageModeSelectAtom } from '../../../atoms';
import { useBlockSuiteMetaHelper } from '../../../hooks/affine/use-block-suite-meta-helper'; import { useBlockSuiteMetaHelper } from '../../../hooks/affine/use-block-suite-meta-helper';
import type { BlockSuiteWorkspace } from '../../../shared'; import type { BlockSuiteWorkspace } from '../../../shared';
import { toast } from '../../../utils'; import { toast } from '../../../utils';
@@ -107,7 +107,7 @@ export const BlockSuitePageList: React.FC<BlockSuitePageListProps> = ({
permanentlyDeletePage, permanentlyDeletePage,
cancelPublicPage, cancelPublicPage,
} = useBlockSuiteMetaHelper(blockSuiteWorkspace); } = useBlockSuiteMetaHelper(blockSuiteWorkspace);
const [filterMode] = useAtom(pageModeSelectAtom); const [filterMode] = useAtom(allPageModeSelectAtom);
const { createPage, createEdgeless, importFile, isPreferredEdgeless } = const { createPage, createEdgeless, importFile, isPreferredEdgeless } =
usePageHelper(blockSuiteWorkspace); usePageHelper(blockSuiteWorkspace);
const t = useAFFiNEI18N(); const t = useAFFiNEI18N();
@@ -1,7 +1,9 @@
import { useBlockSuiteWorkspaceHelper } from '@toeverything/hooks/use-block-suite-workspace-helper'; import { useBlockSuiteWorkspaceHelper } from '@toeverything/hooks/use-block-suite-workspace-helper';
import { useAtomValue, useSetAtom } from 'jotai';
import { useRouter } from 'next/router'; import { useRouter } from 'next/router';
import { useCallback } from 'react';
import { useWorkspacePreferredMode } from '../../../hooks/use-recent-views'; import { pageSettingsAtom, setPageModeAtom } from '../../../atoms';
import { useRouterHelper } from '../../../hooks/use-router-helper'; import { useRouterHelper } from '../../../hooks/use-router-helper';
import type { BlockSuiteWorkspace } from '../../../shared'; import type { BlockSuiteWorkspace } from '../../../shared';
@@ -9,24 +11,25 @@ export const usePageHelper = (blockSuiteWorkspace: BlockSuiteWorkspace) => {
const router = useRouter(); const router = useRouter();
const { openPage } = useRouterHelper(router); const { openPage } = useRouterHelper(router);
const { createPage } = useBlockSuiteWorkspaceHelper(blockSuiteWorkspace); const { createPage } = useBlockSuiteWorkspaceHelper(blockSuiteWorkspace);
const { getPreferredMode, setPreferredMode } = useWorkspacePreferredMode(); const pageSettings = useAtomValue(pageSettingsAtom);
const isPreferredEdgeless = (pageId: string) => { const isPreferredEdgeless = useCallback(
return getPreferredMode(pageId) === 'edgeless'; (pageId: string) => pageSettings[pageId]?.mode === 'edgeless',
}; [pageSettings]
);
const createPageAndOpen = () => { const setPageMode = useSetAtom(setPageModeAtom);
const createPageAndOpen = useCallback(() => {
const page = createPage(); const page = createPage();
return openPage(blockSuiteWorkspace.id, page.id); return openPage(blockSuiteWorkspace.id, page.id);
}; }, [blockSuiteWorkspace.id, createPage, openPage]);
const createEdgelessAndOpen = () => { const createEdgelessAndOpen = useCallback(() => {
const page = createPage(); const page = createPage();
setPreferredMode(page.id, 'edgeless'); setPageMode(page.id, 'edgeless');
return openPage(blockSuiteWorkspace.id, page.id); return openPage(blockSuiteWorkspace.id, page.id);
}; }, [blockSuiteWorkspace.id, createPage, openPage, setPageMode]);
const importFileAndOpen = async () => { const importFileAndOpen = useCallback(async () => {
const { showImportModal } = await import('@blocksuite/blocks'); const { showImportModal } = await import('@blocksuite/blocks');
showImportModal({ workspace: blockSuiteWorkspace }); showImportModal({ workspace: blockSuiteWorkspace });
}; }, [blockSuiteWorkspace]);
return { return {
createPage: createPageAndOpen, createPage: createPageAndOpen,
createEdgeless: createEdgelessAndOpen, createEdgeless: createEdgelessAndOpen,
@@ -1,10 +1,10 @@
import { useAFFiNEI18N } from '@affine/i18n/hooks'; import { useAFFiNEI18N } from '@affine/i18n/hooks';
import { assertExists } from '@blocksuite/store'; import { assertExists } from '@blocksuite/store';
import { useBlockSuitePageMeta } from '@toeverything/hooks/use-block-suite-page-meta'; import { useBlockSuitePageMeta } from '@toeverything/hooks/use-block-suite-page-meta';
import { useAtomValue, useSetAtom } from 'jotai'; import { useAtom } from 'jotai';
import type { CSSProperties } from 'react'; import type { CSSProperties } from 'react';
import { workspacePreferredModeAtom } from '../../../../atoms'; import { pageSettingFamily } from '../../../../atoms';
import type { BlockSuiteWorkspace } from '../../../../shared'; import type { BlockSuiteWorkspace } from '../../../../shared';
import { toast } from '../../../../utils'; import { toast } from '../../../../utils';
import { StyledEditorModeSwitch } from './style'; import { StyledEditorModeSwitch } from './style';
@@ -22,9 +22,8 @@ export const EditorModeSwitch = ({
blockSuiteWorkspace, blockSuiteWorkspace,
pageId, pageId,
}: EditorModeSwitchProps) => { }: EditorModeSwitchProps) => {
const currentMode = const [setting, setSetting] = useAtom(pageSettingFamily(pageId));
useAtomValue(workspacePreferredModeAtom)[pageId] ?? 'page'; const currentMode = setting?.mode ?? 'page';
const setMode = useSetAtom(workspacePreferredModeAtom);
const pageMeta = useBlockSuitePageMeta(blockSuiteWorkspace).find( const pageMeta = useBlockSuitePageMeta(blockSuiteWorkspace).find(
meta => meta.id === pageId meta => meta.id === pageId
); );
@@ -43,11 +42,11 @@ export const EditorModeSwitch = ({
active={currentMode === 'page'} active={currentMode === 'page'}
hide={trash && currentMode !== 'page'} hide={trash && currentMode !== 'page'}
onClick={() => { onClick={() => {
setMode(mode => { setSetting(setting => {
if (mode[pageMeta.id] !== 'page') { if (setting?.mode !== 'page') {
toast(t['com.affine.pageMode']()); toast(t['com.affine.pageMode']());
} }
return { ...mode, [pageMeta.id]: 'page' }; return { ...setting, mode: 'page' };
}); });
}} }}
/> />
@@ -56,11 +55,11 @@ export const EditorModeSwitch = ({
active={currentMode === 'edgeless'} active={currentMode === 'edgeless'}
hide={trash && currentMode !== 'edgeless'} hide={trash && currentMode !== 'edgeless'}
onClick={() => { onClick={() => {
setMode(mode => { setSetting(setting => {
if (mode[pageMeta.id] !== 'edgeless') { if (setting?.mode !== 'edgeless') {
toast(t['com.affine.edgelessMode']()); toast(t['com.affine.pageMode']());
} }
return { ...mode, [pageMeta.id]: 'edgeless' }; return { ...setting, mode: 'edgeless' };
}); });
}} }}
/> />
@@ -18,7 +18,7 @@ import { useAtom } from 'jotai';
import { useRouter } from 'next/router'; import { useRouter } from 'next/router';
import { useState } from 'react'; import { useState } from 'react';
import { workspacePreferredModeAtom } from '../../../../atoms'; import { pageSettingFamily } from '../../../../atoms';
import { useBlockSuiteMetaHelper } from '../../../../hooks/affine/use-block-suite-meta-helper'; import { useBlockSuiteMetaHelper } from '../../../../hooks/affine/use-block-suite-meta-helper';
import { useCurrentPageId } from '../../../../hooks/current/use-current-page-id'; import { useCurrentPageId } from '../../../../hooks/current/use-current-page-id';
import { useCurrentWorkspace } from '../../../../hooks/current/use-current-workspace'; import { useCurrentWorkspace } from '../../../../hooks/current/use-current-workspace';
@@ -64,8 +64,8 @@ const PageMenu = () => {
meta => meta.id === pageId meta => meta.id === pageId
); );
assertExists(pageMeta); assertExists(pageMeta);
const [record, set] = useAtom(workspacePreferredModeAtom); const [setting, setSetting] = useAtom(pageSettingFamily(pageId));
const mode = record[pageId] ?? 'page'; const mode = setting?.mode ?? 'page';
const favorite = pageMeta.favorite ?? false; const favorite = pageMeta.favorite ?? false;
const { setPageMeta } = usePageMetaHelper(blockSuiteWorkspace); const { setPageMeta } = usePageMetaHelper(blockSuiteWorkspace);
@@ -98,9 +98,8 @@ const PageMenu = () => {
icon={mode === 'page' ? <EdgelessIcon /> : <PageIcon />} icon={mode === 'page' ? <EdgelessIcon /> : <PageIcon />}
data-testid="editor-option-menu-edgeless" data-testid="editor-option-menu-edgeless"
onClick={() => { onClick={() => {
set(record => ({ setSetting(setting => ({
...record, mode: setting?.mode === 'page' ? 'edgeless' : 'page',
[pageId]: mode === 'page' ? 'edgeless' : 'page',
})); }));
}} }}
> >
@@ -30,7 +30,7 @@ import React, {
} from 'react'; } from 'react';
import { Panel, PanelGroup, PanelResizeHandle } from 'react-resizable-panels'; import { Panel, PanelGroup, PanelResizeHandle } from 'react-resizable-panels';
import { workspacePreferredModeAtom } from '../atoms'; import { pageSettingFamily } from '../atoms';
import { contentLayoutAtom } from '../atoms/layout'; import { contentLayoutAtom } from '../atoms/layout';
import type { AffineOfficialWorkspace } from '../shared'; import type { AffineOfficialWorkspace } from '../shared';
import { BlockSuiteEditor as Editor } from './blocksuite/block-suite-editor'; import { BlockSuiteEditor as Editor } from './blocksuite/block-suite-editor';
@@ -63,11 +63,13 @@ const EditorWrapper = memo(function EditorWrapper({
const meta = useBlockSuitePageMeta(blockSuiteWorkspace).find( const meta = useBlockSuitePageMeta(blockSuiteWorkspace).find(
meta => meta.id === pageId meta => meta.id === pageId
); );
const pageSettingAtom = pageSettingFamily(pageId);
const pageSetting = useAtomValue(pageSettingAtom);
const currentMode = const currentMode =
useAtomValue(workspacePreferredModeAtom)[pageId] ?? pageSetting?.mode ?? DEFAULT_HELLO_WORLD_PAGE_ID === pageId
DEFAULT_HELLO_WORLD_PAGE_ID === pageId
? 'edgeless' ? 'edgeless'
: 'page'; : 'page';
const setEditor = useSetAtom(rootCurrentEditorAtom); const setEditor = useSetAtom(rootCurrentEditorAtom);
assertExists(meta); assertExists(meta);
return ( return (
@@ -5,12 +5,13 @@ import { assertExists } from '@blocksuite/store';
import { useBlockSuitePageMeta } from '@toeverything/hooks/use-block-suite-page-meta'; import { useBlockSuitePageMeta } from '@toeverything/hooks/use-block-suite-page-meta';
import { useBlockSuiteWorkspaceHelper } from '@toeverything/hooks/use-block-suite-workspace-helper'; import { useBlockSuiteWorkspaceHelper } from '@toeverything/hooks/use-block-suite-workspace-helper';
import { Command } from 'cmdk'; import { Command } from 'cmdk';
import { useAtomValue } from 'jotai';
import Image from 'next/legacy/image'; import Image from 'next/legacy/image';
import type { NextRouter } from 'next/router'; import type { NextRouter } from 'next/router';
import type { Dispatch, FC, SetStateAction } from 'react'; import type { Dispatch, FC, SetStateAction } from 'react';
import { useEffect } from 'react'; import { useEffect } from 'react';
import { useRecentlyViewed } from '../../../hooks/use-recent-views'; import { recentPageSettingsAtom } from '../../../atoms';
import { useRouterHelper } from '../../../hooks/use-router-helper'; import { useRouterHelper } from '../../../hooks/use-router-helper';
import type { BlockSuiteWorkspace } from '../../../shared'; import type { BlockSuiteWorkspace } from '../../../shared';
import { useSwitchToConfig } from './config'; import { useSwitchToConfig } from './config';
@@ -35,7 +36,7 @@ export const Results: FC<ResultsProps> = ({
assertExists(blockSuiteWorkspace.id); assertExists(blockSuiteWorkspace.id);
const List = useSwitchToConfig(blockSuiteWorkspace.id); const List = useSwitchToConfig(blockSuiteWorkspace.id);
const recentlyViewed = useRecentlyViewed(); const recentPageSetting = useAtomValue(recentPageSettingsAtom);
const t = useAFFiNEI18N(); const t = useAFFiNEI18N();
const { jumpToPage } = useRouterHelper(router); const { jumpToPage } = useRouterHelper(router);
const results = blockSuiteWorkspace.search(query); const results = blockSuiteWorkspace.search(query);
@@ -45,7 +46,8 @@ export const Results: FC<ResultsProps> = ({
const resultsPageMeta = pageList.filter( const resultsPageMeta = pageList.filter(
page => pageIds.indexOf(page.id) > -1 && !page.trash page => pageIds.indexOf(page.id) > -1 && !page.trash
); );
const recentlyViewedItem = recentlyViewed.filter(recent => {
const recentlyViewedItem = recentPageSetting.filter(recent => {
const page = pageList.find(page => recent.id === page.id); const page = pageList.find(page => recent.id === page.id);
if (!page) { if (!page) {
return false; return false;
@@ -8,7 +8,7 @@ import { useAtomValue } from 'jotai';
import { useRouter } from 'next/router'; import { useRouter } from 'next/router';
import { useMemo, useState } from 'react'; import { useMemo, useState } from 'react';
import { workspacePreferredModeAtom } from '../../../../atoms'; import { pageSettingFamily } from '../../../../atoms';
import type { FavoriteListProps } from '../index'; import type { FavoriteListProps } from '../index';
import EmptyItem from './empty-item'; import EmptyItem from './empty-item';
import * as styles from './styles.css'; import * as styles from './styles.css';
@@ -27,9 +27,9 @@ function FavoriteMenuItem({
parentIds, parentIds,
}: FavoriteMenuItemProps) { }: FavoriteMenuItemProps) {
const router = useRouter(); const router = useRouter();
const record = useAtomValue(workspacePreferredModeAtom); const setting = useAtomValue(pageSettingFamily(pageId));
const active = router.query.pageId === pageId; const active = router.query.pageId === pageId;
const icon = record[pageId] === 'edgeless' ? <EdgelessIcon /> : <PageIcon />; const icon = setting?.mode === 'edgeless' ? <EdgelessIcon /> : <PageIcon />;
const references = useBlockSuitePageReferences(workspace, pageId); const references = useBlockSuitePageReferences(workspace, pageId);
const referencesToShow = useMemo(() => { const referencesToShow = useMemo(() => {
return [ return [
@@ -5,7 +5,10 @@ import { useAtom } from 'jotai';
import type { ReactNode } from 'react'; import type { ReactNode } from 'react';
import type React from 'react'; import type React from 'react';
import { openQuickSearchModalAtom, pageModeSelectAtom } from '../../../atoms'; import {
allPageModeSelectAtom,
openQuickSearchModalAtom,
} from '../../../atoms';
import type { HeaderProps } from '../../blocksuite/workspace-header/header'; import type { HeaderProps } from '../../blocksuite/workspace-header/header';
import { Header } from '../../blocksuite/workspace-header/header'; import { Header } from '../../blocksuite/workspace-header/header';
import * as styles from '../../blocksuite/workspace-header/styles.css'; import * as styles from '../../blocksuite/workspace-header/styles.css';
@@ -40,7 +43,7 @@ export const WorkspaceTitle: React.FC<WorkspaceTitleProps> = ({
export const WorkspaceModeFilterTab = ({ ...props }: WorkspaceTitleProps) => { export const WorkspaceModeFilterTab = ({ ...props }: WorkspaceTitleProps) => {
const t = useAFFiNEI18N(); const t = useAFFiNEI18N();
const [value, setMode] = useAtom(pageModeSelectAtom); const [value, setMode] = useAtom(allPageModeSelectAtom);
const handleValueChange = (value: string) => { const handleValueChange = (value: string) => {
if (value !== 'all' && value !== 'page' && value !== 'edgeless') { if (value !== 'all' && value !== 'page' && value !== 'edgeless') {
throw new Error('Invalid value for page mode option'); throw new Error('Invalid value for page mode option');
@@ -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 { rootCurrentPageIdAtom } from '@affine/workspace/atom';
import { atom, useAtomValue } from 'jotai'; import { atom, useAtomValue } from 'jotai';
import { workspacePreferredModeAtom } from '../../atoms'; import { pageSettingFamily } from '../../atoms';
const currentModeAtom = atom<'page' | 'edgeless'>(get => { const currentModeAtom = atom<'page' | 'edgeless'>(get => {
const pageId = get(rootCurrentPageIdAtom); const pageId = get(rootCurrentPageIdAtom);
const record = get(workspacePreferredModeAtom); // fixme(himself65): pageId should not be null
if (pageId) return record[pageId] ?? 'page'; if (!pageId) {
else {
return 'page'; return 'page';
} }
return get(pageSettingFamily(pageId))?.mode ?? 'page';
}); });
export const useCurrentMode = () => { 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]);
}
@@ -12,7 +12,6 @@ import { useCallback } from 'react';
import { getUIAdapter } from '../../../adapters/workspace'; import { getUIAdapter } from '../../../adapters/workspace';
import { rootCurrentWorkspaceAtom } from '../../../atoms/root'; import { rootCurrentWorkspaceAtom } from '../../../atoms/root';
import { useCurrentWorkspace } from '../../../hooks/current/use-current-workspace'; import { useCurrentWorkspace } from '../../../hooks/current/use-current-workspace';
import { useSyncRecentViewsWithRouter } from '../../../hooks/use-recent-views';
import { useRouterHelper } from '../../../hooks/use-router-helper'; import { useRouterHelper } from '../../../hooks/use-router-helper';
import { WorkspaceLayout } from '../../../layouts/workspace-layout'; import { WorkspaceLayout } from '../../../layouts/workspace-layout';
import type { NextPageWithLayout } from '../../../shared'; import type { NextPageWithLayout } from '../../../shared';
@@ -25,7 +24,6 @@ const WorkspaceDetail: React.FC = () => {
assertExists(currentWorkspace); assertExists(currentWorkspace);
assertExists(currentPageId); assertExists(currentPageId);
const blockSuiteWorkspace = currentWorkspace.blockSuiteWorkspace; const blockSuiteWorkspace = currentWorkspace.blockSuiteWorkspace;
useSyncRecentViewsWithRouter(router, blockSuiteWorkspace);
const onLoad = useCallback( const onLoad = useCallback(
(page: Page, editor: EditorContainer) => { (page: Page, editor: EditorContainer) => {
+2
View File
@@ -7,6 +7,8 @@
"skipLibCheck": true, "skipLibCheck": true,
"strict": true, "strict": true,
"forceConsistentCasingInFileNames": true, "forceConsistentCasingInFileNames": true,
// FIXME: add this back
"noUncheckedIndexedAccess": false,
"esModuleInterop": true, "esModuleInterop": true,
"module": "esnext", "module": "esnext",
"moduleResolution": "node", "moduleResolution": "node",