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