diff --git a/Logic/Actors/PendingChangesUploader.ts b/Logic/Actors/PendingChangesUploader.ts new file mode 100644 index 0000000..8fe4612 --- /dev/null +++ b/Logic/Actors/PendingChangesUploader.ts @@ -0,0 +1,54 @@ +import {Changes} from "../Osm/Changes"; +import Constants from "../../Models/Constants"; +import {UIEventSource} from "../UIEventSource"; + +export default class PendingChangesUploader{ + + private lastChange : Date; + + constructor(changes: Changes, selectedFeature: UIEventSource) { + const self = this; + this.lastChange = new Date(); + changes.pending.addCallback(() => { + self.lastChange = new Date(); + + window.setTimeout(() => { + const diff = (new Date().getTime() - self.lastChange.getTime()) / 1000; + if(Constants.updateTimeoutSec >= diff - 1){ + changes.flushChanges("Flushing changes due to timeout"); + } + }, Constants.updateTimeoutSec * 1000); + }); + + + selectedFeature + .stabilized(10000) + .addCallback(feature => { + if(feature === undefined){ + // The popup got closed - we flush + changes.flushChanges("Flushing changes due to popup closed"); + } + }); + + document.addEventListener('mouseout', e => { + // @ts-ignore + if (!e.toElement && !e.relatedTarget) { + changes.flushChanges("Flushing changes due to focus lost"); + } + }); + + window.onbeforeunload = function(e){ + + if(changes.pending.data.length == 0){ + return; + } + changes.flushChanges("onbeforeunload - probably closing or something similar"); + e.preventDefault(); + return "Saving your last changes..." + } + + } + + + +} \ No newline at end of file diff --git a/Logic/Osm/Changes.ts b/Logic/Osm/Changes.ts index 05be3f1..ebe2e85 100644 --- a/Logic/Osm/Changes.ts +++ b/Logic/Osm/Changes.ts @@ -12,9 +12,17 @@ import FeatureSource from "../FeatureSource/FeatureSource"; */ export class Changes implements FeatureSource{ + /** + * The newly created points, as a FeatureSource + */ public features = new UIEventSource<{feature: any, freshness: Date}[]>([]); private static _nextId = -1; // Newly assigned ID's are negative + /** + * All the pending changes + */ + public readonly pending: UIEventSource<{ elementId: string, key: string, value: string }[]> = + new UIEventSource<{elementId: string; key: string; value: string}[]>([]); /** * Adds a change to the pending changes @@ -39,6 +47,8 @@ export class Changes implements FeatureSource{ return {k: key, v: value}; } + + addTag(elementId: string, tagsFilter: TagsFilter, tags?: UIEventSource) { const changes = this.tagToChange(tagsFilter); @@ -47,21 +57,30 @@ export class Changes implements FeatureSource{ } const eventSource = tags ?? State.state?.allElements.getEventSourceById(elementId); const elementTags = eventSource.data; - const pending: { elementId: string, key: string, value: string }[] = []; for (const change of changes) { if (elementTags[change.k] !== change.v) { elementTags[change.k] = change.v; - pending.push({elementId: elementTags.id, key: change.k, value: change.v}); + this.pending.data.push({elementId: elementTags.id, key: change.k, value: change.v}); } } - if (pending.length === 0) { - return; - } - console.log("Sending ping", eventSource) + this.pending.ping(); eventSource.ping(); - this.uploadAll([], pending); } + /** + * Uploads all the pending changes in one go. + * Triggered by the 'PendingChangeUploader'-actor in Actors + */ + public flushChanges(flushreason: string = undefined){ + if(this.pending.data.length === 0){ + return; + } + if(flushreason !== undefined){ + console.log(flushreason) + } + this.uploadAll([], this.pending.data); + this.pending.setData([]); + } /** * Create a new node element at the given lat/long. * An internal OsmObject is created to upload later on, a geojson represention is returned. diff --git a/Models/Constants.ts b/Models/Constants.ts index 0d7e55b..cfb40d2 100644 --- a/Models/Constants.ts +++ b/Models/Constants.ts @@ -17,6 +17,11 @@ export default class Constants { addNewPointWithUnreadMessagesUnlock: 500, minZoomLevelToAddNewPoints: (Constants.isRetina() ? 18 : 19) }; + /** + * Used by 'PendingChangesUploader', which waits this amount of seconds to upload changes. + * (Note that pendingChanges might upload sooner if the popup is closed or similar) + */ + static updateTimeoutSec: number = 30; private static isRetina(): boolean { if (Utils.runningFromConsole) { diff --git a/State.ts b/State.ts index 655b667..c7f9625 100644 --- a/State.ts +++ b/State.ts @@ -18,6 +18,7 @@ import Constants from "./Models/Constants"; import UpdateFromOverpass from "./Logic/Actors/UpdateFromOverpass"; import LayerConfig from "./Customizations/JSON/LayerConfig"; import TitleHandler from "./Logic/Actors/TitleHandler"; +import PendingChangesUploader from "./Logic/Actors/PendingChangesUploader"; /** * Contains the global state: a bunch of UI-event sources @@ -207,6 +208,8 @@ export default class State { this.allElements = new ElementStorage(); this.changes = new Changes(); + + new PendingChangesUploader(this.changes, this.selectedElement); this.mangroveIdentity = new MangroveIdentity( this.osmConnection.GetLongPreference("identity", "mangrove")