From 4ee83cfe5c7c23b4e53c52d658283ae38850c8ee Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet Date: Wed, 6 Dec 2023 17:27:30 +0100 Subject: [PATCH] Accessibility: improve keyboard only flow (see #1181); remove some legacy use of Svelte --- .../layers/drinking_water/drinking_water.json | 1 + .../mapcomplete-changes.json | 222 ++------------ package-lock.json | 2 +- package.json | 2 +- public/css/index-tailwind-output.css | 29 +- scripts/generateImageAnalysis.ts | 29 +- .../Actors/SelectedElementTagsUpdater.ts | 152 ++++----- .../ImageProviders/ImageUploadManager.ts | 2 +- src/Logic/UIEventSource.ts | 48 ++- src/Models/MenuState.ts | 5 + src/Models/ThemeViewState.ts | 5 +- src/UI/Base/FileSelector.svelte | 4 +- src/UI/Base/FloatOver.svelte | 10 +- src/UI/Base/ModalRight.svelte | 21 +- src/UI/Base/TabbedGroup.svelte | 2 +- src/UI/Base/WeblateLink.svelte | 2 + src/UI/BigComponents/Geosearch.svelte | 13 +- .../BigComponents/SelectedElementTitle.svelte | 60 ++-- .../BigComponents/SelectedElementView.svelte | 32 +- src/UI/BigComponents/ThemeIntroPanel.svelte | 2 - src/UI/Favourites/FavouriteSummary.svelte | 1 - src/UI/Image/AttributedImage.svelte | 3 +- src/UI/Image/ImageOperations.svelte | 5 +- src/UI/Image/ImagePreview.svelte | 2 +- src/UI/Image/LinkableImage.svelte | 4 +- src/UI/Image/NearbyImagesCollapsed.svelte | 81 +++-- src/UI/Image/UploadImage.svelte | 12 +- src/UI/Map/MapLibreAdaptor.ts | 22 +- src/UI/Map/ShowDataLayer.ts | 9 +- .../TagRendering/TagRenderingAnswer.svelte | 21 +- .../TagRendering/TagRenderingEditable.svelte | 89 +++--- .../TagRendering/TagRenderingQuestion.svelte | 1 - src/UI/ThemeViewGUI.svelte | 288 ++++++++---------- src/Utils.ts | 109 ++++--- src/index.css | 6 +- 35 files changed, 613 insertions(+), 683 deletions(-) diff --git a/assets/layers/drinking_water/drinking_water.json b/assets/layers/drinking_water/drinking_water.json index ec4afb95d9..4daedffc6e 100644 --- a/assets/layers/drinking_water/drinking_water.json +++ b/assets/layers/drinking_water/drinking_water.json @@ -351,6 +351,7 @@ "if": { "and": [ "seasonal!=no", + "seasonal~*", { "or": [ { diff --git a/assets/themes/mapcomplete-changes/mapcomplete-changes.json b/assets/themes/mapcomplete-changes/mapcomplete-changes.json index 667b288784..6c73765960 100644 --- a/assets/themes/mapcomplete-changes/mapcomplete-changes.json +++ b/assets/themes/mapcomplete-changes/mapcomplete-changes.json @@ -1,33 +1,13 @@ { "id": "mapcomplete-changes", "title": { - "en": "Changes made with MapComplete", - "ca": "Canvis fets amb MapComplete", - "cs": "Změny provedené pomocí MapComplete", - "de": "Mit MapComplete erstellte Änderungen", - "es": "Cambios realizados con MapComplete", - "fr": "Changements faits avec MapComplete", - "nl": "Wijzigingen gemaakt met MapComplete", - "pl": "Zmiany wprowadzone za pomocą MapComplete" + "en": "Changes made with MapComplete" }, "shortDescription": { - "en": "Show changes made with MapComplete", - "ca": "Mostra els canvis fets amb MapComplete", - "cs": "Zobrazení změn provedených pomocí nástroje MapComplete", - "de": "Mit MapComplete erstellte Änderungen anzeigen", - "es": "Mostrar cambios realizados con MapComplete", - "nl": "Toon wijzigingen gemaakt met MapComplete", - "pl": "Pokaż zmiany wprowadzone za pomocą MapComplete" + "en": "Shows changes made by MapComplete" }, "description": { - "en": "This maps shows all the changes made with MapComplete", - "ca": "Aquest mapa mostra tots els canvis fets amb MapComplete", - "cs": "Tato mapa zobrazuje všechny změny provedené pomocí MapComplete", - "de": "Diese Karte zeigt alle mit MapComplete vorgenommenen Änderungen", - "es": "Este mapa muestra todos los cambios realizados con MapComplete", - "fr": "Cette carte montre tous les changements faits avec MapComplete", - "nl": "Deze kaart toont alle wijzigingen die met MapComplete gemaakt werden", - "pl": "Ta mapa pokazuje wszystkie zmiany wprowadzone za pomocą MapComplete" + "en": "This maps shows all the changes made with MapComplete" }, "icon": "./assets/svg/logo.svg", "hideFromOverview": true, @@ -40,13 +20,7 @@ { "id": "mapcomplete-changes", "name": { - "en": "Changeset centers", - "ca": "Centre del conjunt de canvis", - "cs": "Centrum změn", - "de": "Zentrum der Änderungssätze", - "es": "Centro del conjunto de cambios", - "nl": "Centerpunt van changeset", - "pl": "Centra zmian" + "en": "Changeset centers" }, "minzoom": 0, "source": { @@ -57,85 +31,41 @@ }, "title": { "render": { - "en": "Changeset for {theme}", - "ca": "Conjunt de canvis per a {theme}", - "cs": "Změna pro {theme}", - "de": "Änderungssatz für {theme}", - "es": "Conjunto de cambios para {theme}", - "fr": "Groupe de modifications pour {theme}", - "pl": "Zestaw zmian dla {theme}" + "en": "Changeset for {theme}" } }, "description": { - "en": "Show all MapComplete changes", - "ca": "Mostra tots els canvis de MapComplete", - "cs": "Zobrazit všechny změny MapComplete", - "de": "Alle MapComplete-Änderungen anzeigen", - "es": "Mostrar todos los cambios de MapComplete", - "nl": "Toon alle MapComplete wijzigingen", - "pl": "Wyświetl wszystkie zmiany MapComplete" + "en": "Shows all MapComplete changes" }, "tagRenderings": [ { "id": "show_changeset_id", "render": { - "en": "Changeset {id}", - "ca": "Conjunt de canvi {id}", - "cs": "Změny {id}", - "de": "Änderungssatz {id}", - "es": "Conjunto de cambios {id}", - "fr": "Groupe de modifications {id}", - "pl": "Zestaw zmian {id}" + "en": "Changeset {id}" } }, { "id": "contributor", "question": { - "en": "Which contributor made this change?", - "ca": "Quin col·laborador va fer aquest canvi?", - "cs": "Který přispěvatel tuto změnu provedl?", - "de": "Wer hat diese Änderung vorgenommen?", - "es": "¿Qué contribuidor hizo este cambio?", - "fr": "Quel contributeur a fait cette modification ?", - "nl": "Welke bijdrager maakte deze wijziging?", - "pl": "Który współautor dokonał tej zmiany?" + "en": "What contributor did make this change?" }, "freeform": { "key": "user" }, "render": { - "en": "Change made by {user}", - "ca": "Canvi fet per {user}", - "cs": "Změna provedená {user}", - "de": "Änderung von {user}", - "es": "Cambio realizado por {user}", - "fr": "Modification faite par {user}", - "nl": "Wijziging gemaakt door {user}", - "pl": "Zmiana dokonana przez {user}" + "en": "Change made by {user}" } }, { "id": "theme-id", "question": { - "en": "What theme was used to make this change?", - "ca": "Quin tema es va utilitzar per fer aquest canvi?", - "cs": "Jaké téma bylo použito k provedení této změny?", - "de": "Welches Thema wurde für diese Änderung verwendet?", - "es": "¿Qué tema se utilizó para realizar este cambio?", - "fr": "Quel thème a été utilisé pour faire cette modification ?", - "pl": "Jakiego tematu użyto do wprowadzenia tej zmiany?" + "en": "What theme was used to make this change?" }, "freeform": { "key": "theme" }, "render": { - "en": "Change with theme {theme}", - "ca": "Canvi amb el tema {theme}", - "cs": "Změna s motivem {theme}", - "de": "Geändert mit Thema {theme}", - "es": "Cambio con tema {theme}", - "fr": "Modifié avec le thème {theme}", - "pl": "Zmiana za pomocą motywu {theme}" + "en": "Change with theme {theme}" } }, { @@ -144,45 +74,19 @@ "key": "locale" }, "question": { - "en": "What locale (language) was this change made in?", - "ca": "Amb quina configuració regional (idioma) s'ha fet aquest canvi?", - "cs": "V jakém národním prostředí (jazyce) byla tato změna provedena?", - "de": "In welcher Benutzersprache wurde diese Änderung vorgenommen?", - "es": "¿En qué configuración regional (idioma) se realizó este cambio?", - "fr": "En quelle langue est-ce que ce changement a été fait ?", - "nl": "In welke locale (taal) werd deze wijziging gemaakt?", - "pl": "W jakim języku wprowadzono tę zmianę?" + "en": "What locale (language) was this change made in?" }, "render": { - "en": "User locale is {locale}", - "ca": "La configuració regional de l'usuari és {locale}", - "cs": "Uživatelské prostředí je {locale}", - "de": "Benutzersprache {locale}", - "es": "La configuración regional del usuario es {locale}", - "nl": "De gebruikerstaal is {locale}", - "pl": "Ustawienia regionalne użytkownika to {locale}" + "en": "User locale is {locale}" } }, { "id": "host", "render": { - "en": "Change made with {host}", - "ca": "Canviat fet amb {host}", - "cs": "Změna provedená pomocí {host}", - "de": "Geändert über {host}", - "es": "Cambio realizado con {host}", - "fr": "Modification faite avec {host}", - "nl": "Wijziging gemaakt met {host}", - "pl": "Zmiana dokonana w {host}" + "en": "Change with with {host}" }, "question": { - "en": "What host (website) was this change made with?", - "ca": "Amb quin amfitrió (lloc web) es va fer aquest canvi?", - "cs": "U jakého hostitele (webové stránky) byla tato změna provedena?", - "de": "Über welchen Host (Webseite) wurde diese Änderung vorgenommen?", - "es": "¿Con qué host (página web) se realizó este cambio?", - "nl": "Met welke host (website) werd deze wijziging gemaakt?", - "pl": "Na jakim hoście (stronie internetowej) dokonano tej zmiany?" + "en": "What host (website) was this change made with?" }, "freeform": { "key": "host" @@ -203,22 +107,10 @@ { "id": "version", "question": { - "en": "What version of MapComplete was used to make this change?", - "ca": "Quina versió de MapComplete es va utilitzar per fer aquest canvi?", - "cs": "Jaká verze aplikace MapComplete byla použita k provedení této změny?", - "de": "Mit welcher Version von MapComplete wurde diese Änderung gemacht?", - "es": "¿Qué versión de MapComplete se usó para realizar este cambio?", - "fr": "Quelle version de MapComplete a été utilisée pour faire cette modification ?", - "pl": "Która wersja MapComplete została wykorzystana, aby zrobić tę zmianę?" + "en": "What version of MapComplete was used to make this change?" }, "render": { - "en": "Made with {editor}", - "ca": "Fet amb {editor}", - "cs": "Vyrobeno pomocí {editor}", - "de": "Erstellt mit {editor}", - "es": "Realizado con {editor}", - "fr": "Fait avec {editor}", - "pl": "Zrobione za pomocą {editor}" + "en": "Made with {editor}" }, "freeform": { "key": "editor" @@ -568,13 +460,7 @@ } ], "question": { - "en": "Theme name contains {search}", - "ca": "El nom del tema conté {search}", - "cs": "Název motivu obsahuje {search}", - "de": "Themenname enthält {search}", - "es": "El nombre del tema contiene {search}", - "nl": "Themenaam bevat {search}", - "pl": "Nazwa tematu zawiera {search}" + "en": "Themename contains {search}" } } ] @@ -590,7 +476,7 @@ } ], "question": { - "en": "Theme name does not contain {search}" + "en": "Themename does not contain {search}" } } ] @@ -606,13 +492,7 @@ } ], "question": { - "en": "Made by contributor {search}", - "ca": "Fet pel col·laborador {search}", - "cs": "Vytvořil přispěvatel {search}", - "de": "Erstellt von {search}", - "es": "Hecho por el colaborador {search}", - "nl": "Gemaakt door bijdrager {search}", - "pl": "Wykonane przez współautora {search}" + "en": "Made by contributor {search}" } } ] @@ -628,13 +508,7 @@ } ], "question": { - "en": "Not made by contributor {search}", - "ca": "No fet pel col·laborador {search}", - "cs": "Není vytvořeno přispěvatelem {search}", - "de": "Nicht erstellt von {search}", - "es": "No hecho por el colaborador {search}", - "nl": "Niet gemaakt door bijdrager {search}", - "pl": "Nie wykonane przez współautora {search}" + "en": "Not made by contributor {search}" } } ] @@ -651,13 +525,7 @@ } ], "question": { - "en": "Made before {search}", - "ca": "Fet abans de {search}", - "cs": "Vytvořeno před {search}", - "de": "Erstellt vor {search}", - "es": "Hecho antes de {search}", - "nl": "Gemaakt voor {search}", - "pl": "Stworzone przed {search}" + "en": "Made before {search}" } } ] @@ -674,13 +542,7 @@ } ], "question": { - "en": "Made after {search}", - "ca": "Fet després de {search}", - "cs": "Vytvořeno po {search}", - "de": "Erstellt nach {search}", - "es": "Hecho después de {search}", - "nl": "Gemaakt na {search}", - "pl": "Stworzone po {search}" + "en": "Made after {search}" } } ] @@ -696,14 +558,7 @@ } ], "question": { - "en": "User language (iso-code) {search}", - "ca": "Idioma de l'usuari (codi iso) {search}", - "cs": "Jazyk uživatele (iso-kód) {search}", - "de": "Benutzersprache (ISO-Code) {search}", - "es": "Use idioma (ISO-code) {search}", - "fr": "Langage utilisateur (code-ISO) {search}", - "nl": "De taal van de bijdrager is {search}", - "pl": "Język użytkownika (kod iso) {search}" + "en": "User language (iso-code) {search}" } } ] @@ -719,13 +574,7 @@ } ], "question": { - "en": "Made with host {search}", - "ca": "Fet amb l'amfitrió {search}", - "cs": "Vytvořeno pomocí hostitele {search}", - "de": "Erstellt mit Host {search}", - "es": "Hecho con el host {search}", - "nl": "Gemaakt met host {search}", - "pl": "Wykonane z hostem {search}" + "en": "Made with host {search}" } } ] @@ -736,14 +585,7 @@ { "osmTags": "add-image>0", "question": { - "en": "Changeset added at least one image", - "ca": "El conjunt de canvis ha afegit almenys una imatge", - "cs": "Sada změn přidala alespoň jeden obrázek", - "de": "Im Änderungssatz wurde mindestens ein Bild hinzugefügt", - "es": "El conjunto de cambios ha añadido al menos una imagen", - "fr": "Le groupe de modifications a ajouté au moins une image", - "nl": "Changeset bevat minstens één afbeelding", - "pl": "Zestaw zmian dodał co najmniej jedno zdjęcie" + "en": "Changeset added at least one image" } } ] @@ -754,7 +596,7 @@ { "osmTags": "theme!=grb", "question": { - "en": "Made with host {search}" + "en": "Exclude GRB theme" } } ] @@ -765,7 +607,7 @@ { "osmTags": "theme!=etymology", "question": { - "en": "Changeset added at least one image" + "en": "Exclude etymology theme" } } ] @@ -780,13 +622,7 @@ { "id": "link_to_more", "render": { - "en": "More statistics can be found here", - "ca": "Es pot trobar més estadística aquí", - "cs": "Další statistiky najdete here", - "de": "Mehr Statistiken gibt es hier", - "es": "Puede encontrar más estadísticas aquí", - "fr": "D'autres statistiques sont disponibles ici", - "pl": "Więcej statystyk można znaleźć tutaj" + "en": "More statistics can be found here" } }, { diff --git a/package-lock.json b/package-lock.json index 8a7bd50471..df38ae4be8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "mapcomplete", - "version": "0.36.1", + "version": "0.36.2", "lockfileVersion": 2, "requires": true, "packages": { diff --git a/package.json b/package.json index 18288ad896..6d8c775218 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "mapcomplete", - "version": "0.36.1", + "version": "0.36.2", "repository": "https://github.com/pietervdvn/MapComplete", "description": "A small website to edit OSM easily", "bugs": "https://github.com/pietervdvn/MapComplete/issues", diff --git a/public/css/index-tailwind-output.css b/public/css/index-tailwind-output.css index 6cbce46099..6d74eb7958 100644 --- a/public/css/index-tailwind-output.css +++ b/public/css/index-tailwind-output.css @@ -777,10 +777,6 @@ video { float: left; } -.m-8 { - margin: 2rem; -} - .m-4 { margin: 1rem; } @@ -793,6 +789,10 @@ video { margin: 0px; } +.m-8 { + margin: 2rem; +} + .m-2 { margin: 0.5rem; } @@ -1188,6 +1188,10 @@ video { max-height: 6rem; } +.max-h-64 { + max-height: 16rem; +} + .max-h-7 { max-height: 1.75rem; } @@ -1266,6 +1270,10 @@ video { width: 3.5rem; } +.w-auto { + width: auto; +} + .w-5 { width: 1.25rem; } @@ -1283,10 +1291,6 @@ video { width: 12rem; } -.w-auto { - width: auto; -} - .max-w-full { max-width: 100%; } @@ -1685,6 +1689,10 @@ video { border-style: dotted; } +.border-none { + border-style: none; +} + .border-black { --tw-border-opacity: 1; border-color: rgb(0 0 0 / var(--tw-border-opacity)); @@ -2234,6 +2242,11 @@ body { font-family: "Helvetica Neue", Arial, sans-serif; } +.focusable { + /* Not a 'real' class, but rather an indication to FloatOver and ModalRight to, when they open, grab the focus */ + border: 1px solid red +} + svg, img { box-sizing: content-box; diff --git a/scripts/generateImageAnalysis.ts b/scripts/generateImageAnalysis.ts index 13478819e5..c704e86229 100644 --- a/scripts/generateImageAnalysis.ts +++ b/scripts/generateImageAnalysis.ts @@ -4,7 +4,7 @@ import { RegexTag } from "../src/Logic/Tags/RegexTag" import { ImmutableStore } from "../src/Logic/UIEventSource" import { BBox } from "../src/Logic/BBox" import * as fs from "fs" -import { writeFileSync } from "fs" +import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs" import { Feature } from "geojson" import ScriptUtils from "./ScriptUtils" import { Imgur } from "../src/Logic/ImageProviders/Imgur" @@ -181,6 +181,32 @@ export default class GenerateImageAnalysis extends Script { } } + async downloadViews(datapath: string): Promise { + const { allImages, imageSource } = this.loadImageUrls(datapath) + console.log("Detected", allImages.size, "images") + const results: [string, number][] = [] + const today = new Date().toISOString().substring(0, "YYYY-MM-DD".length) + const viewDir = datapath + "/views_" + today + if (!existsSync(viewDir)) { + mkdirSync(viewDir) + } + + for (const image of Array.from(allImages)) { + const cachedView = viewDir + "/" + image.replace(/\\/g, "_") + let attribution: LicenseInfo + if (existsSync(cachedView)) { + attribution = JSON.parse(readFileSync(cachedView, "utf8")) + } else { + attribution = await Imgur.singleton.DownloadAttribution(image) + writeFileSync(cachedView, JSON.stringify(attribution)) + } + results.push([image, attribution.views]) + } + const targetpath = datapath + "/views.csv" + console.log("Writing views to", targetpath) + fs.writeFileSync(targetpath, results.map((r) => r.join(",")).join("\n")) + } + async downloadImage(url: string, imagePath: string): Promise { const filenameLong = url.replace(/[\/:.\-%]/g, "_") + ".jpg" const targetPathLong = imagePath + "/" + filenameLong @@ -391,6 +417,7 @@ export default class GenerateImageAnalysis extends Script { await this.downloadData(datapath, cached) await this.downloadMetadata(datapath) + await this.downloadViews(datapath) await this.downloadAllImages(datapath, imageBackupPath) this.analyze(datapath) } diff --git a/src/Logic/Actors/SelectedElementTagsUpdater.ts b/src/Logic/Actors/SelectedElementTagsUpdater.ts index 8bc510c56b..37297111d1 100644 --- a/src/Logic/Actors/SelectedElementTagsUpdater.ts +++ b/src/Logic/Actors/SelectedElementTagsUpdater.ts @@ -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) - } - } } diff --git a/src/Logic/ImageProviders/ImageUploadManager.ts b/src/Logic/ImageProviders/ImageUploadManager.ts index 17b3b8f56a..30d2ea638d 100644 --- a/src/Logic/ImageProviders/ImageUploadManager.ts +++ b/src/Logic/ImageProviders/ImageUploadManager.ts @@ -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 diff --git a/src/Logic/UIEventSource.ts b/src/Logic/UIEventSource.ts index b80c08f384..658d21aff7 100644 --- a/src/Logic/UIEventSource.ts +++ b/src/Logic/UIEventSource.ts @@ -97,7 +97,10 @@ export abstract class Store implements Readable { abstract map(f: (t: T) => J): Store abstract map(f: (t: T) => J, extraStoresToWatch: Store[]): Store - public mapD(f: (t: Exclude) => J, extraStoresToWatch?: Store[]): Store { + public mapD( + f: (t: Exclude) => J, + extraStoresToWatch?: Store[] + ): Store { return this.map((t) => { if (t === undefined) { return undefined @@ -105,7 +108,7 @@ export abstract class Store implements Readable { if (t === null) { return null } - return f(> t) + return f(>t) }, extraStoresToWatch) } @@ -201,24 +204,36 @@ export abstract class Store implements Readable { 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(f: (t: Exclude) => Store): Store { + return this.bind((t) => { + if (t === undefined || t === null) { + return t + } + return f(>t) + }) + } public stabilized(millisToStabilize): Store { if (Utils.runningFromConsole) { return this @@ -771,7 +786,10 @@ export class UIEventSource extends Store implements Writable { * 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(f: (t: Exclude) => J, extraSources: Store[] = []): Store { + public mapD( + f: (t: Exclude) => J, + extraSources: Store[] = [] + ): Store { return new MappedStore( this, (t) => { @@ -781,11 +799,13 @@ export class UIEventSource extends Store implements Writable { if (t === null) { return null } - return f(> t) + return f(>t) }, extraSources, this._callbacks, - (this.data === undefined || this.data === null) ?( this.data) : f( this.data) + this.data === undefined || this.data === null + ? this.data + : f(this.data) ) } diff --git a/src/Models/MenuState.ts b/src/Models/MenuState.ts index 1238580237..a64c05a0a5 100644 --- a/src/Models/MenuState.ts +++ b/src/Models/MenuState.ts @@ -79,6 +79,11 @@ export class MenuState { this.highlightedUserSetting.setData(undefined) } }) + this.menuViewTab.addCallbackD((tab) => { + if (tab !== "settings") { + this.highlightedUserSetting.setData(undefined) + } + }) this.themeViewTab.addCallbackAndRun((tab) => { if (tab !== "filters") { this.highlightedLayerInFilters.setData(undefined) diff --git a/src/Models/ThemeViewState.ts b/src/Models/ThemeViewState.ts index 2086dcef6b..af1de00e97 100644 --- a/src/Models/ThemeViewState.ts +++ b/src/Models/ThemeViewState.ts @@ -475,9 +475,12 @@ export default class ThemeViewState implements SpecialVisualizationState { { nomod: "Escape", onUp: true }, Translations.t.hotkeyDocumentation.closeSidebar, () => { + if (this.previewedImage.data !== undefined) { + this.previewedImage.setData(undefined) + return + } this.selectedElement.setData(undefined) this.guistate.closeAll() - this.previewedImage.setData(undefined) this.focusOnMap() } ) diff --git a/src/UI/Base/FileSelector.svelte b/src/UI/Base/FileSelector.svelte index b23ab96954..f9483c2a6b 100644 --- a/src/UI/Base/FileSelector.svelte +++ b/src/UI/Base/FileSelector.svelte @@ -36,7 +36,9 @@ dispatcher("submit", e.dataTransfer.files) }} > -