Skip to content

Commit 012e75d

Browse files
committed
Emit conflict events asynchronously
This way, apps can use these the same way as they can use other change events.
1 parent 0482c78 commit 012e75d

File tree

3 files changed

+49
-24
lines changed

3 files changed

+49
-24
lines changed

src/baseclient.ts

Lines changed: 9 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -147,8 +147,8 @@ function getModuleNameFromBase(path: string): string {
147147
* during sync.
148148
*
149149
* > [!NOTE]
150-
* > Automatically receiving remote changes depends on the {@link caching!Caching} settings
151-
* > for your module/paths.
150+
* > Automatically receiving remote changes depends on the
151+
* > {@link caching!Caching caching} settings for your module/paths.
152152
*
153153
* ### `window`
154154
*
@@ -180,13 +180,13 @@ function getModuleNameFromBase(path: string): string {
180180
* }
181181
* ```
182182
*
183-
* But when this change is pushed out by asynchronous synchronization, this change
184-
* may be rejected by the server, if the remote version has in the meantime changed
185-
* from `white` to for instance `red`; this will then lead to a change event with
186-
* origin `conflict` (usually a few seconds after the event with origin `window`,
187-
* if you have those activated). Note that since you already changed it from
188-
* `white` to `blue` in the local version a few seconds ago, `oldValue` is now
189-
* your local value of `blue`:
183+
* However, when this change is pushed out by the sync process, it will be
184+
* rejected by the server, if the remote version has changed in the meantime,
185+
* for example from `white` to `red`. This will lead to a change event with
186+
* origin `conflict`, usually a few seconds after the event with origin
187+
* `window`. Note that since you already changed it from `white` to `blue` in
188+
* the local version a few seconds ago, `oldValue` is now your local value of
189+
* `blue`:
190190
*
191191
* ```js
192192
* {
@@ -212,11 +212,6 @@ function getModuleNameFromBase(path: string): string {
212212
*
213213
* If there is an algorithm to merge the differences between local and remote
214214
* versions of the data, conflicts may be automatically resolved.
215-
* {@link storeObject} or {@link storeFile} must not be called synchronously from
216-
* the change event handler, nor by chaining Promises. {@link storeObject} or
217-
* {@link storeFile} must not be called until the next iteration of the JavaScript
218-
* Task Queue, using for example
219-
* [`setTimeout()`](https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/setTimeout).
220215
*
221216
* If no algorithm exists, conflict resolution typically involves displaying local
222217
* and remote versions to the user, and having the user merge them, or choose

src/sync.ts

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -493,17 +493,15 @@ export class Sync {
493493
node = mergeMutualDeletion(node);
494494
delete node.remote;
495495
} else if (node.remote.body !== undefined) {
496-
// keep/revert:
497-
log('[Sync] Emitting keep/revert');
498-
499-
this.rs.local.emitChange({
500-
origin: 'conflict',
501-
path: node.path,
502-
oldValue: node.local.body,
503-
newValue: node.remote.body,
496+
log('[Sync] Emitting conflict event');
497+
setTimeout(this.rs.local.emitChange.bind(this.rs.local), 10, {
498+
origin: 'conflict',
499+
path: node.path,
500+
oldValue: node.local.body,
501+
newValue: node.remote.body,
504502
lastCommonValue: node.common.body,
505-
oldContentType: node.local.contentType,
506-
newContentType: node.remote.contentType,
503+
oldContentType: node.local.contentType,
504+
newContentType: node.remote.contentType,
507505
lastCommonContentType: node.common.contentType
508506
});
509507

test/unit/sync.test.mjs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -838,6 +838,38 @@ describe("Sync", function() {
838838
expect(this.rs.sync.autoMergeDocument(node)).to
839839
.deep.equal(localAndRemoteRemoved);
840840
});
841+
842+
describe("when node was also changed on remote", function() {
843+
beforeEach(function() {
844+
this.emitChange = sinon.spy(this.rs.local, "emitChange");
845+
846+
this.rs.sync.autoMergeDocument({
847+
path: "foo",
848+
common: { body: "foo", contentType: "bloo", revision: "common" },
849+
local: { body: "floo", contentType: "blaloo" },
850+
remote: { body: "florb", revision: "updated-elsewhere" }
851+
});
852+
});
853+
854+
it.only("asynchronously emits a conflict event", function(done) {
855+
expect(this.emitChange.called).to.be.false;
856+
857+
setTimeout(() => {
858+
expect(this.emitChange.called).to.be.true;
859+
expect(this.emitChange.getCall(0).firstArg).to.deep.equal({
860+
origin: 'conflict',
861+
path: 'foo',
862+
oldValue: 'floo',
863+
newValue: 'florb',
864+
lastCommonValue: 'foo',
865+
oldContentType: 'blaloo',
866+
newContentType: undefined,
867+
lastCommonContentType: 'bloo'
868+
});
869+
done();
870+
}, 20);
871+
});
872+
});
841873
});
842874

843875
describe("#autoMerge", function() {

0 commit comments

Comments
 (0)