update
This commit is contained in:
172
src/BeatStore.ts
172
src/BeatStore.ts
@@ -1,6 +1,7 @@
|
||||
import { Beat, deserialise as deserialiseBeat, type BeatManager } from "@/Beat";
|
||||
import { inject, computed, type InjectionKey, onScopeDispose, ref, shallowRef, triggerRef, watch, nextTick } from "vue";
|
||||
import { inject, computed, type InjectionKey, onScopeDispose, ref, shallowRef, triggerRef, watch, nextTick, readonly, provide, getCurrentInstance } from "vue";
|
||||
import { z } from "zod";
|
||||
import { Bound } from "./utils";
|
||||
|
||||
export const DrumSlayerSaveSchema = z.object({
|
||||
orientation: z.union([z.literal("horizontal"), z.literal("vertical")]),
|
||||
@@ -9,9 +10,8 @@ export const DrumSlayerSaveSchema = z.object({
|
||||
});
|
||||
export type DrumSlayerSave = z.infer<typeof DrumSlayerSaveSchema>;
|
||||
|
||||
function createDefaultMainBeatGroup(manager: BeatManager): Beat {
|
||||
function createDefaultMainBeatGroup(): Beat {
|
||||
const defaultSettings = {
|
||||
manager,
|
||||
barCount: 2,
|
||||
isLooping: false,
|
||||
timeSigUp: 8,
|
||||
@@ -24,117 +24,123 @@ function createDefaultMainBeatGroup(manager: BeatManager): Beat {
|
||||
return mainBeatGroup;
|
||||
}
|
||||
|
||||
export function createBeatStore() {
|
||||
const saveDirty = ref(false);
|
||||
export class BeatStore extends Bound {
|
||||
private saveDirtyGlobal = ref(false);
|
||||
beats = shallowRef<Beat[]>([ createDefaultMainBeatGroup() ]);
|
||||
saveDirty = computed(() => this.saveDirtyGlobal.value || this.beats.value.reduce((last, beat) => beat.saveDirty.value || last, false));
|
||||
activeBeatIndex = ref(0);
|
||||
activeBeat = computed<Beat | null>(() => this.beats.value[this.activeBeatIndex.value] ?? null);
|
||||
orientation = ref<"horizontal" | "vertical">("horizontal");
|
||||
|
||||
const manager = {
|
||||
notifyChange() {
|
||||
saveDirty.value = true;
|
||||
},
|
||||
};
|
||||
constructor() {
|
||||
super();
|
||||
watch([this.activeBeatIndex, this.orientation, this.beats], () => {
|
||||
this.saveDirtyGlobal.value = true;
|
||||
});
|
||||
|
||||
const beats = shallowRef<Beat[]>([ createDefaultMainBeatGroup(manager) ]);
|
||||
const activeBeatIndex = ref(0);
|
||||
const activeBeat = computed<Beat | null>(() => beats.value[activeBeatIndex.value] ?? null);
|
||||
const orientation = ref<"horizontal" | "vertical">("horizontal");
|
||||
const saveInterval = setInterval(() => this.saveDirtyGlobal.value && this.save("localStorage"), 5 * 60 * 1000);
|
||||
|
||||
function resetActiveBeat(): void {
|
||||
const current = activeBeat.value;
|
||||
beats.value[activeBeatIndex.value] = createDefaultMainBeatGroup(manager);
|
||||
onScopeDispose(() => clearInterval(saveInterval));
|
||||
|
||||
window.addEventListener("beforeunload", (e) => {
|
||||
if (this.saveDirty.value) {
|
||||
e.preventDefault();
|
||||
e.returnValue = true;
|
||||
}
|
||||
});
|
||||
|
||||
const savedItem = localStorage.getItem("drum-slayer-save");
|
||||
if (savedItem) {
|
||||
const serial = JSON.parse(savedItem);
|
||||
this.beats.value = [createDefaultMainBeatGroup()];
|
||||
this.loadFromSave(serial);
|
||||
}
|
||||
}
|
||||
|
||||
resetActiveBeat(): void {
|
||||
const current = this.activeBeat.value;
|
||||
this.beats.value[this.activeBeatIndex.value] = createDefaultMainBeatGroup();
|
||||
current?.destroy();
|
||||
triggerRef(beats);
|
||||
triggerRef(this.beats);
|
||||
}
|
||||
|
||||
function removeBeat(index: number): void {
|
||||
const beat = beats.value[index];
|
||||
beats.value.splice(index, 1);
|
||||
beat?.destroy();
|
||||
triggerRef(beats);
|
||||
removeBeat(index: number): void {
|
||||
const beat = this.beats.value[index];
|
||||
this.beats.value.splice(index, 1);
|
||||
if (this.activeBeatIndex.value === index) {
|
||||
this.activeBeatIndex.value = 0;
|
||||
}
|
||||
if (this.beats.value.length === 0) {
|
||||
this.addNewBeat();
|
||||
this.activeBeatIndex.value = 0;
|
||||
}
|
||||
triggerRef(this.beats);
|
||||
nextTick(() => {
|
||||
beat?.destroy();
|
||||
});
|
||||
}
|
||||
|
||||
function addNewBeat(): number {
|
||||
const newBeat = createDefaultMainBeatGroup(manager);
|
||||
const newIndex = beats.value.push(newBeat);
|
||||
triggerRef(beats);
|
||||
addNewBeat(config?: unknown): number {
|
||||
let newBeat: Beat | null = null;
|
||||
if (config) {
|
||||
newBeat = deserialiseBeat(config);
|
||||
if (!newBeat) {
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
newBeat = createDefaultMainBeatGroup();
|
||||
}
|
||||
const newIndex = this.beats.value.push(newBeat);
|
||||
triggerRef(this.beats);
|
||||
this.activeBeatIndex.value = newIndex - 1;
|
||||
return newIndex - 1;
|
||||
}
|
||||
|
||||
function save(destination: "localStorage"): void {
|
||||
save(destination: "localStorage"): void {
|
||||
if (destination === "localStorage") {
|
||||
const serials = beats.value.map(beat => beat.serialise());
|
||||
const serials = this.beats.value.map(beat => beat.serialise());
|
||||
localStorage.setItem("drum-slayer-save", JSON.stringify({
|
||||
beats: serials,
|
||||
activeBeatIndex: activeBeatIndex.value,
|
||||
orientation: orientation.value ?? "horizontal",
|
||||
activeBeatIndex: this.activeBeatIndex.value,
|
||||
orientation: this.orientation.value ?? "horizontal",
|
||||
} satisfies DrumSlayerSave));
|
||||
saveDirty.value = false;
|
||||
for (const beat of this.beats.value) {
|
||||
beat.saveDirty.value = false;
|
||||
}
|
||||
this.saveDirtyGlobal.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
function loadFromSave(source: unknown): void {
|
||||
beats.value.length = 0;
|
||||
loadFromSave(source: unknown): void {
|
||||
this.beats.value.length = 0;
|
||||
const parse = DrumSlayerSaveSchema.safeParse(source);
|
||||
if (parse.success) {
|
||||
parse.data.beats.forEach((beat: unknown) => {
|
||||
const deserialisedBeat = deserialiseBeat(beat, manager);
|
||||
const deserialisedBeat = deserialiseBeat(beat);
|
||||
if (deserialisedBeat) {
|
||||
beats.value.push(deserialisedBeat);
|
||||
this.beats.value.push(deserialisedBeat);
|
||||
}
|
||||
});
|
||||
activeBeatIndex.value = parse.data.activeBeatIndex;
|
||||
orientation.value = parse.data.orientation;
|
||||
this.activeBeatIndex.value = parse.data.activeBeatIndex;
|
||||
this.orientation.value = parse.data.orientation;
|
||||
}
|
||||
if (beats.value.length === 0) {
|
||||
resetActiveBeat();
|
||||
if (this.beats.value.length === 0) {
|
||||
this.resetActiveBeat();
|
||||
}
|
||||
nextTick(() => saveDirty.value = false);
|
||||
nextTick(() => this.saveDirtyGlobal.value = false);
|
||||
}
|
||||
|
||||
function bakeAll(): void {
|
||||
activeBeat.value?.bakeLoops();
|
||||
bakeAll(): void {
|
||||
this.activeBeat.value?.bakeLoops();
|
||||
}
|
||||
|
||||
watch([activeBeatIndex, orientation, beats], () => {
|
||||
saveDirty.value = true;
|
||||
});
|
||||
|
||||
const saveInterval = setInterval(() => saveDirty && save("localStorage"), 5 * 60 * 1000);
|
||||
|
||||
onScopeDispose(() => clearInterval(saveInterval));
|
||||
|
||||
window.addEventListener("beforeunload", (e) => {
|
||||
if (saveDirty.value) {
|
||||
e.preventDefault();
|
||||
e.returnValue = true;
|
||||
}
|
||||
});
|
||||
|
||||
const savedItem = localStorage.getItem("drum-slayer-save");
|
||||
if (savedItem) {
|
||||
const serial = JSON.parse(savedItem);
|
||||
beats.value = [createDefaultMainBeatGroup(manager)];
|
||||
loadFromSave(serial);
|
||||
}
|
||||
|
||||
return {
|
||||
beats,
|
||||
activeBeatIndex,
|
||||
activeBeat,
|
||||
saveDirty,
|
||||
orientation,
|
||||
|
||||
save,
|
||||
addNewBeat,
|
||||
removeBeat,
|
||||
resetActiveBeat,
|
||||
bakeAll,
|
||||
};
|
||||
}
|
||||
|
||||
export type BeatStore = ReturnType<typeof createBeatStore>;
|
||||
|
||||
export const BeatStoreKey = Symbol("BeatStore") as InjectionKey<BeatStore>;
|
||||
const BeatStoreKey = Symbol("BeatStore") as InjectionKey<BeatStore>;
|
||||
|
||||
export function useBeatStore(): BeatStore {
|
||||
return inject(BeatStoreKey, createBeatStore, true);
|
||||
return inject(BeatStoreKey, () => {
|
||||
const store = new BeatStore();
|
||||
getCurrentInstance()?.appContext?.app.provide(BeatStoreKey, store);
|
||||
return store;
|
||||
}, true);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user