Skip to content

Commit 6ae8a46

Browse files
committed
feat(json-crdt): 🎸 buffer local transactions and allow to create transactions
1 parent d0036aa commit 6ae8a46

File tree

3 files changed

+37
-5
lines changed

3 files changed

+37
-5
lines changed

‎src/json-crdt/file/File.ts‎

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -79,16 +79,22 @@ export class File implements Printable {
7979
public sync(): (() => void) {
8080
const {model, log} = this;
8181
const api = model.api;
82+
const drain = () => {
83+
if (!api.builder.patch.ops.length) return;
84+
const patch = api.flush();
85+
this.log.push(patch);
86+
};
8287
const onPatchUnsubscribe = api.onPatch.listen((patch) => {
8388
log.push(patch);
8489
});
85-
const onLocalChangeUnsubscribe = api.onLocalChange.listen(() => {
86-
const patch = api.flush();
87-
if (patch.ops.length) this.log.push(patch);
88-
});
90+
const onLocalChangesUnsubscribe = api.onLocalChanges.listen(drain);
91+
const onBeforeTransactionUnsubscribe = api.onBeforeTransaction.listen(drain);
92+
const onTransactionUnsubscribe = api.onTransaction.listen(drain);
8993
return () => {
9094
onPatchUnsubscribe();
91-
onLocalChangeUnsubscribe();
95+
onLocalChangesUnsubscribe();
96+
onBeforeTransactionUnsubscribe();
97+
onTransactionUnsubscribe();
9298
};
9399
}
94100

‎src/json-crdt/file/__tests__/File.spec.ts‎

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,17 @@ describe('.sync()', () => {
130130
expect(file.log.replayToEnd().view()).toEqual({foo: 'bar', x: 1});
131131
});
132132

133+
test('processes local transactions', async () => {
134+
const {file, model} = setup({foo: 'bar'});
135+
file.sync();
136+
const logLength = file.log.patches.size();
137+
model.api.transaction(() => {
138+
model.api.obj([]).set({x: 1});
139+
model.api.obj([]).set({y: 2});
140+
});
141+
expect(file.log.patches.size()).toBe(logLength + 1);
142+
});
143+
133144
test('keeps track of remote changes', async () => {
134145
const {file, model} = setup({foo: 'bar'});
135146
const clone = model.clone();

‎src/json-crdt/model/api/ModelApi.ts‎

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,9 @@ export class ModelApi<N extends JsonNode = JsonNode> implements SyncStore<JsonNo
3939
public readonly onBeforeLocalChange = new FanOut<number>();
4040
/** Emitted after local changes through `model.api` are applied. */
4141
public readonly onLocalChange = new FanOut<number>();
42+
/** Emitted after local changes through `model.api` are applied. Same as
43+
* `.onLocalChange`, but this event buffered withing a microtask. */
44+
public readonly onLocalChanges = new MicrotaskBufferFanOut<number>(this.onLocalChange);
4245
/** Emitted when the model changes. Combines `onReset`, `onPatch` and `onLocalChange`. */
4346
public readonly onChange = new MergeFanOut<number | Patch | void>([this.onReset, this.onPatch, this.onLocalChange]);
4447
/** Emitted when the model changes. Same as `.onChange`, but this event is emitted once per microtask. */
@@ -249,6 +252,7 @@ export class ModelApi<N extends JsonNode = JsonNode> implements SyncStore<JsonNo
249252
* Flushes the builder and returns a patch.
250253
*
251254
* @returns A JSON CRDT patch.
255+
* @todo Make this return undefined if there are no operations in the builder.
252256
*/
253257
public flush(): Patch {
254258
const patch = this.builder.flush();
@@ -257,6 +261,17 @@ export class ModelApi<N extends JsonNode = JsonNode> implements SyncStore<JsonNo
257261
return patch;
258262
}
259263

264+
/** Emitted before a transaction is started. */
265+
public readonly onBeforeTransaction = new FanOut<void>();
266+
/** Emitted after transaction completes. */
267+
public readonly onTransaction = new FanOut<void>();
268+
269+
public transaction(callback: () => void) {
270+
this.onBeforeTransaction.emit();
271+
callback();
272+
this.onTransaction.emit();
273+
}
274+
260275
// ---------------------------------------------------------------- SyncStore
261276

262277
public readonly subscribe = (callback: () => void) => this.onChanges.listen(() => callback());

0 commit comments

Comments
 (0)