forked from MapComplete/MapComplete
		
	Only upload changes after 30s, on focus loss or on closing of the popup - this should improve #162 already a lot
This commit is contained in:
		
							parent
							
								
									3fb2b9adab
								
							
						
					
					
						commit
						df483786b3
					
				
					 4 changed files with 88 additions and 7 deletions
				
			
		
							
								
								
									
										54
									
								
								Logic/Actors/PendingChangesUploader.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										54
									
								
								Logic/Actors/PendingChangesUploader.ts
									
										
									
									
									
										Normal file
									
								
							|  | @ -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<any>) { | ||||||
|  |         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..." | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |     } | ||||||
|  |      | ||||||
|  |      | ||||||
|  |      | ||||||
|  | } | ||||||
|  | @ -12,9 +12,17 @@ import FeatureSource from "../FeatureSource/FeatureSource"; | ||||||
|  */ |  */ | ||||||
| export class Changes implements FeatureSource{ | export class Changes implements FeatureSource{ | ||||||
| 
 | 
 | ||||||
|  |     /** | ||||||
|  |      * The newly created points, as a FeatureSource | ||||||
|  |      */ | ||||||
|     public features = new UIEventSource<{feature: any, freshness: Date}[]>([]); |     public features = new UIEventSource<{feature: any, freshness: Date}[]>([]); | ||||||
|      |      | ||||||
|     private static _nextId = -1; // Newly assigned ID's are negative
 |     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 |      * Adds a change to the pending changes | ||||||
|  | @ -39,6 +47,8 @@ export class Changes implements FeatureSource{ | ||||||
|         return {k: key, v: value}; |         return {k: key, v: value}; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |      | ||||||
|  |      | ||||||
|     addTag(elementId: string, tagsFilter: TagsFilter, |     addTag(elementId: string, tagsFilter: TagsFilter, | ||||||
|            tags?: UIEventSource<any>) { |            tags?: UIEventSource<any>) { | ||||||
|         const changes = this.tagToChange(tagsFilter); |         const changes = this.tagToChange(tagsFilter); | ||||||
|  | @ -47,21 +57,30 @@ export class Changes implements FeatureSource{ | ||||||
|         } |         } | ||||||
|         const eventSource = tags ?? State.state?.allElements.getEventSourceById(elementId); |         const eventSource = tags ?? State.state?.allElements.getEventSourceById(elementId); | ||||||
|         const elementTags = eventSource.data; |         const elementTags = eventSource.data; | ||||||
|         const pending: { elementId: string, key: string, value: string }[] = []; |  | ||||||
|         for (const change of changes) { |         for (const change of changes) { | ||||||
|             if (elementTags[change.k] !== change.v) { |             if (elementTags[change.k] !== change.v) { | ||||||
|                 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) { |         this.pending.ping(); | ||||||
|             return; |  | ||||||
|         } |  | ||||||
|         console.log("Sending ping", eventSource) |  | ||||||
|         eventSource.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. |      * Create a new node element at the given lat/long. | ||||||
|      * An internal OsmObject is created to upload later on, a geojson represention is returned. |      * An internal OsmObject is created to upload later on, a geojson represention is returned. | ||||||
|  |  | ||||||
|  | @ -17,6 +17,11 @@ export default class Constants { | ||||||
|         addNewPointWithUnreadMessagesUnlock: 500, |         addNewPointWithUnreadMessagesUnlock: 500, | ||||||
|         minZoomLevelToAddNewPoints: (Constants.isRetina() ? 18 : 19) |         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 { |     private static isRetina(): boolean { | ||||||
|         if (Utils.runningFromConsole) { |         if (Utils.runningFromConsole) { | ||||||
|  |  | ||||||
							
								
								
									
										3
									
								
								State.ts
									
										
									
									
									
								
							
							
						
						
									
										3
									
								
								State.ts
									
										
									
									
									
								
							|  | @ -18,6 +18,7 @@ import Constants from "./Models/Constants"; | ||||||
| import UpdateFromOverpass from "./Logic/Actors/UpdateFromOverpass"; | import UpdateFromOverpass from "./Logic/Actors/UpdateFromOverpass"; | ||||||
| import LayerConfig from "./Customizations/JSON/LayerConfig"; | import LayerConfig from "./Customizations/JSON/LayerConfig"; | ||||||
| import TitleHandler from "./Logic/Actors/TitleHandler"; | import TitleHandler from "./Logic/Actors/TitleHandler"; | ||||||
|  | import PendingChangesUploader from "./Logic/Actors/PendingChangesUploader"; | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * Contains the global state: a bunch of UI-event sources |  * Contains the global state: a bunch of UI-event sources | ||||||
|  | @ -207,6 +208,8 @@ export default class State { | ||||||
| 
 | 
 | ||||||
|         this.allElements = new ElementStorage(); |         this.allElements = new ElementStorage(); | ||||||
|         this.changes = new Changes(); |         this.changes = new Changes(); | ||||||
|  |          | ||||||
|  |         new PendingChangesUploader(this.changes, this.selectedElement); | ||||||
| 
 | 
 | ||||||
|         this.mangroveIdentity = new MangroveIdentity( |         this.mangroveIdentity = new MangroveIdentity( | ||||||
|             this.osmConnection.GetLongPreference("identity", "mangrove") |             this.osmConnection.GetLongPreference("identity", "mangrove") | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue