diff --git a/blocksuite/affine/widget-remote-selection/src/manager/remote-color-manager.ts b/blocksuite/affine/widget-remote-selection/src/manager/remote-color-manager.ts
index 4971598a44..b90e5b7845 100644
--- a/blocksuite/affine/widget-remote-selection/src/manager/remote-color-manager.ts
+++ b/blocksuite/affine/widget-remote-selection/src/manager/remote-color-manager.ts
@@ -5,7 +5,7 @@ import { multiPlayersColor } from './color-picker';
export class RemoteColorManager {
private get awarenessStore() {
- return this.std.store.workspace.awarenessStore;
+ return this.std.store.awarenessStore;
}
constructor(readonly std: BlockStdScope) {
diff --git a/blocksuite/framework/store/src/model/workspace.ts b/blocksuite/framework/store/src/model/workspace.ts
index 8a005ba96d..6c4590cf09 100644
--- a/blocksuite/framework/store/src/model/workspace.ts
+++ b/blocksuite/framework/store/src/model/workspace.ts
@@ -4,7 +4,6 @@ import type { Awareness } from 'y-protocols/awareness.js';
import type * as Y from 'yjs';
import type { IdGenerator } from '../utils/id-generator.js';
-import type { AwarenessStore } from '../yjs/awareness.js';
import type { CreateBlocksOptions, Doc, GetBlocksOptions } from './doc.js';
import type { Store } from './store/store.js';
import type { WorkspaceMeta } from './workspace-meta.js';
@@ -14,7 +13,6 @@ export interface Workspace {
readonly meta: WorkspaceMeta;
readonly idGenerator: IdGenerator;
readonly blobSync: BlobEngine;
- readonly awarenessStore: AwarenessStore;
readonly onLoadDoc?: (doc: Y.Doc) => void;
readonly onLoadAwareness?: (awareness: Awareness) => void;
diff --git a/packages/frontend/core/src/components/affine/awareness/index.tsx b/packages/frontend/core/src/components/affine/awareness/index.tsx
deleted file mode 100644
index b1531e2489..0000000000
--- a/packages/frontend/core/src/components/affine/awareness/index.tsx
+++ /dev/null
@@ -1,49 +0,0 @@
-import { WorkspaceService } from '@affine/core/modules/workspace';
-import { useLiveData, useService } from '@toeverything/infra';
-import { useEffect } from 'react';
-
-import { AuthService } from '../../../modules/cloud';
-
-const SyncAwarenessInnerLoggedIn = () => {
- const authService = useService(AuthService);
- const account = useLiveData(authService.session.account$);
- const currentWorkspace = useService(WorkspaceService).workspace;
-
- useEffect(() => {
- if (account && currentWorkspace) {
- currentWorkspace.docCollection.awarenessStore.awareness.setLocalStateField(
- 'user',
- {
- name: account.label,
- // TODO(@eyhn): add avatar?
- }
- );
-
- return () => {
- currentWorkspace.docCollection.awarenessStore.awareness.setLocalStateField(
- 'user',
- null
- );
- };
- }
- return;
- }, [currentWorkspace, account]);
-
- return null;
-};
-
-const SyncAwarenessInner = () => {
- const session = useService(AuthService).session;
- const loginStatus = useLiveData(session.status$);
-
- if (loginStatus === 'authenticated') {
- return ;
- }
-
- return null;
-};
-
-// TODO(@eyhn): we could do something more interesting here, e.g., show where the current user is
-export const SyncAwareness = () => {
- return ;
-};
diff --git a/packages/frontend/core/src/components/providers/workspace-side-effects.tsx b/packages/frontend/core/src/components/providers/workspace-side-effects.tsx
index b5e16abb8a..ef4bc26b9a 100644
--- a/packages/frontend/core/src/components/providers/workspace-side-effects.tsx
+++ b/packages/frontend/core/src/components/providers/workspace-side-effects.tsx
@@ -8,7 +8,6 @@ import {
CopilotClient,
setupAIProvider,
} from '@affine/core/blocksuite/ai';
-import { SyncAwareness } from '@affine/core/components/affine/awareness';
import { useRegisterFindInPageCommands } from '@affine/core/components/hooks/affine/use-register-find-in-page-commands';
import { useRegisterWorkspaceCommands } from '@affine/core/components/hooks/use-register-workspace-commands';
import { OverCapacityNotification } from '@affine/core/components/over-capacity';
@@ -176,7 +175,6 @@ export const WorkspaceSideEffects = () => {
return (
<>
-
>
);
diff --git a/packages/frontend/core/src/desktop/pages/workspace/detail-page/detail-page-wrapper.tsx b/packages/frontend/core/src/desktop/pages/workspace/detail-page/detail-page-wrapper.tsx
index 2ceb81f39e..5b08c0c22a 100644
--- a/packages/frontend/core/src/desktop/pages/workspace/detail-page/detail-page-wrapper.tsx
+++ b/packages/frontend/core/src/desktop/pages/workspace/detail-page/detail-page-wrapper.tsx
@@ -50,7 +50,7 @@ const useLoadDoc = (pageId: string) => {
if (doc && isInTrash) {
doc.blockSuiteDoc.readonly = true;
}
- }, [currentWorkspace.docCollection.awarenessStore, doc, isInTrash]);
+ }, [doc, isInTrash]);
return {
doc,
diff --git a/packages/frontend/core/src/modules/cloud/index.ts b/packages/frontend/core/src/modules/cloud/index.ts
index d5046f38d5..3c06452a6d 100644
--- a/packages/frontend/core/src/modules/cloud/index.ts
+++ b/packages/frontend/core/src/modules/cloud/index.ts
@@ -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]);
}
diff --git a/packages/frontend/core/src/modules/cloud/services/editor-user-cursor-label.ts b/packages/frontend/core/src/modules/cloud/services/editor-user-cursor-label.ts
new file mode 100644
index 0000000000..4ff455e952
--- /dev/null
+++ b/packages/frontend/core/src/modules/cloud/services/editor-user-cursor-label.ts
@@ -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());
+ }
+ }
+}
diff --git a/packages/frontend/core/src/modules/doc/events/index.ts b/packages/frontend/core/src/modules/doc/events/index.ts
index b647517595..35affb82e6 100644
--- a/packages/frontend/core/src/modules/doc/events/index.ts
+++ b/packages/frontend/core/src/modules/doc/events/index.ts
@@ -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('DocCreated');
+
+export const DocInitialized = createEvent('DocInitialized');
diff --git a/packages/frontend/core/src/modules/doc/services/docs.ts b/packages/frontend/core/src/modules/doc/services/docs.ts
index 11be2445bb..b8d4d1add3 100644
--- a/packages/frontend/core/src/modules/doc/services/docs.ts
+++ b/packages/frontend/core/src/modules/doc/services/docs.ts
@@ -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 };
diff --git a/packages/frontend/core/src/modules/editor/events/index.ts b/packages/frontend/core/src/modules/editor/events/index.ts
new file mode 100644
index 0000000000..549143b84a
--- /dev/null
+++ b/packages/frontend/core/src/modules/editor/events/index.ts
@@ -0,0 +1,5 @@
+import { createEvent } from '@toeverything/infra';
+
+import type { Editor } from '../entities/editor';
+
+export const EditorInitialized = createEvent('EditorInitialized');
diff --git a/packages/frontend/core/src/modules/editor/services/editors.ts b/packages/frontend/core/src/modules/editor/services/editors.ts
index ee38b5415a..0c68395ade 100644
--- a/packages/frontend/core/src/modules/editor/services/editors.ts
+++ b/packages/frontend/core/src/modules/editor/services/editors.ts
@@ -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;
}
}
diff --git a/packages/frontend/core/src/modules/workspace/entities/workspace.ts b/packages/frontend/core/src/modules/workspace/entities/workspace.ts
index bc40da16fc..bebad39be7 100644
--- a/packages/frontend/core/src/modules/workspace/entities/workspace.ts
+++ b/packages/frontend/core/src/modules/workspace/entities/workspace.ts
@@ -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;
}
diff --git a/packages/frontend/core/src/modules/workspace/impls/doc.ts b/packages/frontend/core/src/modules/workspace/impls/doc.ts
index 73a5a04b15..f042f7bf18 100644
--- a/packages/frontend/core/src/modules/workspace/impls/doc.ts
+++ b/packages/frontend/core/src/modules/workspace/impls/doc.ts
@@ -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();
diff --git a/packages/frontend/core/src/modules/workspace/impls/workspace.ts b/packages/frontend/core/src/modules/workspace/impls/workspace.ts
index 6e1bc02fdf..650d3b1885 100644
--- a/packages/frontend/core/src/modules/workspace/impls/workspace.ts
+++ b/packages/frontend/core/src/modules/workspace/impls/workspace.ts
@@ -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();
@@ -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());
+ }
}