forked from MapComplete/MapComplete
		
	Accessibility: improve keyboard only flow (see #1181); remove some legacy use of Svelte
This commit is contained in:
		
							parent
							
								
									d1a6c11513
								
							
						
					
					
						commit
						4ee83cfe5c
					
				
					 35 changed files with 613 additions and 683 deletions
				
			
		|  | @ -21,6 +21,7 @@ interface TagsUpdaterState { | |||
|     osmObjectDownloader: OsmObjectDownloader | ||||
|     indexedFeatures: IndexedFeatureSource | ||||
| } | ||||
| 
 | ||||
| export default class SelectedElementTagsUpdater { | ||||
|     private static readonly metatags = new Set([ | ||||
|         "timestamp", | ||||
|  | @ -42,6 +43,84 @@ export default class SelectedElementTagsUpdater { | |||
|         }) | ||||
|     } | ||||
| 
 | ||||
|     public static applyUpdate(latestTags: OsmTags, id: string, state: TagsUpdaterState) { | ||||
|         try { | ||||
|             const leftRightSensitive = state.layout.isLeftRightSensitive() | ||||
| 
 | ||||
|             if (leftRightSensitive) { | ||||
|                 SimpleMetaTagger.removeBothTagging(latestTags) | ||||
|             } | ||||
| 
 | ||||
|             const pendingChanges = state.changes.pendingChanges.data | ||||
|                 .filter((change) => change.type + "/" + change.id === id) | ||||
|                 .filter((change) => change.tags !== undefined) | ||||
| 
 | ||||
|             for (const pendingChange of pendingChanges) { | ||||
|                 const tagChanges = pendingChange.tags | ||||
|                 for (const tagChange of tagChanges) { | ||||
|                     const key = tagChange.k | ||||
|                     const v = tagChange.v | ||||
|                     if (v === undefined || v === "") { | ||||
|                         delete latestTags[key] | ||||
|                     } else { | ||||
|                         latestTags[key] = v | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             // With the changes applied, we merge them onto the upstream object
 | ||||
|             let somethingChanged = false | ||||
|             const currentTagsSource = state.featureProperties.getStore(id) | ||||
|             if (currentTagsSource === undefined) { | ||||
|                 console.warn("No tags store found for", id, "cannot update tags") | ||||
|                 return | ||||
|             } | ||||
|             const currentTags = currentTagsSource.data | ||||
|             for (const key in latestTags) { | ||||
|                 let osmValue = latestTags[key] | ||||
| 
 | ||||
|                 if (typeof osmValue === "number") { | ||||
|                     osmValue = "" + osmValue | ||||
|                 } | ||||
| 
 | ||||
|                 const localValue = currentTags[key] | ||||
|                 if (localValue !== osmValue) { | ||||
|                     somethingChanged = true | ||||
|                     currentTags[key] = osmValue | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             for (const currentKey in currentTags) { | ||||
|                 if (currentKey.startsWith("_")) { | ||||
|                     continue | ||||
|                 } | ||||
|                 if (SelectedElementTagsUpdater.metatags.has(currentKey)) { | ||||
|                     continue | ||||
|                 } | ||||
|                 if (currentKey in latestTags) { | ||||
|                     continue | ||||
|                 } | ||||
|                 console.log("Removing key as deleted upstream", currentKey) | ||||
|                 delete currentTags[currentKey] | ||||
|                 somethingChanged = true | ||||
|             } | ||||
| 
 | ||||
|             if (somethingChanged) { | ||||
|                 console.log( | ||||
|                     "Detected upstream changes to the object " + | ||||
|                         id + | ||||
|                         " when opening it, updating..." | ||||
|                 ) | ||||
|                 currentTagsSource.ping() | ||||
|             } else { | ||||
|                 console.debug("Fetched latest tags for ", id, "but detected no changes") | ||||
|             } | ||||
|             return currentTags | ||||
|         } catch (e) { | ||||
|             console.error("Updating the tags of selected element ", id, "failed due to", e) | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private installCallback(state: TagsUpdaterState) { | ||||
|         state.selectedElement.addCallbackAndRunD(async (s) => { | ||||
|             let id = s.properties?.id | ||||
|  | @ -90,77 +169,4 @@ export default class SelectedElementTagsUpdater { | |||
|             } | ||||
|         }) | ||||
|     } | ||||
|     public static applyUpdate(latestTags: OsmTags, id: string, state: TagsUpdaterState) { | ||||
|         try { | ||||
|             const leftRightSensitive = state.layout.isLeftRightSensitive() | ||||
| 
 | ||||
|             if (leftRightSensitive) { | ||||
|                 SimpleMetaTagger.removeBothTagging(latestTags) | ||||
|             } | ||||
| 
 | ||||
|             const pendingChanges = state.changes.pendingChanges.data | ||||
|                 .filter((change) => change.type + "/" + change.id === id) | ||||
|                 .filter((change) => change.tags !== undefined) | ||||
| 
 | ||||
|             for (const pendingChange of pendingChanges) { | ||||
|                 const tagChanges = pendingChange.tags | ||||
|                 for (const tagChange of tagChanges) { | ||||
|                     const key = tagChange.k | ||||
|                     const v = tagChange.v | ||||
|                     if (v === undefined || v === "") { | ||||
|                         delete latestTags[key] | ||||
|                     } else { | ||||
|                         latestTags[key] = v | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             // With the changes applied, we merge them onto the upstream object
 | ||||
|             let somethingChanged = false | ||||
|             const currentTagsSource = state.featureProperties.getStore(id) | ||||
|             const currentTags = currentTagsSource.data | ||||
|             for (const key in latestTags) { | ||||
|                 let osmValue = latestTags[key] | ||||
| 
 | ||||
|                 if (typeof osmValue === "number") { | ||||
|                     osmValue = "" + osmValue | ||||
|                 } | ||||
| 
 | ||||
|                 const localValue = currentTags[key] | ||||
|                 if (localValue !== osmValue) { | ||||
|                     somethingChanged = true | ||||
|                     currentTags[key] = osmValue | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             for (const currentKey in currentTags) { | ||||
|                 if (currentKey.startsWith("_")) { | ||||
|                     continue | ||||
|                 } | ||||
|                 if (SelectedElementTagsUpdater.metatags.has(currentKey)) { | ||||
|                     continue | ||||
|                 } | ||||
|                 if (currentKey in latestTags) { | ||||
|                     continue | ||||
|                 } | ||||
|                 console.log("Removing key as deleted upstream", currentKey) | ||||
|                 delete currentTags[currentKey] | ||||
|                 somethingChanged = true | ||||
|             } | ||||
| 
 | ||||
|             if (somethingChanged) { | ||||
|                 console.log( | ||||
|                     "Detected upstream changes to the object " + | ||||
|                         id + | ||||
|                         " when opening it, updating..." | ||||
|                 ) | ||||
|                 currentTagsSource.ping() | ||||
|             } else { | ||||
|                 console.debug("Fetched latest tags for ", id, "but detected no changes") | ||||
|             } | ||||
|             return currentTags | ||||
|         } catch (e) { | ||||
|             console.error("Updating the tags of selected element ", id, "failed due to", e) | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  |  | |||
|  | @ -108,7 +108,7 @@ export class ImageUploadManager { | |||
|             description, | ||||
|             file, | ||||
|             targetKey, | ||||
|             tags.data["_orig_theme"] | ||||
|             tags?.data?.["_orig_theme"] | ||||
|         ) | ||||
|         if (!isNaN(Number(featureId))) { | ||||
|             // This is a map note
 | ||||
|  |  | |||
|  | @ -97,7 +97,10 @@ export abstract class Store<T> implements Readable<T> { | |||
|     abstract map<J>(f: (t: T) => J): Store<J> | ||||
|     abstract map<J>(f: (t: T) => J, extraStoresToWatch: Store<any>[]): Store<J> | ||||
| 
 | ||||
|     public mapD<J>(f: (t: Exclude<T, undefined | null>) => J, extraStoresToWatch?: Store<any>[]): Store<J> { | ||||
|     public mapD<J>( | ||||
|         f: (t: Exclude<T, undefined | null>) => J, | ||||
|         extraStoresToWatch?: Store<any>[] | ||||
|     ): Store<J> { | ||||
|         return this.map((t) => { | ||||
|             if (t === undefined) { | ||||
|                 return undefined | ||||
|  | @ -105,7 +108,7 @@ export abstract class Store<T> implements Readable<T> { | |||
|             if (t === null) { | ||||
|                 return null | ||||
|             } | ||||
|             return f(<Exclude<T, undefined | null>> t) | ||||
|             return f(<Exclude<T, undefined | null>>t) | ||||
|         }, extraStoresToWatch) | ||||
|     } | ||||
| 
 | ||||
|  | @ -201,24 +204,36 @@ export abstract class Store<T> implements Readable<T> { | |||
|         mapped.addCallbackAndRun((newEventSource) => { | ||||
|             if (newEventSource === null) { | ||||
|                 sink.setData(null) | ||||
|             } else if (newEventSource === undefined) { | ||||
|                 return | ||||
|             } | ||||
|             if (newEventSource === undefined) { | ||||
|                 sink.setData(undefined) | ||||
|             } else if (!seenEventSources.has(newEventSource)) { | ||||
|                 seenEventSources.add(newEventSource) | ||||
|                 newEventSource.addCallbackAndRun((resultData) => { | ||||
|                     if (mapped.data === newEventSource) { | ||||
|                         sink.setData(resultData) | ||||
|                     } | ||||
|                 }) | ||||
|             } else { | ||||
|                 return | ||||
|             } | ||||
|             if (seenEventSources.has(newEventSource)) { | ||||
|                 // Already seen, so we don't have to add a callback, just update the value
 | ||||
|                 sink.setData(newEventSource.data) | ||||
|                 return | ||||
|             } | ||||
|             seenEventSources.add(newEventSource) | ||||
|             newEventSource.addCallbackAndRun((resultData) => { | ||||
|                 if (mapped.data === newEventSource) { | ||||
|                     sink.setData(resultData) | ||||
|                 } | ||||
|             }) | ||||
|         }) | ||||
| 
 | ||||
|         return sink | ||||
|     } | ||||
| 
 | ||||
|     public bindD<X>(f: (t: Exclude<T, undefined | null>) => Store<X>): Store<X> { | ||||
|         return this.bind((t) => { | ||||
|             if (t === undefined || t === null) { | ||||
|                 return <undefined | null>t | ||||
|             } | ||||
|             return f(<Exclude<T, undefined | null>>t) | ||||
|         }) | ||||
|     } | ||||
|     public stabilized(millisToStabilize): Store<T> { | ||||
|         if (Utils.runningFromConsole) { | ||||
|             return this | ||||
|  | @ -771,7 +786,10 @@ export class UIEventSource<T> extends Store<T> implements Writable<T> { | |||
|      * Monoidal map which results in a read-only store. 'undefined' is passed 'as is' | ||||
|      * Given a function 'f', will construct a new UIEventSource where the contents will always be "f(this.data)' | ||||
|      */ | ||||
|     public mapD<J>(f: (t: Exclude<T, undefined | null>) => J, extraSources: Store<any>[] = []): Store<J | undefined> { | ||||
|     public mapD<J>( | ||||
|         f: (t: Exclude<T, undefined | null>) => J, | ||||
|         extraSources: Store<any>[] = [] | ||||
|     ): Store<J | undefined> { | ||||
|         return new MappedStore( | ||||
|             this, | ||||
|             (t) => { | ||||
|  | @ -781,11 +799,13 @@ export class UIEventSource<T> extends Store<T> implements Writable<T> { | |||
|                 if (t === null) { | ||||
|                     return null | ||||
|                 } | ||||
|                 return f(<Exclude<T, undefined | null>> t) | ||||
|                 return f(<Exclude<T, undefined | null>>t) | ||||
|             }, | ||||
|             extraSources, | ||||
|             this._callbacks, | ||||
|             (this.data === undefined || this.data === null) ?(<undefined | null> this.data) : f(<any> this.data) | ||||
|             this.data === undefined || this.data === null | ||||
|                 ? <undefined | null>this.data | ||||
|                 : f(<any>this.data) | ||||
|         ) | ||||
|     } | ||||
| 
 | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue