mirror of
https://github.com/umap-project/umap.git
synced 2025-04-29 03:42:37 +02:00
feat(sync): sync features and map properties
Synced objects now expose different methods, such as: - `getSyncEngine` which returns the location of the sync object. - `getMetadata` which returns the associated metadata with the object. Hooks have been added when features are created or changed, so the changes can be synced with other peers.
This commit is contained in:
parent
f255c3c8a5
commit
698c926997
6 changed files with 67 additions and 7 deletions
|
@ -53,7 +53,7 @@ export class MessagesDispatcher {
|
|||
|
||||
dispatch({ kind, payload }) {
|
||||
console.log(kind, payload)
|
||||
if (kind == 'sync-protocol') {
|
||||
if (kind == 'operation') {
|
||||
let updater = this.getUpdater(payload.subject, payload.metadata)
|
||||
updater.applyMessage(payload)
|
||||
}
|
||||
|
@ -74,7 +74,7 @@ export class MessagesSender {
|
|||
}
|
||||
|
||||
send(message) {
|
||||
this._transport.send('sync-protocol', message)
|
||||
this._transport.send('operation', message)
|
||||
}
|
||||
|
||||
upsert(subject, metadata, value) {
|
||||
|
|
|
@ -1263,6 +1263,7 @@ U.Editable = L.Editable.extend({
|
|||
this.on('editable:drawing:commit', function (e) {
|
||||
e.layer.isDirty = true
|
||||
if (this.map.editedFeature !== e.layer) e.layer.edit(e)
|
||||
e.layer.onCommit()
|
||||
})
|
||||
this.on('editable:editing', (e) => {
|
||||
const layer = e.layer
|
||||
|
|
|
@ -1,6 +1,44 @@
|
|||
U.FeatureMixin = {
|
||||
staticOptions: { mainColor: 'color' },
|
||||
|
||||
getSyncMetadata: function () {
|
||||
return {
|
||||
subject: 'feature',
|
||||
metadata: {
|
||||
id: this.id,
|
||||
layerId: this.datalayer?.id || null,
|
||||
featureType: this.getClassName(),
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
getSyncEngine: function () {
|
||||
// FIXME use a get property / defineProperty
|
||||
return this.map.sync
|
||||
},
|
||||
|
||||
onCommit: function () {
|
||||
this.map.sync.upsert(this.getSyncSubject(), this.getSyncMetadata(), {
|
||||
geometry: this.getGeometry(),
|
||||
})
|
||||
},
|
||||
|
||||
getGeometry: function () {
|
||||
return this.toGeoJSON().geometry
|
||||
},
|
||||
|
||||
syncUpdatedProperties: function (properties) {
|
||||
if ('latlng'.includes(properties)) {
|
||||
const { subject, metadata } = this.getSyncMetadata()
|
||||
this.map.sync.update(subject, metadata, 'geometry', this.getGeometry())
|
||||
}
|
||||
},
|
||||
|
||||
syncDelete: function () {
|
||||
let { subject, metadata } = this.getSyncMetadata()
|
||||
this.map.sync.delete(subject, metadata)
|
||||
},
|
||||
|
||||
initialize: function (map, latlng, options) {
|
||||
this.map = map
|
||||
if (typeof options === 'undefined') {
|
||||
|
@ -241,12 +279,15 @@ U.FeatureMixin = {
|
|||
return false
|
||||
},
|
||||
|
||||
del: function () {
|
||||
del: function (fromSync) {
|
||||
this.isDirty = true
|
||||
this.map.closePopup()
|
||||
if (this.datalayer) {
|
||||
this.datalayer.removeLayer(this)
|
||||
this.disconnectFromDataLayer(this.datalayer)
|
||||
|
||||
// Do not relay the event if we received it.
|
||||
if (!fromSync) this.syncDelete()
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -602,6 +643,7 @@ U.Marker = L.Marker.extend({
|
|||
function (e) {
|
||||
this.isDirty = true
|
||||
this.edit(e)
|
||||
this.syncUpdatedProperties(['latlng'])
|
||||
},
|
||||
this
|
||||
)
|
||||
|
|
|
@ -1186,6 +1186,9 @@ U.FormBuilder = L.FormBuilder.extend({
|
|||
L.FormBuilder.prototype.setter.call(this, field, value)
|
||||
this.obj.isDirty = true
|
||||
if ('render' in this.obj) this.obj.render([field], this)
|
||||
const syncEngine = this.obj.getSyncEngine()
|
||||
const { subject, metadata } = this.obj.getSyncMetadata()
|
||||
syncEngine.update(subject, metadata, field, value)
|
||||
},
|
||||
|
||||
finish: function () {
|
||||
|
|
|
@ -249,10 +249,10 @@ U.Map = L.Map.extend({
|
|||
this.initContextMenu()
|
||||
this.on('click contextmenu.show', this.closeInplaceToolbar)
|
||||
|
||||
Promise.resolve(this.getSyncEngine())
|
||||
Promise.resolve(this.initSyncEngine())
|
||||
},
|
||||
|
||||
getSyncEngine: async function () {
|
||||
initSyncEngine: async function () {
|
||||
// Get the authentication token from the server
|
||||
// And pass it to the sync engine.
|
||||
const [response, _, error] = await this.server.get('/map/2/ws-token/')
|
||||
|
@ -261,6 +261,16 @@ U.Map = L.Map.extend({
|
|||
}
|
||||
},
|
||||
|
||||
getSyncMetadata: function () {
|
||||
return {
|
||||
subject: 'map',
|
||||
}
|
||||
},
|
||||
|
||||
getSyncEngine: function () {
|
||||
return this.sync
|
||||
},
|
||||
|
||||
render: function (fields) {
|
||||
let impacts = U.Utils.getImpactsFromSchema(fields)
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
import asyncio
|
||||
from collections import defaultdict
|
||||
from typing import Literal
|
||||
from typing import Literal, Optional
|
||||
|
||||
import django
|
||||
import websockets
|
||||
|
@ -32,11 +32,15 @@ class JoinMessage(BaseModel):
|
|||
token: str
|
||||
|
||||
|
||||
# FIXME better define the different messages
|
||||
# to ensure only relying valid ones.
|
||||
class OperationMessage(BaseModel):
|
||||
kind: str = "operation"
|
||||
verb: str = Literal["upsert", "update", "delete"]
|
||||
subject: str = Literal["map", "layer", "feature"]
|
||||
metadata: dict
|
||||
data: dict
|
||||
key: str
|
||||
value: Optional[str]
|
||||
|
||||
|
||||
async def join_and_listen(
|
||||
|
|
Loading…
Reference in a new issue