From ad9b0303c4ce7ab9acdcaf686aeb35dabec27578 Mon Sep 17 00:00:00 2001 From: EYHN Date: Tue, 27 Feb 2024 03:50:53 +0000 Subject: [PATCH] refactor(core): refactor atom to use di (#5831) To support multiple instances, this PR removes some atoms and implements them using the new DI system. removed atom - `pageSettingsAtom` - `currentPageIdAtom` - `currentModeAtom` --- package.json | 2 +- packages/common/infra/src/blocksuite/index.ts | 1 - .../src/blocksuite/initialization/index.ts | 114 ----------------- .../common/infra/src/di/core/collection.ts | 2 +- packages/common/infra/src/index.ts | 1 + .../common/infra/src/initialization/index.ts | 117 ++++++++++++++++++ .../initialization/middleware.ts | 0 packages/common/infra/src/livedata/react.ts | 22 +++- .../infra/src/page/__tests__/page.spec.ts | 31 ----- packages/common/infra/src/page/context.ts | 10 +- packages/common/infra/src/page/index.ts | 17 +-- packages/common/infra/src/page/manager.ts | 29 ++--- packages/common/infra/src/page/page.ts | 25 +++- .../src/page/{list.ts => record-list.ts} | 41 +++--- packages/common/infra/src/page/record.ts | 65 ++++++++++ packages/common/infra/src/workspace/index.ts | 8 +- .../common/infra/src/workspace/storage.ts | 8 ++ .../common/infra/src/workspace/workspace.ts | 2 + .../core/src/atoms/__tests__/atom.spec.ts | 37 ------ packages/frontend/core/src/atoms/index.ts | 59 +-------- packages/frontend/core/src/atoms/mode.ts | 13 -- .../core/src/bootstrap/first-app-data.ts | 44 +++---- .../error-basic/info-logger.tsx | 12 +- .../affine/create-workspace-modal/index.tsx | 43 +++---- .../page-history-modal/history-modal.tsx | 19 ++- .../share-menu/share-export.tsx | 7 +- .../share-menu/share-page.tsx | 12 +- .../blocksuite-editor-container.tsx | 2 +- .../block-suite-header/menu/index.tsx | 15 ++- .../block-suite-mode-switch/index.tsx | 27 ++-- .../block-suite-page-list/utils.tsx | 23 ++-- .../cloud/share-header-right-item/index.tsx | 4 +- .../src/components/page-detail-editor.tsx | 23 ++-- .../src/components/pure/cmdk/data-hooks.tsx | 34 +++-- .../src/components/pure/help-island/index.tsx | 11 +- .../workspace-slider-bar/collections/page.tsx | 10 +- .../components/reference-page.tsx | 10 +- .../favorite/favourite-page.tsx | 12 +- .../root-app-sidebar/journal-button.tsx | 7 +- .../affine/use-block-suite-meta-helper.ts | 64 ++-------- .../src/hooks/affine/use-is-shared-page.ts | 3 +- ...se-register-blocksuite-editor-commands.tsx | 16 +-- .../src/hooks/current/use-current-page.ts | 15 --- .../core/src/hooks/use-global-state.ts | 5 +- .../frontend/core/src/hooks/use-journal.ts | 2 +- .../src/modules/infra-web/storage/index.ts | 24 +++- .../frontend/core/src/modules/services.ts | 10 +- .../src/pages/share/share-detail-page.tsx | 11 +- .../core/src/pages/share/share-header.tsx | 2 +- .../workspace/detail-page/detail-page.tsx | 89 ++++++------- packages/frontend/core/src/testing.ts | 2 +- packages/frontend/workspace-impl/src/index.ts | 19 ++- .../workspace-impl/src/local-state.ts | 31 +++++ tests/affine-local/e2e/quick-search.spec.ts | 3 + .../src/stories/blocksuite-editor.stories.tsx | 2 +- .../stories/image-preview-modal.stories.tsx | 4 +- .../stories/page-info-properties.stories.tsx | 2 +- .../src/stories/page-list.stories.tsx | 2 +- .../src/stories/share-menu.stories.tsx | 2 +- tools/@types/env/__all.d.ts | 1 - 60 files changed, 602 insertions(+), 626 deletions(-) delete mode 100644 packages/common/infra/src/blocksuite/initialization/index.ts create mode 100644 packages/common/infra/src/initialization/index.ts rename packages/common/infra/src/{blocksuite => }/initialization/middleware.ts (100%) delete mode 100644 packages/common/infra/src/page/__tests__/page.spec.ts rename packages/common/infra/src/page/{list.ts => record-list.ts} (50%) create mode 100644 packages/common/infra/src/page/record.ts create mode 100644 packages/common/infra/src/workspace/storage.ts delete mode 100644 packages/frontend/core/src/atoms/__tests__/atom.spec.ts delete mode 100644 packages/frontend/core/src/atoms/mode.ts delete mode 100644 packages/frontend/core/src/hooks/current/use-current-page.ts create mode 100644 packages/frontend/workspace-impl/src/local-state.ts diff --git a/package.json b/package.json index 6988e0f223..d47ac5b88c 100644 --- a/package.json +++ b/package.json @@ -36,7 +36,7 @@ "test": "vitest --run", "test:ui": "vitest --ui", "test:coverage": "vitest run --coverage", - "typecheck": "tsc -b tsconfig.json --diagnostics", + "typecheck": "tsc -b tsconfig.json", "postinstall": "node ./scripts/check-version.mjs && yarn i18n-codegen gen && yarn husky install", "prepare": "husky" }, diff --git a/packages/common/infra/src/blocksuite/index.ts b/packages/common/infra/src/blocksuite/index.ts index 09196653c0..04ae7e9fdd 100644 --- a/packages/common/infra/src/blocksuite/index.ts +++ b/packages/common/infra/src/blocksuite/index.ts @@ -1,4 +1,3 @@ -export * from './initialization'; export { migratePages as forceUpgradePages, migrateGuidCompatibility, diff --git a/packages/common/infra/src/blocksuite/initialization/index.ts b/packages/common/infra/src/blocksuite/initialization/index.ts deleted file mode 100644 index 28756701eb..0000000000 --- a/packages/common/infra/src/blocksuite/initialization/index.ts +++ /dev/null @@ -1,114 +0,0 @@ -import type { - JobMiddleware, - Page, - PageMeta, - PageSnapshot, - Workspace, - WorkspaceInfoSnapshot, -} from '@blocksuite/store'; -import { Job } from '@blocksuite/store'; -import type { createStore, WritableAtom } from 'jotai/vanilla'; -import { Map as YMap } from 'yjs'; - -import { getLatestVersions } from '../migration/blocksuite'; -import { replaceIdMiddleware } from './middleware'; - -export function initEmptyPage(page: Page, title?: string) { - page.load(() => { - const pageBlockId = page.addBlock('affine:page', { - title: new page.Text(title ?? ''), - }); - page.addBlock('affine:surface', {}, pageBlockId); - const noteBlockId = page.addBlock('affine:note', {}, pageBlockId); - page.addBlock('affine:paragraph', {}, noteBlockId); - }); -} - -/** - * FIXME: Use exported json data to instead of building data. - */ -export async function buildShowcaseWorkspace( - workspace: Workspace, - { - store, - atoms, - }: { - atoms: { - pageMode: WritableAtom< - undefined, - [pageId: string, mode: 'page' | 'edgeless'], - void - >; - }; - store: ReturnType; - } -) { - const { onboarding } = await import('@affine/templates'); - - const info = onboarding['info.json'] as WorkspaceInfoSnapshot; - - const migrationMiddleware: JobMiddleware = ({ slots, workspace }) => { - slots.afterImport.on(payload => { - if (payload.type === 'page') { - workspace.schema.upgradePage( - info?.pageVersion ?? 0, - {}, - payload.page.spaceDoc - ); - } - }); - }; - - const job = new Job({ - workspace, - middlewares: [replaceIdMiddleware, migrationMiddleware], - }); - - job.snapshotToWorkspaceInfo(info); - - // for now all onboarding assets are considered served via CDN - // hack assets so that every blob exists - // @ts-expect-error - rethinking API - job._assetsManager.writeToBlob = async () => {}; - - const pageSnapshots: PageSnapshot[] = Object.entries(onboarding) - .filter(([key]) => { - return key.endsWith('snapshot.json'); - }) - .map(([_, value]) => value as unknown as PageSnapshot); - - await Promise.all( - pageSnapshots.map(snapshot => { - return job.snapshotToPage(snapshot); - }) - ); - - const newVersions = getLatestVersions(workspace.schema); - workspace.doc - .getMap('meta') - .set('blockVersions', new YMap(Object.entries(newVersions))); - - // todo: find better way to do the following - // perhaps put them into middleware? - { - // the "AFFiNE - not just a note-taking app" page should be set to edgeless mode - const edgelessPage1 = (workspace.meta.pages as PageMeta[])?.find( - p => p.title === 'AFFiNE - not just a note-taking app' - )?.id; - - if (edgelessPage1) { - store.set(atoms.pageMode, edgelessPage1, 'edgeless'); - } - - // should jump to "AFFiNE - not just a note-taking app" by default - const defaultPage = (workspace.meta.pages as PageMeta[])?.find(p => - p.title.startsWith('AFFiNE - not just a note-taking app') - )?.id; - - if (defaultPage) { - workspace.setPageMeta(defaultPage, { - jumpOnce: true, - }); - } - } -} diff --git a/packages/common/infra/src/di/core/collection.ts b/packages/common/infra/src/di/core/collection.ts index ffab4af3ae..640cef0f93 100644 --- a/packages/common/infra/src/di/core/collection.ts +++ b/packages/common/infra/src/di/core/collection.ts @@ -337,7 +337,7 @@ class ServiceCollectionEditor { * ``` */ addImpl = < - Arg1 extends ServiceIdentifier, + Arg1 extends ServiceIdentifier | (new (...args: any) => any), Arg2 extends Type | ServiceFactory | Trait, Trait = ServiceIdentifierType, Deps extends Arg2 extends Type diff --git a/packages/common/infra/src/index.ts b/packages/common/infra/src/index.ts index a72bdc4422..4df804f758 100644 --- a/packages/common/infra/src/index.ts +++ b/packages/common/infra/src/index.ts @@ -3,6 +3,7 @@ export * from './atom'; export * from './blocksuite'; export * from './command'; export * from './di'; +export * from './initialization'; export * from './livedata'; export * from './page'; export * from './storage'; diff --git a/packages/common/infra/src/initialization/index.ts b/packages/common/infra/src/initialization/index.ts new file mode 100644 index 0000000000..dc6afce05f --- /dev/null +++ b/packages/common/infra/src/initialization/index.ts @@ -0,0 +1,117 @@ +import type { WorkspaceFlavour } from '@affine/env/workspace'; +import type { + JobMiddleware, + Page, + PageSnapshot, + WorkspaceInfoSnapshot, +} from '@blocksuite/store'; +import { Job } from '@blocksuite/store'; +import { Map as YMap } from 'yjs'; + +import { getLatestVersions } from '../blocksuite/migration/blocksuite'; +import { PageRecordList } from '../page'; +import type { WorkspaceManager } from '../workspace'; +import { replaceIdMiddleware } from './middleware'; + +export function initEmptyPage(page: Page, title?: string) { + page.load(() => { + const pageBlockId = page.addBlock('affine:page', { + title: new page.Text(title ?? ''), + }); + page.addBlock('affine:surface', {}, pageBlockId); + const noteBlockId = page.addBlock('affine:note', {}, pageBlockId); + page.addBlock('affine:paragraph', {}, noteBlockId); + }); +} + +/** + * FIXME: Use exported json data to instead of building data. + */ +export async function buildShowcaseWorkspace( + workspaceManager: WorkspaceManager, + flavour: WorkspaceFlavour, + workspaceName: string +) { + const meta = await workspaceManager.createWorkspace( + flavour, + async blockSuiteWorkspace => { + blockSuiteWorkspace.meta.setName(workspaceName); + const { onboarding } = await import('@affine/templates'); + + const info = onboarding['info.json'] as WorkspaceInfoSnapshot; + + const migrationMiddleware: JobMiddleware = ({ slots, workspace }) => { + slots.afterImport.on(payload => { + if (payload.type === 'page') { + workspace.schema.upgradePage( + info?.pageVersion ?? 0, + {}, + payload.page.spaceDoc + ); + } + }); + }; + + const job = new Job({ + workspace: blockSuiteWorkspace, + middlewares: [replaceIdMiddleware, migrationMiddleware], + }); + + job.snapshotToWorkspaceInfo(info); + + // for now all onboarding assets are considered served via CDN + // hack assets so that every blob exists + // @ts-expect-error - rethinking API + job._assetsManager.writeToBlob = async () => {}; + + const pageSnapshots: PageSnapshot[] = Object.entries(onboarding) + .filter(([key]) => { + return key.endsWith('snapshot.json'); + }) + .map(([_, value]) => value as unknown as PageSnapshot); + + await Promise.all( + pageSnapshots.map(snapshot => { + return job.snapshotToPage(snapshot); + }) + ); + + const newVersions = getLatestVersions(blockSuiteWorkspace.schema); + blockSuiteWorkspace.doc + .getMap('meta') + .set('blockVersions', new YMap(Object.entries(newVersions))); + } + ); + + const { workspace, release } = workspaceManager.open(meta); + + await workspace.engine.sync.waitForLoadedRootDoc(); + + const pageRecordList = workspace.services.get(PageRecordList); + + // todo: find better way to do the following + // perhaps put them into middleware? + { + // the "AFFiNE - not just a note-taking app" page should be set to edgeless mode + const edgelessPage1 = pageRecordList.records.value.find( + p => p.title.value === 'AFFiNE - not just a note-taking app' + ); + + if (edgelessPage1) { + edgelessPage1.setMode('edgeless'); + } + + // should jump to "AFFiNE - not just a note-taking app" by default + const defaultPage = pageRecordList.records.value.find(p => + p.title.value.startsWith('AFFiNE - not just a note-taking app') + ); + + if (defaultPage) { + defaultPage.setMeta({ + jumpOnce: true, + }); + } + } + release(); + return meta; +} diff --git a/packages/common/infra/src/blocksuite/initialization/middleware.ts b/packages/common/infra/src/initialization/middleware.ts similarity index 100% rename from packages/common/infra/src/blocksuite/initialization/middleware.ts rename to packages/common/infra/src/initialization/middleware.ts diff --git a/packages/common/infra/src/livedata/react.ts b/packages/common/infra/src/livedata/react.ts index 004f21ab7d..1b7ccd93b4 100644 --- a/packages/common/infra/src/livedata/react.ts +++ b/packages/common/infra/src/livedata/react.ts @@ -3,13 +3,29 @@ import { useSyncExternalStore } from 'react'; import type { LiveData } from './index'; +function noopSubscribe() { + return () => {}; +} + +function noopGetSnapshot() { + return null; +} + /** * subscribe LiveData and return the value. */ -export function useLiveData(liveData: LiveData): T { +export function useLiveData | null | undefined>( + liveData: Input +): NonNullable extends LiveData + ? Input extends undefined + ? T | undefined + : Input extends null + ? T | null + : T + : never { return useSyncExternalStore( - liveData.reactSubscribe, - liveData.reactGetSnapshot + liveData ? liveData.reactSubscribe : noopSubscribe, + liveData ? liveData.reactGetSnapshot : noopGetSnapshot ); } diff --git a/packages/common/infra/src/page/__tests__/page.spec.ts b/packages/common/infra/src/page/__tests__/page.spec.ts deleted file mode 100644 index a8ce869346..0000000000 --- a/packages/common/infra/src/page/__tests__/page.spec.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { WorkspaceFlavour } from '@affine/env/workspace'; -import { describe, expect, test } from 'vitest'; - -import { configureInfraServices, configureTestingInfraServices } from '../..'; -import { ServiceCollection } from '../../di'; -import { WorkspaceManager } from '../../workspace'; -import { PageListService } from '..'; - -describe('Page System', () => { - test('basic', async () => { - const services = new ServiceCollection(); - configureInfraServices(services); - configureTestingInfraServices(services); - - const provider = services.provider(); - const workspaceManager = provider.get(WorkspaceManager); - - const { workspace } = workspaceManager.open( - await workspaceManager.createWorkspace(WorkspaceFlavour.LOCAL) - ); - - const pageListService = workspace.services.get(PageListService); - expect(pageListService.pages.value.length).toBe(0); - - workspace.blockSuiteWorkspace.createPage({ - id: 'page0', - }); - - expect(pageListService.pages.value.length).toBe(1); - }); -}); diff --git a/packages/common/infra/src/page/context.ts b/packages/common/infra/src/page/context.ts index 2102cc78ca..646aa4b3d6 100644 --- a/packages/common/infra/src/page/context.ts +++ b/packages/common/infra/src/page/context.ts @@ -1,21 +1,23 @@ -import type { Page as BlockSuitePage, PageMeta } from '@blocksuite/store'; +import type { Page as BlockSuitePage } from '@blocksuite/store'; import { createIdentifier, type ServiceCollection } from '../di'; +import type { PageRecord } from './record'; import { PageScope } from './service-scope'; export const BlockSuitePageContext = createIdentifier( 'BlockSuitePageContext' ); -export const PageMetaContext = createIdentifier('PageMetaContext'); +export const PageRecordContext = + createIdentifier('PageRecordContext'); export function configurePageContext( services: ServiceCollection, blockSuitePage: BlockSuitePage, - pageMeta: PageMeta + pageRecord: PageRecord ) { services .scope(PageScope) - .addImpl(PageMetaContext, pageMeta) + .addImpl(PageRecordContext, pageRecord) .addImpl(BlockSuitePageContext, blockSuitePage); } diff --git a/packages/common/infra/src/page/index.ts b/packages/common/infra/src/page/index.ts index f366d269df..be8e115ca9 100644 --- a/packages/common/infra/src/page/index.ts +++ b/packages/common/infra/src/page/index.ts @@ -1,25 +1,26 @@ -export * from './context'; -export * from './list'; export * from './manager'; export * from './page'; +export * from './record'; +export * from './record-list'; export * from './service-scope'; import { type ServiceCollection, ServiceProvider } from '../di'; import { CleanupService } from '../lifecycle'; -import { Workspace, WorkspaceScope } from '../workspace'; -import { BlockSuitePageContext, PageMetaContext } from './context'; -import { PageListService } from './list'; +import { Workspace, WorkspaceLocalState, WorkspaceScope } from '../workspace'; +import { BlockSuitePageContext, PageRecordContext } from './context'; import { PageManager } from './manager'; import { Page } from './page'; +import { PageRecordList } from './record-list'; import { PageScope } from './service-scope'; export function configurePageServices(services: ServiceCollection) { services .scope(WorkspaceScope) - .add(PageListService, [Workspace]) - .add(PageManager, [Workspace, PageListService, ServiceProvider]); + .add(PageManager, [Workspace, PageRecordList, ServiceProvider]) + .add(PageRecordList, [Workspace, WorkspaceLocalState]); + services .scope(PageScope) .add(CleanupService) - .add(Page, [PageMetaContext, BlockSuitePageContext, ServiceProvider]); + .add(Page, [PageRecordContext, BlockSuitePageContext, ServiceProvider]); } diff --git a/packages/common/infra/src/page/manager.ts b/packages/common/infra/src/page/manager.ts index 974a76fb9e..8ce8a10f2f 100644 --- a/packages/common/infra/src/page/manager.ts +++ b/packages/common/infra/src/page/manager.ts @@ -1,10 +1,8 @@ -import type { PageMeta } from '@blocksuite/store'; - import type { ServiceProvider } from '../di'; import { ObjectPool } from '../utils/object-pool'; import type { Workspace } from '../workspace'; +import type { PageRecordList } from '.'; import { configurePageContext } from './context'; -import type { PageListService } from './list'; import { Page } from './page'; import { PageScope } from './service-scope'; @@ -13,28 +11,21 @@ export class PageManager { constructor( private readonly workspace: Workspace, - private readonly pageList: PageListService, + private readonly pageRecordList: PageRecordList, private readonly serviceProvider: ServiceProvider ) {} - openByPageId(pageId: string) { - const pageMeta = this.pageList.getPageMetaById(pageId); - if (!pageMeta) { - throw new Error('Page not found'); + open(pageId: string) { + const pageRecord = this.pageRecordList.record(pageId).value; + if (!pageRecord) { + throw new Error('Page record not found'); } - - return this.open(pageMeta); - } - - open(pageMeta: PageMeta) { - const blockSuitePage = this.workspace.blockSuiteWorkspace.getPage( - pageMeta.id - ); + const blockSuitePage = this.workspace.blockSuiteWorkspace.getPage(pageId); if (!blockSuitePage) { throw new Error('Page not found'); } - const exists = this.pool.get(pageMeta.id); + const exists = this.pool.get(pageId); if (exists) { return { page: exists.obj, release: exists.release }; } @@ -43,7 +34,7 @@ export class PageManager { // avoid to modify the original service collection .clone(); - configurePageContext(serviceCollection, blockSuitePage, pageMeta); + configurePageContext(serviceCollection, blockSuitePage, pageRecord); const provider = serviceCollection.provider( PageScope, @@ -52,7 +43,7 @@ export class PageManager { const page = provider.get(Page); - const { obj, release } = this.pool.put(pageMeta.id, page); + const { obj, release } = this.pool.put(pageId, page); return { page: obj, release }; } diff --git a/packages/common/infra/src/page/page.ts b/packages/common/infra/src/page/page.ts index bd330cf326..a390ea3a10 100644 --- a/packages/common/infra/src/page/page.ts +++ b/packages/common/infra/src/page/page.ts @@ -1,15 +1,28 @@ import type { Page as BlockSuitePage } from '@blocksuite/store'; -import { type PageMeta } from '@blocksuite/store'; import type { ServiceProvider } from '@toeverything/infra/di'; -export class Page { - get id() { - return this.meta.id; - } +import type { PageMode, PageRecord } from './record'; +export class Page { constructor( - public readonly meta: PageMeta, + public readonly record: PageRecord, public readonly blockSuitePage: BlockSuitePage, public readonly services: ServiceProvider ) {} + + get id() { + return this.record.id; + } + + readonly mete = this.record.meta; + readonly mode = this.record.mode; + readonly title = this.record.title; + + setMode(mode: PageMode) { + this.record.setMode(mode); + } + + toggleMode() { + this.record.toggleMode(); + } } diff --git a/packages/common/infra/src/page/list.ts b/packages/common/infra/src/page/record-list.ts similarity index 50% rename from packages/common/infra/src/page/list.ts rename to packages/common/infra/src/page/record-list.ts index 831df6e19d..92d9686615 100644 --- a/packages/common/infra/src/page/list.ts +++ b/packages/common/infra/src/page/record-list.ts @@ -1,24 +1,35 @@ -import type { PageMeta } from '@blocksuite/store'; import { Observable } from 'rxjs'; import { LiveData } from '../livedata'; -import { SyncEngineStep, type Workspace } from '../workspace'; +import { + SyncEngineStep, + type Workspace, + type WorkspaceLocalState, +} from '../workspace'; +import { PageRecord } from './record'; -export class PageListService { - constructor(private readonly workspace: Workspace) {} +export class PageRecordList { + constructor( + private readonly workspace: Workspace, + private readonly localState: WorkspaceLocalState + ) {} - public readonly pages = LiveData.from( + public readonly records = LiveData.from( new Observable(subscriber => { - subscriber.next( - Array.from(this.workspace.blockSuiteWorkspace.meta.pageMetas) - ); + const emit = () => { + subscriber.next( + this.workspace.blockSuiteWorkspace.meta.pageMetas.map( + v => new PageRecord(v.id, this.workspace, this.localState) + ) + ); + }; + + emit(); const dispose = - this.workspace.blockSuiteWorkspace.meta.pageMetasUpdated.on(() => { - subscriber.next( - Array.from(this.workspace.blockSuiteWorkspace.meta.pageMetas) - ); - }).dispose; + this.workspace.blockSuiteWorkspace.meta.pageMetasUpdated.on( + emit + ).dispose; return () => { dispose(); }; @@ -44,7 +55,7 @@ export class PageListService { false ); - public getPageMetaById(id: string) { - return this.pages.value.find(page => page.id === id); + public record(id: string) { + return this.records.map(record => record.find(record => record.id === id)); } } diff --git a/packages/common/infra/src/page/record.ts b/packages/common/infra/src/page/record.ts new file mode 100644 index 0000000000..1777bed241 --- /dev/null +++ b/packages/common/infra/src/page/record.ts @@ -0,0 +1,65 @@ +import type { PageMeta } from '@blocksuite/store'; +import { Observable } from 'rxjs'; + +import { LiveData } from '../livedata'; +import type { Workspace, WorkspaceLocalState } from '../workspace'; + +export type PageMode = 'edgeless' | 'page'; + +export class PageRecord { + constructor( + public readonly id: string, + private readonly workspace: Workspace, + private readonly localState: WorkspaceLocalState + ) {} + + meta = LiveData.from( + new Observable(subscriber => { + const emit = () => { + const meta = this.workspace.blockSuiteWorkspace.meta.pageMetas.find( + page => page.id === this.id + ); + if (meta === undefined) { + return; + } + subscriber.next(meta); + }; + + emit(); + + const dispose = + this.workspace.blockSuiteWorkspace.meta.pageMetasUpdated.on( + emit + ).dispose; + return () => { + dispose(); + }; + }), + { + id: this.id, + title: '', + tags: [], + createDate: 0, + } + ); + + setMeta(meta: Partial): void { + this.workspace.blockSuiteWorkspace.setPageMeta(this.id, meta); + } + + mode: LiveData = LiveData.from( + this.localState.watch(`page:${this.id}:mode`), + 'page' + ).map(mode => (mode === 'edgeless' ? 'edgeless' : 'page')); + + setMode(mode: PageMode) { + this.localState.set(`page:${this.id}:mode`, mode); + } + + toggleMode() { + this.setMode(this.mode.value === 'edgeless' ? 'page' : 'edgeless'); + return this.mode.value; + } + + title = this.meta.map(meta => meta.title); +} diff --git a/packages/common/infra/src/workspace/index.ts b/packages/common/infra/src/workspace/index.ts index d4997ea1f0..57718ad708 100644 --- a/packages/common/infra/src/workspace/index.ts +++ b/packages/common/infra/src/workspace/index.ts @@ -6,13 +6,14 @@ export * from './list'; export * from './manager'; export * from './metadata'; export * from './service-scope'; +export * from './storage'; export * from './testing'; export * from './upgrade'; export * from './workspace'; import { type ServiceCollection, ServiceProvider } from '../di'; import { CleanupService } from '../lifecycle'; -import { GlobalCache, GlobalState } from '../storage'; +import { GlobalCache, GlobalState, MemoryMemento } from '../storage'; import { BlockSuiteWorkspaceContext, RootYDocContext, @@ -33,6 +34,7 @@ import { WorkspaceFactory } from './factory'; import { WorkspaceListProvider, WorkspaceListService } from './list'; import { WorkspaceManager } from './manager'; import { WorkspaceScope } from './service-scope'; +import { WorkspaceLocalState } from './storage'; import { TestingLocalWorkspaceFactory, TestingLocalWorkspaceListProvider, @@ -83,5 +85,7 @@ export function configureTestingWorkspaceServices(services: ServiceCollection) { ) .override(WorkspaceFactory('local'), TestingLocalWorkspaceFactory, [ GlobalState, - ]); + ]) + .scope(WorkspaceScope) + .override(WorkspaceLocalState, MemoryMemento); } diff --git a/packages/common/infra/src/workspace/storage.ts b/packages/common/infra/src/workspace/storage.ts new file mode 100644 index 0000000000..b7d2fe41f7 --- /dev/null +++ b/packages/common/infra/src/workspace/storage.ts @@ -0,0 +1,8 @@ +import { createIdentifier } from '../di'; +import type { Memento } from '../storage'; + +export interface WorkspaceLocalState extends Memento {} + +export const WorkspaceLocalState = createIdentifier( + 'WorkspaceLocalState' +); diff --git a/packages/common/infra/src/workspace/workspace.ts b/packages/common/infra/src/workspace/workspace.ts index ffca6bf491..2fa2c75303 100644 --- a/packages/common/infra/src/workspace/workspace.ts +++ b/packages/common/infra/src/workspace/workspace.ts @@ -10,6 +10,8 @@ import { type WorkspaceMetadata } from './metadata'; import type { WorkspaceUpgradeController } from './upgrade'; import { type WorkspaceUpgradeStatus } from './upgrade'; +export type { Workspace as BlockSuiteWorkspace } from '@blocksuite/store'; + const logger = new DebugLogger('affine:workspace'); export type WorkspaceStatus = { diff --git a/packages/frontend/core/src/atoms/__tests__/atom.spec.ts b/packages/frontend/core/src/atoms/__tests__/atom.spec.ts deleted file mode 100644 index c2b0cb4767..0000000000 --- a/packages/frontend/core/src/atoms/__tests__/atom.spec.ts +++ /dev/null @@ -1,37 +0,0 @@ -/** - * @vitest-environment happy-dom - */ -import 'fake-indexeddb/auto'; - -import { createStore } from 'jotai'; -import { describe, expect, test } from 'vitest'; - -import { - pageSettingFamily, - pageSettingsAtom, - recentPageIdsBaseAtom, -} from '../index'; - -describe('page mode atom', () => { - test('basic', () => { - const store = createStore(); - const page0SettingAtom = pageSettingFamily('page0'); - store.set(page0SettingAtom, { - mode: 'page', - }); - - expect(store.get(pageSettingsAtom)).toEqual({ - page0: { - mode: 'page', - }, - }); - - expect(store.get(recentPageIdsBaseAtom)).toEqual(['page0']); - - const page1SettingAtom = pageSettingFamily('page1'); - store.set(page1SettingAtom, { - mode: 'edgeless', - }); - expect(store.get(recentPageIdsBaseAtom)).toEqual(['page1', 'page0']); - }); -}); diff --git a/packages/frontend/core/src/atoms/index.ts b/packages/frontend/core/src/atoms/index.ts index 5eac7b974d..cab595dbcb 100644 --- a/packages/frontend/core/src/atoms/index.ts +++ b/packages/frontend/core/src/atoms/index.ts @@ -1,7 +1,5 @@ -import type { PrimitiveAtom } from 'jotai'; import { atom } from 'jotai'; -import { atomFamily, atomWithStorage } from 'jotai/utils'; -import type { AtomFamily } from 'jotai/vanilla/utils/atomFamily'; +import { atomWithStorage } from 'jotai/utils'; import type { AuthProps } from '../components/affine/auth'; import type { CreateWorkspaceMode } from '../components/affine/create-workspace-modal'; @@ -45,66 +43,11 @@ export const authAtom = atom({ export const openDisableCloudAlertModalAtom = atom(false); -export type PageMode = 'page' | 'edgeless'; -type PageLocalSetting = { - mode: PageMode; -}; - -const pageSettingsBaseAtom = atomWithStorage( - 'pageSettings', - {} as Record -); - -// readonly atom by design -export const pageSettingsAtom = atom(get => get(pageSettingsBaseAtom)); - export const recentPageIdsBaseAtom = atomWithStorage( 'recentPageSettings', [] ); -const defaultPageSetting = { - mode: 'page', -} satisfies PageLocalSetting; - -export const pageSettingFamily: AtomFamily< - string, - PrimitiveAtom -> = atomFamily((pageId: string) => - atom( - get => - get(pageSettingsBaseAtom)[pageId] ?? { - ...defaultPageSetting, - }, - (get, set, patch) => { - // fixme: this does not work when page reload, - // since atomWithStorage is async - set(recentPageIdsBaseAtom, ids => { - // pick 3 recent page ids - return [...new Set([pageId, ...ids]).values()].slice(0, 3); - }); - const prevSetting = { - ...defaultPageSetting, - ...get(pageSettingsBaseAtom)[pageId], - }; - set(pageSettingsBaseAtom, settings => ({ - ...settings, - [pageId]: { - ...prevSetting, - ...(typeof patch === 'function' ? patch(prevSetting) : patch), - }, - })); - } - ) -); - -export const setPageModeAtom = atom( - void 0, - (_, set, pageId: string, mode: PageMode) => { - set(pageSettingFamily(pageId), { mode }); - } -); - export type PageModeOption = 'all' | 'page' | 'edgeless'; export const allPageModeSelectAtom = atom('all'); diff --git a/packages/frontend/core/src/atoms/mode.ts b/packages/frontend/core/src/atoms/mode.ts deleted file mode 100644 index 3a79f90a75..0000000000 --- a/packages/frontend/core/src/atoms/mode.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { atom } from 'jotai/vanilla'; - -import { pageSettingFamily } from './index'; - -export const currentPageIdAtom = atom(null); - -export const currentModeAtom = atom<'page' | 'edgeless'>(get => { - const pageId = get(currentPageIdAtom); - if (!pageId) { - return 'page'; - } - return get(pageSettingFamily(pageId)).mode; -}); diff --git a/packages/frontend/core/src/bootstrap/first-app-data.ts b/packages/frontend/core/src/bootstrap/first-app-data.ts index 156a8c4b54..a3a76c589c 100644 --- a/packages/frontend/core/src/bootstrap/first-app-data.ts +++ b/packages/frontend/core/src/bootstrap/first-app-data.ts @@ -2,42 +2,36 @@ import { DebugLogger } from '@affine/debug'; import { DEFAULT_WORKSPACE_NAME } from '@affine/env/constant'; import { WorkspaceFlavour } from '@affine/env/workspace'; import type { WorkspaceManager } from '@toeverything/infra'; -import { getCurrentStore } from '@toeverything/infra/atom'; -import { - buildShowcaseWorkspace, - initEmptyPage, -} from '@toeverything/infra/blocksuite'; +import { buildShowcaseWorkspace, initEmptyPage } from '@toeverything/infra'; -import { setPageModeAtom } from '../atoms'; - -const logger = new DebugLogger('affine:first-app-data'); +const logger = new DebugLogger('createFirstAppData'); export async function createFirstAppData(workspaceManager: WorkspaceManager) { if (localStorage.getItem('is-first-open') !== null) { return; } localStorage.setItem('is-first-open', 'false'); - const workspaceMetadata = await workspaceManager.createWorkspace( - WorkspaceFlavour.LOCAL, - async workspace => { - workspace.meta.setName(DEFAULT_WORKSPACE_NAME); - if (runtimeConfig.enablePreloading) { - await buildShowcaseWorkspace(workspace, { - store: getCurrentStore(), - atoms: { - pageMode: setPageModeAtom, - }, - }); - } else { + if (runtimeConfig.enablePreloading) { + const workspaceMetadata = await buildShowcaseWorkspace( + workspaceManager, + WorkspaceFlavour.LOCAL, + DEFAULT_WORKSPACE_NAME + ); + logger.info('create first workspace', workspaceMetadata); + return workspaceMetadata; + } else { + const workspaceMetadata = await workspaceManager.createWorkspace( + WorkspaceFlavour.LOCAL, + async workspace => { + workspace.meta.setName(DEFAULT_WORKSPACE_NAME); const page = workspace.createPage(); workspace.setPageMeta(page.id, { jumpOnce: true, }); initEmptyPage(page); } - logger.debug('create first workspace'); - } - ); - console.info('create first workspace', workspaceMetadata); - return workspaceMetadata; + ); + logger.info('create first workspace', workspaceMetadata); + return workspaceMetadata; + } } diff --git a/packages/frontend/core/src/components/affine/affine-error-boundary/error-basic/info-logger.tsx b/packages/frontend/core/src/components/affine/affine-error-boundary/error-basic/info-logger.tsx index 1377accc78..2c6a340723 100644 --- a/packages/frontend/core/src/components/affine/affine-error-boundary/error-basic/info-logger.tsx +++ b/packages/frontend/core/src/components/affine/affine-error-boundary/error-basic/info-logger.tsx @@ -1,11 +1,9 @@ -import { WorkspaceListService } from '@toeverything/infra'; -import { useService } from '@toeverything/infra/di'; +import { Page, WorkspaceListService } from '@toeverything/infra'; +import { useService, useServiceOptional } from '@toeverything/infra/di'; import { useLiveData } from '@toeverything/infra/livedata'; -import { useAtomValue } from 'jotai/react'; import { useEffect } from 'react'; import { useLocation, useParams } from 'react-router-dom'; -import { currentPageIdAtom } from '../../../../atoms/mode'; import { CurrentWorkspaceService } from '../../../../modules/workspace/current-workspace'; export interface DumpInfoProps { @@ -18,7 +16,7 @@ export const DumpInfo = (_props: DumpInfoProps) => { const currentWorkspace = useLiveData( useService(CurrentWorkspaceService).currentWorkspace ); - const currentPageId = useAtomValue(currentPageIdAtom); + const currentPage = useServiceOptional(Page); const path = location.pathname; const query = useParams(); useEffect(() => { @@ -26,9 +24,9 @@ export const DumpInfo = (_props: DumpInfoProps) => { path, query, currentWorkspaceId: currentWorkspace?.id, - currentPageId, + currentPageId: currentPage?.id, workspaceList, }); - }, [path, query, currentWorkspace, currentPageId, workspaceList]); + }, [path, query, currentWorkspace, workspaceList, currentPage?.id]); return null; }; diff --git a/packages/frontend/core/src/components/affine/create-workspace-modal/index.tsx b/packages/frontend/core/src/components/affine/create-workspace-modal/index.tsx index a27b670012..14b6c8df1e 100644 --- a/packages/frontend/core/src/components/affine/create-workspace-modal/index.tsx +++ b/packages/frontend/core/src/components/affine/create-workspace-modal/index.tsx @@ -4,11 +4,7 @@ import { type ConfirmModalProps, Modal, } from '@affine/component/ui/modal'; -import { - authAtom, - openDisableCloudAlertModalAtom, - setPageModeAtom, -} from '@affine/core/atoms'; +import { authAtom, openDisableCloudAlertModalAtom } from '@affine/core/atoms'; import { useCurrentLoginStatus } from '@affine/core/hooks/affine/use-current-login-status'; import { useAsyncCallback } from '@affine/core/hooks/affine-async-hooks'; import { DebugLogger } from '@affine/debug'; @@ -17,11 +13,7 @@ import { WorkspaceFlavour } from '@affine/env/workspace'; import { useAFFiNEI18N } from '@affine/i18n/hooks'; import { _addLocalWorkspace } from '@affine/workspace-impl'; import { WorkspaceManager } from '@toeverything/infra'; -import { getCurrentStore } from '@toeverything/infra/atom'; -import { - buildShowcaseWorkspace, - initEmptyPage, -} from '@toeverything/infra/blocksuite'; +import { buildShowcaseWorkspace, initEmptyPage } from '@toeverything/infra'; import { useService } from '@toeverything/infra/di'; import { useSetAtom } from 'jotai'; import type { KeyboardEvent } from 'react'; @@ -234,28 +226,27 @@ export const CreateWorkspaceModal = ({ async (name: string, workspaceFlavour: WorkspaceFlavour) => { // this will be the last step for web for now // fix me later - const { id } = await workspaceManager.createWorkspace( - workspaceFlavour, - async workspace => { - workspace.meta.setName(name); - if (runtimeConfig.enablePreloading) { - await buildShowcaseWorkspace(workspace, { - store: getCurrentStore(), - atoms: { - pageMode: setPageModeAtom, - }, - }); - } else { + if (runtimeConfig.enablePreloading) { + const { id } = await buildShowcaseWorkspace( + workspaceManager, + workspaceFlavour, + name + ); + onCreate(id); + } else { + const { id } = await workspaceManager.createWorkspace( + workspaceFlavour, + async workspace => { + workspace.meta.setName(name); const page = workspace.createPage(); workspace.setPageMeta(page.id, { jumpOnce: true, }); initEmptyPage(page); } - logger.debug('create first workspace'); - } - ); - onCreate(id); + ); + onCreate(id); + } }, [onCreate, workspaceManager] ); diff --git a/packages/frontend/core/src/components/affine/page-history-modal/history-modal.tsx b/packages/frontend/core/src/components/affine/page-history-modal/history-modal.tsx index deaec3054a..5784e63139 100644 --- a/packages/frontend/core/src/components/affine/page-history-modal/history-modal.tsx +++ b/packages/frontend/core/src/components/affine/page-history-modal/history-modal.tsx @@ -2,7 +2,7 @@ import { Loading, Scrollable } from '@affine/component'; import { EditorLoading } from '@affine/component/page-detail-skeleton'; import { Button, IconButton } from '@affine/component/ui/button'; import { ConfirmModal, Modal } from '@affine/component/ui/modal'; -import { openSettingModalAtom, type PageMode } from '@affine/core/atoms'; +import { openSettingModalAtom } from '@affine/core/atoms'; import { useIsWorkspaceOwner } from '@affine/core/hooks/affine/use-is-workspace-owner'; import { useAsyncCallback } from '@affine/core/hooks/affine-async-hooks'; import { useBlockSuiteWorkspacePageTitle } from '@affine/core/hooks/use-block-suite-workspace-page-title'; @@ -10,15 +10,13 @@ import { useWorkspaceQuota } from '@affine/core/hooks/use-workspace-quota'; import { Trans } from '@affine/i18n'; import { useAFFiNEI18N } from '@affine/i18n/hooks'; import { CloseIcon, ToggleCollapseIcon } from '@blocksuite/icons'; -import { - type Page, - type Workspace as BlockSuiteWorkspace, -} from '@blocksuite/store'; +import type { Page as BlockSuitePage } from '@blocksuite/store'; +import { type Workspace as BlockSuiteWorkspace } from '@blocksuite/store'; import * as Collapsible from '@radix-ui/react-collapsible'; import type { DialogContentProps } from '@radix-ui/react-dialog'; -import { Workspace } from '@toeverything/infra'; +import { Page, type PageMode, Workspace } from '@toeverything/infra'; import { useService } from '@toeverything/infra/di'; -import { atom, useAtom, useAtomValue, useSetAtom } from 'jotai'; +import { atom, useAtom, useSetAtom } from 'jotai'; import { Fragment, type PropsWithChildren, @@ -30,7 +28,6 @@ import { } from 'react'; import { encodeStateAsUpdate } from 'yjs'; -import { currentModeAtom } from '../../../atoms/mode'; import { pageHistoryModalAtom } from '../../../atoms/page-history'; import { timestampToLocalTime } from '../../../utils'; import { BlockSuiteEditor } from '../../blocksuite/block-suite-editor'; @@ -93,7 +90,7 @@ const ModalContainer = ({ interface HistoryEditorPreviewProps { ts?: string; - snapshotPage?: Page; + snapshotPage?: BlockSuitePage; mode: PageMode; onModeChange: (mode: PageMode) => void; title: string; @@ -450,8 +447,8 @@ const PageHistoryManager = ({ [activeVersion, onClose, onRestore, snapshotPage] ); - const defaultPreviewPageMode = useAtomValue(currentModeAtom); - const [mode, setMode] = useState(defaultPreviewPageMode); + const page = useService(Page); + const [mode, setMode] = useState(page.mode.value); const title = useBlockSuiteWorkspacePageTitle(workspace, pageId); diff --git a/packages/frontend/core/src/components/affine/share-page-modal/share-menu/share-export.tsx b/packages/frontend/core/src/components/affine/share-page-modal/share-menu/share-export.tsx index 84376ba644..2e192a386d 100644 --- a/packages/frontend/core/src/components/affine/share-page-modal/share-menu/share-export.tsx +++ b/packages/frontend/core/src/components/affine/share-page-modal/share-menu/share-export.tsx @@ -4,9 +4,9 @@ import { ExportMenuItems } from '@affine/core/components/page-list'; import { WorkspaceFlavour } from '@affine/env/workspace'; import { useAFFiNEI18N } from '@affine/i18n/hooks'; import { LinkIcon } from '@blocksuite/icons'; -import { useAtomValue } from 'jotai'; +import { Page, useLiveData } from '@toeverything/infra'; +import { useService } from '@toeverything/infra/di'; -import { currentModeAtom } from '../../../../atoms/mode'; import { useExportPage } from '../../../../hooks/affine/use-export-page'; import * as styles from './index.css'; import type { ShareMenuProps } from './share-menu'; @@ -17,6 +17,7 @@ export const ShareExport = ({ currentPage, }: ShareMenuProps) => { const t = useAFFiNEI18N(); + const page = useService(Page); const workspaceId = workspace.id; const pageId = currentPage.id; const { sharingUrl, onClickCopyLink } = useSharingUrl({ @@ -25,7 +26,7 @@ export const ShareExport = ({ urlType: 'workspace', }); const exportHandler = useExportPage(currentPage); - const currentMode = useAtomValue(currentModeAtom); + const currentMode = useLiveData(page.mode); return ( <> diff --git a/packages/frontend/core/src/components/affine/share-page-modal/share-menu/share-page.tsx b/packages/frontend/core/src/components/affine/share-page-modal/share-menu/share-page.tsx index 84d804bd7e..82af849a08 100644 --- a/packages/frontend/core/src/components/affine/share-page-modal/share-menu/share-page.tsx +++ b/packages/frontend/core/src/components/affine/share-page-modal/share-menu/share-page.tsx @@ -8,14 +8,14 @@ import { import { PublicLinkDisableModal } from '@affine/component/disable-public-link'; import { Button } from '@affine/component/ui/button'; import { Menu, MenuItem, MenuTrigger } from '@affine/component/ui/menu'; -import type { PageMode } from '@affine/core/atoms'; -import { currentModeAtom } from '@affine/core/atoms/mode'; import { useIsSharedPage } from '@affine/core/hooks/affine/use-is-shared-page'; import { useServerBaseUrl } from '@affine/core/hooks/affine/use-server-config'; import { WorkspaceFlavour } from '@affine/env/workspace'; import { useAFFiNEI18N } from '@affine/i18n/hooks'; import { ArrowRightSmallIcon } from '@blocksuite/icons'; -import { useAtomValue } from 'jotai'; +import { useService } from '@toeverything/infra'; +import { useLiveData } from '@toeverything/infra'; +import { Page, type PageMode } from '@toeverything/infra'; import { useMemo, useState } from 'react'; import { useCallback } from 'react'; @@ -56,6 +56,7 @@ export const AffineSharePage = (props: ShareMenuProps) => { currentPage, } = props; const pageId = currentPage.id; + const page = useService(Page); const [showDisable, setShowDisable] = useState(false); const { isSharedPage, @@ -64,14 +65,15 @@ export const AffineSharePage = (props: ShareMenuProps) => { currentShareMode, disableShare, } = useIsSharedPage(workspaceId, currentPage.id); - const currentPageMode = useAtomValue(currentModeAtom); + + const currentPageMode = useLiveData(page.mode); const defaultMode = useMemo(() => { if (isSharedPage) { // if it's a shared page, use the share mode return currentShareMode; } - // default to current page mode + // default to page mode return currentPageMode; }, [currentPageMode, currentShareMode, isSharedPage]); const [mode, setMode] = useState(defaultMode); diff --git a/packages/frontend/core/src/components/blocksuite/block-suite-editor/blocksuite-editor-container.tsx b/packages/frontend/core/src/components/blocksuite/block-suite-editor/blocksuite-editor-container.tsx index 72dbefbd72..0c83d953f3 100644 --- a/packages/frontend/core/src/components/blocksuite/block-suite-editor/blocksuite-editor-container.tsx +++ b/packages/frontend/core/src/components/blocksuite/block-suite-editor/blocksuite-editor-container.tsx @@ -1,4 +1,3 @@ -import type { PageMode } from '@affine/core/atoms'; import type { BlockElement } from '@blocksuite/lit'; import type { AffineEditorContainer, @@ -6,6 +5,7 @@ import type { EdgelessEditor, } from '@blocksuite/presets'; import { type Page, Slot } from '@blocksuite/store'; +import type { PageMode } from '@toeverything/infra'; import clsx from 'clsx'; import type React from 'react'; import { diff --git a/packages/frontend/core/src/components/blocksuite/block-suite-header/menu/index.tsx b/packages/frontend/core/src/components/blocksuite/block-suite-header/menu/index.tsx index df8a4f4e63..76f1719c43 100644 --- a/packages/frontend/core/src/components/blocksuite/block-suite-header/menu/index.tsx +++ b/packages/frontend/core/src/components/blocksuite/block-suite-header/menu/index.tsx @@ -6,7 +6,6 @@ import { MenuSeparator, } from '@affine/component/ui/menu'; import { openHistoryTipsModalAtom } from '@affine/core/atoms'; -import { currentModeAtom } from '@affine/core/atoms/mode'; import { PageHistoryModal } from '@affine/core/components/affine/page-history-modal'; import { Export, MoveToTrash } from '@affine/core/components/page-list'; import { useBlockSuiteMetaHelper } from '@affine/core/hooks/affine/use-block-suite-meta-helper'; @@ -26,8 +25,8 @@ import { ImportIcon, PageIcon, } from '@blocksuite/icons'; -import { useService, Workspace } from '@toeverything/infra'; -import { useAtomValue, useSetAtom } from 'jotai'; +import { Page, useLiveData, useService, Workspace } from '@toeverything/infra'; +import { useSetAtom } from 'jotai'; import { useCallback, useState } from 'react'; import { HeaderDropDownButton } from '../../../pure/header-drop-down-button'; @@ -55,12 +54,12 @@ export const PageHeaderMenuButton = ({ const pageMeta = useBlockSuitePageMeta(blockSuiteWorkspace).find( meta => meta.id === pageId ); - const currentMode = useAtomValue(currentModeAtom); + const page = useService(Page); + const currentMode = useLiveData(page.mode); const { favorite, toggleFavorite } = useFavorite(pageId); - const { togglePageMode, duplicate } = - useBlockSuiteMetaHelper(blockSuiteWorkspace); + const { duplicate } = useBlockSuiteMetaHelper(blockSuiteWorkspace); const { importFile } = usePageHelper(blockSuiteWorkspace); const { setTrashModal } = useTrashModalHelper(blockSuiteWorkspace); @@ -86,13 +85,13 @@ export const PageHeaderMenuButton = ({ }, [pageId, pageMeta, setTrashModal]); const handleSwitchMode = useCallback(() => { - togglePageMode(pageId); + page.toggleMode(); toast( currentMode === 'page' ? t['com.affine.toastMessage.edgelessMode']() : t['com.affine.toastMessage.pageMode']() ); - }, [currentMode, pageId, t, togglePageMode]); + }, [currentMode, page, t]); const menuItemStyle = { padding: '4px 12px', transition: 'all 0.3s', diff --git a/packages/frontend/core/src/components/blocksuite/block-suite-mode-switch/index.tsx b/packages/frontend/core/src/components/blocksuite/block-suite-mode-switch/index.tsx index dd6f3158f3..0237354561 100644 --- a/packages/frontend/core/src/components/blocksuite/block-suite-mode-switch/index.tsx +++ b/packages/frontend/core/src/components/blocksuite/block-suite-mode-switch/index.tsx @@ -1,13 +1,15 @@ import { Tooltip } from '@affine/component/ui/tooltip'; import { useBlockSuitePageMeta } from '@affine/core/hooks/use-block-suite-page-meta'; import { useAFFiNEI18N } from '@affine/i18n/hooks'; -import { useAtomValue } from 'jotai'; +import { + Page, + type PageMode, + useLiveData, + useService, +} from '@toeverything/infra'; import type { CSSProperties } from 'react'; import { useCallback, useEffect } from 'react'; -import type { PageMode } from '../../../atoms'; -import { currentModeAtom } from '../../../atoms/mode'; -import { useBlockSuiteMetaHelper } from '../../../hooks/affine/use-block-suite-meta-helper'; import type { BlockSuiteWorkspace } from '../../../shared'; import { toast } from '../../../utils'; import { StyledEditorModeSwitch, StyledKeyboardItem } from './style'; @@ -44,10 +46,9 @@ export const EditorModeSwitch = ({ meta => meta.id === pageId ); const trash = pageMeta?.trash ?? false; + const page = useService(Page); - const { togglePageMode, switchToEdgelessMode, switchToPageMode } = - useBlockSuiteMetaHelper(blockSuiteWorkspace); - const currentMode = useAtomValue(currentModeAtom); + const currentMode = useLiveData(page.mode); useEffect(() => { if (trash || isPublic) { @@ -56,7 +57,7 @@ export const EditorModeSwitch = ({ const keydown = (e: KeyboardEvent) => { if (e.code === 'KeyS' && e.altKey) { e.preventDefault(); - togglePageMode(pageId); + page.toggleMode(); toast( currentMode === 'page' ? t['com.affine.toastMessage.edgelessMode']() @@ -67,23 +68,23 @@ export const EditorModeSwitch = ({ document.addEventListener('keydown', keydown, { capture: true }); return () => document.removeEventListener('keydown', keydown, { capture: true }); - }, [currentMode, isPublic, pageId, t, togglePageMode, trash]); + }, [currentMode, isPublic, page, pageId, t, trash]); const onSwitchToPageMode = useCallback(() => { if (currentMode === 'page' || isPublic) { return; } - switchToPageMode(pageId); + page.setMode('page'); toast(t['com.affine.toastMessage.pageMode']()); - }, [currentMode, isPublic, pageId, switchToPageMode, t]); + }, [currentMode, isPublic, page, t]); const onSwitchToEdgelessMode = useCallback(() => { if (currentMode === 'edgeless' || isPublic) { return; } - switchToEdgelessMode(pageId); + page.setMode('edgeless'); toast(t['com.affine.toastMessage.edgelessMode']()); - }, [currentMode, isPublic, pageId, switchToEdgelessMode, t]); + }, [currentMode, isPublic, page, t]); const shouldHide = useCallback( (mode: PageMode) => diff --git a/packages/frontend/core/src/components/blocksuite/block-suite-page-list/utils.tsx b/packages/frontend/core/src/components/blocksuite/block-suite-page-list/utils.tsx index a288ba9a70..5dcf82e1d5 100644 --- a/packages/frontend/core/src/components/blocksuite/block-suite-page-list/utils.tsx +++ b/packages/frontend/core/src/components/blocksuite/block-suite-page-list/utils.tsx @@ -3,36 +3,35 @@ import { useAsyncCallback } from '@affine/core/hooks/affine-async-hooks'; import { usePageMetaHelper } from '@affine/core/hooks/use-block-suite-page-meta'; import { useBlockSuiteWorkspaceHelper } from '@affine/core/hooks/use-block-suite-workspace-helper'; import { WorkspaceSubPath } from '@affine/core/shared'; -import { initEmptyPage } from '@toeverything/infra/blocksuite'; -import { useAtomValue, useSetAtom } from 'jotai'; +import { useService } from '@toeverything/infra'; +import { PageRecordList } from '@toeverything/infra'; +import { initEmptyPage } from '@toeverything/infra'; import { useCallback, useMemo } from 'react'; -import { pageSettingsAtom, setPageModeAtom } from '../../../atoms'; import { useNavigateHelper } from '../../../hooks/use-navigate-helper'; import type { BlockSuiteWorkspace } from '../../../shared'; export const usePageHelper = (blockSuiteWorkspace: BlockSuiteWorkspace) => { const { openPage, jumpToSubPath } = useNavigateHelper(); const { createPage } = useBlockSuiteWorkspaceHelper(blockSuiteWorkspace); - const pageSettings = useAtomValue(pageSettingsAtom); const { setPageMeta } = usePageMetaHelper(blockSuiteWorkspace); + const pageRecordList = useService(PageRecordList); const isPreferredEdgeless = useCallback( - (pageId: string) => pageSettings[pageId]?.mode === 'edgeless', - [pageSettings] + (pageId: string) => + pageRecordList.record(pageId).value?.mode.value === 'edgeless', + [pageRecordList] ); - const setPageMode = useSetAtom(setPageModeAtom); - const createPageAndOpen = useCallback( (mode?: 'page' | 'edgeless') => { const page = createPage(); initEmptyPage(page); - setPageMode(page.id, mode || 'page'); + pageRecordList.record(page.id).value?.setMode(mode || 'page'); openPage(blockSuiteWorkspace.id, page.id); return page; }, - [blockSuiteWorkspace.id, createPage, openPage, setPageMode] + [blockSuiteWorkspace.id, createPage, openPage, pageRecordList] ); const createEdgelessAndOpen = useCallback(() => { @@ -89,17 +88,17 @@ export const usePageHelper = (blockSuiteWorkspace: BlockSuiteWorkspace) => { return useMemo(() => { return { + isPreferredEdgeless, createPage: createPageAndOpen, createEdgeless: createEdgelessAndOpen, importFile: importFileAndOpen, - isPreferredEdgeless: isPreferredEdgeless, createLinkedPage: createLinkedPageAndOpen, }; }, [ + isPreferredEdgeless, createEdgelessAndOpen, createLinkedPageAndOpen, createPageAndOpen, importFileAndOpen, - isPreferredEdgeless, ]); }; diff --git a/packages/frontend/core/src/components/cloud/share-header-right-item/index.tsx b/packages/frontend/core/src/components/cloud/share-header-right-item/index.tsx index 931b6d2da6..2950ad1096 100644 --- a/packages/frontend/core/src/components/cloud/share-header-right-item/index.tsx +++ b/packages/frontend/core/src/components/cloud/share-header-right-item/index.tsx @@ -1,7 +1,7 @@ -import type { PageMode } from '@affine/core/atoms'; -import { useCurrentLoginStatus } from '@affine/core/hooks/affine/use-current-login-status'; +import type { PageMode } from '@toeverything/infra'; import { useState } from 'react'; +import { useCurrentLoginStatus } from '../../../hooks/affine/use-current-login-status'; import { AuthenticatedItem } from './authenticated-item'; import { PresentButton } from './present'; import * as styles from './styles.css'; diff --git a/packages/frontend/core/src/components/page-detail-editor.tsx b/packages/frontend/core/src/components/page-detail-editor.tsx index af9a7ebbaa..51f0328b47 100644 --- a/packages/frontend/core/src/components/page-detail-editor.tsx +++ b/packages/frontend/core/src/components/page-detail-editor.tsx @@ -4,15 +4,20 @@ import { useActiveBlocksuiteEditor } from '@affine/core/hooks/use-block-suite-ed import { useBlockSuiteWorkspacePage } from '@affine/core/hooks/use-block-suite-workspace-page'; import { assertExists, DisposableGroup } from '@blocksuite/global/utils'; import type { AffineEditorContainer } from '@blocksuite/presets'; -import type { Page, Workspace } from '@blocksuite/store'; +import type { Workspace } from '@blocksuite/store'; +import type { Page as BlockSuitePage } from '@blocksuite/store'; +import { + Page, + type PageMode, + useLiveData, + useService, +} from '@toeverything/infra'; import { fontStyleOptions } from '@toeverything/infra/atom'; import clsx from 'clsx'; -import { useAtomValue } from 'jotai'; import type { CSSProperties } from 'react'; import { memo, Suspense, useCallback, useMemo } from 'react'; import { useLocation } from 'react-router-dom'; -import { type PageMode, pageSettingFamily } from '../atoms'; import { useAppSettingHelper } from '../hooks/affine/use-app-setting-helper'; import { BlockSuiteEditor as Editor } from './blocksuite/block-suite-editor'; import * as styles from './page-detail-editor.css'; @@ -23,7 +28,7 @@ declare global { } export type OnLoadEditor = ( - page: Page, + page: BlockSuitePage, editor: AffineEditorContainer ) => () => void; @@ -41,23 +46,19 @@ function useRouterHash() { const PageDetailEditorMain = memo(function PageDetailEditorMain({ page, - pageId, onLoad, isPublic, publishMode, -}: PageDetailEditorProps & { page: Page }) { - const pageSettingAtom = pageSettingFamily(pageId); - const pageSetting = useAtomValue(pageSettingAtom); - +}: PageDetailEditorProps & { page: BlockSuitePage }) { + const currentMode = useLiveData(useService(Page).mode); const mode = useMemo(() => { - const currentMode = pageSetting.mode; const shareMode = publishMode || currentMode; if (isPublic) { return shareMode; } return currentMode; - }, [isPublic, publishMode, pageSetting.mode]); + }, [isPublic, publishMode, currentMode]); const { appSettings } = useAppSettingHelper(); diff --git a/packages/frontend/core/src/components/pure/cmdk/data-hooks.tsx b/packages/frontend/core/src/components/pure/cmdk/data-hooks.tsx index 1049f2002f..88310a907d 100644 --- a/packages/frontend/core/src/components/pure/cmdk/data-hooks.tsx +++ b/packages/frontend/core/src/components/pure/cmdk/data-hooks.tsx @@ -1,4 +1,3 @@ -import { currentPageIdAtom } from '@affine/core/atoms/mode'; import { useCollectionManager } from '@affine/core/components/page-list'; import { useBlockSuitePageMeta, @@ -16,19 +15,24 @@ import { TodayIcon, ViewLayersIcon, } from '@blocksuite/icons'; -import { type PageMeta } from '@blocksuite/store'; -import { useService, Workspace } from '@toeverything/infra'; -import { getCurrentStore } from '@toeverything/infra/atom'; +import type { PageMeta } from '@blocksuite/store'; +import { + Page, + PageRecordList, + useLiveData, + Workspace, +} from '@toeverything/infra'; import { type AffineCommand, AffineCommandRegistry, type CommandCategory, PreconditionStrategy, } from '@toeverything/infra/command'; +import { useService, useServiceOptional } from '@toeverything/infra/di'; import { atom, useAtomValue } from 'jotai'; import { useCallback, useEffect, useMemo, useState } from 'react'; -import { pageSettingsAtom, recentPageIdsBaseAtom } from '../../../atoms'; +import { recentPageIdsBaseAtom } from '../../../atoms'; import { useNavigateHelper } from '../../../hooks/use-navigate-helper'; import { usePageHelper } from '../../blocksuite/block-suite-page-list/utils'; import { filterSortAndGroupCommands } from './filter-commands'; @@ -96,7 +100,6 @@ const useRecentPages = () => { export const pageToCommand = ( category: CommandCategory, page: PageMeta, - store: ReturnType, navigationHelper: ReturnType, getPageTitle: ReturnType, isPageJournal: (pageId: string) => boolean, @@ -105,7 +108,8 @@ export const pageToCommand = ( subTitle?: string, blockId?: string ): CMDKCommand => { - const pageMode = store.get(pageSettingsAtom)?.[page.id]?.mode; + const pageMode = workspace.services.get(PageRecordList).record(page.id).value + ?.mode.value; const title = getPageTitle(page.id) || t['Untitled'](); const commandLabel = { @@ -147,7 +151,6 @@ export const pageToCommand = ( export const usePageCommands = () => { const recentPages = useRecentPages(); const pages = useWorkspacePages(); - const store = getCurrentStore(); const workspace = useService(Workspace); const pageHelper = usePageHelper(workspace.blockSuiteWorkspace); const pageMetaHelper = usePageMetaHelper(workspace.blockSuiteWorkspace); @@ -185,7 +188,6 @@ export const usePageCommands = () => { return pageToCommand( 'affine:recent', page, - store, navigationHelper, getPageTitle, isPageJournal, @@ -207,9 +209,7 @@ export const usePageCommands = () => { }); results = pages.map(page => { - const pageMode = store.get(pageSettingsAtom)?.[page.id]?.mode; - const category = - pageMode === 'edgeless' ? 'affine:edgeless' : 'affine:pages'; + const category = 'affine:pages'; const subTitle = resultValues.find( result => result.space === page.id @@ -220,7 +220,6 @@ export const usePageCommands = () => { const command = pageToCommand( category, page, - store, navigationHelper, getPageTitle, isPageJournal, @@ -288,7 +287,6 @@ export const usePageCommands = () => { searchTime, query, recentPages, - store, navigationHelper, getPageTitle, isPageJournal, @@ -367,11 +365,9 @@ export const useCollectionsCommands = () => { export const useCMDKCommandGroups = () => { const pageCommands = usePageCommands(); const collectionCommands = useCollectionsCommands(); - const currentPageId = useAtomValue(currentPageIdAtom); - const pageSettings = useAtomValue(pageSettingsAtom); - const currentPageMode = currentPageId - ? pageSettings[currentPageId]?.mode - : undefined; + + const currentPage = useServiceOptional(Page); + const currentPageMode = useLiveData(currentPage?.mode); const affineCommands = useMemo(() => { return getAllCommand({ pageMode: currentPageMode, diff --git a/packages/frontend/core/src/components/pure/help-island/index.tsx b/packages/frontend/core/src/components/pure/help-island/index.tsx index 24304504ef..325f927c05 100644 --- a/packages/frontend/core/src/components/pure/help-island/index.tsx +++ b/packages/frontend/core/src/components/pure/help-island/index.tsx @@ -1,13 +1,12 @@ import { Tooltip } from '@affine/component/ui/tooltip'; import { useAFFiNEI18N } from '@affine/i18n/hooks'; import { CloseIcon, NewIcon } from '@blocksuite/icons'; +import { useLiveData, useServiceOptional } from '@toeverything/infra'; +import { Page } from '@toeverything/infra'; import { useSetAtom } from 'jotai/react'; -import { useAtomValue } from 'jotai/react'; import { useCallback, useState } from 'react'; -import { useParams } from 'react-router-dom'; import { openSettingModalAtom } from '../../../atoms'; -import { currentModeAtom } from '../../../atoms/mode'; import type { SettingProps } from '../../affine/setting-modal'; import { ContactIcon, HelpIcon, KeyboardIcon } from './icons'; import { @@ -29,7 +28,9 @@ type IslandItemNames = 'whatNew' | 'contact' | 'shortcuts'; const showList = environment.isDesktop ? DESKTOP_SHOW_LIST : DEFAULT_SHOW_LIST; export const HelpIsland = () => { - const mode = useAtomValue(currentModeAtom); + const page = useServiceOptional(Page); + const pageId = page?.id; + const mode = useLiveData(page?.mode); const setOpenSettingModalAtom = useSetAtom(openSettingModalAtom); const [spread, setShowSpread] = useState(false); const t = useAFFiNEI18N(); @@ -53,8 +54,6 @@ export const HelpIsland = () => { [openSettingModal] ); - const { pageId } = useParams(); - return ( { - return setting?.mode === 'edgeless' ? : ; - }, [setting?.mode]); + return pageMode === 'edgeless' ? : ; + }, [pageMode]); const { jumpToPage } = useNavigateHelper(); const clickPage = useCallback(() => { diff --git a/packages/frontend/core/src/components/pure/workspace-slider-bar/components/reference-page.tsx b/packages/frontend/core/src/components/pure/workspace-slider-bar/components/reference-page.tsx index 9d7e13756e..5a0737b83e 100644 --- a/packages/frontend/core/src/components/pure/workspace-slider-bar/components/reference-page.tsx +++ b/packages/frontend/core/src/components/pure/workspace-slider-bar/components/reference-page.tsx @@ -4,11 +4,10 @@ import { useAFFiNEI18N } from '@affine/i18n/hooks'; import { EdgelessIcon, PageIcon } from '@blocksuite/icons'; import { type PageMeta, type Workspace } from '@blocksuite/store'; import * as Collapsible from '@radix-ui/react-collapsible'; -import { useAtomValue } from 'jotai/react'; +import { PageRecordList, useLiveData, useService } from '@toeverything/infra'; import { useMemo, useState } from 'react'; import { useParams } from 'react-router-dom'; -import { pageSettingFamily } from '../../../../atoms'; import * as styles from '../favorite/styles.css'; import { PostfixItem } from './postfix-item'; export interface ReferencePageProps { @@ -28,10 +27,11 @@ export const ReferencePage = ({ const params = useParams(); const active = params.pageId === pageId; - const setting = useAtomValue(pageSettingFamily(pageId)); + const pageRecord = useLiveData(useService(PageRecordList).record(pageId)); + const pageMode = useLiveData(pageRecord?.mode); const icon = useMemo(() => { - return setting?.mode === 'edgeless' ? : ; - }, [setting?.mode]); + return pageMode === 'edgeless' ? : ; + }, [pageMode]); const references = useBlockSuitePageReferences(workspace, pageId); const referencesToShow = useMemo(() => { diff --git a/packages/frontend/core/src/components/pure/workspace-slider-bar/favorite/favourite-page.tsx b/packages/frontend/core/src/components/pure/workspace-slider-bar/favorite/favourite-page.tsx index f6b4d6d2a3..a654d4e0d3 100644 --- a/packages/frontend/core/src/components/pure/workspace-slider-bar/favorite/favourite-page.tsx +++ b/packages/frontend/core/src/components/pure/workspace-slider-bar/favorite/favourite-page.tsx @@ -4,11 +4,12 @@ import { useAFFiNEI18N } from '@affine/i18n/hooks'; import { EdgelessIcon, PageIcon } from '@blocksuite/icons'; import { useDraggable } from '@dnd-kit/core'; import * as Collapsible from '@radix-ui/react-collapsible'; -import { useAtomValue } from 'jotai/index'; +import { useService } from '@toeverything/infra'; +import { useLiveData } from '@toeverything/infra'; +import { PageRecordList } from '@toeverything/infra'; import { useMemo, useState } from 'react'; import { useParams } from 'react-router-dom'; -import { pageSettingFamily } from '../../../../atoms'; import { getDragItemId } from '../../../../hooks/affine/use-sidebar-drag'; import { DragMenuItemOverlay } from '../components/drag-menu-item-overlay'; import { PostfixItem } from '../components/postfix-item'; @@ -28,11 +29,12 @@ export const FavouritePage = ({ const params = useParams(); const active = params.pageId === pageId; const dragItemId = getDragItemId('favouritePage', pageId); + const pageRecord = useLiveData(useService(PageRecordList).record(pageId)); + const pageMode = useLiveData(pageRecord?.mode); - const setting = useAtomValue(pageSettingFamily(pageId)); const icon = useMemo(() => { - return setting?.mode === 'edgeless' ? : ; - }, [setting?.mode]); + return pageMode === 'edgeless' ? : ; + }, [pageMode]); const references = useBlockSuitePageReferences(workspace, pageId); const referencesToShow = useMemo(() => { diff --git a/packages/frontend/core/src/components/root-app-sidebar/journal-button.tsx b/packages/frontend/core/src/components/root-app-sidebar/journal-button.tsx index cd0d1afb18..09a02aa309 100644 --- a/packages/frontend/core/src/components/root-app-sidebar/journal-button.tsx +++ b/packages/frontend/core/src/components/root-app-sidebar/journal-button.tsx @@ -1,5 +1,4 @@ import { MenuItem } from '@affine/component/app-sidebar'; -import { currentPageIdAtom } from '@affine/core/atoms/mode'; import { useJournalInfoHelper, useJournalRouteHelper, @@ -7,7 +6,7 @@ import { import type { BlockSuiteWorkspace } from '@affine/core/shared'; import { useAFFiNEI18N } from '@affine/i18n/hooks'; import { TodayIcon, TomorrowIcon, YesterdayIcon } from '@blocksuite/icons'; -import { useAtomValue } from 'jotai'; +import { Page, useServiceOptional } from '@toeverything/infra'; import { useParams } from 'react-router-dom'; interface AppSidebarJournalButtonProps { @@ -18,11 +17,11 @@ export const AppSidebarJournalButton = ({ workspace, }: AppSidebarJournalButtonProps) => { const t = useAFFiNEI18N(); - const currentPageId = useAtomValue(currentPageIdAtom); + const currentPage = useServiceOptional(Page); const { openToday } = useJournalRouteHelper(workspace); const { journalDate, isJournal } = useJournalInfoHelper( workspace, - currentPageId + currentPage?.id ); const params = useParams(); const isJournalActive = isJournal && !!params.pageId; diff --git a/packages/frontend/core/src/hooks/affine/use-block-suite-meta-helper.ts b/packages/frontend/core/src/hooks/affine/use-block-suite-meta-helper.ts index aa6ac4b3cc..7ba11a0aca 100644 --- a/packages/frontend/core/src/hooks/affine/use-block-suite-meta-helper.ts +++ b/packages/frontend/core/src/hooks/affine/use-block-suite-meta-helper.ts @@ -1,17 +1,11 @@ import { useAsyncCallback } from '@affine/core/hooks/affine-async-hooks'; -import { - useBlockSuitePageMeta, - usePageMetaHelper, -} from '@affine/core/hooks/use-block-suite-page-meta'; +import { usePageMetaHelper } from '@affine/core/hooks/use-block-suite-page-meta'; import { useBlockSuiteWorkspaceHelper } from '@affine/core/hooks/use-block-suite-workspace-helper'; import { CollectionService } from '@affine/core/modules/collection'; -import { useService } from '@toeverything/infra'; -import { useAtomValue, useSetAtom } from 'jotai'; +import { PageRecordList, useService } from '@toeverything/infra'; import { useCallback } from 'react'; import { applyUpdate, encodeStateAsUpdate } from 'yjs'; -import { setPageModeAtom } from '../../atoms'; -import { currentModeAtom } from '../../atoms/mode'; import type { BlockSuiteWorkspace } from '../../shared'; import { useNavigateHelper } from '../use-navigate-helper'; import { useReferenceLinkHelper } from './use-reference-link-helper'; @@ -22,31 +16,10 @@ export function useBlockSuiteMetaHelper( const { setPageMeta, getPageMeta, setPageReadonly, setPageTitle } = usePageMetaHelper(blockSuiteWorkspace); const { addReferenceLink } = useReferenceLinkHelper(blockSuiteWorkspace); - const metas = useBlockSuitePageMeta(blockSuiteWorkspace); - const setPageMode = useSetAtom(setPageModeAtom); - const currentMode = useAtomValue(currentModeAtom); const { createPage } = useBlockSuiteWorkspaceHelper(blockSuiteWorkspace); const { openPage } = useNavigateHelper(); const collectionService = useService(CollectionService); - - const switchToPageMode = useCallback( - (pageId: string) => { - setPageMode(pageId, 'page'); - }, - [setPageMode] - ); - const switchToEdgelessMode = useCallback( - (pageId: string) => { - setPageMode(pageId, 'edgeless'); - }, - [setPageMode] - ); - const togglePageMode = useCallback( - (pageId: string) => { - setPageMode(pageId, currentMode === 'edgeless' ? 'page' : 'edgeless'); - }, - [currentMode, setPageMode] - ); + const pageRecordList = useService(PageRecordList); const addToFavorite = useCallback( (pageId: string) => { @@ -77,28 +50,21 @@ export function useBlockSuiteMetaHelper( // TODO-Doma // "Remove" may cause ambiguity here. Consider renaming as "moveToTrash". const removeToTrash = useCallback( - (pageId: string, isRoot = true) => { - const parentMeta = metas.find(m => m.subpageIds?.includes(pageId)); - const { subpageIds = [] } = getPageMeta(pageId) ?? {}; - - subpageIds.forEach(id => { - removeToTrash(id, false); - }); - + (pageId: string) => { setPageMeta(pageId, { trash: true, trashDate: Date.now(), - trashRelate: isRoot ? parentMeta?.id : undefined, + trashRelate: undefined, }); setPageReadonly(pageId, true); collectionService.deletePagesFromCollections([pageId]); }, - [collectionService, getPageMeta, metas, setPageMeta, setPageReadonly] + [collectionService, setPageMeta, setPageReadonly] ); const restoreFromTrash = useCallback( (pageId: string) => { - const { subpageIds = [], trashRelate } = getPageMeta(pageId) ?? {}; + const { trashRelate } = getPageMeta(pageId) ?? {}; if (trashRelate) { addReferenceLink(trashRelate, pageId); @@ -110,9 +76,6 @@ export function useBlockSuiteMetaHelper( trashRelate: undefined, }); setPageReadonly(pageId, false); - subpageIds.forEach(id => { - restoreFromTrash(id); - }); }, [addReferenceLink, getPageMeta, setPageMeta, setPageReadonly] ); @@ -150,6 +113,7 @@ export function useBlockSuiteMetaHelper( const duplicate = useAsyncCallback( async (pageId: string, openPageAfterDuplication: boolean = true) => { + const currentPageMode = pageRecordList.record(pageId).value?.mode.value; const currentPageMeta = getPageMeta(pageId); const newPage = createPage(); const currentPage = blockSuiteWorkspace.getPage(pageId); @@ -174,28 +138,24 @@ export function useBlockSuiteMetaHelper( const newPageTitle = currentPageMeta.title.replace(lastDigitRegex, '') + `(${newNumber})`; - setPageMode(newPage.id, currentMode); + pageRecordList + .record(newPage.id) + .value?.setMode(currentPageMode || 'page'); setPageTitle(newPage.id, newPageTitle); - openPageAfterDuplication && openPage(blockSuiteWorkspace.id, newPage.id); }, [ blockSuiteWorkspace, createPage, - currentMode, getPageMeta, openPage, + pageRecordList, setPageMeta, - setPageMode, setPageTitle, ] ); return { - switchToPageMode, - switchToEdgelessMode, - togglePageMode, - publicPage, cancelPublicPage, diff --git a/packages/frontend/core/src/hooks/affine/use-is-shared-page.ts b/packages/frontend/core/src/hooks/affine/use-is-shared-page.ts index 08316a1702..61e891f29a 100644 --- a/packages/frontend/core/src/hooks/affine/use-is-shared-page.ts +++ b/packages/frontend/core/src/hooks/affine/use-is-shared-page.ts @@ -7,11 +7,10 @@ import { revokePublicPageMutation, } from '@affine/graphql'; import { useAFFiNEI18N } from '@affine/i18n/hooks'; -import type { Workspace } from '@toeverything/infra/workspace'; +import type { PageMode, Workspace } from '@toeverything/infra'; import { useSetAtom } from 'jotai'; import { useCallback, useMemo } from 'react'; -import type { PageMode } from '../../atoms'; import { useMutation } from '../use-mutation'; import { useQuery } from '../use-query'; diff --git a/packages/frontend/core/src/hooks/affine/use-register-blocksuite-editor-commands.tsx b/packages/frontend/core/src/hooks/affine/use-register-blocksuite-editor-commands.tsx index bc23896854..2f71774444 100644 --- a/packages/frontend/core/src/hooks/affine/use-register-blocksuite-editor-commands.tsx +++ b/packages/frontend/core/src/hooks/affine/use-register-blocksuite-editor-commands.tsx @@ -4,7 +4,7 @@ import { WorkspaceFlavour } from '@affine/env/workspace'; import { useAFFiNEI18N } from '@affine/i18n/hooks'; import { assertExists } from '@blocksuite/global/utils'; import { EdgelessIcon, HistoryIcon, PageIcon } from '@blocksuite/icons'; -import { Workspace } from '@toeverything/infra'; +import { Page, useLiveData, Workspace } from '@toeverything/infra'; import { PreconditionStrategy, registerAffineCommand, @@ -18,10 +18,10 @@ import { useBlockSuiteMetaHelper } from './use-block-suite-meta-helper'; import { useExportPage } from './use-export-page'; import { useTrashModalHelper } from './use-trash-modal-helper'; -export function useRegisterBlocksuiteEditorCommands( - pageId: string, - mode: 'page' | 'edgeless' -) { +export function useRegisterBlocksuiteEditorCommands() { + const page = useService(Page); + const pageId = page.id; + const mode = useLiveData(page.mode); const t = useAFFiNEI18N(); const workspace = useService(Workspace); const blockSuiteWorkspace = workspace.blockSuiteWorkspace; @@ -42,7 +42,7 @@ export function useRegisterBlocksuiteEditorCommands( })); }, [pageId, setPageHistoryModalState]); - const { togglePageMode, toggleFavorite, restoreFromTrash, duplicate } = + const { toggleFavorite, restoreFromTrash, duplicate } = useBlockSuiteMetaHelper(blockSuiteWorkspace); const exportHandler = useExportPage(currentPage); const { setTrashModal } = useTrashModalHelper(blockSuiteWorkspace); @@ -116,7 +116,7 @@ export function useRegisterBlocksuiteEditorCommands( : t['com.affine.pageMode.page']() }`, run() { - togglePageMode(pageId); + page.toggleMode(); toast( mode === 'page' ? t['com.affine.toastMessage.edgelessMode']() @@ -245,10 +245,10 @@ export function useRegisterBlocksuiteEditorCommands( restoreFromTrash, t, toggleFavorite, - togglePageMode, trash, isCloudWorkspace, openHistoryModal, duplicate, + page, ]); } diff --git a/packages/frontend/core/src/hooks/current/use-current-page.ts b/packages/frontend/core/src/hooks/current/use-current-page.ts deleted file mode 100644 index 47f73cc340..0000000000 --- a/packages/frontend/core/src/hooks/current/use-current-page.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { useBlockSuiteWorkspacePage } from '@affine/core/hooks/use-block-suite-workspace-page'; -import { Workspace } from '@toeverything/infra'; -import { useService } from '@toeverything/infra/di'; -import { useAtomValue } from 'jotai'; - -import { currentPageIdAtom } from '../../atoms/mode'; - -export const useCurrentPage = () => { - const currentPageId = useAtomValue(currentPageIdAtom); - const currentWorkspace = useService(Workspace); - return useBlockSuiteWorkspacePage( - currentWorkspace.blockSuiteWorkspace, - currentPageId - ); -}; diff --git a/packages/frontend/core/src/hooks/use-global-state.ts b/packages/frontend/core/src/hooks/use-global-state.ts index dface9a866..16bff87581 100644 --- a/packages/frontend/core/src/hooks/use-global-state.ts +++ b/packages/frontend/core/src/hooks/use-global-state.ts @@ -1,4 +1,3 @@ -import type { PageMeta } from '@blocksuite/store'; import { noop } from 'lodash-es'; import { useEffect } from 'react'; @@ -16,6 +15,6 @@ export function useDocumentTitle(newTitle?: string | null) { }, [newTitle]); } -export function usePageDocumentTitle(pageMeta?: PageMeta) { - useDocumentTitle(pageMeta?.title ? `${pageMeta.title} · AFFiNE` : null); +export function usePageDocumentTitle(pageTitle?: string) { + useDocumentTitle(pageTitle ? `${pageTitle} · AFFiNE` : null); } diff --git a/packages/frontend/core/src/hooks/use-journal.ts b/packages/frontend/core/src/hooks/use-journal.ts index e10af088be..b8dd998b29 100644 --- a/packages/frontend/core/src/hooks/use-journal.ts +++ b/packages/frontend/core/src/hooks/use-journal.ts @@ -1,4 +1,4 @@ -import { initEmptyPage } from '@toeverything/infra/blocksuite'; +import { initEmptyPage } from '@toeverything/infra'; import dayjs from 'dayjs'; import { useCallback, useMemo } from 'react'; diff --git a/packages/frontend/core/src/modules/infra-web/storage/index.ts b/packages/frontend/core/src/modules/infra-web/storage/index.ts index bff8cc5e18..650dff141a 100644 --- a/packages/frontend/core/src/modules/infra-web/storage/index.ts +++ b/packages/frontend/core/src/modules/infra-web/storage/index.ts @@ -1,8 +1,8 @@ -import type { GlobalCache } from '@toeverything/infra'; +import type { GlobalCache, GlobalState, Memento } from '@toeverything/infra'; import { Observable } from 'rxjs'; -export class LocalStorageGlobalCache implements GlobalCache { - prefix = 'cache:'; +export class LocalStorageMemento implements Memento { + constructor(private readonly prefix: string) {} get(key: string): T | null { const json = localStorage.getItem(this.prefix + key); @@ -30,3 +30,21 @@ export class LocalStorageGlobalCache implements GlobalCache { channel.close(); } } + +export class LocalStorageGlobalCache + extends LocalStorageMemento + implements GlobalCache +{ + constructor() { + super('global-cache:'); + } +} + +export class LocalStorageGlobalState + extends LocalStorageMemento + implements GlobalState +{ + constructor() { + super('global-state:'); + } +} diff --git a/packages/frontend/core/src/modules/services.ts b/packages/frontend/core/src/modules/services.ts index 43a38777c5..c0c1e8550e 100644 --- a/packages/frontend/core/src/modules/services.ts +++ b/packages/frontend/core/src/modules/services.ts @@ -1,12 +1,16 @@ import { GlobalCache, + GlobalState, type ServiceCollection, Workspace, WorkspaceScope, } from '@toeverything/infra'; import { CollectionService } from './collection'; -import { LocalStorageGlobalCache } from './infra-web/storage'; +import { + LocalStorageGlobalCache, + LocalStorageGlobalState, +} from './infra-web/storage'; import { CurrentPageService } from './page'; import { CurrentWorkspaceService, @@ -25,5 +29,7 @@ export function configureBusinessServices(services: ServiceCollection) { } export function configureWebInfraServices(services: ServiceCollection) { - services.addImpl(GlobalCache, LocalStorageGlobalCache); + services + .addImpl(GlobalCache, LocalStorageGlobalCache) + .addImpl(GlobalState, LocalStorageGlobalState); } diff --git a/packages/frontend/core/src/pages/share/share-detail-page.tsx b/packages/frontend/core/src/pages/share/share-detail-page.tsx index 10df1b186d..ce398b3a40 100644 --- a/packages/frontend/core/src/pages/share/share-detail-page.tsx +++ b/packages/frontend/core/src/pages/share/share-detail-page.tsx @@ -16,8 +16,10 @@ import { LocalSyncStorage, Page, PageManager, + type PageMode, ReadonlyMappingSyncStorage, RemoteBlobStorage, + useLiveData, useService, useServiceOptional, WorkspaceIdContext, @@ -34,7 +36,6 @@ import { useRouteError, } from 'react-router-dom'; -import type { PageMode } from '../../atoms'; import { AppContainer } from '../../components/affine/app-container'; import { PageDetailEditor } from '../../components/page-detail-editor'; import { SharePageNotFoundError } from '../../components/share-page-not-found-error'; @@ -159,9 +160,7 @@ export const Component = () => { workspace.engine.sync .waitForSynced() .then(() => { - const { page } = workspace.services - .get(PageManager) - .openByPageId(pageId); + const { page } = workspace.services.get(PageManager).open(pageId); workspace.blockSuiteWorkspace.awarenessStore.setReadonly( page.blockSuitePage, @@ -186,9 +185,11 @@ export const Component = () => { ]); const page = useServiceOptional(Page); + const pageTitle = useLiveData(page?.title); - usePageDocumentTitle(page?.meta); + usePageDocumentTitle(pageTitle); const loginStatus = useCurrentLoginStatus(); + if (!page) { return; } diff --git a/packages/frontend/core/src/pages/share/share-header.tsx b/packages/frontend/core/src/pages/share/share-header.tsx index e697686244..3d25cd052e 100644 --- a/packages/frontend/core/src/pages/share/share-header.tsx +++ b/packages/frontend/core/src/pages/share/share-header.tsx @@ -1,8 +1,8 @@ import { EditorModeSwitch } from '@affine/core/components/blocksuite/block-suite-mode-switch'; import ShareHeaderRightItem from '@affine/core/components/cloud/share-header-right-item'; import type { Workspace as BlockSuiteWorkspace } from '@blocksuite/store'; +import type { PageMode } from '@toeverything/infra'; -import type { PageMode } from '../../atoms'; import { BlocksuiteHeaderTitle } from '../../components/blocksuite/block-suite-header/title/index'; import * as styles from './share-header.css'; diff --git a/packages/frontend/core/src/pages/workspace/detail-page/detail-page.tsx b/packages/frontend/core/src/pages/workspace/detail-page/detail-page.tsx index ee4308f5c9..b352744e19 100644 --- a/packages/frontend/core/src/pages/workspace/detail-page/detail-page.tsx +++ b/packages/frontend/core/src/pages/workspace/detail-page/detail-page.tsx @@ -17,14 +17,14 @@ import type { Page as BlockSuitePage } from '@blocksuite/store'; import { globalBlockSuiteSchema, Page, - PageListService, PageManager, + PageRecordList, useLiveData, useServiceOptional, } from '@toeverything/infra'; import { appSettingAtom, Workspace } from '@toeverything/infra'; import { useService } from '@toeverything/infra'; -import { useAtom, useAtomValue, useSetAtom, useStore } from 'jotai'; +import { useAtom, useAtomValue, useSetAtom } from 'jotai'; import { memo, type ReactElement, @@ -36,8 +36,7 @@ import { import { useParams } from 'react-router-dom'; import type { Map as YMap } from 'yjs'; -import { pageSettingFamily, setPageModeAtom } from '../../../atoms'; -import { currentModeAtom, currentPageIdAtom } from '../../../atoms/mode'; +import { recentPageIdsBaseAtom } from '../../../atoms'; import { AffineErrorBoundary } from '../../../components/affine/affine-error-boundary'; import { HubIsland } from '../../../components/affine/hub-island'; import { GlobalPageHistoryModal } from '../../../components/affine/page-history-modal'; @@ -116,6 +115,7 @@ const DetailPageLayout = ({ const DetailPageImpl = memo(function DetailPageImpl() { const page = useService(Page); + const pageRecordList = useService(PageRecordList); const currentPageId = page.id; const { openPage, jumpToSubPath } = useNavigateHelper(); const currentWorkspace = useService(Workspace); @@ -129,17 +129,16 @@ const DetailPageImpl = memo(function DetailPageImpl() { const collectionService = useService(CollectionService); const { setTemporaryFilter } = useCollectionManager(collectionService); - const mode = useAtomValue(currentModeAtom); - const setPageMode = useSetAtom(setPageModeAtom); - useRegisterBlocksuiteEditorCommands(currentPageId, mode); - usePageDocumentTitle(pageMeta); - const rootStore = useStore(); + const mode = useLiveData(page.mode); + useRegisterBlocksuiteEditorCommands(); + const title = useLiveData(page.title); + usePageDocumentTitle(title); const onLoad = useCallback( - (page: BlockSuitePage, editor: AffineEditorContainer) => { + (bsPage: BlockSuitePage, editor: AffineEditorContainer) => { try { // todo(joooye34): improve the following migration code - const surfaceBlock = page.getBlockByFlavour('affine:surface')[0]; + const surfaceBlock = bsPage.getBlockByFlavour('affine:surface')[0]; // hotfix for old page if ( surfaceBlock && @@ -152,7 +151,7 @@ const DetailPageImpl = memo(function DetailPageImpl() { { 'affine:surface': 3, }, - page.spaceDoc + bsPage.spaceDoc ); } } catch {} @@ -177,17 +176,17 @@ const DetailPageImpl = memo(function DetailPageImpl() { 'affine:page' ) as PageService; pageService.getPageMode = (pageId: string) => - rootStore.get(pageSettingFamily(pageId)).mode; + pageRecordList.record(pageId).value?.mode.value ?? 'page'; pageService.getPageUpdatedAt = (pageId: string) => { - const linkedPage = page.workspace.getPage(pageId); + const linkedPage = pageRecordList.record(pageId).value; if (!linkedPage) return new Date(); - const updatedDate = linkedPage.meta.updatedDate; - const createDate = linkedPage.meta.createDate; + const updatedDate = linkedPage.meta.value.updatedDate; + const createDate = linkedPage.meta.value.createDate; return updatedDate ? new Date(updatedDate) : new Date(createDate); }; - setPageMode(currentPageId, mode); + page.setMode(mode); // fixme: it seems pageLinkClicked is not triggered sometimes? const dispose = editor.slots.pageLinkClicked.on(({ pageId }) => { return openPage(blockSuiteWorkspace.id, pageId); @@ -202,15 +201,14 @@ const DetailPageImpl = memo(function DetailPageImpl() { }; }, [ - blockSuiteWorkspace.id, - currentPageId, - currentWorkspace.id, - jumpToSubPath, + page, mode, + pageRecordList, openPage, - setPageMode, + blockSuiteWorkspace.id, + jumpToSubPath, + currentWorkspace.id, setTemporaryFilter, - rootStore, ] ); @@ -269,31 +267,31 @@ const DetailPageImpl = memo(function DetailPageImpl() { }); export const DetailPage = ({ pageId }: { pageId: string }): ReactElement => { - const pageListService = useService(PageListService); + const pageRecordList = useService(PageRecordList); - const pageListReady = useLiveData(pageListService.isReady); + const pageListReady = useLiveData(pageRecordList.isReady); - const pageMetas = useLiveData(pageListService.pages); + const pageRecords = useLiveData(pageRecordList.records); - const pageMeta = useMemo( - () => pageMetas.find(page => page.id === pageId), - [pageMetas, pageId] + const pageRecord = useMemo( + () => pageRecords.find(page => page.id === pageId), + [pageRecords, pageId] ); const pageManager = useService(PageManager); const currentPageService = useService(CurrentPageService); useEffect(() => { - if (!pageMeta) { + if (!pageRecord) { return; } - const { page, release } = pageManager.open(pageMeta); + const { page, release } = pageManager.open(pageRecord.id); currentPageService.openPage(page); return () => { currentPageService.closePage(); release(); }; - }, [currentPageService, pageManager, pageMeta]); + }, [currentPageService, pageManager, pageRecord]); const page = useServiceOptional(Page); @@ -304,6 +302,14 @@ export const DetailPage = ({ pageId }: { pageId: string }): ReactElement => { currentWorkspace.setPriorityRule(id => id.endsWith(pageId)); }, [pageId, currentWorkspace]); + const jumpOnce = useLiveData(pageRecord?.meta.map(meta => meta.jumpOnce)); + + useEffect(() => { + if (jumpOnce) { + pageRecord?.setMeta({ jumpOnce: false }); + } + }, [jumpOnce, pageRecord]); + // if sync engine has been synced and the page is null, show 404 page. if (pageListReady && !page) { return ; @@ -313,27 +319,26 @@ export const DetailPage = ({ pageId }: { pageId: string }): ReactElement => { return ; } - if (page.meta.jumpOnce) { - currentWorkspace.blockSuiteWorkspace.setPageMeta(page.id, { - jumpOnce: false, - }); - } - return ; }; export const Component = () => { performanceRenderLogger.info('DetailPage'); - const setCurrentPageId = useSetAtom(currentPageIdAtom); const params = useParams(); + const setRecentPageIds = useSetAtom(recentPageIdsBaseAtom); useEffect(() => { if (params.pageId) { - localStorage.setItem('last_page_id', params.pageId); - setCurrentPageId(params.pageId); + const pageId = params.pageId; + localStorage.setItem('last_page_id', pageId); + + setRecentPageIds(ids => { + // pick 3 recent page ids + return [...new Set([pageId, ...ids]).values()].slice(0, 3); + }); } - }, [params, setCurrentPageId]); + }, [params, setRecentPageIds]); const pageId = params.pageId; diff --git a/packages/frontend/core/src/testing.ts b/packages/frontend/core/src/testing.ts index fd963cc908..8f466dfcf7 100644 --- a/packages/frontend/core/src/testing.ts +++ b/packages/frontend/core/src/testing.ts @@ -37,7 +37,7 @@ export async function configureTestingEnvironment() { await workspace.engine.sync.waitForSynced(); - const { page } = workspace.services.get(PageManager).openByPageId('page0'); + const { page } = workspace.services.get(PageManager).open('page0'); rootServices.get(CurrentWorkspaceService).openWorkspace(workspace); workspace.services.get(CurrentPageService).openPage(page); diff --git a/packages/frontend/workspace-impl/src/index.ts b/packages/frontend/workspace-impl/src/index.ts index a517d5871e..16d21dd09d 100644 --- a/packages/frontend/workspace-impl/src/index.ts +++ b/packages/frontend/workspace-impl/src/index.ts @@ -1,5 +1,12 @@ -import { WorkspaceFactory, WorkspaceListProvider } from '@toeverything/infra'; -import type { ServiceCollection } from '@toeverything/infra/di'; +import type { ServiceCollection } from '@toeverything/infra'; +import { + GlobalState, + Workspace, + WorkspaceFactory, + WorkspaceListProvider, + WorkspaceLocalState, + WorkspaceScope, +} from '@toeverything/infra'; import { CloudWorkspaceFactory, CloudWorkspaceListProvider } from './cloud'; import { @@ -7,6 +14,7 @@ import { LocalWorkspaceFactory, LocalWorkspaceListProvider, } from './local'; +import { WorkspaceLocalStateImpl } from './local-state'; export * from './cloud'; export * from './local'; @@ -16,7 +24,12 @@ export function configureWorkspaceImplServices(services: ServiceCollection) { .addImpl(WorkspaceListProvider('affine-cloud'), CloudWorkspaceListProvider) .addImpl(WorkspaceFactory('affine-cloud'), CloudWorkspaceFactory) .addImpl(WorkspaceListProvider('local'), LocalWorkspaceListProvider) - .addImpl(WorkspaceFactory('local'), LocalWorkspaceFactory); + .addImpl(WorkspaceFactory('local'), LocalWorkspaceFactory) + .scope(WorkspaceScope) + .addImpl(WorkspaceLocalState, WorkspaceLocalStateImpl, [ + Workspace, + GlobalState, + ]); } /** diff --git a/packages/frontend/workspace-impl/src/local-state.ts b/packages/frontend/workspace-impl/src/local-state.ts new file mode 100644 index 0000000000..764f3c181c --- /dev/null +++ b/packages/frontend/workspace-impl/src/local-state.ts @@ -0,0 +1,31 @@ +import type { + GlobalState, + Workspace, + WorkspaceLocalState, +} from '@toeverything/infra'; + +export class WorkspaceLocalStateImpl implements WorkspaceLocalState { + constructor( + private readonly workspace: Workspace, + private readonly globalState: GlobalState + ) {} + + get(key: string): T | null { + return this.globalState.get( + `workspace-state:${this.workspace.id}:${key}` + ); + } + + watch(key: string) { + return this.globalState.watch( + `workspace-state:${this.workspace.id}:${key}` + ); + } + + set(key: string, value: T | null): void { + return this.globalState.set( + `workspace-state:${this.workspace.id}:${key}`, + value + ); + } +} diff --git a/tests/affine-local/e2e/quick-search.spec.ts b/tests/affine-local/e2e/quick-search.spec.ts index ffa711d9e4..8874e00aa2 100644 --- a/tests/affine-local/e2e/quick-search.spec.ts +++ b/tests/affine-local/e2e/quick-search.spec.ts @@ -264,6 +264,7 @@ test('assert the recent browse pages are on the recent list', async ({ { const title = getBlockSuiteEditorTitle(page); await title.click(); + await page.waitForTimeout(200); await title.pressSequentially('sgtokidoki', { delay: 100 }); await expect(title).toHaveText('sgtokidoki'); } @@ -276,6 +277,7 @@ test('assert the recent browse pages are on the recent list', async ({ { const title = getBlockSuiteEditorTitle(page); await title.click(); + await page.waitForTimeout(200); await title.pressSequentially('theliquidhorse', { delay: 100 }); await expect(title).toHaveText('theliquidhorse'); } @@ -288,6 +290,7 @@ test('assert the recent browse pages are on the recent list', async ({ { const title = getBlockSuiteEditorTitle(page); await title.click(); + await page.waitForTimeout(200); await title.pressSequentially('battlekot', { delay: 100 }); await expect(title).toHaveText('battlekot'); } diff --git a/tests/storybook/src/stories/blocksuite-editor.stories.tsx b/tests/storybook/src/stories/blocksuite-editor.stories.tsx index 1b67384a18..c63a14f841 100644 --- a/tests/storybook/src/stories/blocksuite-editor.stories.tsx +++ b/tests/storybook/src/stories/blocksuite-editor.stories.tsx @@ -3,7 +3,7 @@ import { __unstableSchemas, AffineSchemas } from '@blocksuite/blocks/models'; import { Workspace } from '@blocksuite/store'; import { Schema } from '@blocksuite/store'; import type { StoryFn } from '@storybook/react'; -import { initEmptyPage } from '@toeverything/infra/blocksuite'; +import { initEmptyPage } from '@toeverything/infra'; const schema = new Schema(); schema.register(AffineSchemas).register(__unstableSchemas); diff --git a/tests/storybook/src/stories/image-preview-modal.stories.tsx b/tests/storybook/src/stories/image-preview-modal.stories.tsx index 307c1329f1..691d98e8ea 100644 --- a/tests/storybook/src/stories/image-preview-modal.stories.tsx +++ b/tests/storybook/src/stories/image-preview-modal.stories.tsx @@ -4,7 +4,7 @@ import { CurrentPageService } from '@affine/core/modules/page'; import type { Page } from '@blocksuite/store'; import type { Meta } from '@storybook/react'; import { PageManager, useService, Workspace } from '@toeverything/infra'; -import { initEmptyPage } from '@toeverything/infra/blocksuite'; +import { initEmptyPage } from '@toeverything/infra'; import { useEffect, useState } from 'react'; import { createPortal } from 'react-dom'; @@ -24,7 +24,7 @@ export const Default = () => { const bsPage = workspace.blockSuiteWorkspace.createPage('page0'); initEmptyPage(bsPage); - const { page, release } = pageManager.open(bsPage.meta); + const { page, release } = pageManager.open(bsPage.meta.id); currentPageService.openPage(page); fetch(new URL('@affine-test/fixtures/large-image.png', import.meta.url)) diff --git a/tests/storybook/src/stories/page-info-properties.stories.tsx b/tests/storybook/src/stories/page-info-properties.stories.tsx index 80da6e4476..b5cae03030 100644 --- a/tests/storybook/src/stories/page-info-properties.stories.tsx +++ b/tests/storybook/src/stories/page-info-properties.stories.tsx @@ -3,7 +3,7 @@ import { __unstableSchemas, AffineSchemas } from '@blocksuite/blocks/models'; import { Workspace } from '@blocksuite/store'; import { Schema } from '@blocksuite/store'; import type { StoryFn } from '@storybook/react'; -import { initEmptyPage } from '@toeverything/infra/blocksuite'; +import { initEmptyPage } from '@toeverything/infra'; const schema = new Schema(); schema.register(AffineSchemas).register(__unstableSchemas); diff --git a/tests/storybook/src/stories/page-list.stories.tsx b/tests/storybook/src/stories/page-list.stories.tsx index f7da05cdd5..7845723760 100644 --- a/tests/storybook/src/stories/page-list.stories.tsx +++ b/tests/storybook/src/stories/page-list.stories.tsx @@ -18,7 +18,7 @@ import { PageIcon, TagsIcon } from '@blocksuite/icons'; import { Schema, Workspace } from '@blocksuite/store'; import type { Meta, StoryFn } from '@storybook/react'; import { userEvent } from '@storybook/testing-library'; -import { initEmptyPage } from '@toeverything/infra/blocksuite'; +import { initEmptyPage } from '@toeverything/infra'; import { useState } from 'react'; import { withRouter } from 'storybook-addon-react-router-v6'; diff --git a/tests/storybook/src/stories/share-menu.stories.tsx b/tests/storybook/src/stories/share-menu.stories.tsx index ffdf3f2b2c..e10bc25151 100644 --- a/tests/storybook/src/stories/share-menu.stories.tsx +++ b/tests/storybook/src/stories/share-menu.stories.tsx @@ -6,7 +6,7 @@ import { type Page } from '@blocksuite/store'; import { expect } from '@storybook/jest'; import type { Meta, StoryFn } from '@storybook/react'; import { Workspace } from '@toeverything/infra'; -import { initEmptyPage } from '@toeverything/infra/blocksuite'; +import { initEmptyPage } from '@toeverything/infra'; import { useService } from '@toeverything/infra/di'; import { nanoid } from 'nanoid'; import { useEffect, useState } from 'react'; diff --git a/tools/@types/env/__all.d.ts b/tools/@types/env/__all.d.ts index 5722295cdc..c8f0321e7c 100644 --- a/tools/@types/env/__all.d.ts +++ b/tools/@types/env/__all.d.ts @@ -21,7 +21,6 @@ declare global { declare module '@blocksuite/store' { interface PageMeta { favorite?: boolean; - subpageIds: string[]; // If a page remove to trash, and it is a subpage, it will remove from its parent `subpageIds`, 'trashRelate' is use for save it parent trashRelate?: string; trash?: boolean;