Feature: improve offline data management

This commit is contained in:
Pieter Vander Vennet 2025-08-01 00:44:08 +02:00
parent 6f44fe31d0
commit 77ef3a3572

View file

@ -18,8 +18,9 @@
import ShowDataLayer from "../Map/ShowDataLayer"
import StaticFeatureSource from "../../Logic/FeatureSource/Sources/StaticFeatureSource"
import LayerConfig from "../../Models/ThemeConfig/LayerConfig"
import AccordionSingle from "../Flowbite/AccordionSingle.svelte"
import { DownloadIcon, TrashIcon } from "@rgossiaux/svelte-heroicons/solid"
import { Accordion, AccordionItem } from "flowbite-svelte"
import ServiceWorkerStatus from "./ServiceWorkerStatus.svelte"
export let state: ThemeViewState & SpecialVisualizationState = undefined
@ -35,7 +36,7 @@
const offlineMapManager = new OfflineBasemapManager("https://cache.mapcomplete.org/")
let installedMeta: UIEventSource<AreaDescription[]> = new UIEventSource()
let installedMeta: UIEventSource<AreaDescription[]> = new UIEventSource([])
function updateMeta() {
@ -43,15 +44,7 @@
}
async function pingServiceWorker() {
const l = window.location
const sw = await Utils.downloadJson(l.protocol + "//" + l.host + "/service-worker/offline-basemapM/update")
console.log("Service worker has data:", sw)
}
updateMeta()
pingServiceWorker()
let installing = new UIEventSource<string[]>([])
@ -62,7 +55,6 @@
const descr = OfflineBasemapManager.getAreaDescriptionForMapcomplete(key + ".pmtiles")
await offlineMapManager.installArea(descr)
updateMeta()
pingServiceWorker()
} catch (e) {
installing.set(installing.data.filter(k => k !== key))
} finally {
@ -71,13 +63,14 @@
}
let installed: Store<Feature<Polygon>> = installedMeta.map(meta =>
let installed: Store<Feature<Polygon>[]> = installedMeta.map(meta =>
(meta ?? [])
.map(area => {
const f = Tiles.asGeojson(area.minzoom, area.x, area.y)
f.properties = {
id: area.minzoom + "-" + area.x + "-" + area.y,
downloaded: "yes"
downloaded: "yes",
text: area.name + " " + area.dataVersion + " " + Utils.toHumanByteSize(Number(area.size))
}
return f
}
@ -104,7 +97,7 @@
async function download() {
const areasToInstall = Array.from(offlineMapManager.getInstallCandidates(focusTile.data))
for (const area: AreaDescription of areasToInstall) {
for (const area of areasToInstall) {
console.log("Attempting to install", area)
await install(area)
}
@ -135,7 +128,14 @@
]
}
}],
pointRendering: null
pointRendering: [
{
location: ["point", "centroid"],
label: "{text}",
labelCss: "width: w-min",
labelCssClasses: "bg-white rounded px-2"
}
]
})
})
@ -173,71 +173,101 @@
{#if $installedMeta === undefined}
<Loading />
{:else}
<div class="relative w-full h-3/4">
<div class="rounded-lg absolute top-0 left-0 h-full w-full">
<MaplibreMap {map} {mapProperties} />
</div>
<div class="absolute top-0 left-0 h-full w-full flex flex-col justify-center items-center pointer-events-none">
<div class="w-16 h-32 mb-16"></div>
{#if $focusTileIsInstalling}
<div class="normal-background rounded-lg">
<Loading>
Data is being downloaded
</Loading>
<div class="h-full overflow-auto pb-16">
<Accordion class="" inactiveClass="text-black">
<AccordionItem paddingDefault="p-2">
<div slot="header">Map</div>
<div class="relative leave-room">
<div class="rounded-lg absolute top-0 left-0 h-full w-full">
<MaplibreMap {map} {mapProperties} />
</div>
<div
class="absolute top-0 left-0 h-full w-full flex flex-col justify-center items-center pointer-events-none">
<div class="w-16 h-32 mb-16"></div>
{#if $focusTileIsInstalling}
<div class="normal-background rounded-lg">
<Loading>
Data is being downloaded
</Loading>
</div>
{:else}
<button class="primary pointer-events-auto" on:click={() => download()}
class:disabled={$focusTileIsInstalled}>
<DownloadIcon class="w-8 h-8" />
Download
</button>
{/if}
</div>
</div>
{:else}
<button class="primary pointer-events-auto" on:click={() => download()}
class:disabled={$focusTileIsInstalled}>
<DownloadIcon class="w-8 h-8" />
Download
</button>
{/if}
</div>
</div>
</AccordionItem>
<AccordionSingle>
<div slot="header">
Offline tile management
</div>
<AccordionItem paddingDefault="p-2">
<div slot="header">
Offline tile management
</div>
{Utils.toHumanByteSize(Utils.sum($installedMeta.map(area => area.size)))}
<button on:click={() => {
<div class="leave-room">
{Utils.toHumanByteSize(Utils.sum($installedMeta.map(area => area.size)))}
<button on:click={() => {
installedMeta?.data?.forEach(area => del(area))
}}>
<TrashIcon class="w-6" />
Delete all
</button>
<table class="w-full">
<tr>
<th>Name</th>
<th>Map generation date</th>
<th>Size</th>
<th>Zoom ranges</th>
<th>Actions</th>
</tr>
{#each ($installedMeta ?? []) as area }
<tr>
<td>{area.name}</td>
<td>{area.dataVersion}</td>
<td>{Utils.toHumanByteSize(area.size ?? -1)}</td>
<td>{area.minzoom}
{#if area.maxzoom !== undefined}
- {area.maxzoom}
{:else}
and above
{/if}
</td>
<td>
<button on:click={() => del(area)}>
<TrashIcon class="w-6" />
Delete this map
</button>
</td>
</tr>
{/each}
<TrashIcon class="w-6" />
Delete all
</button>
<table class="w-full ">
<tr>
<th>Name</th>
<th>Map generation date</th>
<th>Size</th>
<th>Zoom ranges</th>
<th>Actions</th>
</tr>
{#each ($installedMeta ?? []) as area }
<tr>
<td>{area.name}</td>
<td>{area.dataVersion}</td>
<td>{Utils.toHumanByteSize(area.size ?? -1)}</td>
<td>{area.minzoom}
{#if area.maxzoom !== undefined}
- {area.maxzoom}
{:else}
and above
{/if}
</td>
<td>
<button on:click={() => del(area)}>
<TrashIcon class="w-6" />
Delete this map
</button>
</td>
</tr>
{/each}
</table>
</AccordionSingle>
</table>
</div>
</AccordionItem>
<AccordionItem paddingDefault="p-2">
<div slot="header">
Service worker status
</div>
<div class="leave-room">
<ServiceWorkerStatus />
</div>
</AccordionItem>
</Accordion>
</div>
{/if}
</div>
<style>
.leave-room {
height: calc(100vh - 18rem);
overflow-x: auto;
width: 100%;
color: var(--foreground-color);
}
</style>