mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-07-16 09:36:17 +08:00
refactor(editor): history as a store extension (#12214)
<!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **Refactor** - Improved history and undo/redo management across the app by introducing a dedicated history extension. Undo/redo operations now use a more focused undo manager, resulting in clearer and more consistent behavior. - **Documentation** - Updated API documentation to reflect changes in history management, including revised method signatures and removal of outdated event references. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
@@ -9,6 +9,7 @@ import type { ExtensionType } from '../../extension/extension.js';
|
||||
import {
|
||||
BlockSchemaIdentifier,
|
||||
type Doc,
|
||||
HistoryExtension,
|
||||
StoreExtensionIdentifier,
|
||||
StoreSelectionExtension,
|
||||
} from '../../extension/index.js';
|
||||
@@ -163,10 +164,6 @@ export type StoreSlots = {
|
||||
*
|
||||
*/
|
||||
blockUpdated: Subject<StoreBlockUpdatedPayloads>;
|
||||
/**
|
||||
* This fires when the history is updated.
|
||||
*/
|
||||
historyUpdated: Subject<void>;
|
||||
/** @internal */
|
||||
yBlockUpdated: Subject<
|
||||
| {
|
||||
@@ -182,7 +179,7 @@ export type StoreSlots = {
|
||||
>;
|
||||
};
|
||||
|
||||
const internalExtensions = [StoreSelectionExtension];
|
||||
const internalExtensions = [StoreSelectionExtension, HistoryExtension];
|
||||
|
||||
/**
|
||||
* Core store class that manages blocks and their lifecycle in BlockSuite
|
||||
@@ -231,10 +228,6 @@ export class Store {
|
||||
|
||||
private readonly _schema: Schema;
|
||||
|
||||
private readonly _canRedo = signal(false);
|
||||
|
||||
private readonly _canUndo = signal(false);
|
||||
|
||||
/**
|
||||
* Get the id of the store.
|
||||
*
|
||||
@@ -310,7 +303,7 @@ export class Store {
|
||||
if (this.readonly) {
|
||||
return false;
|
||||
}
|
||||
return this._canRedo.peek();
|
||||
return this._history.canRedo;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -322,7 +315,7 @@ export class Store {
|
||||
if (this.readonly) {
|
||||
return false;
|
||||
}
|
||||
return this._canUndo.peek();
|
||||
return this._history.canUndo;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -330,35 +323,35 @@ export class Store {
|
||||
*
|
||||
* @category History
|
||||
*/
|
||||
undo() {
|
||||
undo = () => {
|
||||
if (this.readonly) {
|
||||
console.error('cannot undo in readonly mode');
|
||||
return;
|
||||
}
|
||||
this._history.undo();
|
||||
}
|
||||
this._history.undoManager.undo();
|
||||
};
|
||||
|
||||
/**
|
||||
* Redo the last undone transaction.
|
||||
*
|
||||
* @category History
|
||||
*/
|
||||
redo() {
|
||||
redo = () => {
|
||||
if (this.readonly) {
|
||||
console.error('cannot undo in readonly mode');
|
||||
return;
|
||||
}
|
||||
this._history.redo();
|
||||
}
|
||||
this._history.undoManager.redo();
|
||||
};
|
||||
|
||||
/**
|
||||
* Reset the history of the store.
|
||||
*
|
||||
* @category History
|
||||
*/
|
||||
resetHistory() {
|
||||
return this._history.clear();
|
||||
}
|
||||
resetHistory = () => {
|
||||
return this._history.undoManager.clear();
|
||||
};
|
||||
|
||||
/**
|
||||
* Execute a transaction.
|
||||
@@ -374,7 +367,7 @@ export class Store {
|
||||
* @category History
|
||||
*/
|
||||
transact(fn: () => void, shouldTransact: boolean = this._shouldTransact) {
|
||||
const spaceDoc = this.doc.spaceDoc;
|
||||
const spaceDoc = this._doc.spaceDoc;
|
||||
spaceDoc.transact(
|
||||
() => {
|
||||
try {
|
||||
@@ -425,9 +418,9 @@ export class Store {
|
||||
*
|
||||
* @category History
|
||||
*/
|
||||
captureSync() {
|
||||
this._history.stopCapturing();
|
||||
}
|
||||
captureSync = () => {
|
||||
this._history.undoManager.stopCapturing();
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the {@link Workspace} instance for current store.
|
||||
@@ -555,7 +548,9 @@ export class Store {
|
||||
|
||||
private _isDisposed = false;
|
||||
|
||||
private readonly _history!: Y.UndoManager;
|
||||
private get _history() {
|
||||
return this._provider.get(HistoryExtension);
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
@@ -568,7 +563,6 @@ export class Store {
|
||||
rootAdded: new Subject(),
|
||||
rootDeleted: new Subject(),
|
||||
blockUpdated: new Subject(),
|
||||
historyUpdated: new Subject(),
|
||||
yBlockUpdated: new Subject(),
|
||||
};
|
||||
this._schema = new Schema();
|
||||
@@ -609,35 +603,9 @@ export class Store {
|
||||
this._onBlockAdded(id, false, true);
|
||||
});
|
||||
|
||||
this._history = new Y.UndoManager([this._yBlocks], {
|
||||
trackedOrigins: new Set([this.doc.spaceDoc.clientID]),
|
||||
});
|
||||
|
||||
this._updateCanUndoRedoSignals();
|
||||
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);
|
||||
|
||||
this._subscribeToSlots();
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
};
|
||||
|
||||
private readonly _historyObserver = () => {
|
||||
this._updateCanUndoRedoSignals();
|
||||
this.slots.historyUpdated.next();
|
||||
};
|
||||
|
||||
private readonly _subscribeToSlots = () => {
|
||||
this.disposableGroup.add(
|
||||
this.slots.yBlockUpdated.subscribe(({ type, id, isLocal }) => {
|
||||
@@ -1270,14 +1238,13 @@ export class Store {
|
||||
this._provider.getAll(StoreExtensionIdentifier).forEach(ext => {
|
||||
ext.disposed();
|
||||
});
|
||||
if (this.doc.ready) {
|
||||
if (this._doc.ready) {
|
||||
this._yBlocks.unobserveDeep(this._handleYEvents);
|
||||
}
|
||||
this.slots.ready.complete();
|
||||
this.slots.rootAdded.complete();
|
||||
this.slots.rootDeleted.complete();
|
||||
this.slots.blockUpdated.complete();
|
||||
this.slots.historyUpdated.complete();
|
||||
this.slots.yBlockUpdated.complete();
|
||||
this.disposableGroup.dispose();
|
||||
this._isDisposed = true;
|
||||
|
||||
Reference in New Issue
Block a user