feat!: improve saving locations
All checks were successful
TrafficCue CI / check (push) Successful in 1m6s
TrafficCue CI / build (push) Successful in 48s

Shows saved home and work locations with a marker on the map and provides a better API for saved locations opening room for more locations in the future

BREAKING CHANGE: no longer uses saved.home, saved.work localStorage entries, locations need to be re-entered to use the saved entry instead
This commit is contained in:
Cfp
2025-07-12 10:54:22 +02:00
parent 4b20143480
commit 4b520be5f9
7 changed files with 61 additions and 15 deletions

BIN
public/img/saved/home.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.8 KiB

BIN
public/img/saved/work.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.2 KiB

View File

@ -15,6 +15,7 @@
} from "$lib/services/navigation/routing.svelte"; } from "$lib/services/navigation/routing.svelte";
import { location } from "./location.svelte"; import { location } from "./location.svelte";
import { protocol } from "$lib/services/OfflineTiles"; import { protocol } from "$lib/services/OfflineTiles";
import { saved } from "$lib/saved.svelte";
onMount(() => { onMount(() => {
window.addEventListener("resize", map.updateMapPadding); window.addEventListener("resize", map.updateMapPadding);
@ -23,6 +24,8 @@
let locationDot: HTMLDivElement | undefined = $state(); let locationDot: HTMLDivElement | undefined = $state();
let locationAccuracyCircle: HTMLDivElement | undefined = $state(); let locationAccuracyCircle: HTMLDivElement | undefined = $state();
let homeMarker: HTMLImageElement | undefined = $state();
let workMarker: HTMLImageElement | undefined = $state();
const DEBUG_POINTS = false; // Set to true to show debug points on the map const DEBUG_POINTS = false; // Set to true to show debug points on the map
</script> </script>
@ -63,6 +66,7 @@
class="w-full h-full" class="w-full h-full"
style="/style.json" style="/style.json"
bind:map={map.value} bind:map={map.value}
bind:zoom={map.zoom}
padding={map.padding} padding={map.padding}
onload={async () => { onload={async () => {
map.updateMapPadding(); map.updateMapPadding();
@ -119,7 +123,7 @@
enableHighAccuracy: true, enableHighAccuracy: true,
}} }}
trackUserLocation={true} trackUserLocation={true}
autoTrigger={true} autoTrigger={true}""
ongeolocate={(e: GeolocationPosition) => { ongeolocate={(e: GeolocationPosition) => {
const speed = Math.round((e.coords.speed || 0) * 3.6); // In km/h const speed = Math.round((e.coords.speed || 0) * 3.6); // In km/h
const accuracy = Math.round(e.coords.accuracy); const accuracy = Math.round(e.coords.accuracy);
@ -133,6 +137,36 @@
{#if pin.isDropped} {#if pin.isDropped}
<Marker lnglat={{ lat: pin.lat, lng: pin.lng }} /> <Marker lnglat={{ lat: pin.lat, lng: pin.lng }} />
{/if} {/if}
{#if saved.home && map.zoom > 9}
<img
src="/img/saved/home.png"
alt="Home Marker"
bind:this={homeMarker}
style="width: 32px;"
/>
<Marker
lnglat={{
lat: saved.home.lat,
lng: saved.home.lon,
}}
element={homeMarker}
/>
{/if}
{#if saved.work && map.zoom > 9}
<img
src="/img/saved/work.png"
alt="Work Marker"
bind:this={workMarker}
style="width: 32px;"
/>
<Marker
lnglat={{
lat: saved.work.lat,
lng: saved.work.lon,
}}
element={workMarker}
/>
{/if}
{#if routing.geojson.routePast} {#if routing.geojson.routePast}
<GeoJSONSource id="route-past" data={routing.geojson.routePast}> <GeoJSONSource id="route-past" data={routing.geojson.routePast}>

View File

@ -7,6 +7,7 @@ import { view } from "./sidebar.svelte";
export const map = $state({ export const map = $state({
value: undefined as maplibregl.Map | undefined, value: undefined as maplibregl.Map | undefined,
zoom: 0,
updateMapPadding: () => { updateMapPadding: () => {
if (document.querySelector<HTMLDivElement>("#sidebar") == null) { if (document.querySelector<HTMLDivElement>("#sidebar") == null) {
map._setPadding({ map._setPadding({

View File

@ -21,6 +21,7 @@
import Reviews from "../info/Reviews.svelte"; import Reviews from "../info/Reviews.svelte";
import MapAi from "../info/MapAI.svelte"; import MapAi from "../info/MapAI.svelte";
import RequiresCapability from "../RequiresCapability.svelte"; import RequiresCapability from "../RequiresCapability.svelte";
import { saved, saveLocations } from "$lib/saved.svelte";
// let { feature }: { feature: Feature } = $props(); // let { feature }: { feature: Feature } = $props();
@ -170,10 +171,12 @@
<Button <Button
variant="outline" variant="outline"
onclick={() => { onclick={() => {
localStorage.setItem( // localStorage.setItem(
"saved.home", // "saved.home",
JSON.stringify({ lat, lon: lng }), // JSON.stringify({ lat, lon: lng }),
); // );
saved.home = { lat, lon: lng };
saveLocations();
}} }}
> >
<HomeIcon /> <HomeIcon />
@ -182,10 +185,12 @@
<Button <Button
variant="outline" variant="outline"
onclick={() => { onclick={() => {
localStorage.setItem( // localStorage.setItem(
"saved.work", // "saved.work",
JSON.stringify({ lat, lon: lng }), // JSON.stringify({ lat, lon: lng }),
); // );
saved.work = { lat, lon: lng };
saveLocations();
}} }}
> >
<BriefcaseIcon /> <BriefcaseIcon />

View File

@ -7,6 +7,7 @@
import VehicleSelector from "../VehicleSelector.svelte"; import VehicleSelector from "../VehicleSelector.svelte";
import Post from "../Post.svelte"; import Post from "../Post.svelte";
import RequiresCapability from "../RequiresCapability.svelte"; import RequiresCapability from "../RequiresCapability.svelte";
import { saved } from "$lib/saved.svelte";
</script> </script>
<div <div
@ -18,12 +19,11 @@
variant="secondary" variant="secondary"
class="flex-1" class="flex-1"
onclick={() => { onclick={() => {
const home = localStorage.getItem("saved.home"); const { lat, lon } = saved.home;
if (!home) { if (!lat || !lon) {
alert("No home location saved."); alert("No home location saved.");
return; return;
} }
const { lat, lon } = JSON.parse(home);
pin.dropPin(lat, lon); pin.dropPin(lat, lon);
pin.showInfo(); pin.showInfo();
map.value?.flyTo({ map.value?.flyTo({
@ -39,12 +39,11 @@
variant="secondary" variant="secondary"
class="flex-1" class="flex-1"
onclick={() => { onclick={() => {
const work = localStorage.getItem("saved.work"); const { lat, lon } = saved.work;
if (!work) { if (!lat || !lon) {
alert("No work location saved."); alert("No work location saved.");
return; return;
} }
const { lat, lon } = JSON.parse(work);
pin.dropPin(lat, lon); pin.dropPin(lat, lon);
pin.showInfo(); pin.showInfo();
map.value?.flyTo({ map.value?.flyTo({

7
src/lib/saved.svelte.ts Normal file
View File

@ -0,0 +1,7 @@
export const saved: Record<string, WorldLocation> = $state(
JSON.parse(localStorage.getItem("saved") ?? "{}"),
);
export function saveLocations() {
localStorage.setItem("saved", JSON.stringify(saved));
}