mirror of
https://github.com/umap-project/umap.git
synced 2025-04-29 20:02:36 +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 }) {
|
dispatch({ kind, payload }) {
|
||||||
console.log(kind, payload)
|
console.log(kind, payload)
|
||||||
if (kind == 'sync-protocol') {
|
if (kind == 'operation') {
|
||||||
let updater = this.getUpdater(payload.subject, payload.metadata)
|
let updater = this.getUpdater(payload.subject, payload.metadata)
|
||||||
updater.applyMessage(payload)
|
updater.applyMessage(payload)
|
||||||
}
|
}
|
||||||
|
@ -74,7 +74,7 @@ export class MessagesSender {
|
||||||
}
|
}
|
||||||
|
|
||||||
send(message) {
|
send(message) {
|
||||||
this._transport.send('sync-protocol', message)
|
this._transport.send('operation', message)
|
||||||
}
|
}
|
||||||
|
|
||||||
upsert(subject, metadata, value) {
|
upsert(subject, metadata, value) {
|
||||||
|
|
|
@ -1263,6 +1263,7 @@ U.Editable = L.Editable.extend({
|
||||||
this.on('editable:drawing:commit', function (e) {
|
this.on('editable:drawing:commit', function (e) {
|
||||||
e.layer.isDirty = true
|
e.layer.isDirty = true
|
||||||
if (this.map.editedFeature !== e.layer) e.layer.edit(e)
|
if (this.map.editedFeature !== e.layer) e.layer.edit(e)
|
||||||
|
e.layer.onCommit()
|
||||||
})
|
})
|
||||||
this.on('editable:editing', (e) => {
|
this.on('editable:editing', (e) => {
|
||||||
const layer = e.layer
|
const layer = e.layer
|
||||||
|
|
|
@ -1,6 +1,44 @@
|
||||||
U.FeatureMixin = {
|
U.FeatureMixin = {
|
||||||
staticOptions: { mainColor: 'color' },
|
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) {
|
initialize: function (map, latlng, options) {
|
||||||
this.map = map
|
this.map = map
|
||||||
if (typeof options === 'undefined') {
|
if (typeof options === 'undefined') {
|
||||||
|
@ -241,12 +279,15 @@ U.FeatureMixin = {
|
||||||
return false
|
return false
|
||||||
},
|
},
|
||||||
|
|
||||||
del: function () {
|
del: function (fromSync) {
|
||||||
this.isDirty = true
|
this.isDirty = true
|
||||||
this.map.closePopup()
|
this.map.closePopup()
|
||||||
if (this.datalayer) {
|
if (this.datalayer) {
|
||||||
this.datalayer.removeLayer(this)
|
this.datalayer.removeLayer(this)
|
||||||
this.disconnectFromDataLayer(this.datalayer)
|
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) {
|
function (e) {
|
||||||
this.isDirty = true
|
this.isDirty = true
|
||||||
this.edit(e)
|
this.edit(e)
|
||||||
|
this.syncUpdatedProperties(['latlng'])
|
||||||
},
|
},
|
||||||
this
|
this
|
||||||
)
|
)
|
||||||
|
|
|
@ -1186,6 +1186,9 @@ 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()
|
||||||
|
const { subject, metadata } = this.obj.getSyncMetadata()
|
||||||
|
syncEngine.update(subject, metadata, field, value)
|
||||||
},
|
},
|
||||||
|
|
||||||
finish: function () {
|
finish: function () {
|
||||||
|
|
|
@ -249,10 +249,10 @@ U.Map = L.Map.extend({
|
||||||
this.initContextMenu()
|
this.initContextMenu()
|
||||||
this.on('click contextmenu.show', this.closeInplaceToolbar)
|
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
|
// Get the authentication token from the server
|
||||||
// And pass it to the sync engine.
|
// And pass it to the sync engine.
|
||||||
const [response, _, error] = await this.server.get('/map/2/ws-token/')
|
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) {
|
render: function (fields) {
|
||||||
let impacts = U.Utils.getImpactsFromSchema(fields)
|
let impacts = U.Utils.getImpactsFromSchema(fields)
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
from typing import Literal
|
from typing import Literal, Optional
|
||||||
|
|
||||||
import django
|
import django
|
||||||
import websockets
|
import websockets
|
||||||
|
@ -32,11 +32,15 @@ class JoinMessage(BaseModel):
|
||||||
token: str
|
token: str
|
||||||
|
|
||||||
|
|
||||||
|
# FIXME better define the different messages
|
||||||
|
# to ensure only relying valid ones.
|
||||||
class OperationMessage(BaseModel):
|
class OperationMessage(BaseModel):
|
||||||
kind: str = "operation"
|
kind: str = "operation"
|
||||||
|
verb: str = Literal["upsert", "update", "delete"]
|
||||||
subject: str = Literal["map", "layer", "feature"]
|
subject: str = Literal["map", "layer", "feature"]
|
||||||
metadata: dict
|
metadata: dict
|
||||||
data: dict
|
key: str
|
||||||
|
value: Optional[str]
|
||||||
|
|
||||||
|
|
||||||
async def join_and_listen(
|
async def join_and_listen(
|
||||||
|
|
Loading…
Reference in a new issue