From de93750a12fda96f53b93d7e2bc1f7ee0d4ee463 Mon Sep 17 00:00:00 2001 From: Jessie <2302541+Kangaroux@users.noreply.github.com> Date: Wed, 13 Oct 2021 22:48:42 -0400 Subject: [PATCH 1/4] Create an ActionStore --- client/src/store/action.ts | 14 ++++++++++++++ client/src/store/root.ts | 3 +++ 2 files changed, 17 insertions(+) create mode 100644 client/src/store/action.ts diff --git a/client/src/store/action.ts b/client/src/store/action.ts new file mode 100644 index 0000000..f4c20d7 --- /dev/null +++ b/client/src/store/action.ts @@ -0,0 +1,14 @@ +import { Action } from "../actions/action"; +import { RootStore } from "./root"; + +export class ActionStore { + readonly root: RootStore; + + stack: Action[]; + + constructor(root: RootStore) { + this.root = root; + + this.stack = []; + } +} diff --git a/client/src/store/root.ts b/client/src/store/root.ts index 18c09d6..180c8f2 100644 --- a/client/src/store/root.ts +++ b/client/src/store/root.ts @@ -1,3 +1,4 @@ +import { ActionStore } from "./action"; import { NotefieldStore } from "./notefield"; import { NotefieldDisplayStore } from "./notefieldDisplay"; import { ProjectStore } from "./project"; @@ -8,6 +9,7 @@ import { WaveformStore } from "./waveform"; * The root store for the application that contains all of the application data. */ export class RootStore { + readonly actions: ActionStore; readonly notefieldDisplay: NotefieldDisplayStore; readonly notefield: NotefieldStore; readonly project: ProjectStore; @@ -15,6 +17,7 @@ export class RootStore { readonly ui: UIStore; constructor() { + this.actions = new ActionStore(this); this.ui = new UIStore(this); this.notefieldDisplay = new NotefieldDisplayStore(this); this.project = new ProjectStore(this); From 4e804a44cb5d30f89ea4a64e3bad31f40e47f159 Mon Sep 17 00:00:00 2001 From: Jessie <2302541+Kangaroux@users.noreply.github.com> Date: Wed, 13 Oct 2021 22:55:19 -0400 Subject: [PATCH 2/4] Implement undo/redo store --- client/src/actions/action.ts | 1 + client/src/store/action.ts | 46 ++++++++++++++++++++++++++++++++++-- 2 files changed, 45 insertions(+), 2 deletions(-) diff --git a/client/src/actions/action.ts b/client/src/actions/action.ts index 6a486c9..255d041 100644 --- a/client/src/actions/action.ts +++ b/client/src/actions/action.ts @@ -1,3 +1,4 @@ export interface Action { run(): void; + undo(): void; } diff --git a/client/src/store/action.ts b/client/src/store/action.ts index f4c20d7..64817fc 100644 --- a/client/src/store/action.ts +++ b/client/src/store/action.ts @@ -4,11 +4,53 @@ import { RootStore } from "./root"; export class ActionStore { readonly root: RootStore; - stack: Action[]; + stack: { + undo: Action[]; + redo: Action[]; + }; constructor(root: RootStore) { this.root = root; - this.stack = []; + this.stack = { + undo: [], + redo: [], + }; + } + + canUndo(): boolean { + return this.stack.undo.length > 0; + } + + canRedo(): boolean { + return this.stack.redo.length > 0; + } + + run(action: Action) { + action.run(); + this.stack.redo = []; + this.stack.undo.push(action); + } + + undo() { + const action = this.stack.undo.pop(); + + if (!action) { + return; + } + + action.undo(); + this.stack.redo.push(action); + } + + redo() { + const action = this.stack.redo.pop(); + + if (!action) { + return; + } + + action.run(); + this.stack.undo.push(action); } } From f5c18acc2041069ac59236814e4a2fc54770718f Mon Sep 17 00:00:00 2001 From: Jessie <2302541+Kangaroux@users.noreply.github.com> Date: Wed, 13 Oct 2021 23:04:09 -0400 Subject: [PATCH 3/4] Add undo methods to actions --- client/src/actions/notefield/placeTap.ts | 5 +++++ client/src/actions/notefield/playPause.ts | 4 ++++ client/src/actions/notefield/scroll.ts | 9 ++++++++- client/src/actions/notefield/scrollDirection.ts | 8 ++++++++ client/src/actions/notefield/snapAdjust.ts | 9 +++++++++ client/src/actions/notefield/snapScroll.ts | 9 +++++++++ client/src/actions/notefield/zoom.ts | 11 ++++++++++- 7 files changed, 53 insertions(+), 2 deletions(-) diff --git a/client/src/actions/notefield/placeTap.ts b/client/src/actions/notefield/placeTap.ts index 422ea08..b4783af 100644 --- a/client/src/actions/notefield/placeTap.ts +++ b/client/src/actions/notefield/placeTap.ts @@ -37,4 +37,9 @@ export class PlaceTapAction implements Action { removeIfExists: true, }); } + + undo(): void { + // FIXME: This won't work correctly if placing a note gets rid of a hold. + this.run(); + } } diff --git a/client/src/actions/notefield/playPause.ts b/client/src/actions/notefield/playPause.ts index 81d9cb8..0010a26 100644 --- a/client/src/actions/notefield/playPause.ts +++ b/client/src/actions/notefield/playPause.ts @@ -15,4 +15,8 @@ export class PlayPauseAction implements Action { const { notefield } = this.store; notefield.setPlaying(!notefield.data.isPlaying); } + + undo(): void { + this.run(); + } } diff --git a/client/src/actions/notefield/scroll.ts b/client/src/actions/notefield/scroll.ts index 74f4c90..950b266 100644 --- a/client/src/actions/notefield/scroll.ts +++ b/client/src/actions/notefield/scroll.ts @@ -1,6 +1,6 @@ import assert from "assert"; -import { BeatTime } from "../../charting"; +import { Beat, BeatTime } from "../../charting"; import { RootStore } from "../../store"; import { Action } from "../action"; @@ -17,6 +17,8 @@ export interface ScrollArgs { */ export class ScrollAction implements Action { args: ScrollArgs; + oldScroll!: Beat; + store: RootStore; constructor(store: RootStore, args: ScrollArgs) { @@ -28,6 +30,7 @@ export class ScrollAction implements Action { run(): void { const args = this.args; + this.oldScroll = this.store.notefield.data.scroll.beat; if (args.by) { this.store.notefield.scrollBy(args.by); @@ -35,4 +38,8 @@ export class ScrollAction implements Action { this.store.notefield.setScroll(args.to); } } + + undo(): void { + this.store.notefield.setScroll({ beat: this.oldScroll }); + } } diff --git a/client/src/actions/notefield/scrollDirection.ts b/client/src/actions/notefield/scrollDirection.ts index 4db2b7b..ffa109d 100644 --- a/client/src/actions/notefield/scrollDirection.ts +++ b/client/src/actions/notefield/scrollDirection.ts @@ -13,6 +13,8 @@ export interface ScrollDirectionArgs { */ export class ScrollDirectionAction implements Action { args: ScrollDirectionArgs; + oldDirection!: ScrollDirection; + store: RootStore; constructor(store: RootStore, args: ScrollDirectionArgs) { @@ -24,6 +26,8 @@ export class ScrollDirectionAction implements Action { const { to } = this.args; const { data } = this.store.notefieldDisplay; + this.oldDirection = this.store.notefieldDisplay.data.scrollDirection; + if (to === "swap") { if (data.scrollDirection === "up") { this.store.notefieldDisplay.update({ scrollDirection: "down" }); @@ -34,4 +38,8 @@ export class ScrollDirectionAction implements Action { this.store.notefieldDisplay.update({ scrollDirection: to }); } } + + undo(): void { + this.store.notefieldDisplay.update({ scrollDirection: this.oldDirection }); + } } diff --git a/client/src/actions/notefield/snapAdjust.ts b/client/src/actions/notefield/snapAdjust.ts index bfb8c1c..42a2842 100644 --- a/client/src/actions/notefield/snapAdjust.ts +++ b/client/src/actions/notefield/snapAdjust.ts @@ -1,5 +1,6 @@ import assert from "assert"; import Fraction from "fraction.js"; +import { BeatSnap } from "../../notefield/beatsnap"; import { RootStore } from "../../store"; import { Action } from "../action"; @@ -17,6 +18,8 @@ export interface SnapAdjustArgs { */ export class SnapAdjustAction implements Action { args: SnapAdjustArgs; + oldSnap!: BeatSnap; + store: RootStore; constructor(store: RootStore, args: SnapAdjustArgs) { @@ -30,6 +33,8 @@ export class SnapAdjustAction implements Action { const args = this.args; const { snap } = this.store.notefield.data; + this.oldSnap = snap; + if (args.adjust === "next") { snap.nextSnap(); } else if (args.adjust === "prev") { @@ -38,4 +43,8 @@ export class SnapAdjustAction implements Action { snap.setSnap(args.to); } } + + undo(): void { + this.store.notefield.data.snap.setSnap(this.oldSnap.current); + } } diff --git a/client/src/actions/notefield/snapScroll.ts b/client/src/actions/notefield/snapScroll.ts index 5babfee..5001210 100644 --- a/client/src/actions/notefield/snapScroll.ts +++ b/client/src/actions/notefield/snapScroll.ts @@ -1,3 +1,4 @@ +import { Beat } from "../../charting"; import { RootStore } from "../../store"; import { Action } from "../action"; @@ -14,6 +15,8 @@ export interface SnapScrollArgs { */ export class SnapScrollAction implements Action { args: SnapScrollArgs; + oldScroll!: Beat; + store: RootStore; constructor(store: RootStore, args: SnapScrollArgs) { @@ -34,6 +37,8 @@ export class SnapScrollAction implements Action { return; } + this.oldScroll = notefield.data.scroll.beat; + const { scroll, snap } = notefield.data; let dir = this.args.direction; @@ -45,4 +50,8 @@ export class SnapScrollAction implements Action { this.store.notefield.setScroll({ beat }); } + + undo(): void { + this.store.notefield.setScroll({ beat: this.oldScroll }); + } } diff --git a/client/src/actions/notefield/zoom.ts b/client/src/actions/notefield/zoom.ts index be0aa61..e8d7575 100644 --- a/client/src/actions/notefield/zoom.ts +++ b/client/src/actions/notefield/zoom.ts @@ -16,6 +16,8 @@ export interface ZoomArgs { */ export class ZoomAction implements Action { args: ZoomArgs; + oldZoom!: Fraction; + store: RootStore; /** @@ -31,6 +33,13 @@ export class ZoomAction implements Action { } run(): void { - this.store.notefield.setZoom(this.args.to); + const { notefield } = this.store; + + this.oldZoom = notefield.data.zoom; + notefield.setZoom(this.args.to); + } + + undo(): void { + this.store.notefield.setZoom(this.oldZoom); } } From e17a1a898b6c8ab20bc44b095759cbb5950ee671 Mon Sep 17 00:00:00 2001 From: Jessie <2302541+Kangaroux@users.noreply.github.com> Date: Wed, 13 Oct 2021 23:06:05 -0400 Subject: [PATCH 4/4] Make undo action method optional --- client/src/actions/action.ts | 2 +- client/src/store/action.ts | 9 ++++++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/client/src/actions/action.ts b/client/src/actions/action.ts index 255d041..755ea1e 100644 --- a/client/src/actions/action.ts +++ b/client/src/actions/action.ts @@ -1,4 +1,4 @@ export interface Action { run(): void; - undo(): void; + undo?(): void; } diff --git a/client/src/store/action.ts b/client/src/store/action.ts index 64817fc..e9c17d3 100644 --- a/client/src/store/action.ts +++ b/client/src/store/action.ts @@ -28,8 +28,11 @@ export class ActionStore { run(action: Action) { action.run(); - this.stack.redo = []; - this.stack.undo.push(action); + + if (action.undo) { + this.stack.redo = []; + this.stack.undo.push(action); + } } undo() { @@ -39,7 +42,7 @@ export class ActionStore { return; } - action.undo(); + action.undo!(); this.stack.redo.push(action); }