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")
```
This commit is contained in:
Alexis Métaireau 2024-05-31 19:22:26 +02:00
parent 4e7ac23b53
commit 137cc21af2
5 changed files with 46 additions and 25 deletions

View file

@ -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)

View file

@ -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
)

View file

@ -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)
}
},

View file

@ -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()
},

View file

@ -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)