refactor(editor): move block yjs listener to store (#12140)
<!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **Refactor** - Streamlined internal event handling for block updates, removing previous notification mechanisms from several components. - **Chores** - Simplified and cleaned up internal logic related to block addition and deletion tracking. No visible changes to the user interface or end-user features. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
@@ -1,4 +1,3 @@
|
|||||||
import type { Subject } from 'rxjs';
|
|
||||||
import type * as Y from 'yjs';
|
import type * as Y from 'yjs';
|
||||||
|
|
||||||
import type { AwarenessStore } from '../yjs/awareness.js';
|
import type { AwarenessStore } from '../yjs/awareness.js';
|
||||||
@@ -19,18 +18,6 @@ export interface Doc {
|
|||||||
get ready(): boolean;
|
get ready(): boolean;
|
||||||
dispose(): void;
|
dispose(): void;
|
||||||
|
|
||||||
slots: {
|
|
||||||
/**
|
|
||||||
* @internal
|
|
||||||
* This fires when the doc yBlock is updated.
|
|
||||||
*/
|
|
||||||
yBlockUpdated: Subject<{
|
|
||||||
type: 'add' | 'delete';
|
|
||||||
id: string;
|
|
||||||
isLocal: boolean;
|
|
||||||
}>;
|
|
||||||
};
|
|
||||||
|
|
||||||
clear(): void;
|
clear(): void;
|
||||||
getStore(options?: GetBlocksOptions): Store;
|
getStore(options?: GetBlocksOptions): Store;
|
||||||
clearQuery(query: Query, readonly?: boolean): void;
|
clearQuery(query: Query, readonly?: boolean): void;
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import {
|
|||||||
type BlockModel,
|
type BlockModel,
|
||||||
type BlockOptions,
|
type BlockOptions,
|
||||||
type BlockProps,
|
type BlockProps,
|
||||||
|
type YBlock,
|
||||||
} from '../block/index.js';
|
} from '../block/index.js';
|
||||||
import type { Doc } from '../doc.js';
|
import type { Doc } from '../doc.js';
|
||||||
import { DocCRUD } from './crud.js';
|
import { DocCRUD } from './crud.js';
|
||||||
@@ -134,7 +135,7 @@ type StoreBlockUpdatedPayloads =
|
|||||||
* @interface
|
* @interface
|
||||||
* @category Store
|
* @category Store
|
||||||
*/
|
*/
|
||||||
export type StoreSlots = Doc['slots'] & {
|
export type StoreSlots = {
|
||||||
/**
|
/**
|
||||||
* This fires after `doc.load` is called.
|
* This fires after `doc.load` is called.
|
||||||
* The Y.Doc is fully loaded and ready to use.
|
* The Y.Doc is fully loaded and ready to use.
|
||||||
@@ -165,6 +166,19 @@ export type StoreSlots = Doc['slots'] & {
|
|||||||
* This fires when the history is updated.
|
* This fires when the history is updated.
|
||||||
*/
|
*/
|
||||||
historyUpdated: Subject<void>;
|
historyUpdated: Subject<void>;
|
||||||
|
/** @internal */
|
||||||
|
yBlockUpdated: Subject<
|
||||||
|
| {
|
||||||
|
type: 'add';
|
||||||
|
id: string;
|
||||||
|
isLocal: boolean;
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
type: 'delete';
|
||||||
|
id: string;
|
||||||
|
isLocal: boolean;
|
||||||
|
}
|
||||||
|
>;
|
||||||
};
|
};
|
||||||
|
|
||||||
const internalExtensions = [StoreSelectionExtension];
|
const internalExtensions = [StoreSelectionExtension];
|
||||||
@@ -555,7 +569,7 @@ export class Store {
|
|||||||
rootDeleted: new Subject(),
|
rootDeleted: new Subject(),
|
||||||
blockUpdated: new Subject(),
|
blockUpdated: new Subject(),
|
||||||
historyUpdated: new Subject(),
|
historyUpdated: new Subject(),
|
||||||
yBlockUpdated: this._doc.slots.yBlockUpdated,
|
yBlockUpdated: new Subject(),
|
||||||
};
|
};
|
||||||
this._schema = new Schema();
|
this._schema = new Schema();
|
||||||
|
|
||||||
@@ -584,6 +598,11 @@ export class Store {
|
|||||||
this._query = query;
|
this._query = query;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this._yBlocks.observeDeep(this._handleYEvents);
|
||||||
|
this._yBlocks.forEach((_, id) => {
|
||||||
|
this._handleYBlockAdd(id, false);
|
||||||
|
});
|
||||||
|
|
||||||
this._yBlocks.forEach((_, id) => {
|
this._yBlocks.forEach((_, id) => {
|
||||||
if (id in this._blocks.peek()) {
|
if (id in this._blocks.peek()) {
|
||||||
return;
|
return;
|
||||||
@@ -622,7 +641,7 @@ export class Store {
|
|||||||
|
|
||||||
private readonly _subscribeToSlots = () => {
|
private readonly _subscribeToSlots = () => {
|
||||||
this.disposableGroup.add(
|
this.disposableGroup.add(
|
||||||
this._doc.slots.yBlockUpdated.subscribe(({ type, id, isLocal }) => {
|
this.slots.yBlockUpdated.subscribe(({ type, id, isLocal }) => {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case 'add': {
|
case 'add': {
|
||||||
this._onBlockAdded(id, isLocal, false);
|
this._onBlockAdded(id, isLocal, false);
|
||||||
@@ -1252,12 +1271,57 @@ export class Store {
|
|||||||
this._provider.getAll(StoreExtensionIdentifier).forEach(ext => {
|
this._provider.getAll(StoreExtensionIdentifier).forEach(ext => {
|
||||||
ext.disposed();
|
ext.disposed();
|
||||||
});
|
});
|
||||||
|
if (this.doc.ready) {
|
||||||
|
this._yBlocks.unobserveDeep(this._handleYEvents);
|
||||||
|
}
|
||||||
this.slots.ready.complete();
|
this.slots.ready.complete();
|
||||||
this.slots.rootAdded.complete();
|
this.slots.rootAdded.complete();
|
||||||
this.slots.rootDeleted.complete();
|
this.slots.rootDeleted.complete();
|
||||||
this.slots.blockUpdated.complete();
|
this.slots.blockUpdated.complete();
|
||||||
this.slots.historyUpdated.complete();
|
this.slots.historyUpdated.complete();
|
||||||
|
this.slots.yBlockUpdated.complete();
|
||||||
this.disposableGroup.dispose();
|
this.disposableGroup.dispose();
|
||||||
this._isDisposed = true;
|
this._isDisposed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _handleYBlockAdd(id: string, isLocal: boolean) {
|
||||||
|
this.slots.yBlockUpdated.next({ type: 'add', id, isLocal });
|
||||||
|
}
|
||||||
|
|
||||||
|
private _handleYBlockDelete(id: string, isLocal: boolean) {
|
||||||
|
this.slots.yBlockUpdated.next({ type: 'delete', id, isLocal });
|
||||||
|
}
|
||||||
|
|
||||||
|
private _handleYEvent(event: Y.YEvent<YBlock | Y.Text | Y.Array<unknown>>) {
|
||||||
|
// event on top-level block store
|
||||||
|
if (event.target !== this._yBlocks) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const isLocal =
|
||||||
|
!event.transaction.origin ||
|
||||||
|
!this._yBlocks.doc ||
|
||||||
|
event.transaction.origin instanceof Y.UndoManager ||
|
||||||
|
event.transaction.origin.proxy
|
||||||
|
? true
|
||||||
|
: event.transaction.origin === this._yBlocks.doc.clientID;
|
||||||
|
event.keys.forEach((value, id) => {
|
||||||
|
try {
|
||||||
|
if (value.action === 'add') {
|
||||||
|
this._handleYBlockAdd(id, isLocal);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (value.action === 'delete') {
|
||||||
|
this._handleYBlockDelete(id, isLocal);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error('An error occurred while handling Yjs event:');
|
||||||
|
console.error(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private readonly _handleYEvents = (events: Y.YEvent<YBlock | Y.Text>[]) => {
|
||||||
|
events.forEach(event => this._handleYEvent(event));
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,11 +20,6 @@ export class TestDoc implements Doc {
|
|||||||
|
|
||||||
private readonly _storeMap = new Map<string, Store>();
|
private readonly _storeMap = new Map<string, Store>();
|
||||||
|
|
||||||
// doc/space container.
|
|
||||||
private readonly _handleYEvents = (events: Y.YEvent<YBlock | Y.Text>[]) => {
|
|
||||||
events.forEach(event => this._handleYEvent(event));
|
|
||||||
};
|
|
||||||
|
|
||||||
private readonly _initSubDoc = () => {
|
private readonly _initSubDoc = () => {
|
||||||
let subDoc = this.rootDoc.getMap('spaces').get(this.id);
|
let subDoc = this.rootDoc.getMap('spaces').get(this.id);
|
||||||
if (!subDoc) {
|
if (!subDoc) {
|
||||||
@@ -79,21 +74,6 @@ export class TestDoc implements Doc {
|
|||||||
|
|
||||||
readonly rootDoc: Y.Doc;
|
readonly rootDoc: Y.Doc;
|
||||||
|
|
||||||
readonly slots = {
|
|
||||||
yBlockUpdated: new Subject<
|
|
||||||
| {
|
|
||||||
type: 'add';
|
|
||||||
id: string;
|
|
||||||
isLocal: boolean;
|
|
||||||
}
|
|
||||||
| {
|
|
||||||
type: 'delete';
|
|
||||||
id: string;
|
|
||||||
isLocal: boolean;
|
|
||||||
}
|
|
||||||
>(),
|
|
||||||
};
|
|
||||||
|
|
||||||
get blobSync() {
|
get blobSync() {
|
||||||
return this.workspace.blobSync;
|
return this.workspace.blobSync;
|
||||||
}
|
}
|
||||||
@@ -141,48 +121,6 @@ export class TestDoc implements Doc {
|
|||||||
return (readonly?.toString() as 'true' | 'false') ?? 'false';
|
return (readonly?.toString() as 'true' | 'false') ?? 'false';
|
||||||
}
|
}
|
||||||
|
|
||||||
private _handleYBlockAdd(id: string, isLocal: boolean) {
|
|
||||||
this.slots.yBlockUpdated.next({ type: 'add', id, isLocal });
|
|
||||||
}
|
|
||||||
|
|
||||||
private _handleYBlockDelete(id: string, isLocal: boolean) {
|
|
||||||
this.slots.yBlockUpdated.next({ type: 'delete', id, isLocal });
|
|
||||||
}
|
|
||||||
|
|
||||||
private _handleYEvent(event: Y.YEvent<YBlock | Y.Text | Y.Array<unknown>>) {
|
|
||||||
// event on top-level block store
|
|
||||||
if (event.target !== this._yBlocks) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const isLocal =
|
|
||||||
!event.transaction.origin ||
|
|
||||||
!this._yBlocks.doc ||
|
|
||||||
event.transaction.origin instanceof Y.UndoManager ||
|
|
||||||
event.transaction.origin.proxy
|
|
||||||
? true
|
|
||||||
: event.transaction.origin === this._yBlocks.doc.clientID;
|
|
||||||
event.keys.forEach((value, id) => {
|
|
||||||
try {
|
|
||||||
if (value.action === 'add') {
|
|
||||||
this._handleYBlockAdd(id, isLocal);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (value.action === 'delete') {
|
|
||||||
this._handleYBlockDelete(id, isLocal);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
console.error('An error occurred while handling Yjs event:');
|
|
||||||
console.error(e);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private _initYBlocks() {
|
|
||||||
const { _yBlocks } = this;
|
|
||||||
_yBlocks.observeDeep(this._handleYEvents);
|
|
||||||
}
|
|
||||||
|
|
||||||
clear() {
|
clear() {
|
||||||
this._yBlocks.clear();
|
this._yBlocks.clear();
|
||||||
}
|
}
|
||||||
@@ -200,7 +138,6 @@ export class TestDoc implements Doc {
|
|||||||
|
|
||||||
dispose() {
|
dispose() {
|
||||||
if (this.ready) {
|
if (this.ready) {
|
||||||
this._yBlocks.unobserveDeep(this._handleYEvents);
|
|
||||||
this._yBlocks.clear();
|
this._yBlocks.clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -263,12 +200,6 @@ export class TestDoc implements Doc {
|
|||||||
|
|
||||||
this._ySpaceDoc.load();
|
this._ySpaceDoc.load();
|
||||||
|
|
||||||
this._initYBlocks();
|
|
||||||
|
|
||||||
this._yBlocks.forEach((_, id) => {
|
|
||||||
this._handleYBlockAdd(id, false);
|
|
||||||
});
|
|
||||||
|
|
||||||
initFn?.();
|
initFn?.();
|
||||||
|
|
||||||
this._ready = true;
|
this._ready = true;
|
||||||
|
|||||||
@@ -24,11 +24,6 @@ export class DocImpl implements Doc {
|
|||||||
|
|
||||||
private readonly _storeMap = new Map<string, Store>();
|
private readonly _storeMap = new Map<string, Store>();
|
||||||
|
|
||||||
// doc/space container.
|
|
||||||
private readonly _handleYEvents = (events: Y.YEvent<YBlock | Y.Text>[]) => {
|
|
||||||
events.forEach(event => this._handleYEvent(event));
|
|
||||||
};
|
|
||||||
|
|
||||||
private readonly _initSubDoc = () => {
|
private readonly _initSubDoc = () => {
|
||||||
{
|
{
|
||||||
// This is a piece of old version compatible code. The old version relies on the subdoc instance on `spaces`.
|
// This is a piece of old version compatible code. The old version relies on the subdoc instance on `spaces`.
|
||||||
@@ -74,22 +69,6 @@ export class DocImpl implements Doc {
|
|||||||
|
|
||||||
readonly rootDoc: Y.Doc;
|
readonly rootDoc: Y.Doc;
|
||||||
|
|
||||||
readonly slots = {
|
|
||||||
// eslint-disable-next-line rxjs/finnish
|
|
||||||
yBlockUpdated: new Subject<
|
|
||||||
| {
|
|
||||||
type: 'add';
|
|
||||||
id: string;
|
|
||||||
isLocal: boolean;
|
|
||||||
}
|
|
||||||
| {
|
|
||||||
type: 'delete';
|
|
||||||
id: string;
|
|
||||||
isLocal: boolean;
|
|
||||||
}
|
|
||||||
>(),
|
|
||||||
};
|
|
||||||
|
|
||||||
get blobSync() {
|
get blobSync() {
|
||||||
return this.workspace.blobSync;
|
return this.workspace.blobSync;
|
||||||
}
|
}
|
||||||
@@ -136,48 +115,6 @@ export class DocImpl implements Doc {
|
|||||||
return (readonly?.toString() as 'true' | 'false') ?? 'false';
|
return (readonly?.toString() as 'true' | 'false') ?? 'false';
|
||||||
}
|
}
|
||||||
|
|
||||||
private _handleYBlockAdd(id: string, isLocal: boolean) {
|
|
||||||
this.slots.yBlockUpdated.next({ type: 'add', id, isLocal });
|
|
||||||
}
|
|
||||||
|
|
||||||
private _handleYBlockDelete(id: string, isLocal: boolean) {
|
|
||||||
this.slots.yBlockUpdated.next({ type: 'delete', id, isLocal });
|
|
||||||
}
|
|
||||||
|
|
||||||
private _handleYEvent(event: Y.YEvent<YBlock | Y.Text | Y.Array<unknown>>) {
|
|
||||||
// event on top-level block store
|
|
||||||
if (event.target !== this._yBlocks) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const isLocal =
|
|
||||||
!event.transaction.origin ||
|
|
||||||
!this._yBlocks.doc ||
|
|
||||||
event.transaction.origin instanceof Y.UndoManager ||
|
|
||||||
event.transaction.origin.proxy
|
|
||||||
? true
|
|
||||||
: event.transaction.origin === this._yBlocks.doc.clientID;
|
|
||||||
event.keys.forEach((value, id) => {
|
|
||||||
try {
|
|
||||||
if (value.action === 'add') {
|
|
||||||
this._handleYBlockAdd(id, isLocal);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (value.action === 'delete') {
|
|
||||||
this._handleYBlockDelete(id, isLocal);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
console.error('An error occurred while handling Yjs event:');
|
|
||||||
console.error(e);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private _initYBlocks() {
|
|
||||||
const { _yBlocks } = this;
|
|
||||||
_yBlocks.observeDeep(this._handleYEvents);
|
|
||||||
}
|
|
||||||
|
|
||||||
clear() {
|
clear() {
|
||||||
this._yBlocks.clear();
|
this._yBlocks.clear();
|
||||||
}
|
}
|
||||||
@@ -198,7 +135,6 @@ export class DocImpl implements Doc {
|
|||||||
this._destroy();
|
this._destroy();
|
||||||
|
|
||||||
if (this.ready) {
|
if (this.ready) {
|
||||||
this._yBlocks.unobserveDeep(this._handleYEvents);
|
|
||||||
this._yBlocks.clear();
|
this._yBlocks.clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -266,12 +202,6 @@ export class DocImpl implements Doc {
|
|||||||
this.workspace.onLoadDoc?.(this.spaceDoc);
|
this.workspace.onLoadDoc?.(this.spaceDoc);
|
||||||
this.workspace.onLoadAwareness?.(this.awarenessStore.awareness);
|
this.workspace.onLoadAwareness?.(this.awarenessStore.awareness);
|
||||||
|
|
||||||
this._initYBlocks();
|
|
||||||
|
|
||||||
this._yBlocks.forEach((_, id) => {
|
|
||||||
this._handleYBlockAdd(id, false);
|
|
||||||
});
|
|
||||||
|
|
||||||
initFn?.();
|
initFn?.();
|
||||||
|
|
||||||
this._loaded = true;
|
this._loaded = true;
|
||||||
|
|||||||
Reference in New Issue
Block a user