forked from MapComplete/MapComplete
		
	UX: add pending changes indicator
This commit is contained in:
		
							parent
							
								
									09e50464b8
								
							
						
					
					
						commit
						d7d6c3142c
					
				
					 6 changed files with 56 additions and 16 deletions
				
			
		|  | @ -316,6 +316,7 @@ | |||
|         }, | ||||
|         "skip": "Skip this question", | ||||
|         "testing": "Testing - changes won't be saved", | ||||
|         "uploadError": "Error while uploading changes: {error}", | ||||
|         "uploadGpx": { | ||||
|             "choosePermission": "Choose below if your track should be shared:", | ||||
|             "confirm": "Confirm upload", | ||||
|  | @ -343,6 +344,9 @@ | |||
|             "uploadFinished": "Your track has been uploaded!", | ||||
|             "uploading": "Uploading your trace…" | ||||
|         }, | ||||
|         "uploadPending": "{count} changes pending", | ||||
|         "uploadPendingSingle": "One change pending", | ||||
|         "uploadingChanges": "Uploading changes…", | ||||
|         "useSearch": "Use the search above to see presets", | ||||
|         "useSearchForMore": "Use the search function to search within {total} more values…", | ||||
|         "waitingForGeopermission": "Waiting for your permission to use the geolocation…", | ||||
|  |  | |||
|  | @ -5,23 +5,11 @@ import { Utils } from "../../Utils" | |||
| import { Feature } from "geojson" | ||||
| 
 | ||||
| export default class PendingChangesUploader { | ||||
|     private lastChange: Date | ||||
| 
 | ||||
|     constructor(changes: Changes, selectedFeature: UIEventSource<Feature>) { | ||||
|         const self = this | ||||
|         this.lastChange = new Date() | ||||
|         changes.pendingChanges.addCallback(() => { | ||||
|             self.lastChange = new Date() | ||||
|         changes.pendingChanges.stabilized(Constants.updateTimeoutSec * 1000).addCallback(() => changes.flushChanges("Flushing changes due to timeout")) | ||||
| 
 | ||||
|             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) => { | ||||
|         selectedFeature.stabilized(1000).addCallback((feature) => { | ||||
|             if (feature === undefined) { | ||||
|                 // The popup got closed - we flush
 | ||||
|                 changes.flushChanges("Flushing changes due to popup closed") | ||||
|  |  | |||
|  | @ -26,6 +26,7 @@ export class Changes { | |||
|     public readonly extraComment: UIEventSource<string> = new UIEventSource(undefined) | ||||
|     public readonly backend: string | ||||
|     public readonly isUploading = new UIEventSource(false) | ||||
|     public readonly errors = new UIEventSource<string[]>([], "upload-errors") | ||||
|     private readonly historicalUserLocations?: FeatureSource | ||||
|     private _nextId: number = -1 // Newly assigned ID's are negative
 | ||||
|     private readonly previouslyCreated: OsmObject[] = [] | ||||
|  | @ -128,8 +129,11 @@ export class Changes { | |||
|             const csNumber = await this.flushChangesAsync() | ||||
|             this.isUploading.setData(false) | ||||
|             console.log("Changes flushed. Your changeset is " + csNumber) | ||||
|             this.errors.setData([]) | ||||
|         } catch (e) { | ||||
|             this.isUploading.setData(false) | ||||
|             this.errors.data.push(e) | ||||
|             this.errors.ping() | ||||
|             console.error("Flushing changes failed due to", e) | ||||
|         } | ||||
|     } | ||||
|  | @ -415,6 +419,8 @@ export class Changes { | |||
|                         id, | ||||
|                         " dropping it from the changes (" + e + ")" | ||||
|                     ) | ||||
|                     this.errors.data.push(e) | ||||
|                     this.errors.ping() | ||||
|                     return undefined | ||||
|                 } | ||||
|             }) | ||||
|  | @ -572,9 +578,15 @@ export class Changes { | |||
|                                 openChangeset.data | ||||
|                         ) | ||||
| 
 | ||||
|                         return await self.flushSelectChanges(pendingChanges, openChangeset) | ||||
|                         const result = await self.flushSelectChanges(pendingChanges, openChangeset) | ||||
|                         if(result){ | ||||
|                             this.errors.setData([]) | ||||
|                         } | ||||
|                         return result | ||||
|                     } catch (e) { | ||||
|                         console.error("Could not upload some changes:", e) | ||||
|                         this.errors.data.push(e) | ||||
|                         this.errors.ping() | ||||
|                         return false | ||||
|                     } | ||||
|                 }) | ||||
|  | @ -589,6 +601,8 @@ export class Changes { | |||
|                 "Could not handle changes - probably an old, pending changeset in localstorage with an invalid format; erasing those", | ||||
|                 e | ||||
|             ) | ||||
|             this.errors.data.push(e) | ||||
|             this.errors.ping() | ||||
|             self.pendingChanges.setData([]) | ||||
|         } finally { | ||||
|             self.isUploading.setData(false) | ||||
|  |  | |||
|  | @ -63,7 +63,7 @@ export default class Constants { | |||
|      * 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 | ||||
|     static updateTimeoutSec: number = 15 | ||||
|     /** | ||||
|      * If the contributor has their GPS location enabled and makes a change, | ||||
|      * the points visited less then `nearbyVisitTime`-seconds ago will be inspected. | ||||
|  |  | |||
							
								
								
									
										32
									
								
								src/UI/BigComponents/PendingChangesIndicator.svelte
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								src/UI/BigComponents/PendingChangesIndicator.svelte
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,32 @@ | |||
| <script lang="ts"> | ||||
|     import type { SpecialVisualizationState } from "../SpecialVisualization" | ||||
|     import { Store } from "../../Logic/UIEventSource" | ||||
|     import { Changes } from "../../Logic/Osm/Changes" | ||||
|     import Loading from "../Base/Loading.svelte" | ||||
|     import Translations from "../i18n/Translations" | ||||
|     import Tr from "../Base/Tr.svelte" | ||||
| 
 | ||||
|     export let state: SpecialVisualizationState | ||||
| 
 | ||||
|     const changes: Changes = state.changes | ||||
|     const isUploading: Store<boolean> = changes.isUploading | ||||
|     const pendingChangesCount: Store<number> = changes.pendingChanges.map(ls => ls.length) | ||||
|     const errors = changes.errors | ||||
| </script> | ||||
| 
 | ||||
| 
 | ||||
| <div class="flex flex-col pointer-events-auto" on:click={() => changes.flushChanges("Pending changes indicator clicked")}> | ||||
|   {#if $isUploading} | ||||
|     <Loading> | ||||
|       <Tr cls="thx" t={Translations.t.general.uploadingChanges} /> | ||||
|     </Loading> | ||||
|   {:else if $pendingChangesCount === 1} | ||||
|     <Tr cls="alert" t={Translations.t.general.uploadPendingSingle} /> | ||||
|   {:else if $pendingChangesCount > 1} | ||||
|     <Tr cls="alert" t={Translations.t.general.uploadPending.Subs({count: $pendingChangesCount})} /> | ||||
|   {/if} | ||||
| 
 | ||||
|   {#each $errors as error} | ||||
|     <Tr cls="alert" t={Translations.t.general.uploadError.Subs({error})} /> | ||||
|   {/each} | ||||
| </div> | ||||
|  | @ -53,6 +53,7 @@ | |||
|     import Locale from "./i18n/Locale"; | ||||
|     import ShareScreen from "./BigComponents/ShareScreen.svelte"; | ||||
|     import UploadingImageCounter from "./Image/UploadingImageCounter.svelte" | ||||
|     import PendingChangesIndicator from "./BigComponents/PendingChangesIndicator.svelte" | ||||
| 
 | ||||
|     export let state: ThemeViewState; | ||||
|     let layout = state.layout; | ||||
|  | @ -156,6 +157,7 @@ | |||
|       construct={() => new ExtraLinkButton(state, layout.extraLink).SetClass("pointer-events-auto")} | ||||
|     /> | ||||
|     <UploadingImageCounter {state} featureId="*" showThankYou={false}/> | ||||
|     <PendingChangesIndicator {state}/> | ||||
|     <If condition={state.featureSwitchIsTesting}> | ||||
|       <div class="alert w-fit">Testmode</div> | ||||
|     </If> | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue