mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-02-27 02:42:25 +08:00
refactor(editor): separate yjs subscribe logic of flat model (#10863)
This commit is contained in:
@@ -4,19 +4,13 @@ import { Array as YArray, type Map as YMap, type YMapEvent } from 'yjs';
|
|||||||
|
|
||||||
import { BaseReactiveYData } from '../base-reactive-data';
|
import { BaseReactiveYData } from '../base-reactive-data';
|
||||||
import { Boxed, type OnBoxedChange } from '../boxed';
|
import { Boxed, type OnBoxedChange } from '../boxed';
|
||||||
import { y2Native } from '../native-y';
|
|
||||||
import { ReactiveYArray } from '../proxy';
|
import { ReactiveYArray } from '../proxy';
|
||||||
import { type OnTextChange, Text } from '../text';
|
import { type OnTextChange, Text } from '../text';
|
||||||
import type { ProxyOptions, UnRecord } from '../types';
|
import type { ProxyOptions, UnRecord } from '../types';
|
||||||
import { initializeData } from './initialize';
|
import { initializeData } from './initialize';
|
||||||
import { createProxy } from './proxy';
|
import { createProxy } from './proxy';
|
||||||
import type { OnChange } from './types';
|
import type { OnChange } from './types';
|
||||||
import {
|
import { getYEventHandler } from './y-event-handler';
|
||||||
deleteEmptyObject,
|
|
||||||
getFirstKey,
|
|
||||||
isEmptyObject,
|
|
||||||
keyWithoutPrefix,
|
|
||||||
} from './utils';
|
|
||||||
|
|
||||||
export class ReactiveFlatYMap extends BaseReactiveYData<
|
export class ReactiveFlatYMap extends BaseReactiveYData<
|
||||||
UnRecord,
|
UnRecord,
|
||||||
@@ -32,72 +26,14 @@ export class ReactiveFlatYMap extends BaseReactiveYData<
|
|||||||
const yMap = this._ySource;
|
const yMap = this._ySource;
|
||||||
const proxy = this._proxy;
|
const proxy = this._proxy;
|
||||||
this._onObserve(event, () => {
|
this._onObserve(event, () => {
|
||||||
event.keysChanged.forEach(key => {
|
getYEventHandler({
|
||||||
const type = event.changes.keys.get(key);
|
yMap,
|
||||||
if (!type) {
|
proxy,
|
||||||
return;
|
stashed: this._stashed,
|
||||||
}
|
updateWithYjsSkip: this._updateWithYjsSkip,
|
||||||
if (type.action === 'update' || type.action === 'add') {
|
transform: this._transform,
|
||||||
const value = yMap.get(key);
|
onChange: this._onChange,
|
||||||
const keyName: string = keyWithoutPrefix(key);
|
event,
|
||||||
const firstKey = getFirstKey(keyName);
|
|
||||||
if (this._stashed.has(firstKey)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this._updateWithYjsSkip(() => {
|
|
||||||
const keys = keyName.split('.');
|
|
||||||
void keys.reduce((acc, key, index, arr) => {
|
|
||||||
if (!acc[key] && index !== arr.length - 1) {
|
|
||||||
acc[key] = {};
|
|
||||||
}
|
|
||||||
if (index === arr.length - 1) {
|
|
||||||
acc[key] = y2Native(value, {
|
|
||||||
transform: (value, origin) => {
|
|
||||||
return this._transform(firstKey, value, origin);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return acc[key] as UnRecord;
|
|
||||||
}, proxy as UnRecord);
|
|
||||||
});
|
|
||||||
this._onChange?.(firstKey, false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (type.action === 'delete') {
|
|
||||||
const keyName: string = keyWithoutPrefix(key);
|
|
||||||
const firstKey = getFirstKey(keyName);
|
|
||||||
if (this._stashed.has(firstKey)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this._updateWithYjsSkip(() => {
|
|
||||||
const keys = keyName.split('.');
|
|
||||||
void keys.reduce((acc, key, index) => {
|
|
||||||
if (index === keys.length - 1) {
|
|
||||||
delete acc[key];
|
|
||||||
let curr = acc;
|
|
||||||
let parentKey = keys[index - 1];
|
|
||||||
let parent = proxy as UnRecord;
|
|
||||||
let path = keys.slice(0, -2);
|
|
||||||
|
|
||||||
for (let i = keys.length - 2; i > 0; i--) {
|
|
||||||
for (const pathKey of path) {
|
|
||||||
parent = parent[pathKey] as UnRecord;
|
|
||||||
}
|
|
||||||
if (!isEmptyObject(curr)) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
deleteEmptyObject(curr, parentKey, parent);
|
|
||||||
curr = parent;
|
|
||||||
parentKey = keys[i - 1];
|
|
||||||
path = path.slice(0, -1);
|
|
||||||
parent = proxy as UnRecord;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return acc[key] as UnRecord;
|
|
||||||
}, proxy as UnRecord);
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -0,0 +1,117 @@
|
|||||||
|
import type { Map as YMap, YMapEvent } from 'yjs';
|
||||||
|
|
||||||
|
import { y2Native } from '../native-y';
|
||||||
|
import type { UnRecord } from '../types';
|
||||||
|
import {
|
||||||
|
deleteEmptyObject,
|
||||||
|
getFirstKey,
|
||||||
|
isEmptyObject,
|
||||||
|
keyWithoutPrefix,
|
||||||
|
} from './utils';
|
||||||
|
|
||||||
|
type YEventHandlerOptions = {
|
||||||
|
event: YMapEvent<unknown>;
|
||||||
|
yMap: YMap<unknown>;
|
||||||
|
proxy: UnRecord;
|
||||||
|
stashed: Set<string | number>;
|
||||||
|
updateWithYjsSkip: (fn: () => void) => void;
|
||||||
|
transform: (key: string, value: unknown, origin: unknown) => unknown;
|
||||||
|
onChange?: (key: string, isAdd: boolean) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
// update proxy when yjs map changes
|
||||||
|
export const getYEventHandler = (options: YEventHandlerOptions) => {
|
||||||
|
const { event } = options;
|
||||||
|
const { keysChanged, changes } = event;
|
||||||
|
|
||||||
|
keysChanged.forEach(key => {
|
||||||
|
const type = changes.keys.get(key);
|
||||||
|
if (!type) return;
|
||||||
|
|
||||||
|
if (type.action === 'update' || type.action === 'add') {
|
||||||
|
return handleUpdateOrAdd(key, options);
|
||||||
|
}
|
||||||
|
if (type.action === 'delete') {
|
||||||
|
return handleDelete(key, options);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
function isStashed(key: string, stashed: Set<string | number>) {
|
||||||
|
const keyName: string = keyWithoutPrefix(key);
|
||||||
|
const firstKey = getFirstKey(keyName);
|
||||||
|
return stashed.has(firstKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleUpdateOrAdd(
|
||||||
|
key: string,
|
||||||
|
{
|
||||||
|
yMap,
|
||||||
|
proxy,
|
||||||
|
stashed,
|
||||||
|
updateWithYjsSkip,
|
||||||
|
transform,
|
||||||
|
onChange,
|
||||||
|
}: YEventHandlerOptions
|
||||||
|
) {
|
||||||
|
if (isStashed(key, stashed)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const keyName: string = keyWithoutPrefix(key);
|
||||||
|
const firstKey = getFirstKey(keyName);
|
||||||
|
updateWithYjsSkip(() => {
|
||||||
|
const value = yMap.get(key);
|
||||||
|
const keys = keyName.split('.');
|
||||||
|
void keys.reduce((acc, key, index, arr) => {
|
||||||
|
if (!acc[key] && index !== arr.length - 1) {
|
||||||
|
acc[key] = {};
|
||||||
|
}
|
||||||
|
if (index === arr.length - 1) {
|
||||||
|
acc[key] = y2Native(value, {
|
||||||
|
transform: (value, origin) => transform(firstKey, value, origin),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return acc[key] as UnRecord;
|
||||||
|
}, proxy as UnRecord);
|
||||||
|
});
|
||||||
|
onChange?.(firstKey, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleDelete(
|
||||||
|
key: string,
|
||||||
|
{ proxy, stashed, updateWithYjsSkip, onChange }: YEventHandlerOptions
|
||||||
|
) {
|
||||||
|
if (isStashed(key, stashed)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const keyName: string = keyWithoutPrefix(key);
|
||||||
|
const firstKey = getFirstKey(keyName);
|
||||||
|
updateWithYjsSkip(() => {
|
||||||
|
const keys = keyName.split('.');
|
||||||
|
void keys.reduce((acc, key, index) => {
|
||||||
|
if (index === keys.length - 1) {
|
||||||
|
delete acc[key];
|
||||||
|
let curr = acc;
|
||||||
|
let parentKey = keys[index - 1];
|
||||||
|
let parent = proxy as UnRecord;
|
||||||
|
let path = keys.slice(0, -2);
|
||||||
|
|
||||||
|
for (let i = keys.length - 2; i > 0; i--) {
|
||||||
|
for (const pathKey of path) {
|
||||||
|
parent = parent[pathKey] as UnRecord;
|
||||||
|
}
|
||||||
|
if (!isEmptyObject(curr)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
deleteEmptyObject(curr, parentKey, parent);
|
||||||
|
curr = parent;
|
||||||
|
parentKey = keys[i - 1];
|
||||||
|
path = path.slice(0, -1);
|
||||||
|
parent = proxy as UnRecord;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return acc[key] as UnRecord;
|
||||||
|
}, proxy as UnRecord);
|
||||||
|
});
|
||||||
|
onChange?.(firstKey, false);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user