From 561e4cb00990cb1e1e38f9459ff858256b9acfaf Mon Sep 17 00:00:00 2001 From: Pieter Vander Vennet Date: Fri, 8 Aug 2025 13:19:49 +0200 Subject: [PATCH] Feature(offline): more offline hardening --- src/Models/ThemeConfig/TagRenderingConfig.ts | 22 ++++-- src/UI/Comparison/ComparisonTool.svelte | 73 ++++++++++---------- src/UI/Image/UploadingImageCounter.svelte | 2 +- src/UI/Popup/DeleteFlow/DeleteFlowState.ts | 6 +- src/UI/Popup/DeleteFlow/DeleteWizard.svelte | 11 ++- src/UI/Popup/MarkAsFavourite.svelte | 48 +++++++------ src/UI/SingleThemeGui.svelte | 10 +++ 7 files changed, 103 insertions(+), 69 deletions(-) diff --git a/src/Models/ThemeConfig/TagRenderingConfig.ts b/src/Models/ThemeConfig/TagRenderingConfig.ts index c4bbdb7f1..f034e3d1a 100644 --- a/src/Models/ThemeConfig/TagRenderingConfig.ts +++ b/src/Models/ThemeConfig/TagRenderingConfig.ts @@ -19,6 +19,7 @@ import LayerConfig from "./LayerConfig" import ComparingTag from "../../Logic/Tags/ComparingTag" import { Unit } from "../Unit" import { Lists } from "../../Utils/Lists" +import { IsOnline } from "../../Logic/Web/IsOnline" export interface Mapping { readonly if: UploadableTag @@ -1154,10 +1155,10 @@ export class TagRenderingConfigUtils { const extraMappings = tags.bindD((tags) => { const country = tags._country if (country === undefined) { - return undefined + return undefined } const center = GeoOperations.centerpointCoordinates(feature) - return UIEventSource.fromPromise( + return UIEventSource.fromPromiseWithErr( NameSuggestionIndex.generateMappings( config.freeform.key, tags, @@ -1167,7 +1168,20 @@ export class TagRenderingConfigUtils { ) ) }) - return extraMappings.mapD((extraMappings) => { + return extraMappings.map((extraMappingsErr) => { + if(extraMappingsErr?.["error"]){ + console.log("Could not download the NSI: ", extraMappingsErr["error"]) + return config + } + const extraMappings = extraMappingsErr?.success + if(extraMappings === undefined){ + if(!IsOnline.isOnline.data){ + // The 'extraMappings' will still attempt to download the NSI - it might be in the service worker's cache + // As such, if they happen to come through anyway, they'll be shown + return config + } + return undefined + } if (extraMappings.length == 0) { return config } @@ -1187,6 +1201,6 @@ export class TagRenderingConfigUtils { }) ?? [] clone.mappings = [...oldMappingsCloned, ...extraMappings] return clone - }) + }, [IsOnline.isOnline]) } } diff --git a/src/UI/Comparison/ComparisonTool.svelte b/src/UI/Comparison/ComparisonTool.svelte index 58f90f27c..fb8b3fff0 100644 --- a/src/UI/Comparison/ComparisonTool.svelte +++ b/src/UI/Comparison/ComparisonTool.svelte @@ -13,9 +13,10 @@ import Translations from "../i18n/Translations" import Tr from "../Base/Tr.svelte" import AccordionSingle from "../Flowbite/AccordionSingle.svelte" - import GlobeAlt from "@babeard/svelte-heroicons/mini/GlobeAlt" import { ComparisonState } from "./ComparisonState" import LoginToggle from "../Base/LoginToggle.svelte" + import { IsOnline } from "../../Logic/Web/IsOnline" + import GlobeAlt from "@babeard/svelte-heroicons/mini/GlobeAlt" export let externalData: Store< | { success: { content: Record } } @@ -33,7 +34,7 @@ * A switch that signals that the information should be downloaded. * The actual 'download' code is _not_ implemented here */ - export let downloadInformation : UIEventSource + export let downloadInformation: UIEventSource export let collapsed: boolean const t = Translations.t.external @@ -48,45 +49,47 @@ let propertyKeysExternal = comparisonState.mapD((ct) => ct.propertyKeysExternal) let hasDifferencesAtStart = comparisonState.mapD((ct) => ct.hasDifferencesAtStart) let enableLogin = state.featureSwitches.featureSwitchEnableLogin + const online = IsOnline.isOnline - - - {#if !$sourceUrl || !$enableLogin} - +{#if $online} + + {#if !$sourceUrl || !$enableLogin} + {:else if !$downloadInformation} - - {:else if $externalData === undefined} -
- -
- {:else if $externalData["error"] !== undefined} -
- -
- {:else if $propertyKeysExternal.length === 0 && $knownImages.size + $unknownImages.length === 0} - - {:else if !$hasDifferencesAtStart} + + {:else if $externalData === undefined} +
+ +
+ {:else if $externalData["error"] !== undefined} +
+ +
+ {:else if $propertyKeysExternal.length === 0 && $knownImages.size + $unknownImages.length === 0} + + {:else if !$hasDifferencesAtStart} - {:else if $comparisonState !== undefined} - + {:else if $comparisonState !== undefined} + - - - {/if} -
+ + + {/if} +
+{/if} diff --git a/src/UI/Image/UploadingImageCounter.svelte b/src/UI/Image/UploadingImageCounter.svelte index e8aac6e49..4e214a4d2 100644 --- a/src/UI/Image/UploadingImageCounter.svelte +++ b/src/UI/Image/UploadingImageCounter.svelte @@ -93,7 +93,7 @@ {/if} -{#if !$online} +{#if !$online && $pending > 0}
diff --git a/src/UI/Popup/DeleteFlow/DeleteFlowState.ts b/src/UI/Popup/DeleteFlow/DeleteFlowState.ts index 53f815290..6f750e7f5 100644 --- a/src/UI/Popup/DeleteFlow/DeleteFlowState.ts +++ b/src/UI/Popup/DeleteFlow/DeleteFlowState.ts @@ -17,20 +17,18 @@ export class DeleteFlowState { private readonly _id: OsmId private readonly _allowDeletionAtChangesetCount: number private readonly _osmConnection: OsmConnection - private readonly state: SpecialVisualizationState constructor( id: OsmId, state: SpecialVisualizationState, allowDeletionAtChangesetCount?: number ) { - this.state = state this.objectDownloader = state.osmObjectDownloader this._id = id this._osmConnection = state.osmConnection this._allowDeletionAtChangesetCount = allowDeletionAtChangesetCount ?? Number.MAX_VALUE - this.CheckDeleteability(false) + this.checkDeleteability(false) } /** @@ -39,7 +37,7 @@ export class DeleteFlowState { * @constructor * @private */ - public CheckDeleteability(useTheInternet: boolean): void { + public checkDeleteability(useTheInternet: boolean): void { console.log("Checking deleteability (internet?", useTheInternet, ")") const t = Translations.t.delete const id = this._id diff --git a/src/UI/Popup/DeleteFlow/DeleteWizard.svelte b/src/UI/Popup/DeleteFlow/DeleteWizard.svelte index 11dc23a4f..27c9ce11e 100644 --- a/src/UI/Popup/DeleteFlow/DeleteWizard.svelte +++ b/src/UI/Popup/DeleteFlow/DeleteWizard.svelte @@ -22,6 +22,7 @@ import Invalid from "../../../assets/svg/Invalid.svelte" import { And } from "../../../Logic/Tags/And" import type { UploadableTag } from "../../../Logic/Tags/TagTypes" + import { IsOnline } from "../../../Logic/Web/IsOnline" export let state: SpecialVisualizationState export let deleteConfig: DeleteConfig @@ -39,9 +40,10 @@ const canBeDeletedReason = deleteAbility.canBeDeletedReason const hasSoftDeletion = deleteConfig.softDeletionTags !== undefined + const online = IsOnline.isOnline let currentState: "confirm" | "applying" | "deleted" = "confirm" $: { - deleteAbility.CheckDeleteability(true) + deleteAbility.checkDeleteability(true) } const t = Translations.t.delete @@ -97,8 +99,10 @@ currentState = "deleted" } - - +{#if !$online} +
You are offline. Deleting points is not possible
+ {:else} + {#if $canBeDeleted === false && !hasSoftDeletion}
@@ -171,3 +175,4 @@ {/if} + {/if} diff --git a/src/UI/Popup/MarkAsFavourite.svelte b/src/UI/Popup/MarkAsFavourite.svelte index b6df673a6..e15f28d3c 100644 --- a/src/UI/Popup/MarkAsFavourite.svelte +++ b/src/UI/Popup/MarkAsFavourite.svelte @@ -7,41 +7,45 @@ import LoginToggle from "../Base/LoginToggle.svelte" import type { Feature } from "geojson" import LayerConfig from "../../Models/ThemeConfig/LayerConfig" + import { IsOnline } from "../../Logic/Web/IsOnline" + import { Store } from "../../Logic/UIEventSource.js" /** * A full-blown 'mark as favourite'-button */ export let state: SpecialVisualizationState export let feature: Feature - export let tags: Record + export let tags: Store> export let layer: LayerConfig let isFavourite = tags?.map((tags) => tags._favourite === "yes") const t = Translations.t.favouritePoi + const online = IsOnline.isOnline function markFavourite(isFavourite: boolean) { state.favourites.markAsFavourite(feature, layer.id, state.theme.id, tags, isFavourite) } - - - {#if $isFavourite} -
- +
+ + {:else} + -
- - {:else} - - {/if} - + {/if} + +{/if} diff --git a/src/UI/SingleThemeGui.svelte b/src/UI/SingleThemeGui.svelte index a7e83c5d0..7b0a76b61 100644 --- a/src/UI/SingleThemeGui.svelte +++ b/src/UI/SingleThemeGui.svelte @@ -71,6 +71,16 @@ window.requestIdleCallback(() => { InstallServiceWorker.precache(layer["_usedImages"]?.filter(i => i.startsWith("./"))) }) + + // The NSI + window.requestIdleCallback(() => { + InstallServiceWorker.precache( + [Constants.nsiLogosEndpoint + "nsi.min.json", + Constants.nsiLogosEndpoint + "featureCollection.min.json", + ], + ) + }) + } }).catch(e => console.error("Could not install service worker:", e))