feat(sync): sync layer's properties.

This also changes the interface between the synced classes and the sync
engine.

The sync engines only requires two methods now:

- `getSyncMetadata()` which returns all the metadata needed, including
the `engine`.
- `render()` which renders the object, updating the needed parts.
This commit is contained in:
Alexis Métaireau 2024-04-29 18:09:47 +02:00
parent 66105127cb
commit b956101d5d
6 changed files with 29 additions and 25 deletions

View file

@ -41,7 +41,7 @@ export class MapUpdater extends BaseUpdater {
update({ key, value }) { update({ key, value }) {
console.log(key, value) console.log(key, value)
this.updateObjectValue(this.map, key, value) this.updateObjectValue(this.map, key, value)
this.map.render([key.replace('options.', '')]) this.map.render([key])
} }
} }
@ -50,8 +50,7 @@ export class DatalayerUpdater extends BaseUpdater {
const datalayer = this.getLayerFromID(metadata.id) const datalayer = this.getLayerFromID(metadata.id)
console.log(datalayer, key, value) console.log(datalayer, key, value)
this.updateObjectValue(datalayer, key, value) this.updateObjectValue(datalayer, key, value)
const property = key.replace('options.', '') datalayer.render([key])
datalayer.render([property])
} }
} }

View file

@ -3,6 +3,7 @@ U.FeatureMixin = {
getSyncMetadata: function () { getSyncMetadata: function () {
return { return {
engine: this.map.sync,
subject: 'feature', subject: 'feature',
metadata: { metadata: {
id: this.id, id: this.id,
@ -12,14 +13,9 @@ U.FeatureMixin = {
} }
}, },
getSyncEngine: function () {
// FIXME use a get property / defineProperty
return this.map.sync
},
onCommit: function () { onCommit: function () {
const { subject, metadata } = this.getSyncMetadata() const { subject, metadata, engine } = this.getSyncMetadata()
this.map.sync.upsert(subject, metadata, { engine.upsert(subject, metadata, {
geometry: this.getGeometry(), geometry: this.getGeometry(),
}) })
}, },
@ -30,14 +26,14 @@ U.FeatureMixin = {
syncUpdatedProperties: function (properties) { syncUpdatedProperties: function (properties) {
if ('latlng'.includes(properties)) { if ('latlng'.includes(properties)) {
const { subject, metadata } = this.getSyncMetadata() const { subject, metadata, engine } = this.getSyncMetadata()
this.map.sync.update(subject, metadata, 'geometry', this.getGeometry()) engine.update(subject, metadata, 'geometry', this.getGeometry())
} }
}, },
syncDelete: function () { syncDelete: function () {
let { subject, metadata } = this.getSyncMetadata() let { subject, metadata, engine } = this.getSyncMetadata()
this.map.sync.delete(subject, metadata) engine.delete(subject, metadata)
}, },
initialize: function (map, latlng, options, id) { initialize: function (map, latlng, options, id) {

View file

@ -1186,9 +1186,10 @@ U.FormBuilder = L.FormBuilder.extend({
L.FormBuilder.prototype.setter.call(this, field, value) L.FormBuilder.prototype.setter.call(this, field, value)
this.obj.isDirty = true this.obj.isDirty = true
if ('render' in this.obj) this.obj.render([field], this) if ('render' in this.obj) this.obj.render([field], this)
const syncEngine = this.obj.getSyncEngine() console.log('setter', field, value)
const { subject, metadata } = this.obj.getSyncMetadata() const { subject, metadata, engine } = this.obj.getSyncMetadata()
syncEngine.update(subject, metadata, field, value) console.log('metadata', metadata)
engine.update(subject, metadata, field, value)
}, },
finish: function () { finish: function () {

View file

@ -266,14 +266,11 @@ U.Map = L.Map.extend({
getSyncMetadata: function () { getSyncMetadata: function () {
return { return {
engine: this.sync,
subject: 'map', subject: 'map',
} }
}, },
getSyncEngine: function () {
return this.sync
},
render: function (fields) { render: function (fields) {
let impacts = U.Utils.getImpactsFromSchema(fields) let impacts = U.Utils.getImpactsFromSchema(fields)

View file

@ -597,6 +597,16 @@ U.DataLayer = L.Evented.extend({
this.on('datachanged', this.map.onDataLayersChanged, this.map) this.on('datachanged', this.map.onDataLayersChanged, this.map)
}, },
getSyncMetadata: function () {
return {
engine: this.map.sync,
subject: 'datalayer',
metadata: {
id: this.umap_id, // XXX: should we specify it's the layerID here?
},
}
},
render: function (fields, builder) { render: function (fields, builder) {
let impacts = U.Utils.getImpactsFromSchema(fields) let impacts = U.Utils.getImpactsFromSchema(fields)

View file

@ -33,7 +33,7 @@ class JoinMessage(BaseModel):
class Geometry(BaseModel): class Geometry(BaseModel):
type: Literal["Point",] type: Literal["Point", "Polygon"]
coordinates: list coordinates: list
@ -43,13 +43,15 @@ class GeometryValue(BaseModel):
# FIXME better define the different messages # FIXME better define the different messages
# to ensure only relying valid ones. # to ensure only relying valid ones.
# This would mean having different kind of validation types
# based on the kind and verb.
class OperationMessage(BaseModel): class OperationMessage(BaseModel):
kind: str = "operation" kind: str = "operation"
verb: str = Literal["upsert", "update", "delete"] verb: str = Literal["upsert", "update", "delete"]
subject: str = Literal["map", "layer", "feature"] subject: str = Literal["map", "layer", "feature"]
metadata: Optional[dict] = None metadata: Optional[dict] = None
key: Optional[str] = None key: Optional[str] = None
value: Optional[str | bool | int | GeometryValue] value: Optional[str | bool | int | GeometryValue] = None
async def join_and_listen( async def join_and_listen(
@ -90,8 +92,7 @@ async def handler(websocket):
signed = TimestampSigner().unsign_object(message.token, max_age=30) signed = TimestampSigner().unsign_object(message.token, max_age=30)
user, map_id, permissions = signed.values() user, map_id, permissions = signed.values()
# We trust the signed info from the server to give access # Check if permissions for this map have been granted by the server
# If the user can edit the map, let her in
if "edit" in signed["permissions"]: if "edit" in signed["permissions"]:
await join_and_listen(map_id, permissions, user, websocket) await join_and_listen(map_id, permissions, user, websocket)