mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-02-12 04:18:54 +00:00
refactor(editor): move history from doc to store (#12131)
<!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **New Features** - Undo/redo history management is now centralized in the workspace, providing more consistent and robust undo/redo behavior. - History update events are emitted at the workspace level, enabling better tracking of changes. - **Bug Fixes** - Improved reliability of undo/redo actions by shifting history management from documents to the workspace. - **Documentation** - Updated and clarified documentation for history-related APIs, including improved examples and clearer descriptions. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
@@ -9,7 +9,6 @@ import {
|
||||
type Workspace,
|
||||
type YBlock,
|
||||
} from '@blocksuite/affine/store';
|
||||
import { signal } from '@preact/signals-core';
|
||||
import { Subject } from 'rxjs';
|
||||
import { Awareness } from 'y-protocols/awareness.js';
|
||||
import * as Y from 'yjs';
|
||||
@@ -21,10 +20,6 @@ type DocOptions = {
|
||||
};
|
||||
|
||||
export class DocImpl implements Doc {
|
||||
private readonly _canRedo = signal(false);
|
||||
|
||||
private readonly _canUndo = signal(false);
|
||||
|
||||
private readonly _collection: Workspace;
|
||||
|
||||
private readonly _storeMap = new Map<string, Store>();
|
||||
@@ -34,13 +29,6 @@ export class DocImpl implements Doc {
|
||||
events.forEach(event => this._handleYEvent(event));
|
||||
};
|
||||
|
||||
private _history!: Y.UndoManager;
|
||||
|
||||
private readonly _historyObserver = () => {
|
||||
this._updateCanUndoRedoSignals();
|
||||
this.slots.historyUpdated.next();
|
||||
};
|
||||
|
||||
private readonly _initSubDoc = () => {
|
||||
{
|
||||
// This is a piece of old version compatible code. The old version relies on the subdoc instance on `spaces`.
|
||||
@@ -55,9 +43,7 @@ export class DocImpl implements Doc {
|
||||
}
|
||||
}
|
||||
|
||||
const spaceDoc = new Y.Doc({
|
||||
guid: this.id,
|
||||
});
|
||||
const spaceDoc = new Y.Doc({ guid: this.id });
|
||||
spaceDoc.clientID = this.rootDoc.clientID;
|
||||
this._loaded = false;
|
||||
|
||||
@@ -72,19 +58,6 @@ export class DocImpl implements Doc {
|
||||
/** Indicate whether the block tree is ready */
|
||||
private _ready = false;
|
||||
|
||||
private _shouldTransact = true;
|
||||
|
||||
private readonly _updateCanUndoRedoSignals = () => {
|
||||
const canRedo = this._history.canRedo();
|
||||
const canUndo = this._history.canUndo();
|
||||
if (this._canRedo.peek() !== canRedo) {
|
||||
this._canRedo.value = canRedo;
|
||||
}
|
||||
if (this._canUndo.peek() !== canUndo) {
|
||||
this._canUndo.value = canUndo;
|
||||
}
|
||||
};
|
||||
|
||||
protected readonly _yBlocks: Y.Map<YBlock>;
|
||||
|
||||
/**
|
||||
@@ -102,8 +75,6 @@ export class DocImpl implements Doc {
|
||||
readonly rootDoc: Y.Doc;
|
||||
|
||||
readonly slots = {
|
||||
// eslint-disable-next-line rxjs/finnish
|
||||
historyUpdated: new Subject<void>(),
|
||||
// eslint-disable-next-line rxjs/finnish
|
||||
yBlockUpdated: new Subject<
|
||||
| {
|
||||
@@ -123,22 +94,10 @@ export class DocImpl implements Doc {
|
||||
return this.workspace.blobSync;
|
||||
}
|
||||
|
||||
get canRedo() {
|
||||
return this._canRedo.peek();
|
||||
}
|
||||
|
||||
get canUndo() {
|
||||
return this._canUndo.peek();
|
||||
}
|
||||
|
||||
get workspace() {
|
||||
return this._collection;
|
||||
}
|
||||
|
||||
get history() {
|
||||
return this._history;
|
||||
}
|
||||
|
||||
get isEmpty() {
|
||||
return this._yBlocks.size === 0;
|
||||
}
|
||||
@@ -217,19 +176,6 @@ export class DocImpl implements Doc {
|
||||
private _initYBlocks() {
|
||||
const { _yBlocks } = this;
|
||||
_yBlocks.observeDeep(this._handleYEvents);
|
||||
this._history = new Y.UndoManager([_yBlocks], {
|
||||
trackedOrigins: new Set([this._ySpaceDoc.clientID]),
|
||||
});
|
||||
|
||||
this._history.on('stack-cleared', this._historyObserver);
|
||||
this._history.on('stack-item-added', this._historyObserver);
|
||||
this._history.on('stack-item-popped', this._historyObserver);
|
||||
this._history.on('stack-item-updated', this._historyObserver);
|
||||
}
|
||||
|
||||
/** Capture current operations to undo stack synchronously. */
|
||||
captureSync() {
|
||||
this._history.stopCapturing();
|
||||
}
|
||||
|
||||
clear() {
|
||||
@@ -250,7 +196,6 @@ export class DocImpl implements Doc {
|
||||
|
||||
dispose() {
|
||||
this._destroy();
|
||||
this.slots.historyUpdated.unsubscribe();
|
||||
|
||||
if (this.ready) {
|
||||
this._yBlocks.unobserveDeep(this._handleYEvents);
|
||||
@@ -335,45 +280,8 @@ export class DocImpl implements Doc {
|
||||
return this;
|
||||
}
|
||||
|
||||
redo() {
|
||||
this._history.redo();
|
||||
}
|
||||
|
||||
undo() {
|
||||
this._history.undo();
|
||||
}
|
||||
|
||||
remove() {
|
||||
this._destroy();
|
||||
this.rootDoc.getMap('spaces').delete(this.id);
|
||||
}
|
||||
|
||||
resetHistory() {
|
||||
this._history.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* If `shouldTransact` is `false`, the transaction will not be push to the history stack.
|
||||
*/
|
||||
transact(fn: () => void, shouldTransact: boolean = this._shouldTransact) {
|
||||
this._ySpaceDoc.transact(
|
||||
() => {
|
||||
try {
|
||||
fn();
|
||||
} catch (e) {
|
||||
console.error(
|
||||
`An error occurred while Y.doc ${this._ySpaceDoc.guid} transacting:`
|
||||
);
|
||||
console.error(e);
|
||||
}
|
||||
},
|
||||
shouldTransact ? this.rootDoc.clientID : null
|
||||
);
|
||||
}
|
||||
|
||||
withoutTransact(callback: () => void) {
|
||||
this._shouldTransact = false;
|
||||
callback();
|
||||
this._shouldTransact = true;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user