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`
This commit is contained in:
EYHN
2024-02-27 03:50:53 +00:00
parent 0dabb08217
commit ad9b0303c4
60 changed files with 602 additions and 626 deletions

View File

@@ -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);
});
});

View File

@@ -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<BlockSuitePage>(
'BlockSuitePageContext'
);
export const PageMetaContext = createIdentifier<PageMeta>('PageMetaContext');
export const PageRecordContext =
createIdentifier<PageRecord>('PageRecordContext');
export function configurePageContext(
services: ServiceCollection,
blockSuitePage: BlockSuitePage,
pageMeta: PageMeta
pageRecord: PageRecord
) {
services
.scope(PageScope)
.addImpl(PageMetaContext, pageMeta)
.addImpl(PageRecordContext, pageRecord)
.addImpl(BlockSuitePageContext, blockSuitePage);
}

View File

@@ -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]);
}

View File

@@ -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 };
}

View File

@@ -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();
}
}

View File

@@ -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<PageMeta[]>(
public readonly records = LiveData.from<PageRecord[]>(
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));
}
}

View File

@@ -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<PageMeta>(
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<PageMeta>): void {
this.workspace.blockSuiteWorkspace.setPageMeta(this.id, meta);
}
mode: LiveData<PageMode> = LiveData.from(
this.localState.watch<PageMode>(`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);
}