Compare commits

..

No commits in common. "48daa0a77fad148125b69e62563cb4a84568b969" and "fc6ea191cc4f45a3cf248f303f60d1eae4cacdf0" have entirely different histories.

13 changed files with 369 additions and 231 deletions

View file

@ -45,7 +45,7 @@
"georsstogeojson": "^0.2.0", "georsstogeojson": "^0.2.0",
"jsdom": "^24.0.0", "jsdom": "^24.0.0",
"leaflet": "1.9.4", "leaflet": "1.9.4",
"leaflet-editable": "^1.3.1", "leaflet-editable": "^1.3.0",
"leaflet-editinosm": "0.2.3", "leaflet-editinosm": "0.2.3",
"leaflet-fullscreen": "1.0.2", "leaflet-fullscreen": "1.0.2",
"leaflet-hash": "0.2.1", "leaflet-hash": "0.2.1",

View file

@ -36,7 +36,7 @@ dependencies = [
"psycopg==3.2.4", "psycopg==3.2.4",
"requests==2.32.3", "requests==2.32.3",
"rcssmin==1.2.1", "rcssmin==1.2.1",
"rjsmin==1.2.4", "rjsmin==1.2.3",
"social-auth-core==4.5.4", "social-auth-core==4.5.4",
"social-auth-app-django==5.4.2", "social-auth-app-django==5.4.2",
] ]

View file

@ -335,15 +335,15 @@ class Feature {
// Variables mode. // Variables mode.
if (labelKey) { if (labelKey) {
if (Utils.hasVar(labelKey)) { if (Utils.hasVar(labelKey)) {
return Utils.greedyTemplate(labelKey, this.extendedProperties()).trim() return Utils.greedyTemplate(labelKey, this.extendedProperties())
} }
keys.unshift(labelKey) keys.unshift(labelKey)
} }
for (const key of keys) { for (const key of keys) {
const value = this.properties[key] const value = this.properties[key]
if (value) return value.trim() if (value) return value
} }
return this.datalayer.getName().trim() return this.datalayer.getName()
} }
hasPopupFooter() { hasPopupFooter() {
@ -640,12 +640,6 @@ class Feature {
window.open(permalink) window.open(permalink)
}, },
}) })
items.push({
label: translate('Layer permalink'),
action: () => {
window.open(this.datalayer.getPermalink())
},
})
} }
items.push({ items.push({
label: translate('Copy as GeoJSON'), label: translate('Copy as GeoJSON'),

View file

@ -1174,12 +1174,6 @@ export class DataLayer extends ServerStored {
return this.options.name || translate('Untitled layer') return this.options.name || translate('Untitled layer')
} }
getPermalink() {
return `${Utils.getBaseUrl()}?${Utils.buildQueryString({ datalayers: this.id })}${
window.location.hash
}`
}
tableEdit() { tableEdit() {
if (!this.isVisible()) return if (!this.isVisible()) return
const editor = new TableEditor(this._umap, this, this._leafletMap) const editor = new TableEditor(this._umap, this, this._leafletMap)

View file

@ -60,7 +60,8 @@ const FeatureMixin = {
if (event.originalEvent.ctrlKey || event.originalEvent.metaKey) { if (event.originalEvent.ctrlKey || event.originalEvent.metaKey) {
this.feature.datalayer.edit(event) this.feature.datalayer.edit(event)
} else { } else {
this.feature.toggleEditing(event) if (this.feature._toggleEditing) this.feature._toggleEditing(event)
else this.feature.edit(event)
} }
} else if (!this._map.editTools?.drawing()) { } else if (!this._map.editTools?.drawing()) {
this._map._umap.editContextmenu.open( this._map._umap.editContextmenu.open(

View file

@ -66,12 +66,7 @@ export class SyncEngine {
this.peerId = Utils.generateId() this.peerId = Utils.generateId()
} }
get isOpen() {
return this.transport?.isOpen
}
async authenticate() { async authenticate() {
if (this.isOpen) return
const websocketTokenURI = this._umap.urls.get('map_websocket_auth_token', { const websocketTokenURI = this._umap.urls.get('map_websocket_auth_token', {
map_id: this._umap.id, map_id: this._umap.id,
}) })
@ -203,7 +198,6 @@ export class SyncEngine {
*/ */
onOperationMessage(payload) { onOperationMessage(payload) {
if (payload.sender === this.peerId) return if (payload.sender === this.peerId) return
debug('received operation', payload)
this._operations.storeRemoteOperations([payload]) this._operations.storeRemoteOperations([payload])
this._applyOperation(payload) this._applyOperation(payload)
} }
@ -267,7 +261,7 @@ export class SyncEngine {
* @param {*} operations The list of (encoded operations) * @param {*} operations The list of (encoded operations)
*/ */
onListOperationsResponse({ sender, message }) { onListOperationsResponse({ sender, message }) {
debug(`received operations list from peer ${sender}`, message.operations) debug(`received operations from peer ${sender}`, message.operations)
if (message.operations.length === 0) return if (message.operations.length === 0) return
@ -508,7 +502,11 @@ export class Operations {
* @return {bool} true if the two operations share the same context. * @return {bool} true if the two operations share the same context.
*/ */
static haveSameContext(local, remote) { static haveSameContext(local, remote) {
const shouldCheckKey = local.key !== undefined && remote.key !== undefined const shouldCheckKey =
local.hasOwnProperty('key') &&
remote.hasOwnProperty('key') &&
typeof local.key !== 'undefined' &&
typeof remote.key !== 'undefined'
return ( return (
Utils.deepEqual(local.subject, remote.subject) && Utils.deepEqual(local.subject, remote.subject) &&

View file

@ -54,11 +54,7 @@ export class MapUpdater extends BaseUpdater {
export class DataLayerUpdater extends BaseUpdater { export class DataLayerUpdater extends BaseUpdater {
upsert({ value }) { upsert({ value }) {
// Upsert only happens when a new datalayer is created. // Upsert only happens when a new datalayer is created.
try { const datalayer = this._umap.createDataLayer(value, false)
this.getDataLayerFromID(value.id)
} catch {
this._umap.createDataLayer(value, false)
}
} }
update({ key, metadata, value }) { update({ key, metadata, value }) {

View file

@ -76,8 +76,4 @@ export class WebSocketTransport {
this.receiver.closeRequested = true this.receiver.closeRequested = true
this.websocket.close() this.websocket.close()
} }
get isOpen() {
return this.websocket?.readyState === WebSocket.OPEN
}
} }

View file

@ -699,7 +699,6 @@ U.Editable = L.Editable.extend({
if (!event.layer.feature.hasGeom()) { if (!event.layer.feature.hasGeom()) {
event.layer.feature.del() event.layer.feature.del()
} else { } else {
event.layer.feature.onCommit()
event.layer.feature.edit() event.layer.feature.edit()
} }
}) })

File diff suppressed because it is too large Load diff

File diff suppressed because one or more lines are too long

View file

@ -1248,7 +1248,6 @@
// 🍂event editable:vertex:deleted: VertexEvent // 🍂event editable:vertex:deleted: VertexEvent
// Fired after a vertex has been deleted by user. // Fired after a vertex has been deleted by user.
this.fireAndForward('editable:vertex:deleted', e) this.fireAndForward('editable:vertex:deleted', e)
this.onEdited()
}, },
onVertexMarkerCtrlClick: function (e) { onVertexMarkerCtrlClick: function (e) {

View file

@ -535,11 +535,6 @@ def test_create_and_sync_map(new_page, asgi_live_server, tilelayer, login, user)
expect(markersA).to_have_count(1) expect(markersA).to_have_count(1)
expect(markersB).to_have_count(1) expect(markersB).to_have_count(1)
# Make sure only one layer has been created on peer B
peerB.get_by_role("button", name="Open browser").click()
expect(peerB.locator("h5").get_by_text("Layer 1")).to_be_visible()
peerB.get_by_role("button", name="Close").click()
# Save and quit edit mode again # Save and quit edit mode again
with peerA.expect_response(re.compile("./datalayer/create/.*")): with peerA.expect_response(re.compile("./datalayer/create/.*")):
peerA.get_by_role("button", name="Save").click() peerA.get_by_role("button", name="Save").click()
@ -568,34 +563,6 @@ def test_create_and_sync_map(new_page, asgi_live_server, tilelayer, login, user)
expect(markersB).to_have_count(2) expect(markersB).to_have_count(2)
@pytest.mark.xdist_group(name="websockets")
def test_saved_datalayer_are_not_duplicated(new_page, asgi_live_server, tilelayer):
map = MapFactory(name="sync", edit_status=Map.ANONYMOUS)
map.settings["properties"]["syncEnabled"] = True
map.save()
# Create one tab
peerA = new_page("Page A")
peerA.goto(f"{asgi_live_server.url}{map.get_absolute_url()}?edit")
# Create a new datalayer
peerA.get_by_title("Manage layers").click()
peerA.get_by_title("Add a layer").click()
peerA.locator("#map").click(position={"x": 220, "y": 220})
# Save layer to the server, so now the datalayer exist on the server AND
# is still in the live operations of peer A
with peerA.expect_response(re.compile(".*/datalayer/create/.*")):
peerA.get_by_role("button", name="Save").click()
# Now load the map from another tab
peerB = new_page("Page B")
peerB.goto(peerA.url)
peerB.get_by_role("button", name="Open browser").click()
expect(peerB.get_by_text("Layer 1")).to_be_visible()
peerB.get_by_role("button", name="Edit").click()
peerA.wait_for_timeout(300) # Let the synchro roll on.
expect(peerB.get_by_text("Layer 1")).to_be_visible()
@pytest.mark.xdist_group(name="websockets") @pytest.mark.xdist_group(name="websockets")
def test_should_sync_saved_status(new_page, asgi_live_server, tilelayer): def test_should_sync_saved_status(new_page, asgi_live_server, tilelayer):
map = MapFactory(name="sync", edit_status=Map.ANONYMOUS) map = MapFactory(name="sync", edit_status=Map.ANONYMOUS)
@ -637,25 +604,3 @@ def test_should_sync_saved_status(new_page, asgi_live_server, tilelayer):
# Peer A should not be in dirty state # Peer A should not be in dirty state
expect(peerA.locator("body")).not_to_have_class(re.compile(".*umap-is-dirty.*")) expect(peerA.locator("body")).not_to_have_class(re.compile(".*umap-is-dirty.*"))
@pytest.mark.xdist_group(name="websockets")
def test_should_sync_line_on_escape(new_page, asgi_live_server, tilelayer):
map = MapFactory(name="sync", edit_status=Map.ANONYMOUS)
map.settings["properties"]["syncEnabled"] = True
map.save()
# Create two tabs
peerA = new_page("Page A")
peerA.goto(f"{asgi_live_server.url}{map.get_absolute_url()}?edit")
peerB = new_page("Page B")
peerB.goto(f"{asgi_live_server.url}{map.get_absolute_url()}?edit")
# Create a new marker from peerA
peerA.get_by_title("Draw a polyline").click()
peerA.locator("#map").click(position={"x": 220, "y": 220})
peerA.locator("#map").click(position={"x": 200, "y": 200})
peerA.locator("body").press("Escape")
expect(peerA.locator("path")).to_have_count(1)
expect(peerB.locator("path")).to_have_count(1)