mirror of
https://github.com/umap-project/umap.git
synced 2025-04-29 11:52:38 +02:00
feat(sync): Add a enableSync
option.
This changes how the syncEngine works. At the moment, it's always instanciated, even if no syncing is configured. It just does nothing. This is to avoid doing `if (engine) engine.update()` calls everywhere we use it. You now need to `start()` and `stop()` it.
This commit is contained in:
parent
9a74cc370c
commit
5e692d2280
8 changed files with 60 additions and 28 deletions
|
@ -1,8 +1,9 @@
|
||||||
import { translate } from './i18n.js'
|
import { translate } from './i18n.js'
|
||||||
|
|
||||||
// Possible impacts
|
// Possible impacts
|
||||||
// ['ui', 'data', 'limit-bounds', 'datalayer-index', 'remote-data', 'background']
|
// ['ui', 'data', 'limit-bounds', 'datalayer-index', 'remote-data', 'background' 'sync']
|
||||||
|
|
||||||
|
// This is sorted alphabetically
|
||||||
export const SCHEMA = {
|
export const SCHEMA = {
|
||||||
browsable: {
|
browsable: {
|
||||||
impacts: ['ui'],
|
impacts: ['ui'],
|
||||||
|
@ -14,6 +15,12 @@ export const SCHEMA = {
|
||||||
label: translate('Do you want to display a caption bar?'),
|
label: translate('Do you want to display a caption bar?'),
|
||||||
default: false,
|
default: false,
|
||||||
},
|
},
|
||||||
|
captionControl: {
|
||||||
|
type: Boolean,
|
||||||
|
nullable: true,
|
||||||
|
label: translate('Display the caption control'),
|
||||||
|
default: true,
|
||||||
|
},
|
||||||
captionMenus: {
|
captionMenus: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
impacts: ['ui'],
|
impacts: ['ui'],
|
||||||
|
@ -184,7 +191,6 @@ export const SCHEMA = {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
impacts: ['ui'],
|
impacts: ['ui'],
|
||||||
},
|
},
|
||||||
|
|
||||||
interactive: {
|
interactive: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
impacts: ['data'],
|
impacts: ['data'],
|
||||||
|
@ -373,12 +379,6 @@ export const SCHEMA = {
|
||||||
impacts: ['ui'],
|
impacts: ['ui'],
|
||||||
label: translate('Allow scroll wheel zoom?'),
|
label: translate('Allow scroll wheel zoom?'),
|
||||||
},
|
},
|
||||||
captionControl: {
|
|
||||||
type: Boolean,
|
|
||||||
nullable: true,
|
|
||||||
label: translate('Display the caption control'),
|
|
||||||
default: true,
|
|
||||||
},
|
|
||||||
searchControl: {
|
searchControl: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
impacts: ['ui'],
|
impacts: ['ui'],
|
||||||
|
@ -437,6 +437,13 @@ export const SCHEMA = {
|
||||||
inheritable: true,
|
inheritable: true,
|
||||||
default: true,
|
default: true,
|
||||||
},
|
},
|
||||||
|
syncEnabled: {
|
||||||
|
type: Boolean,
|
||||||
|
impacts: ['sync', 'ui'],
|
||||||
|
label: translate('Enable real-time collaboration'),
|
||||||
|
helpEntries: 'sync',
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
tilelayer: {
|
tilelayer: {
|
||||||
type: Object,
|
type: Object,
|
||||||
impacts: ['background'],
|
impacts: ['background'],
|
||||||
|
|
|
@ -8,16 +8,31 @@ import {
|
||||||
} from './updaters.js'
|
} from './updaters.js'
|
||||||
|
|
||||||
export class SyncEngine {
|
export class SyncEngine {
|
||||||
constructor(map, webSocketURI, authToken) {
|
constructor(map) {
|
||||||
this.map = map
|
this.map = map
|
||||||
this.receiver = new MessagesDispatcher(this.map)
|
this.receiver = new MessagesDispatcher(this.map)
|
||||||
|
|
||||||
|
this._initialize()
|
||||||
|
}
|
||||||
|
_initialize() {
|
||||||
|
this.transport = undefined
|
||||||
|
const noop = () => undefined
|
||||||
|
// by default, all operations do nothing, until the engine is started.
|
||||||
|
this.upsert = this.update = this.delete = noop
|
||||||
|
}
|
||||||
|
|
||||||
|
start(webSocketURI, authToken) {
|
||||||
this.transport = new WebSocketTransport(webSocketURI, authToken, this.receiver)
|
this.transport = new WebSocketTransport(webSocketURI, authToken, this.receiver)
|
||||||
this.sender = new MessagesSender(this.transport)
|
this.sender = new MessagesSender(this.transport)
|
||||||
|
|
||||||
this.upsert = this.sender.upsert.bind(this.sender)
|
this.upsert = this.sender.upsert.bind(this.sender)
|
||||||
this.update = this.sender.update.bind(this.sender)
|
this.update = this.sender.update.bind(this.sender)
|
||||||
this.delete = this.sender.delete.bind(this.sender)
|
this.delete = this.sender.delete.bind(this.sender)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
stop() {
|
||||||
|
if (this.transport) this.transport.close()
|
||||||
|
this._initialize()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class MessagesDispatcher {
|
export class MessagesDispatcher {
|
||||||
|
|
|
@ -19,4 +19,8 @@ export class WebSocketTransport {
|
||||||
let encoded = JSON.stringify(message)
|
let encoded = JSON.stringify(message)
|
||||||
this.websocket.send(encoded)
|
this.websocket.send(encoded)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
close() {
|
||||||
|
this.websocket.close()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1186,10 +1186,8 @@ 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)
|
||||||
console.log('setter', field, value)
|
|
||||||
const { subject, metadata, engine } = this.obj.getSyncMetadata()
|
const { subject, metadata, engine } = this.obj.getSyncMetadata()
|
||||||
console.log('metadata', metadata)
|
if (engine) engine.update(subject, metadata, field, value)
|
||||||
engine.update(subject, metadata, field, value)
|
|
||||||
},
|
},
|
||||||
|
|
||||||
finish: function () {
|
finish: function () {
|
||||||
|
|
|
@ -248,19 +248,25 @@ U.Map = L.Map.extend({
|
||||||
this.backup()
|
this.backup()
|
||||||
this.initContextMenu()
|
this.initContextMenu()
|
||||||
this.on('click contextmenu.show', this.closeInplaceToolbar)
|
this.on('click contextmenu.show', this.closeInplaceToolbar)
|
||||||
|
this.sync = new U.SyncEngine(this)
|
||||||
|
|
||||||
Promise.resolve(this.initSyncEngine())
|
Promise.resolve(this.initSyncEngine())
|
||||||
},
|
},
|
||||||
|
|
||||||
initSyncEngine: async function () {
|
initSyncEngine: async function () {
|
||||||
// Get the authentication token from the server
|
console.log('this.options.syncEnabled', this.options.syncEnabled)
|
||||||
// And pass it to the sync engine.
|
if (this.options.syncEnabled != true) {
|
||||||
// FIXME: use `this.urls`
|
this.sync.stop()
|
||||||
const [response, _, error] = await this.server.get(
|
} else {
|
||||||
`/map/${this.options.umap_id}/ws-token/`
|
// Get the authentication token from the server
|
||||||
)
|
// And pass it to the sync engine.
|
||||||
if (!error) {
|
// FIXME: use `this.urls`
|
||||||
this.sync = new U.SyncEngine(this, 'ws://localhost:8001', response.token)
|
const [response, _, error] = await this.server.get(
|
||||||
|
`/map/${this.options.umap_id}/ws-token/`
|
||||||
|
)
|
||||||
|
if (!error) {
|
||||||
|
this.sync.start('ws://localhost:8001', response.token)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -294,6 +300,8 @@ U.Map = L.Map.extend({
|
||||||
case 'bounds':
|
case 'bounds':
|
||||||
this.handleLimitBounds()
|
this.handleLimitBounds()
|
||||||
break
|
break
|
||||||
|
case 'sync':
|
||||||
|
this.initSyncEngine()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -1036,7 +1044,7 @@ U.Map = L.Map.extend({
|
||||||
formData.append('center', JSON.stringify(this.geometry()))
|
formData.append('center', JSON.stringify(this.geometry()))
|
||||||
formData.append('settings', JSON.stringify(geojson))
|
formData.append('settings', JSON.stringify(geojson))
|
||||||
const uri = this.urls.get('map_save', { map_id: this.options.umap_id })
|
const uri = this.urls.get('map_save', { map_id: this.options.umap_id })
|
||||||
const [data, response, error] = await this.server.post(uri, {}, formData)
|
const [data, _, error] = await this.server.post(uri, {}, formData)
|
||||||
// FIXME: login_required response will not be an error, so it will not
|
// FIXME: login_required response will not be an error, so it will not
|
||||||
// stop code while it should
|
// stop code while it should
|
||||||
if (!error) {
|
if (!error) {
|
||||||
|
@ -1538,7 +1546,7 @@ U.Map = L.Map.extend({
|
||||||
if (!this.editEnabled) return
|
if (!this.editEnabled) return
|
||||||
if (this.options.editMode !== 'advanced') return
|
if (this.options.editMode !== 'advanced') return
|
||||||
const container = L.DomUtil.create('div', 'umap-edit-container'),
|
const container = L.DomUtil.create('div', 'umap-edit-container'),
|
||||||
metadataFields = ['options.name', 'options.description'],
|
metadataFields = ['options.name', 'options.description', 'options.syncEnabled'],
|
||||||
title = L.DomUtil.create('h3', '', container)
|
title = L.DomUtil.create('h3', '', container)
|
||||||
title.textContent = L._('Edit map details')
|
title.textContent = L._('Edit map details')
|
||||||
const builder = new U.FormBuilder(this, metadataFields, {
|
const builder = new U.FormBuilder(this, metadataFields, {
|
||||||
|
|
|
@ -60,6 +60,9 @@ U.Layer.Cluster = L.MarkerClusterGroup.extend({
|
||||||
|
|
||||||
initialize: function (datalayer) {
|
initialize: function (datalayer) {
|
||||||
this.datalayer = datalayer
|
this.datalayer = datalayer
|
||||||
|
if (!U.Utils.isObject(this.datalayer.options.cluster)) {
|
||||||
|
this.datalayer.options.cluster = {}
|
||||||
|
}
|
||||||
const options = {
|
const options = {
|
||||||
polygonOptions: {
|
polygonOptions: {
|
||||||
color: this.datalayer.getColor(),
|
color: this.datalayer.getColor(),
|
||||||
|
@ -74,10 +77,6 @@ U.Layer.Cluster = L.MarkerClusterGroup.extend({
|
||||||
L.MarkerClusterGroup.prototype.initialize.call(this, options)
|
L.MarkerClusterGroup.prototype.initialize.call(this, options)
|
||||||
this._markerCluster = U.MarkerCluster
|
this._markerCluster = U.MarkerCluster
|
||||||
this._layers = []
|
this._layers = []
|
||||||
|
|
||||||
if (!U.Utils.isObject(this.datalayer.options.cluster)) {
|
|
||||||
this.datalayer.options.cluster = {}
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
onRemove: function (map) {
|
onRemove: function (map) {
|
||||||
|
|
|
@ -109,6 +109,7 @@ U.MapPermissions = L.Class.extend({
|
||||||
{ handler: 'ManageEditors', label: L._("Map's editors") },
|
{ handler: 'ManageEditors', label: L._("Map's editors") },
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
|
||||||
const builder = new U.FormBuilder(this, fields)
|
const builder = new U.FormBuilder(this, fields)
|
||||||
const form = builder.build()
|
const form = builder.build()
|
||||||
container.appendChild(form)
|
container.appendChild(form)
|
||||||
|
|
|
@ -51,7 +51,7 @@ class OperationMessage(BaseModel):
|
||||||
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] = None
|
value: Optional[str | bool | int | GeometryValue | Geometry] = None
|
||||||
|
|
||||||
|
|
||||||
async def join_and_listen(
|
async def join_and_listen(
|
||||||
|
|
Loading…
Reference in a new issue