Accessibility: improve keyboard only flow (see #1181); remove some legacy use of Svelte

This commit is contained in:
Pieter Vander Vennet 2023-12-06 17:27:30 +01:00
parent d1a6c11513
commit 4ee83cfe5c
35 changed files with 613 additions and 683 deletions

View file

@ -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)
}
}
}

View file

@ -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

View file

@ -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)
)
}