Feature(offline): more offline hardening

This commit is contained in:
Pieter Vander Vennet 2025-08-08 13:19:49 +02:00
parent f1da97285f
commit 561e4cb009
7 changed files with 103 additions and 69 deletions

View file

@ -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
@ -1157,7 +1158,7 @@ export class TagRenderingConfigUtils {
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])
}
}

View file

@ -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<string, string> } }
@ -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<boolean>
export let downloadInformation: UIEventSource<boolean>
export let collapsed: boolean
const t = Translations.t.external
@ -48,9 +49,10 @@
let propertyKeysExternal = comparisonState.mapD((ct) => ct.propertyKeysExternal)
let hasDifferencesAtStart = comparisonState.mapD((ct) => ct.hasDifferencesAtStart)
let enableLogin = state.featureSwitches.featureSwitchEnableLogin
const online = IsOnline.isOnline
</script>
<LoginToggle {state} silentFail>
{#if $online}
<LoginToggle {state} hiddenFail>
{#if !$sourceUrl || !$enableLogin}
<!-- empty block -->
{:else if !$downloadInformation}
@ -89,4 +91,5 @@
/>
</AccordionSingle>
{/if}
</LoginToggle>
</LoginToggle>
{/if}

View file

@ -93,7 +93,7 @@
</Loading>
</div>
{/if}
{#if !$online}
{#if !$online && $pending > 0}
<div class="alert">
<Tr t={t.upload.offline} />
</div>

View file

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

View file

@ -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"
}
</script>
<LoginToggle ignoreLoading={true} {state} silentFail>
{#if !$online}
<div class="subtle">You are offline. Deleting points is not possible</div>
{:else}
<LoginToggle ignoreLoading={true} {state} hiddenFail>
{#if $canBeDeleted === false && !hasSoftDeletion}
<div class="low-interaction subtle flex gap-x-1 rounded p-2 text-sm italic">
<div class="relative h-fit">
@ -171,3 +175,4 @@
</AccordionSingle>
{/if}
</LoginToggle>
{/if}

View file

@ -7,23 +7,26 @@
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<string, string>
export let tags: Store<Record<string, string>>
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)
}
</script>
<LoginToggle ignoreLoading={true} {state}>
{#if $online}
<LoginToggle ignoreLoading hiddenFail {state}>
{#if $isFavourite}
<div class="flex h-fit items-start">
<button class="w-full" on:click={() => markFavourite(false)}>
@ -44,4 +47,5 @@
</div>
</button>
{/if}
</LoginToggle>
</LoginToggle>
{/if}

View file

@ -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))
</script>