mirror of
https://github.com/umap-project/umap.git
synced 2025-04-28 19:42:36 +02:00
Batch operations
Co-authored-by: Alexis Métaireau <alexis@notmyidea.org> Co-authored-by: David Larlet <david@larlet.fr>
This commit is contained in:
parent
cc2625bfac
commit
cb46a5f875
5 changed files with 90 additions and 30 deletions
|
@ -464,7 +464,10 @@ export class DataLayer extends ServerStored {
|
||||||
try {
|
try {
|
||||||
// Do not fail if remote data is somehow invalid,
|
// Do not fail if remote data is somehow invalid,
|
||||||
// otherwise the layer becomes uneditable.
|
// otherwise the layer becomes uneditable.
|
||||||
return this.makeFeatures(geojson, sync)
|
this.sync.startBatch()
|
||||||
|
const features = this.makeFeatures(geojson, sync)
|
||||||
|
this.sync.commitBatch()
|
||||||
|
return features
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.debug('Error with DataLayer', this.id)
|
console.debug('Error with DataLayer', this.id)
|
||||||
console.error(err)
|
console.error(err)
|
||||||
|
@ -522,7 +525,7 @@ export class DataLayer extends ServerStored {
|
||||||
}
|
}
|
||||||
if (feature && !feature.isEmpty()) {
|
if (feature && !feature.isEmpty()) {
|
||||||
this.addFeature(feature)
|
this.addFeature(feature)
|
||||||
if (sync) feature.onCommit()
|
if (sync) feature.sync.upsert(feature.toGeoJSON(), null)
|
||||||
return feature
|
return feature
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -124,38 +124,83 @@ export class SyncEngine {
|
||||||
await this.authenticate()
|
await this.authenticate()
|
||||||
}, this._reconnectDelay)
|
}, this._reconnectDelay)
|
||||||
}
|
}
|
||||||
upsert(subject, metadata, value, oldValue) {
|
|
||||||
|
startBatch() {
|
||||||
|
this._batch = []
|
||||||
|
}
|
||||||
|
|
||||||
|
commitBatch() {
|
||||||
|
if (!this._batch.length) {
|
||||||
|
this._batch = null
|
||||||
|
return
|
||||||
|
}
|
||||||
this._undoManager.add({
|
this._undoManager.add({
|
||||||
|
verb: 'batch',
|
||||||
|
operations: this._batch,
|
||||||
|
})
|
||||||
|
const syncOperations = this._batch.map((operation) =>
|
||||||
|
this.convertToSyncOperation(operation)
|
||||||
|
)
|
||||||
|
this._send({ verb: 'batch', operations: syncOperations, subject: 'batch' })
|
||||||
|
this._batch = null
|
||||||
|
}
|
||||||
|
|
||||||
|
convertToSyncOperation(undoOperation) {
|
||||||
|
const syncOperation = { ...undoOperation, value: undoOperation.newValue }
|
||||||
|
delete syncOperation.oldValue
|
||||||
|
delete syncOperation.newValue
|
||||||
|
return syncOperation
|
||||||
|
}
|
||||||
|
|
||||||
|
upsert(subject, metadata, value, oldValue) {
|
||||||
|
const undoOperation = {
|
||||||
verb: 'upsert',
|
verb: 'upsert',
|
||||||
subject,
|
subject,
|
||||||
metadata,
|
metadata,
|
||||||
oldValue: oldValue,
|
|
||||||
newValue: value,
|
newValue: value,
|
||||||
})
|
oldValue: oldValue,
|
||||||
this._send({ verb: 'upsert', subject, metadata, value })
|
}
|
||||||
|
if (this._batch) {
|
||||||
|
this._batch.push(undoOperation)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this._undoManager.add(undoOperation)
|
||||||
|
const syncOperation = this.convertToSyncOperation(undoOperation)
|
||||||
|
this._send(syncOperation)
|
||||||
}
|
}
|
||||||
|
|
||||||
update(subject, metadata, key, value, oldValue) {
|
update(subject, metadata, key, value, oldValue) {
|
||||||
this._undoManager.add({
|
const undoOperation = {
|
||||||
verb: 'update',
|
verb: 'update',
|
||||||
subject,
|
subject,
|
||||||
metadata,
|
metadata,
|
||||||
key,
|
key,
|
||||||
oldValue: oldValue,
|
oldValue: oldValue,
|
||||||
newValue: value,
|
newValue: value,
|
||||||
})
|
}
|
||||||
this._send({ verb: 'update', subject, metadata, key, value })
|
if (this._batch) {
|
||||||
|
this._batch.push(undoOperation)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this._undoManager.add(undoOperation)
|
||||||
|
const syncOperation = this.convertToSyncOperation(undoOperation)
|
||||||
|
this._send(syncOperation)
|
||||||
}
|
}
|
||||||
|
|
||||||
delete(subject, metadata, oldValue) {
|
delete(subject, metadata, oldValue) {
|
||||||
console.log('oldValue', oldValue)
|
const undoOperation = {
|
||||||
this._undoManager.add({
|
|
||||||
verb: 'delete',
|
verb: 'delete',
|
||||||
subject,
|
subject,
|
||||||
metadata,
|
metadata,
|
||||||
oldValue: oldValue,
|
oldValue: oldValue,
|
||||||
})
|
}
|
||||||
this._send({ verb: 'delete', subject, metadata })
|
if (this._batch) {
|
||||||
|
this._batch.push(undoOperation)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this._undoManager.add(undoOperation)
|
||||||
|
const syncOperation = this.convertToSyncOperation(undoOperation)
|
||||||
|
this._send(syncOperation)
|
||||||
}
|
}
|
||||||
|
|
||||||
saved() {
|
saved() {
|
||||||
|
@ -185,6 +230,10 @@ export class SyncEngine {
|
||||||
}
|
}
|
||||||
|
|
||||||
_applyOperation(operation) {
|
_applyOperation(operation) {
|
||||||
|
if (operation.verb === 'batch') {
|
||||||
|
operation.operations.map((op) => this._applyOperation(op))
|
||||||
|
return
|
||||||
|
}
|
||||||
const updater = this._getUpdater(operation.subject, operation.metadata)
|
const updater = this._getUpdater(operation.subject, operation.metadata)
|
||||||
updater.applyMessage(operation)
|
updater.applyMessage(operation)
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,28 +10,38 @@ export class UndoManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
toggleState() {
|
toggleState() {
|
||||||
document.querySelector('.edit-undo').disabled = !this._undoStack.length
|
const undoButton = document.querySelector('.edit-undo')
|
||||||
document.querySelector('.edit-redo').disabled = !this._redoStack.length
|
const redoButton = document.querySelector('.edit-redo')
|
||||||
|
if (undoButton) undoButton.disabled = !this._undoStack.length
|
||||||
|
if (redoButton) redoButton.disabled = !this._redoStack.length
|
||||||
}
|
}
|
||||||
|
|
||||||
add(operation) {
|
add(operation) {
|
||||||
console.debug('New entry in undo stack', operation)
|
|
||||||
this._redoStack = []
|
this._redoStack = []
|
||||||
this._undoStack.push(operation)
|
this._undoStack.push(operation)
|
||||||
this.toggleState()
|
this.toggleState()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cleanOperation(operation, redo) {
|
||||||
|
const syncOperation = Utils.CopyJSON(operation)
|
||||||
|
delete syncOperation.oldValue
|
||||||
|
delete syncOperation.newValue
|
||||||
|
syncOperation.value = redo ? operation.newValue : operation.oldValue
|
||||||
|
return syncOperation
|
||||||
|
}
|
||||||
|
|
||||||
undo(redo = false) {
|
undo(redo = false) {
|
||||||
const fromStack = redo ? this._redoStack : this._undoStack
|
const fromStack = redo ? this._redoStack : this._undoStack
|
||||||
const toStack = redo ? this._undoStack : this._redoStack
|
const toStack = redo ? this._undoStack : this._redoStack
|
||||||
const operation = fromStack.pop()
|
const operation = fromStack.pop()
|
||||||
if (!operation) return
|
if (!operation) return
|
||||||
const syncOperation = Utils.CopyJSON(operation)
|
if (operation.verb === 'batch') {
|
||||||
console.log('old/new', syncOperation.oldValue, syncOperation.newValue)
|
for (const op of operation.operations) {
|
||||||
delete syncOperation.oldValue
|
this.applyOperation(this.cleanOperation(op, redo))
|
||||||
delete syncOperation.newValue
|
}
|
||||||
syncOperation.value = redo ? operation.newValue : operation.oldValue
|
} else {
|
||||||
this.applyOperation(syncOperation)
|
this.applyOperation(this.cleanOperation(operation, redo))
|
||||||
|
}
|
||||||
toStack.push(operation)
|
toStack.push(operation)
|
||||||
this.toggleState()
|
this.toggleState()
|
||||||
}
|
}
|
||||||
|
@ -49,12 +59,9 @@ export class UndoManager {
|
||||||
break
|
break
|
||||||
case 'delete':
|
case 'delete':
|
||||||
case 'upsert':
|
case 'upsert':
|
||||||
console.log('undo upsert/delete', syncOperation.value)
|
|
||||||
if (syncOperation.value === null || syncOperation.value === undefined) {
|
if (syncOperation.value === null || syncOperation.value === undefined) {
|
||||||
console.log('case delete')
|
|
||||||
updater.delete(syncOperation)
|
updater.delete(syncOperation)
|
||||||
} else {
|
} else {
|
||||||
console.log('case upsert')
|
|
||||||
updater.upsert(syncOperation)
|
updater.upsert(syncOperation)
|
||||||
}
|
}
|
||||||
this._syncEngine._send(syncOperation)
|
this._syncEngine._send(syncOperation)
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
from typing import Literal, Optional, Union
|
from typing import List, Literal, Optional, Union
|
||||||
|
|
||||||
from pydantic import BaseModel, Field, RootModel
|
from pydantic import BaseModel, Field, RootModel
|
||||||
|
|
||||||
|
@ -14,10 +14,11 @@ class OperationMessage(BaseModel):
|
||||||
"""Message sent from one peer to all the others"""
|
"""Message sent from one peer to all the others"""
|
||||||
|
|
||||||
kind: Literal["OperationMessage"] = "OperationMessage"
|
kind: Literal["OperationMessage"] = "OperationMessage"
|
||||||
verb: Literal["upsert", "update", "delete"]
|
verb: Literal["upsert", "update", "delete", "batch"]
|
||||||
subject: Literal["map", "datalayer", "feature"]
|
subject: Literal["map", "datalayer", "feature", "batch"]
|
||||||
metadata: Optional[dict] = None
|
metadata: Optional[dict] = None
|
||||||
key: Optional[str] = None
|
key: Optional[str] = None
|
||||||
|
operations: Optional[List] = None
|
||||||
|
|
||||||
|
|
||||||
class PeerMessage(BaseModel):
|
class PeerMessage(BaseModel):
|
||||||
|
|
|
@ -89,8 +89,8 @@ def test_can_undo_redo_layer_color_change(
|
||||||
):
|
):
|
||||||
page.goto(f"{live_server.url}{map_with_polygon.get_absolute_url()}?edit")
|
page.goto(f"{live_server.url}{map_with_polygon.get_absolute_url()}?edit")
|
||||||
|
|
||||||
expect(page.locator(".edit-undo")).to_be_disabled()
|
expect(page.locator(".edit-undo")).to_be_hidden()
|
||||||
expect(page.locator(".edit-redo")).to_be_disabled()
|
expect(page.locator(".edit-redo")).to_be_hidden()
|
||||||
page.get_by_role("button", name="Manage layers").click()
|
page.get_by_role("button", name="Manage layers").click()
|
||||||
page.locator(".panel").get_by_title("Edit", exact=True).click()
|
page.locator(".panel").get_by_title("Edit", exact=True).click()
|
||||||
page.get_by_text("Shape properties").click()
|
page.get_by_text("Shape properties").click()
|
||||||
|
|
Loading…
Reference in a new issue