Skip to content

Commit c72bae3

Browse files
committed
chore(json-crdt): 🤖 progress on history implementation
1 parent 86ea93b commit c72bae3

File tree

2 files changed

+86
-2
lines changed

2 files changed

+86
-2
lines changed

src/json-crdt/history/LocalHistoryCrud.ts

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,8 +52,18 @@ export class LocalHistoryCrud implements LocalHistory {
5252
throw new Error('Method not implemented.');
5353
}
5454

55-
public update(collection: string[], id: string, patches: Patch[]): Promise<void> {
56-
throw new Error('Method not implemented.');
55+
public async update(collection: string[], id: string, patches: Patch[]): Promise<void> {
56+
await this.lock(collection, id, async () => {
57+
const blob = await this.crud.get([...collection, id], STATE_FILE_NAME);
58+
const {log} = File.fromSeqCbor(blob);
59+
log.end.applyBatch(patches);
60+
const file = new File(log.end, log, this.fileOpts);
61+
const blob2 = file.toBinary({
62+
format: "seq.cbor",
63+
model: 'binary',
64+
});
65+
await this.crud.put([...collection, id], STATE_FILE_NAME, blob2, {throwIf: 'missing'});
66+
});
5767
}
5868

5969
public async delete(collection: string[], id: string): Promise<void> {
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
import {createRace} from 'thingies/es2020/createRace';
2+
import {FanOutUnsubscribe} from 'thingies/es2020/fanout';
3+
import {InsValOp, Patch} from "../../json-crdt-patch";
4+
import {ValNode} from "../nodes";
5+
import {toSchema} from "../schema/toSchema";
6+
import {PatchLog} from "./PatchLog";
7+
import {RedoItem, UndoItem, UndoRedoStack} from "./UndoRedoStack";
8+
9+
class Undo implements UndoItem {
10+
constructor (public readonly undo: () => Redo) {}
11+
}
12+
13+
class Redo implements RedoItem {
14+
constructor (public readonly redo: () => Undo) {}
15+
}
16+
17+
export class SessionHistory {
18+
constructor (
19+
public readonly log: PatchLog,
20+
) {}
21+
22+
private readonly __onPatchRace = createRace();
23+
24+
public attachUndoRedo(stack: UndoRedoStack): FanOutUnsubscribe {
25+
const onBeforePatch = (patch: Patch) => {
26+
this.__onPatchRace(() => {
27+
const undo = this.createUndo(patch);
28+
stack.push(undo);
29+
});
30+
};
31+
const unsubscribe = this.log.end.api.onBeforePatch.listen(onBeforePatch);
32+
return unsubscribe;
33+
}
34+
35+
public createUndo(patch: Patch): Undo {
36+
const undoTasks: Array<() => void> = [];
37+
const ops = patch.ops;
38+
const length = ops.length;
39+
for (let i = length - 1; i >= 0; i--) {
40+
const op = ops[i];
41+
switch (op.name()) {
42+
case 'ins_val': {
43+
const insOp = op as InsValOp;
44+
const valNode = this.log.end.index.get(insOp.obj);
45+
if (!(valNode instanceof ValNode)) throw new Error('INVALID_NODE');
46+
const copy = toSchema(valNode.node());
47+
const valNodeId = valNode.id;
48+
const task = () => {
49+
const end = this.log.end;
50+
const valNode = end.index.get(valNodeId);
51+
if (!valNode) return;
52+
end.api.wrap(valNode).asVal().set(copy);
53+
};
54+
undoTasks.push(task);
55+
}
56+
}
57+
}
58+
const undo = new Undo(() => {
59+
this.__onPatchRace(() => {
60+
for (const task of undoTasks) task();
61+
});
62+
return new Redo(() => {
63+
const undo = this.__onPatchRace(() => {
64+
// TODO: This line needs to be changed:
65+
const redoPatch = Patch.fromBinary(patch.toBinary());
66+
this.log.end.api.builder.patch = redoPatch;
67+
return this.createUndo(redoPatch);
68+
});
69+
return undo!;
70+
});
71+
});
72+
return undo;
73+
}
74+
}

0 commit comments

Comments
 (0)