From 137cc21af2b533adade2d06840c9050615292a22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexis=20M=C3=A9taireau?= Date: Fri, 31 May 2024 19:22:26 +0200 Subject: [PATCH] feat(sync): Add a proxy to the `SyncEngine` objects. It is now possible to create proxy objects using `sync_engine.proxy(object)`. The returned proxy object will automatically inject `metadata` and `subject` parameters, after looking for them in the `getSyncMetadata` method (these are only known to the synced objects). As a result, the calls are now simplified: ``` this.sync.update("key", "value") ``` --- umap/static/umap/js/modules/sync/engine.js | 31 +++++++++++++++++++++- umap/static/umap/js/umap.features.js | 12 ++++----- umap/static/umap/js/umap.forms.js | 9 ++++--- umap/static/umap/js/umap.js | 11 +++----- umap/static/umap/js/umap.layer.js | 8 +++--- 5 files changed, 46 insertions(+), 25 deletions(-) diff --git a/umap/static/umap/js/modules/sync/engine.js b/umap/static/umap/js/modules/sync/engine.js index 70cc974c..186615b6 100644 --- a/umap/static/umap/js/modules/sync/engine.js +++ b/umap/static/umap/js/modules/sync/engine.js @@ -33,6 +33,35 @@ export class SyncEngine { if (this.transport) this.transport.close() this._initialize() } + + /** + * Create a proxy for this sync engine. + * + * The proxy will automatically call `object.getSyncMetadata` and inject the returned + * `subject` and `metadata`` to the `upsert`, `update` and `delete` calls. + * + * The proxy can be used as follows: + * + * ``` + * const proxy = sync.proxy(object) + * proxy.update('key', 'value') + *``` + */ + proxy(object) { + const handler = { + get(target, prop) { + // Only proxy these methods + if (['upsert', 'update', 'delete'].includes(prop)) { + const { subject, metadata } = object.getSyncMetadata() + // Reflect.get is calling the original method. + // .bind is adding the parameters automatically + return Reflect.get(...arguments).bind(target, subject, metadata) + } + return Reflect.get(...arguments) + }, + } + return new Proxy(this, handler) + } } export class MessagesDispatcher { @@ -63,7 +92,7 @@ export class MessagesDispatcher { } /** - * Sends the message to the other party (using the specified transport): + * Send messages to other connected peers, using the specified transport. * * - `subject` is the type of object this is referering to (map, feature, layer) * - `metadata` contains information about the object we're refering to (id, layerId for instance) diff --git a/umap/static/umap/js/umap.features.js b/umap/static/umap/js/umap.features.js index f157b6d1..a4cf8f1d 100644 --- a/umap/static/umap/js/umap.features.js +++ b/umap/static/umap/js/umap.features.js @@ -3,7 +3,6 @@ U.FeatureMixin = { getSyncMetadata: function () { return { - engine: this.map.sync, subject: 'feature', metadata: { id: this.id, @@ -17,8 +16,7 @@ U.FeatureMixin = { // When the layer is a remote layer, we don't want to sync the creation of the // points via the websocket, as the other peers will get them themselves. if (this.datalayer.isRemoteLayer()) return - const { subject, metadata, engine } = this.getSyncMetadata() - engine.upsert(subject, metadata, this.toGeoJSON()) + this.sync.upsert(this.toGeoJSON()) }, getGeometry: function () { @@ -26,12 +24,13 @@ U.FeatureMixin = { }, syncDelete: function () { - let { subject, metadata, engine } = this.getSyncMetadata() - engine.delete(subject, metadata) + this.sync.delete() }, initialize: function (map, latlng, options, id) { this.map = map + this.sync = map.sync_engine.proxy(this) + if (typeof options === 'undefined') { options = {} } @@ -639,8 +638,7 @@ U.Marker = L.Marker.extend({ function (e) { this.isDirty = true this.edit(e) - const { subject, metadata, engine } = this.getSyncMetadata() - engine.update(subject, metadata, 'geometry', this.getGeometry()) + this.sync.update('geometry', this.getGeometry()) }, this ) diff --git a/umap/static/umap/js/umap.forms.js b/umap/static/umap/js/umap.forms.js index 04271a17..92c1cdfa 100644 --- a/umap/static/umap/js/umap.forms.js +++ b/umap/static/umap/js/umap.forms.js @@ -1185,10 +1185,11 @@ U.FormBuilder = L.FormBuilder.extend({ setter: function (field, value) { L.FormBuilder.prototype.setter.call(this, field, value) this.obj.isDirty = true - if ('render' in this.obj) this.obj.render([field], this) - if ('getSyncMetadata' in this.obj) { - const { subject, metadata, engine } = this.obj.getSyncMetadata() - if (engine) engine.update(subject, metadata, field, value) + if ('render' in this.obj) { + this.obj.render([field], this) + } + if ('sync' in this.obj) { + this.obj.sync.update(field, value) } }, diff --git a/umap/static/umap/js/umap.js b/umap/static/umap/js/umap.js index 66b2539c..cb717d05 100644 --- a/umap/static/umap/js/umap.js +++ b/umap/static/umap/js/umap.js @@ -33,7 +33,8 @@ U.Map = L.Map.extend({ includes: [ControlsMixin], initialize: function (el, geojson) { - this.sync = new U.SyncEngine(this) + this.sync_engine = new U.SyncEngine(this) + this.sync = this.sync_engine.proxy(this) // Locale name (pt_PT, en_US…) // To be used for Django localization if (geojson.properties.locale) L.setLocale(geojson.properties.locale) @@ -1440,13 +1441,7 @@ U.Map = L.Map.extend({ this.options.limitBounds.east = L.Util.formatNum(bounds.getEast()) boundsBuilder.fetchAll() - const { subject, metadata, engine } = this.getSyncMetadata() - engine.update( - subject, - metadata, - 'options.limitBounds', - this.options.limitBounds - ) + this.sync.update(this, 'options.limitBounds', this.options.limitBounds) this.isDirty = true this.handleLimitBounds() }, diff --git a/umap/static/umap/js/umap.layer.js b/umap/static/umap/js/umap.layer.js index f9c688e3..fcc7872a 100644 --- a/umap/static/umap/js/umap.layer.js +++ b/umap/static/umap/js/umap.layer.js @@ -521,8 +521,9 @@ U.DataLayer = L.Evented.extend({ editMode: 'advanced', }, - initialize: function (map, data, sync) { + initialize: function (map, data) { this.map = map + this.sync = map.sync_engine.proxy(this) this._index = Array() this._layers = {} this._geojson = null @@ -614,7 +615,6 @@ U.DataLayer = L.Evented.extend({ getSyncMetadata: function () { return { - engine: this.map.sync, subject: 'datalayer', metadata: { id: this.umap_id, @@ -1740,9 +1740,7 @@ U.DataLayer = L.Evented.extend({ delete data.geojson } this._reference_version = response.headers.get('X-Datalayer-Version') - - const { engine, subject, metadata } = this.getSyncMetadata() - engine.update(subject, metadata, '_reference_version', this._reference_version) + this.sync.update('_reference_version', this._reference_version) this.setUmapId(data.id) this.updateOptions(data)