66 lines
2.2 KiB
TypeScript
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;
|
|
});
|
|
}
|
|
}
|