feat(editor): add isLocal flag in blockUpdated subject (#10799)

This commit is contained in:
Saul-Mirone
2025-03-13 05:33:06 +00:00
parent c023b724d0
commit 250f3f1efd
15 changed files with 167 additions and 119 deletions

View File

@@ -1,13 +1,15 @@
import type { Doc as YDoc, YEvent } from 'yjs';
import { UndoManager } from 'yjs';
import * as Y from 'yjs';
import type { ProxyOptions } from './types';
export abstract class BaseReactiveYData<T, Y> {
export abstract class BaseReactiveYData<
T,
YSource extends Y.AbstractType<any>,
> {
protected _getOrigin = (
doc: YDoc
doc: Y.Doc
): {
doc: YDoc;
doc: Y.Doc;
proxy: true;
target: BaseReactiveYData<any, any>;
@@ -19,16 +21,24 @@ export abstract class BaseReactiveYData<T, Y> {
};
};
protected _onObserve = (event: YEvent<any>, handler: () => void) => {
protected _onObserve = (event: Y.YEvent<any>, handler: () => void) => {
if (
event.transaction.origin?.proxy !== true &&
(!event.transaction.local ||
event.transaction.origin instanceof UndoManager)
event.transaction.origin instanceof Y.UndoManager)
) {
handler();
}
this._options?.onChange?.(this._proxy);
const isLocal =
!event.transaction.origin ||
!this._ySource.doc ||
event.transaction.origin instanceof Y.UndoManager ||
event.transaction.origin.proxy
? true
: event.transaction.origin === this._ySource.doc.clientID;
this._options?.onChange?.(this._proxy, isLocal);
};
protected abstract readonly _options?: ProxyOptions<T>;
@@ -41,7 +51,7 @@ export abstract class BaseReactiveYData<T, Y> {
protected readonly _stashed = new Set<string | number>();
protected _transact = (doc: YDoc, fn: () => void) => {
protected _transact = (doc: Y.Doc, fn: () => void) => {
doc.transact(fn, this._getOrigin(doc));
};
@@ -54,7 +64,7 @@ export abstract class BaseReactiveYData<T, Y> {
this._skipNext = false;
};
protected abstract readonly _ySource: Y;
protected abstract readonly _ySource: YSource;
get proxy() {
return this._proxy;

View File

@@ -2,7 +2,7 @@ import * as Y from 'yjs';
import { NATIVE_UNIQ_IDENTIFIER } from '../consts.js';
export type OnBoxedChange = (data: unknown) => void;
export type OnBoxedChange = (data: unknown, isLocal: boolean) => void;
export class Boxed<T = unknown> {
static from = <T>(map: Y.Map<T>, onChange?: OnBoxedChange): Boxed<T> => {
@@ -44,8 +44,17 @@ export class Boxed<T = unknown> {
this._map.set('type', NATIVE_UNIQ_IDENTIFIER as T);
this._map.set('value', value);
}
this._map.observeDeep(() => {
this._onChange?.(this.getValue());
this._map.observeDeep(events => {
events.forEach(event => {
const isLocal =
!event.transaction.origin ||
!this._map.doc ||
event.transaction.origin instanceof Y.UndoManager ||
event.transaction.origin.proxy
? true
: event.transaction.origin === this._map.doc.clientID;
this._onChange?.(this.getValue(), isLocal);
});
});
}

View File

@@ -22,7 +22,7 @@ const keyWithoutPrefix = (key: string) => key.replace(/(prop|sys):/, '');
const keyWithPrefix = (key: string) =>
SYS_KEYS.has(key) ? `sys:${key}` : `prop:${key}`;
type OnChange = (key: string) => void;
type OnChange = (key: string, isLocal: boolean) => void;
type Transform = (key: string, value: unknown, origin: unknown) => unknown;
type CreateProxyOptions = {
@@ -119,7 +119,7 @@ function createProxy(
}
byPassSignalUpdate(() => {
proxy[p] = next;
onChange?.(firstKey);
onChange?.(firstKey, true);
});
});
const subscription = onDispose.subscribe(() => {
@@ -139,7 +139,7 @@ function createProxy(
: prev;
// @ts-expect-error allow magic props
root[signalKey].value = next;
onChange?.(firstKey);
onChange?.(firstKey, true);
});
};
@@ -162,7 +162,7 @@ function createProxy(
list.push(() => {
if (value instanceof Text || Boxed.is(value)) {
value.bind(() => {
onChange?.(firstKey);
onChange?.(firstKey, true);
});
}
yMap.set(keyWithPrefix(fullPath), native2Y(value));
@@ -197,7 +197,7 @@ function createProxy(
if (value instanceof Text || Boxed.is(value)) {
value.bind(() => {
onChange?.(firstKey);
onChange?.(firstKey, true);
});
}
const yValue = native2Y(value);
@@ -251,7 +251,7 @@ function createProxy(
: prev;
// @ts-expect-error allow magic props
root[signalKey].value = next;
onChange?.(firstKey);
onChange?.(firstKey, true);
});
};
@@ -324,6 +324,7 @@ export class ReactiveFlatYMap extends BaseReactiveYData<
return acc[key] as UnRecord;
}, proxy as UnRecord);
});
this._onChange?.(firstKey, false);
return;
}
if (type.action === 'delete') {
@@ -390,8 +391,8 @@ export class ReactiveFlatYMap extends BaseReactiveYData<
};
private readonly _getPropOnChange = (key: string) => {
return () => {
this._onChange?.(key);
return (_: unknown, isLocal: boolean) => {
this._onChange?.(key, isLocal);
};
};
@@ -485,7 +486,7 @@ export class ReactiveFlatYMap extends BaseReactiveYData<
}
this._updateWithSkip(() => {
proxy[key] = next;
this._onChange?.(key);
this._onChange?.(key, true);
});
});
const subscription = _onDispose.subscribe(() => {

View File

@@ -62,7 +62,7 @@ export class ReactiveYArray extends BaseReactiveYData<
if (this._stashed.has(index)) {
const result = Reflect.set(target, p, value, receiver);
this._options.onChange?.(this._proxy);
this._options.onChange?.(this._proxy, true);
return result;
}
@@ -196,7 +196,7 @@ export class ReactiveYMap extends BaseReactiveYData<UnRecord, YMap<unknown>> {
if (this._stashed.has(p)) {
const result = Reflect.set(target, p, value, receiver);
this._options.onChange?.(this._proxy);
this._options.onChange?.(this._proxy, true);
return result;
}

View File

@@ -13,7 +13,7 @@ export type DeltaOperation = {
retain?: number;
} & OptionalAttributes;
export type OnTextChange = (data: Y.Text) => void;
export type OnTextChange = (data: Y.Text, isLocal: boolean) => void;
export class Text {
private readonly _deltas$: Signal<DeltaOperation[]>;
@@ -67,10 +67,17 @@ export class Text {
this._length$ = signal(length);
this._deltas$ = signal(this._yText.doc ? this._yText.toDelta() : []);
this._yText.observe(() => {
this._yText.observe(event => {
const isLocal =
!event.transaction.origin ||
!this._yText.doc ||
event.transaction.origin instanceof Y.UndoManager ||
event.transaction.origin.proxy
? true
: event.transaction.origin === this._yText.doc.clientID;
this._length$.value = this._yText.length;
this._deltas$.value = this._yText.toDelta();
this._onChange?.(this._yText);
this._onChange?.(this._yText, isLocal);
});
}

View File

@@ -15,5 +15,5 @@ export type TransformOptions = {
};
export type ProxyOptions<T> = {
onChange?: (data: T) => void;
onChange?: (data: T, isLocal: boolean) => void;
};