Files
todotree/src/stores/RuntimeTodoStore.ts
T

66 lines
2.2 KiB
TypeScript

import { makeAutoObservable, runInAction } from "mobx";
import type { RuntimeTodo, Todo } from "../types";
import { buildTodoTree, filterTodoTree } from "../utils/todoTree";
import { store } from "./RootStore";
export class RuntimeTodoStore {
items: RuntimeTodo[] = [];
dirty: boolean = false;
private saveTimer: number | undefined;
constructor() {
makeAutoObservable(this);
}
public setFromServer(items: Todo[]) {
this.items = buildTodoTree(items);
}
public modifyTodo(item: RuntimeTodo, mutation: Partial<Todo>) {
runInAction(() => {
Object.assign(item, mutation);
const serverItem = store.server.getItemByUid(item.uid);
const keys = Object.keys(mutation) as (keyof Todo)[];
item.dirty = !serverItem || keys.some((key) => item[key] !== serverItem[key]);
this.setDirty();
});
}
private setDirty(value: boolean = true) {
this.dirty = value;
if (this.saveTimer)
clearTimeout(this.saveTimer);
this.saveTimer = setTimeout(this.saveToServer.bind(this), 3000);
}
public async saveToServer(): Promise<void> {
if (this.saveTimer)
clearTimeout(this.saveTimer);
const dirtyItems = Array.from(filterTodoTree(this.items, (item) => item.dirty));
if (dirtyItems.length === 0) {
return;
}
// Snapshot what we're about to send: if a todo gets edited again
// while its save is in flight, the response no longer reflects its
// current state, so it must stay dirty (setDirty() already queued a
// fresh save for it).
const summarySnapshot = new Map(dirtyItems.map((item) => [item.uid, item.summary]));
const savedUids = new Set(await store.server.save(dirtyItems));
runInAction(() => {
for (const item of dirtyItems) {
if (savedUids.has(item.uid) && item.summary === summarySnapshot.get(item.uid)) {
item.dirty = false;
}
}
this.dirty = Array.from(filterTodoTree(this.items, (item) => item.dirty)).length > 0;
});
}
}