UX: indicate incomplete search in offline mode

This commit is contained in:
Pieter Vander Vennet 2025-08-01 00:42:15 +02:00
parent 76dbf50db1
commit b6366412ea
13 changed files with 69 additions and 5 deletions

View file

@ -6,6 +6,8 @@ export default class CombinedSearcher implements GeocodingProvider {
public readonly name = "CombinedSearcher"
private _providers: ReadonlyArray<GeocodingProvider>
private _providersWithSuggest: ReadonlyArray<GeocodingProvider>
public readonly needsInternet
/**
* Merges the various providers together; ignores errors.
@ -15,6 +17,7 @@ export default class CombinedSearcher implements GeocodingProvider {
constructor(...providers: ReadonlyArray<GeocodingProvider>) {
this._providers = Utils.NoNull(providers)
this._providersWithSuggest = this._providers.filter((pr) => pr.suggest !== undefined)
this.needsInternet = this._providers.some(p => p.needsInternet)
}
/**

View file

@ -8,6 +8,8 @@ import CoordinateParser from "coordinate-parser"
*/
export default class CoordinateSearch implements GeocodingProvider {
public readonly name = "CoordinateSearch"
public readonly needsInternet = false
private static readonly latLonRegexes: ReadonlyArray<RegExp> = [
/^ *(-?[0-9]+\.[0-9]+)[ ,;/\\]+(-?[0-9]+\.[0-9]+)/,
/^ *(-?[0-9]+,[0-9]+)[ ;/\\]+(-?[0-9]+,[0-9]+)/,

View file

@ -49,6 +49,7 @@ export interface GeocodingOptions {
export default interface GeocodingProvider {
readonly name: string
readonly needsInternet: boolean
/**
* Performs search.

View file

@ -21,6 +21,8 @@ export default class LocalElementSearch implements GeocodingProvider {
private readonly _state: ThemeViewState
private readonly _limit: number
public readonly name = "LocalElementSearch"
public readonly needsInternet = false
constructor(state: ThemeViewState, limit: number) {
this._state = state
this._limit = limit

View file

@ -10,6 +10,7 @@ export class NominatimGeocoding implements GeocodingProvider {
private readonly _host
private readonly limit: number
public readonly name = "Nominatim"
public readonly needsInternet = true
constructor(limit: number = 3, host: string = Constants.nominatimEndpoint) {
this.limit = limit

View file

@ -3,6 +3,8 @@ import GeocodingProvider, { GeocodeResult, GeocodingOptions } from "./GeocodingP
import { decode as pluscode_decode } from "pluscodes"
export default class OpenLocationCodeSearch implements GeocodingProvider {
public readonly needsInternet = false
/**
* A regex describing all plus-codes
*/

View file

@ -7,6 +7,7 @@ import OsmObjectDownloader from "../Osm/OsmObjectDownloader"
export default class OpenStreetMapIdSearch implements GeocodingProvider {
private static readonly regex =
/((https?:\/\/)?(www.)?(osm|openstreetmap).org\/)?(n|node|w|way|r|relation)[/ ]?([0-9]+)/
public readonly needsInternet = true
public readonly name = "OpenStreetMapId"
private static readonly types: Readonly<Record<string, "node" | "way" | "relation">> = {
n: "node",

View file

@ -5,7 +5,7 @@ import GeocodingProvider, {
GeocodingOptions,
GeocodingUtils,
ReverseGeocodingProvider,
ReverseGeocodingResult,
ReverseGeocodingResult
} from "./GeocodingProvider"
import { Utils } from "../../Utils"
import { Feature, FeatureCollection } from "geojson"
@ -15,6 +15,7 @@ import { Store, Stores } from "../UIEventSource"
export default class PhotonSearch implements GeocodingProvider, ReverseGeocodingProvider {
private readonly _endpoint: string
public readonly needsInternet = true
public readonly name = "photon"
private supportedLanguages = ["en", "de", "fr"]
private static readonly types = {
@ -23,17 +24,14 @@ export default class PhotonSearch implements GeocodingProvider, ReverseGeocoding
N: "node",
}
private readonly ignoreBounds: boolean
private readonly suggestionLimit: number = 5
private readonly searchLimit: number = 1
constructor(
ignoreBounds: boolean = false,
suggestionLimit: number = 5,
searchLimit: number = 1,
endpoint?: string
) {
this.ignoreBounds = ignoreBounds
this.suggestionLimit = suggestionLimit
this.searchLimit = searchLimit
this._endpoint = endpoint ?? Constants.photonEndpoint ?? "https://photon.komoot.io/"

View file

@ -20,6 +20,7 @@ import { BBox } from "../BBox"
import { QueryParameters } from "../Web/QueryParameters"
import { Utils } from "../../Utils"
import { NominatimGeocoding } from "../Search/NominatimGeocoding"
import { IsOnline } from "../Web/IsOnline"
export default class SearchState {
public readonly feedback: UIEventSource<Translation> = new UIEventSource<Translation>(undefined)
@ -62,7 +63,9 @@ export default class SearchState {
if (search.length === 0) {
return undefined
}
return this.locationSearchers.map((ls) => ({
return this.locationSearchers
.filter(ls => !ls.needsInternet || IsOnline.isOnline.data)
.map((ls) => ({
source: ls,
results: ls.suggest(search, { bbox: bounds.data }),
}))

View file

@ -0,0 +1,15 @@
<!-- Shows a red cross at the bottom of the icon-->
<script>
import Cross_bottom_right from "../../assets/svg/Cross_bottom_right.svelte"
export let size = "w-12 h-12"
</script>
<div class={size+" relative"}>
<div class="absolute top-0 left-0">
<slot />
</div>
<div class="absolute top-0 left-0">
<Cross_bottom_right class={size} color="red" />
</div>
</div>

View file

@ -0,0 +1,22 @@
<script lang="ts">
import { UIEventSource } from "../../Logic/UIEventSource"
import { Utils } from "../../Utils"
import Loading from "../Base/Loading.svelte"
let loadedAssets = new UIEventSource<any>(undefined)
async function update() {
loadedAssets.set(await Utils.downloadJson("./service-worker/status.json"))
}
update()
</script>
{#if $loadedAssets === undefined}
<Loading />
{:else}
<button on:click={() => update()}>Update</button>
{JSON.stringify($loadedAssets)}
{/if}

View file

@ -12,6 +12,7 @@
import type { GeocodeResult } from "../../Logic/Search/GeocodingProvider"
import type { MapProperties } from "../../Models/MapProperties"
import { ExclamationTriangle } from "@babeard/svelte-heroicons/solid/ExclamationTriangle"
import { IsOnline } from "../../Logic/Web/IsOnline"
export let state: {
searchState: {

View file

@ -16,6 +16,9 @@
import DotMenu from "../Base/DotMenu.svelte"
import type { GeocodeResult } from "../../Logic/Search/GeocodingProvider"
import { default as GeocodeResultSvelte } from "./GeocodeResult.svelte"
import { IsOnline } from "../../Logic/Web/IsOnline"
import CrossedOut from "../Base/CrossedOut.svelte"
import Wifi from "@babeard/svelte-heroicons/solid/Wifi"
/**
* The big overview of all search bar results
@ -41,6 +44,7 @@
let allowOtherThemes = state.featureSwitches.featureSwitchBackToThemeOverview
let allowFilters = state.featureSwitches.featureSwitchFilter
let isOnline = IsOnline.isOnline
</script>
<div class="low-interaction flex flex-col gap-y-2 p-4">
@ -55,6 +59,15 @@
</div>
{/if}
{#if !$isOnline}
<div class="alert flex items-center">
<CrossedOut size="w-8 h-8">
<Wifi class="w-8 h-8" />
</CrossedOut>
Your device is currently offline. This impacts search results
</div>
{/if}
{#if $allowFilters}
<FilterResults {state} />
{/if}