feat(stores): location stores
Some checks failed
TrafficCue CI / check (push) Failing after 1m29s
TrafficCue CI / build-android (push) Has been cancelled
TrafficCue CI / build (push) Has been cancelled

This commit is contained in:
2025-10-03 15:17:35 +02:00
parent 46bf44a324
commit 6e11b438a2
7 changed files with 144 additions and 163 deletions

View File

@ -8,7 +8,6 @@
routing,
} from "$lib/services/navigation/routing.svelte";
import { location } from "./location.svelte";
import { saved } from "$lib/saved.svelte";
import RoutingLayers from "$lib/services/navigation/RoutingLayers.svelte";
import {
getPMTilesURL,
@ -20,6 +19,7 @@
import HazardMarker from "./HazardMarker.svelte";
import { hazards } from "./hazards.svelte";
import RequiresCapability from "./RequiresCapability.svelte";
import MapLocationMarkers from "./MapLocationMarkers.svelte";
onMount(() => {
window.addEventListener("resize", map.updateMapPadding);
@ -28,9 +28,6 @@
let locationDot: HTMLDivElement | undefined = $state();
let locationAccuracyCircle: HTMLDivElement | undefined = $state();
let homeMarker: HTMLImageElement | undefined = $state();
let workMarker: HTMLImageElement | undefined = $state();
let schoolMarker: HTMLImageElement | undefined = $state();
const DEBUG_POINTS = false; // Set to true to show debug points on the map
</script>
@ -137,51 +134,7 @@
/>
{/if}
{#if saved.home}
<img
src={map.zoom > 9 ? "/img/saved/home.png" : "/img/saved/small.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.school}
<img
src={map.zoom > 9 ? "/img/saved/school.png" : "/img/saved/small.png"}
alt="School Marker"
bind:this={schoolMarker}
style="width: 32px;"
/>
<Marker
lnglat={{
lat: saved.school.lat,
lng: saved.school.lon,
}}
element={schoolMarker}
/>
{/if}
{#if saved.work}
<img
src={map.zoom > 9 ? "/img/saved/work.png" : "/img/saved/small.png"}
alt="Work Marker"
bind:this={workMarker}
style="width: 32px;"
/>
<Marker
lnglat={{
lat: saved.work.lat,
lng: saved.work.lon,
}}
element={workMarker}
/>
{/if}
<MapLocationMarkers />
<RequiresCapability capability="hazards">
{#each hazards as hazard (hazard.latitude + "-" + hazard.longitude)}

View File

@ -0,0 +1,22 @@
<script lang="ts">
import { MAP_ICONS, type Location } from "$lib/saved.svelte";
import { Marker } from "svelte-maplibre-gl";
import { map } from "./map.svelte";
let { store }: { store: Location } = $props();
let marker: HTMLImageElement | undefined = $state();
</script>
<img
src={map.zoom > 9 ? `/img/saved/${MAP_ICONS[store.icon ?? "small.png"]}` : "/img/saved/small.png"}
alt="Work Marker"
bind:this={marker}
style="width: 32px;"
/>
<Marker
lnglat={{
lat: store.lat,
lng: store.lng,
}}
element={marker}
/>

View File

@ -0,0 +1,11 @@
<script lang="ts">
import type { Location } from "$lib/saved.svelte";
import { stores } from "$lib/services/stores.svelte";
import MapLocationMarker from "./MapLocationMarker.svelte";
const locations = stores<Location>("location");
</script>
{#each locations.current as location (location.data.lat + "-" + location.data.lng)}
<MapLocationMarker store={location.data} />
{/each}

View File

@ -0,0 +1,57 @@
<script lang="ts">
import { m } from "$lang/messages";
import Button from "$lib/components/ui/button/button.svelte";
import { circInOut } from "svelte/easing";
import { fly } from "svelte/transition";
import { map, pin } from "../map.svelte";
import { MapPinIcon } from "@lucide/svelte";
import { stores } from "$lib/services/stores.svelte";
import { ICONS, type Location } from "$lib/saved.svelte";
const locations = stores<Location>("location");
function getName(name: string) {
if(name == "home") {
return m["saved.home"]();
} else if(name == "work") {
return m["saved.work"]();
} else if(name == "school") {
return m["saved.school"]();
}
return name;
}
</script>
<div
id="saved"
class="mt-2 mb-2"
in:fly={{ y: 20, duration: 200, easing: circInOut }}
>
{#each locations.current as location (location.data.lat + "-" + location.data.lng)}
<Button
variant="secondary"
class="flex-1"
onclick={() => {
const { lat, lng } = location.data;
pin.dropPin(lat, lng);
pin.showInfo();
map.value?.flyTo({
center: [lng, lat],
zoom: 19,
});
}}>
{@const Icon = ICONS[location.data.icon ?? "pin"] ?? MapPinIcon}
<Icon />
{getName(location.data.name)}
</Button>
{/each}
</div>
<style>
#saved {
display: flex;
gap: 0.5rem;
/* justify-content: space-evenly; */
width: 100%;
max-width: 100%;
}
</style>

View File

@ -22,12 +22,12 @@
import Reviews from "../info/Reviews.svelte";
import MapAi from "../info/MapAI.svelte";
import RequiresCapability from "../RequiresCapability.svelte";
import { saved, saveLocations } from "$lib/saved.svelte";
import { getDeveloperToggle } from "./settings/developer.svelte";
import InternetAccess from "../info/InternetAccess.svelte";
import RestaurantInfo from "../info/RestaurantInfo.svelte";
import { m } from "$lang/messages";
import { onMount } from "svelte";
import { updateStore } from "$lib/services/stores.svelte";
// let { feature }: { feature: Feature } = $props();
@ -185,12 +185,12 @@
<Button
variant="outline"
onclick={() => {
// localStorage.setItem(
// "saved.home",
// JSON.stringify({ lat, lon: lng }),
// );
saved.home = { lat, lon: lng };
saveLocations();
updateStore({ name: "home", type: "location" }, {
lat,
lng,
name: "home",
icon: "home"
})
}}
>
<HomeIcon />
@ -199,8 +199,12 @@
<Button
variant="outline"
onclick={() => {
saved.school = { lat, lon: lng };
saveLocations();
updateStore({ name: "school", type: "location" }, {
lat,
lng,
name: "school",
icon: "school"
})
}}
>
<SchoolIcon />
@ -209,12 +213,12 @@
<Button
variant="outline"
onclick={() => {
// localStorage.setItem(
// "saved.work",
// JSON.stringify({ lat, lon: lng }),
// );
saved.work = { lat, lon: lng };
saveLocations();
updateStore({ name: "work", type: "location" }, {
lat,
lng,
name: "work",
icon: "work"
})
}}
>
<BriefcaseIcon />

View File

@ -1,108 +1,21 @@
<script lang="ts">
import {
BriefcaseIcon,
DownloadIcon,
FuelIcon,
HomeIcon,
ParkingSquareIcon,
SchoolIcon,
} from "@lucide/svelte";
import { Button } from "../../ui/button";
import { fly } from "svelte/transition";
import { circInOut } from "svelte/easing";
import { map, pin } from "../map.svelte";
import VehicleSelector from "../VehicleSelector.svelte";
import Post from "../Post.svelte";
import RequiresCapability from "../RequiresCapability.svelte";
import { saved } from "$lib/saved.svelte";
import { m } from "$lang/messages";
import { view } from "../view.svelte";
import * as Card from "$lib/components/ui/card";
import SavedRoutes from "../main/SavedRoutes.svelte";
import SavedLocations from "../main/SavedLocations.svelte";
</script>
<div
id="saved"
class="mt-2 mb-2"
in:fly={{ y: 20, duration: 200, easing: circInOut }}
>
<Button
variant="secondary"
class="flex-1"
onclick={() => {
const loc = saved.home;
if (!loc) {
alert(m["saved.no-location"]({ name: m["saved.home"]() }));
return;
}
const { lat, lon } = loc;
if (!lat || !lon) {
alert(m["saved.no-location"]({ name: m["saved.home"]() }));
return;
}
pin.dropPin(lat, lon);
pin.showInfo();
map.value?.flyTo({
center: [lon, lat],
zoom: 19,
});
}}
>
<HomeIcon />
{m["saved.home"]()}
</Button>
<Button
variant="secondary"
class="flex-1"
onclick={() => {
console.log(saved);
const loc = saved.school;
if (!loc) {
alert(m["saved.no-location"]({ name: m["saved.school"]() }));
return;
}
const { lat, lon } = loc;
if (!lat || !lon) {
alert(m["saved.no-location"]({ name: m["saved.school"]() }));
return;
}
pin.dropPin(lat, lon);
pin.showInfo();
map.value?.flyTo({
center: [lon, lat],
zoom: 19,
});
}}
>
<SchoolIcon />
{m["saved.school"]()}
</Button>
<Button
variant="secondary"
class="flex-1"
onclick={() => {
const loc = saved.work;
if (!loc) {
alert(m["saved.no-location"]({ name: m["saved.work"]() }));
return;
}
const { lat, lon } = loc;
if (!lat || !lon) {
alert(m["saved.no-location"]({ name: m["saved.work"]() }));
return;
}
pin.dropPin(lat, lon);
pin.showInfo();
map.value?.flyTo({
center: [lon, lat],
zoom: 19,
});
}}
>
<BriefcaseIcon />
{m["saved.work"]()}
</Button>
</div>
<SavedLocations />
<VehicleSelector />
@ -170,13 +83,3 @@
<Post />
</div>
</RequiresCapability>
<style>
#saved {
display: flex;
gap: 0.5rem;
/* justify-content: space-evenly; */
width: 100%;
max-width: 100%;
}
</style>

View File

@ -1,13 +1,24 @@
import type { Component } from "svelte";
import { reverseGeocode } from "./services/Search";
import { BriefcaseIcon, HomeIcon, MapPinIcon, SchoolIcon, type IconProps } from "@lucide/svelte";
/**
* @deprecated Use stores instead.
*/
export const saved: Record<string, WorldLocation> = $state(
JSON.parse(localStorage.getItem("saved") ?? "{}"),
);
/**
* @deprecated Use stores instead.
*/
export function saveLocations() {
localStorage.setItem("saved", JSON.stringify(saved));
}
/**
* @deprecated Use stores instead.
*/
export async function geocode(name: string) {
const loc = saved[name];
if (!loc) return;
@ -18,3 +29,23 @@ export async function geocode(name: string) {
const feature = geocode[0];
return `${feature.properties.street}${feature.properties.housenumber ? " " + feature.properties.housenumber : ""}, ${feature.properties.city}`;
}
export const ICONS: Record<string, Component<IconProps>> = {
home: HomeIcon,
work: BriefcaseIcon,
school: SchoolIcon,
pin: MapPinIcon
};
export const MAP_ICONS: Record<string, string> = {
home: "home.png",
school: "school.png",
work: "work.png"
}; // TODO: add generic pin icon
export interface Location {
lat: number;
lng: number;
name: string;
icon?: string;
}