mirror of
https://github.com/laurent22/joplin.git
synced 2024-12-12 08:54:00 +02:00
104 lines
2.2 KiB
TypeScript
104 lines
2.2 KiB
TypeScript
|
import AsyncActionQueue from '../AsyncActionQueue';
|
||
|
const EventEmitter = require('events');
|
||
|
|
||
|
class UndoQueue {
|
||
|
|
||
|
private inner_:any[] = [];
|
||
|
private size_:number = 20;
|
||
|
|
||
|
pop() {
|
||
|
return this.inner_.pop();
|
||
|
}
|
||
|
|
||
|
push(e:any) {
|
||
|
this.inner_.push(e);
|
||
|
while (this.length > this.size_) {
|
||
|
this.inner_.splice(0,1);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
get length():number {
|
||
|
return this.inner_.length;
|
||
|
}
|
||
|
|
||
|
at(index:number):any {
|
||
|
return this.inner_[index];
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
export default class UndoRedoService {
|
||
|
|
||
|
private pushAsyncQueue:AsyncActionQueue = new AsyncActionQueue(700);
|
||
|
private undoStates:UndoQueue = new UndoQueue();
|
||
|
private redoStates:UndoQueue = new UndoQueue();
|
||
|
private eventEmitter:any = new EventEmitter();
|
||
|
private isUndoing:boolean = false;
|
||
|
|
||
|
constructor() {
|
||
|
this.push = this.push.bind(this);
|
||
|
}
|
||
|
|
||
|
on(eventName:string, callback:Function) {
|
||
|
return this.eventEmitter.on(eventName, callback);
|
||
|
}
|
||
|
|
||
|
off(eventName:string, callback:Function) {
|
||
|
return this.eventEmitter.removeListener(eventName, callback);
|
||
|
}
|
||
|
|
||
|
push(state:any) {
|
||
|
this.undoStates.push(state);
|
||
|
this.redoStates = new UndoQueue();
|
||
|
this.eventEmitter.emit('stackChange');
|
||
|
}
|
||
|
|
||
|
schedulePush(state:any) {
|
||
|
this.pushAsyncQueue.push(async () => {
|
||
|
this.push(state);
|
||
|
});
|
||
|
}
|
||
|
|
||
|
async undo(redoState:any) {
|
||
|
if (this.isUndoing) return;
|
||
|
if (!this.canUndo) throw new Error('Nothing to undo');
|
||
|
this.isUndoing = true;
|
||
|
await this.pushAsyncQueue.processAllNow();
|
||
|
const state = this.undoStates.pop();
|
||
|
this.redoStates.push(redoState);
|
||
|
this.eventEmitter.emit('stackChange');
|
||
|
this.isUndoing = false;
|
||
|
return state;
|
||
|
}
|
||
|
|
||
|
async redo(undoState:any) {
|
||
|
if (this.isUndoing) return;
|
||
|
if (!this.canRedo) throw new Error('Nothing to redo');
|
||
|
this.isUndoing = true;
|
||
|
await this.pushAsyncQueue.processAllNow();
|
||
|
const state = this.redoStates.pop();
|
||
|
this.undoStates.push(undoState);
|
||
|
this.eventEmitter.emit('stackChange');
|
||
|
this.isUndoing = false;
|
||
|
return state;
|
||
|
}
|
||
|
|
||
|
async reset() {
|
||
|
this.undoStates = new UndoQueue();
|
||
|
this.redoStates = new UndoQueue();
|
||
|
this.isUndoing = false;
|
||
|
const output = this.pushAsyncQueue.reset();
|
||
|
this.eventEmitter.emit('stackChange');
|
||
|
return output;
|
||
|
}
|
||
|
|
||
|
get canUndo():boolean {
|
||
|
return !!this.undoStates.length;
|
||
|
}
|
||
|
|
||
|
get canRedo():boolean {
|
||
|
return !!this.redoStates.length;
|
||
|
}
|
||
|
|
||
|
}
|