Skip to content

Commit

Permalink
fix(json-crdt): 🐛 do not fire view events when arr node view does not…
Browse files Browse the repository at this point in the history
… change
  • Loading branch information
streamich committed Feb 27, 2024
1 parent 7e99298 commit 312285f
Show file tree
Hide file tree
Showing 4 changed files with 47 additions and 12 deletions.
2 changes: 1 addition & 1 deletion src/json-crdt/model/api/NodeEvents.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export class NodeEvents<N extends JsonNode = JsonNode> implements SyncStore<Json

constructor(private readonly api: NodeApi<N>) {
this.onChanges = new MapFanOut(this.api.api.onChanges, this.getSnapshot);
this.onViewChanges = new OnNewFanOut(this.onChanges);
this.onViewChanges = new OnNewFanOut(this.onChanges, this.api.view());
}

/**
Expand Down
39 changes: 39 additions & 0 deletions src/json-crdt/model/api/__tests__/ArrayApi.spec.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import {konst} from '../../../../json-crdt-patch';
import {Model} from '../../Model';

test('can insert a value and delete all previous ones', () => {
Expand All @@ -21,3 +22,41 @@ test('.length()', () => {
const arr = doc.api.arr(['arr']);
expect(arr.length()).toBe(3);
});

describe('events', () => {
test('fires onViewChanges event on change', async () => {
const doc = Model.withLogicalClock();
doc.api.root({
myArr: [1, 2, 3],
});
const events: any[] = [];
doc.api.arr(['myArr']).events.onViewChanges.listen(data => {
events.push(data);
});
expect(events.length).toBe(0);
doc.api.arr(['myArr']).del(1, 1);
await new Promise(r => setTimeout(r, 1));
expect(events.length).toBe(1);
});

test('does not fire onViewChanges event when resulting view is the same', async () => {
const doc = Model.withLogicalClock();
doc.api.root({
myArr: [1, 2, 3],
});
const events: any[] = [];
doc.api.arr(['myArr']).events.onViewChanges.listen(data => {
events.push(data);
});
await new Promise(r => setTimeout(r, 1));
expect(events.length).toBe(0);
doc.api.arr(['myArr']).del(1, 1);
doc.api.arr(['myArr']).ins(1, [konst(2)]);
await new Promise(r => setTimeout(r, 1));
expect(events.length).toBe(0);
doc.api.arr(['myArr']).del(1, 1);
doc.api.arr(['myArr']).ins(1, [2]);
await new Promise(r => setTimeout(r, 1));
expect(events.length).toBe(0);
});
});
3 changes: 1 addition & 2 deletions src/json-crdt/model/api/fanout.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,10 +94,9 @@ export class MapFanOut<I, O> extends FanOut<O> {
* emitted immediately.
*/
export class OnNewFanOut<D> extends FanOut<D> {
private last: D | undefined = undefined;
private unsub?: FanOutUnsubscribe = undefined;

constructor(private readonly source: FanOut<D>) {
constructor(private readonly source: FanOut<D>, private last: D | undefined = undefined) {
super();
}

Expand Down
15 changes: 6 additions & 9 deletions src/json-crdt/nodes/arr/ArrNode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,6 @@ import type {Printable} from '../../../util/print/types';

type E = ITimestampStruct;

const Empty = [] as any[];

/**
* @ignore
* @category CRDT Node
Expand Down Expand Up @@ -120,9 +118,7 @@ export class ArrNode<Element extends JsonNode = JsonNode>
}

/** @ignore */
protected onChange(): void {
this._view = Empty as any;
}
protected onChange(): void {}

protected toStringName(): string {
return this.name();
Expand All @@ -143,13 +139,13 @@ export class ArrNode<Element extends JsonNode = JsonNode>
/** @ignore */
private _tick: number = 0;
/** @ignore */
private _view = Empty;
private _view: unknown[] = [];
public view(): JsonNodeView<Element>[] {
const doc = this.doc;
const tick = doc.clock.time + doc.tick;
const _view = this._view;
if (this._tick === tick) return _view;
const view = [] as JsonNodeView<Element>[];
if (this._tick === tick) return _view as JsonNodeView<Element>[];
const view = [] as unknown[];
const index = doc.index;
let useCache = true;
for (let chunk = this.first(); chunk; chunk = this.next(chunk)) {
Expand All @@ -166,7 +162,8 @@ export class ArrNode<Element extends JsonNode = JsonNode>
}
}
if (_view.length !== view.length) useCache = false;
return useCache ? _view : ((this._tick = tick), (this._view = view));
const result = useCache ? _view : ((this._tick = tick), (this._view = view));
return result as JsonNodeView<Element>[];
}

/** @ignore */
Expand Down

0 comments on commit 312285f

Please sign in to comment.