feat(core): doc level awareness (#10646)

This commit is contained in:
EYHN
2025-03-06 06:05:45 +00:00
parent 2b30d756e2
commit 5c8b81581c
14 changed files with 65 additions and 79 deletions

View File

@@ -39,6 +39,7 @@ import { type Framework } from '@toeverything/infra';
import { DocScope } from '../doc/scopes/doc';
import { DocService } from '../doc/services/doc';
import { EditorScope } from '../editor';
import { GlobalCache, GlobalState } from '../storage/providers/global';
import { GlobalStateService } from '../storage/services/global';
import { UrlService } from '../url';
@@ -63,6 +64,7 @@ import { AuthService } from './services/auth';
import { CaptchaService } from './services/captcha';
import { CloudDocMetaService } from './services/cloud-doc-meta';
import { DefaultServerService } from './services/default-server';
import { EditorUserCursorLabelService } from './services/editor-user-cursor-label';
import { EventSourceService } from './services/eventsource';
import { FetchService } from './services/fetch';
import { GraphQLService } from './services/graphql';
@@ -168,4 +170,10 @@ export function configureCloudModule(framework: Framework) {
.entity(WorkspaceInvoices, [WorkspaceService, WorkspaceServerService])
.service(SelfhostLicenseService, [SelfhostLicenseStore, WorkspaceService])
.store(SelfhostLicenseStore, [WorkspaceServerService]);
framework
.scope(WorkspaceScope)
.scope(DocScope)
.scope(EditorScope)
.service(EditorUserCursorLabelService, [WorkspaceServerService]);
}

View File

@@ -0,0 +1,27 @@
import { OnEvent, Service } from '@toeverything/infra';
import type { Editor } from '../../editor';
import { EditorInitialized } from '../../editor/events';
import type { WorkspaceServerService } from './workspace-server';
@OnEvent(EditorInitialized, i => i.onEditorInitialized)
export class EditorUserCursorLabelService extends Service {
constructor(private readonly workspaceServerService: WorkspaceServerService) {
super();
}
onEditorInitialized(editor: Editor) {
if (this.workspaceServerService.server) {
const subscription =
this.workspaceServerService.server.account$.subscribe(account => {
editor.doc.blockSuiteDoc.awarenessStore.awareness.setLocalStateField(
'user',
{
name: account?.label,
}
);
});
this.disposables.push(() => subscription.unsubscribe());
}
}
}

View File

@@ -1,5 +1,8 @@
import { createEvent } from '@toeverything/infra';
import type { Doc } from '../entities/doc';
import type { DocRecord } from '../entities/record';
export const DocCreated = createEvent<DocRecord>('DocCreated');
export const DocInitialized = createEvent<Doc>('DocInitialized');

View File

@@ -20,7 +20,7 @@ import { getAFFiNEWorkspaceSchema } from '../../workspace';
import type { Doc } from '../entities/doc';
import { DocPropertyList } from '../entities/property-list';
import { DocRecordList } from '../entities/record-list';
import { DocCreated } from '../events';
import { DocCreated, DocInitialized } from '../events';
import { DocScope } from '../scopes/doc';
import type { DocPropertiesStore } from '../stores/doc-properties';
import type { DocsStore } from '../stores/docs';
@@ -105,6 +105,8 @@ export class DocsService extends Service {
const doc = docScope.get(DocService).doc;
doc.scope.emitEvent(DocInitialized, doc);
const { obj, release } = this.pool.put(docId, doc);
return { doc: obj, release };

View File

@@ -0,0 +1,5 @@
import { createEvent } from '@toeverything/infra';
import type { Editor } from '../entities/editor';
export const EditorInitialized = createEvent<Editor>('EditorInitialized');

View File

@@ -1,9 +1,12 @@
import { Service } from '@toeverything/infra';
import { Editor } from '../entities/editor';
import { EditorInitialized } from '../events';
export class EditorsService extends Service {
createEditor() {
return this.framework.createEntity(Editor);
const editor = this.framework.createEntity(Editor);
editor.scope.emitEvent(EditorInitialized, editor);
return editor;
}
}

View File

@@ -1,7 +1,6 @@
import type { Workspace as WorkspaceInterface } from '@blocksuite/affine/store';
import { Entity, LiveData } from '@toeverything/infra';
import { Observable } from 'rxjs';
import type { Awareness } from 'y-protocols/awareness.js';
import { WorkspaceImpl } from '../impls/workspace';
import type { WorkspaceScope } from '../scopes/workspace';
@@ -58,10 +57,6 @@ export class Workspace extends Entity {
return this._docCollection;
}
get awareness() {
return this.docCollection.awarenessStore.awareness as Awareness;
}
get rootYDoc() {
return this.docCollection.doc;
}

View File

@@ -1,7 +1,7 @@
import { SpecProvider } from '@blocksuite/affine/blocks';
import { Slot } from '@blocksuite/affine/global/utils';
import {
type AwarenessStore,
AwarenessStore,
type Doc,
type GetBlocksOptions,
type Query,
@@ -10,13 +10,13 @@ import {
type YBlock,
} from '@blocksuite/affine/store';
import { signal } from '@preact/signals-core';
import { Awareness } from 'y-protocols/awareness.js';
import * as Y from 'yjs';
type DocOptions = {
id: string;
collection: Workspace;
doc: Y.Doc;
awarenessStore: AwarenessStore;
};
export class DocImpl implements Doc {
@@ -155,12 +155,11 @@ export class DocImpl implements Doc {
return this._yBlocks;
}
constructor({ id, collection, doc, awarenessStore }: DocOptions) {
constructor({ id, collection, doc }: DocOptions) {
this.id = id;
this.rootDoc = doc;
this.awarenessStore = awarenessStore;
this._ySpaceDoc = this._initSubDoc() as Y.Doc;
this.awarenessStore = new AwarenessStore(new Awareness(this._ySpaceDoc));
this._yBlocks = this._ySpaceDoc.getMap('blocks');
this._collection = collection;
@@ -228,12 +227,14 @@ export class DocImpl implements Doc {
}
private _destroy() {
this.awarenessStore.destroy();
this._ySpaceDoc.destroy();
this._onLoadSlot.dispose();
this._loaded = false;
}
dispose() {
this._destroy();
this.slots.historyUpdated.dispose();
if (this.ready) {
@@ -303,6 +304,7 @@ export class DocImpl implements Doc {
this.spaceDoc.load();
this.workspace.onLoadDoc?.(this.spaceDoc);
this.workspace.onLoadAwareness?.(this.awarenessStore.awareness);
this._initYBlocks();

View File

@@ -4,7 +4,6 @@ import {
} from '@blocksuite/affine/global/exceptions';
import { NoopLogger, Slot } from '@blocksuite/affine/global/utils';
import {
AwarenessStore,
type CreateBlocksOptions,
type Doc,
type GetBlocksOptions,
@@ -19,7 +18,7 @@ import {
type BlobSource,
MemoryBlobSource,
} from '@blocksuite/affine/sync';
import { Awareness } from 'y-protocols/awareness.js';
import type { Awareness } from 'y-protocols/awareness.js';
import * as Y from 'yjs';
import { DocImpl } from './doc';
@@ -33,8 +32,6 @@ type WorkspaceOptions = {
};
export class WorkspaceImpl implements Workspace {
readonly awarenessStore: AwarenessStore;
readonly blobSync: BlobEngine;
readonly blockCollections = new Map<string, Doc>();
@@ -68,11 +65,9 @@ export class WorkspaceImpl implements Workspace {
}: WorkspaceOptions = {}) {
this.id = id || '';
this.doc = new Y.Doc({ guid: id });
this.awarenessStore = new AwarenessStore(new Awareness(this.doc));
this.onLoadDoc = onLoadDoc;
this.onLoadAwareness = onLoadAwareness;
this.onLoadDoc?.(this.doc);
this.onLoadAwareness?.(this.awarenessStore.awareness);
this.onLoadAwareness = onLoadAwareness;
blobSource = blobSource ?? new MemoryBlobSource();
const logger = new NoopLogger();
@@ -91,7 +86,6 @@ export class WorkspaceImpl implements Workspace {
id: docId,
collection: this,
doc: this.doc,
awarenessStore: this.awarenessStore,
});
this.blockCollections.set(doc.id, doc);
});
@@ -139,10 +133,6 @@ export class WorkspaceImpl implements Workspace {
}) as Store;
}
dispose() {
this.awarenessStore.destroy();
}
private _getDoc(docId: string): Doc | null {
const space = this.docs.get(docId) as Doc | undefined;
return space ?? null;
@@ -172,4 +162,8 @@ export class WorkspaceImpl implements Workspace {
this.meta.removeDocMeta(docId);
this.blockCollections.delete(docId);
}
dispose() {
this.blockCollections.forEach(doc => doc.dispose());
}
}