feat(stores): location stores
This commit is contained in:
@ -8,7 +8,6 @@
|
|||||||
routing,
|
routing,
|
||||||
} from "$lib/services/navigation/routing.svelte";
|
} from "$lib/services/navigation/routing.svelte";
|
||||||
import { location } from "./location.svelte";
|
import { location } from "./location.svelte";
|
||||||
import { saved } from "$lib/saved.svelte";
|
|
||||||
import RoutingLayers from "$lib/services/navigation/RoutingLayers.svelte";
|
import RoutingLayers from "$lib/services/navigation/RoutingLayers.svelte";
|
||||||
import {
|
import {
|
||||||
getPMTilesURL,
|
getPMTilesURL,
|
||||||
@ -20,6 +19,7 @@
|
|||||||
import HazardMarker from "./HazardMarker.svelte";
|
import HazardMarker from "./HazardMarker.svelte";
|
||||||
import { hazards } from "./hazards.svelte";
|
import { hazards } from "./hazards.svelte";
|
||||||
import RequiresCapability from "./RequiresCapability.svelte";
|
import RequiresCapability from "./RequiresCapability.svelte";
|
||||||
|
import MapLocationMarkers from "./MapLocationMarkers.svelte";
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
window.addEventListener("resize", map.updateMapPadding);
|
window.addEventListener("resize", map.updateMapPadding);
|
||||||
@ -28,9 +28,6 @@
|
|||||||
|
|
||||||
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();
|
|
||||||
let schoolMarker: 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>
|
||||||
@ -137,51 +134,7 @@
|
|||||||
/>
|
/>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
{#if saved.home}
|
<MapLocationMarkers />
|
||||||
<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}
|
|
||||||
|
|
||||||
<RequiresCapability capability="hazards">
|
<RequiresCapability capability="hazards">
|
||||||
{#each hazards as hazard (hazard.latitude + "-" + hazard.longitude)}
|
{#each hazards as hazard (hazard.latitude + "-" + hazard.longitude)}
|
||||||
|
|||||||
22
src/lib/components/lnv/MapLocationMarker.svelte
Normal file
22
src/lib/components/lnv/MapLocationMarker.svelte
Normal 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}
|
||||||
|
/>
|
||||||
11
src/lib/components/lnv/MapLocationMarkers.svelte
Normal file
11
src/lib/components/lnv/MapLocationMarkers.svelte
Normal 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}
|
||||||
57
src/lib/components/lnv/main/SavedLocations.svelte
Normal file
57
src/lib/components/lnv/main/SavedLocations.svelte
Normal 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>
|
||||||
@ -22,12 +22,12 @@
|
|||||||
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";
|
|
||||||
import { getDeveloperToggle } from "./settings/developer.svelte";
|
import { getDeveloperToggle } from "./settings/developer.svelte";
|
||||||
import InternetAccess from "../info/InternetAccess.svelte";
|
import InternetAccess from "../info/InternetAccess.svelte";
|
||||||
import RestaurantInfo from "../info/RestaurantInfo.svelte";
|
import RestaurantInfo from "../info/RestaurantInfo.svelte";
|
||||||
import { m } from "$lang/messages";
|
import { m } from "$lang/messages";
|
||||||
import { onMount } from "svelte";
|
import { onMount } from "svelte";
|
||||||
|
import { updateStore } from "$lib/services/stores.svelte";
|
||||||
|
|
||||||
// let { feature }: { feature: Feature } = $props();
|
// let { feature }: { feature: Feature } = $props();
|
||||||
|
|
||||||
@ -185,12 +185,12 @@
|
|||||||
<Button
|
<Button
|
||||||
variant="outline"
|
variant="outline"
|
||||||
onclick={() => {
|
onclick={() => {
|
||||||
// localStorage.setItem(
|
updateStore({ name: "home", type: "location" }, {
|
||||||
// "saved.home",
|
lat,
|
||||||
// JSON.stringify({ lat, lon: lng }),
|
lng,
|
||||||
// );
|
name: "home",
|
||||||
saved.home = { lat, lon: lng };
|
icon: "home"
|
||||||
saveLocations();
|
})
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<HomeIcon />
|
<HomeIcon />
|
||||||
@ -199,8 +199,12 @@
|
|||||||
<Button
|
<Button
|
||||||
variant="outline"
|
variant="outline"
|
||||||
onclick={() => {
|
onclick={() => {
|
||||||
saved.school = { lat, lon: lng };
|
updateStore({ name: "school", type: "location" }, {
|
||||||
saveLocations();
|
lat,
|
||||||
|
lng,
|
||||||
|
name: "school",
|
||||||
|
icon: "school"
|
||||||
|
})
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<SchoolIcon />
|
<SchoolIcon />
|
||||||
@ -209,12 +213,12 @@
|
|||||||
<Button
|
<Button
|
||||||
variant="outline"
|
variant="outline"
|
||||||
onclick={() => {
|
onclick={() => {
|
||||||
// localStorage.setItem(
|
updateStore({ name: "work", type: "location" }, {
|
||||||
// "saved.work",
|
lat,
|
||||||
// JSON.stringify({ lat, lon: lng }),
|
lng,
|
||||||
// );
|
name: "work",
|
||||||
saved.work = { lat, lon: lng };
|
icon: "work"
|
||||||
saveLocations();
|
})
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<BriefcaseIcon />
|
<BriefcaseIcon />
|
||||||
|
|||||||
@ -1,108 +1,21 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import {
|
import {
|
||||||
BriefcaseIcon,
|
|
||||||
DownloadIcon,
|
DownloadIcon,
|
||||||
FuelIcon,
|
FuelIcon,
|
||||||
HomeIcon,
|
|
||||||
ParkingSquareIcon,
|
ParkingSquareIcon,
|
||||||
SchoolIcon,
|
|
||||||
} from "@lucide/svelte";
|
} from "@lucide/svelte";
|
||||||
import { Button } from "../../ui/button";
|
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 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";
|
|
||||||
import { m } from "$lang/messages";
|
import { m } from "$lang/messages";
|
||||||
import { view } from "../view.svelte";
|
import { view } from "../view.svelte";
|
||||||
import * as Card from "$lib/components/ui/card";
|
import * as Card from "$lib/components/ui/card";
|
||||||
import SavedRoutes from "../main/SavedRoutes.svelte";
|
import SavedRoutes from "../main/SavedRoutes.svelte";
|
||||||
|
import SavedLocations from "../main/SavedLocations.svelte";
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div
|
<SavedLocations />
|
||||||
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>
|
|
||||||
|
|
||||||
<VehicleSelector />
|
<VehicleSelector />
|
||||||
|
|
||||||
@ -170,13 +83,3 @@
|
|||||||
<Post />
|
<Post />
|
||||||
</div>
|
</div>
|
||||||
</RequiresCapability>
|
</RequiresCapability>
|
||||||
|
|
||||||
<style>
|
|
||||||
#saved {
|
|
||||||
display: flex;
|
|
||||||
gap: 0.5rem;
|
|
||||||
/* justify-content: space-evenly; */
|
|
||||||
width: 100%;
|
|
||||||
max-width: 100%;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|||||||
@ -1,13 +1,24 @@
|
|||||||
|
import type { Component } from "svelte";
|
||||||
import { reverseGeocode } from "./services/Search";
|
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(
|
export const saved: Record<string, WorldLocation> = $state(
|
||||||
JSON.parse(localStorage.getItem("saved") ?? "{}"),
|
JSON.parse(localStorage.getItem("saved") ?? "{}"),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated Use stores instead.
|
||||||
|
*/
|
||||||
export function saveLocations() {
|
export function saveLocations() {
|
||||||
localStorage.setItem("saved", JSON.stringify(saved));
|
localStorage.setItem("saved", JSON.stringify(saved));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated Use stores instead.
|
||||||
|
*/
|
||||||
export async function geocode(name: string) {
|
export async function geocode(name: string) {
|
||||||
const loc = saved[name];
|
const loc = saved[name];
|
||||||
if (!loc) return;
|
if (!loc) return;
|
||||||
@ -18,3 +29,23 @@ export async function geocode(name: string) {
|
|||||||
const feature = geocode[0];
|
const feature = geocode[0];
|
||||||
return `${feature.properties.street}${feature.properties.housenumber ? " " + feature.properties.housenumber : ""}, ${feature.properties.city}`;
|
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;
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user