diff --git a/public/img/hazards/bumpy-road.png b/public/img/hazards/bumpy-road.png new file mode 100644 index 0000000..f2a0859 Binary files /dev/null and b/public/img/hazards/bumpy-road.png differ diff --git a/src/lib/components/lnv/FullscreenMap.svelte b/src/lib/components/lnv/FullscreenMap.svelte index cf6e73a..2f66617 100644 --- a/src/lib/components/lnv/FullscreenMap.svelte +++ b/src/lib/components/lnv/FullscreenMap.svelte @@ -17,6 +17,8 @@ } from "$lib/services/OfflineTiles"; import { layers, worldLayers } from "$lib/mapLayers"; import { PMTilesProtocol } from "svelte-maplibre-gl/pmtiles"; + import HazardMarker from "./HazardMarker.svelte"; + import { hazards } from "./hazards.svelte"; onMount(() => { window.addEventListener("resize", map.updateMapPadding); @@ -178,4 +180,9 @@ element={workMarker} /> {/if} + + + {#each hazards as hazard (hazard.latitude + "-" + hazard.longitude)} + + {/each} diff --git a/src/lib/components/lnv/HazardMarker.svelte b/src/lib/components/lnv/HazardMarker.svelte new file mode 100644 index 0000000..3948b89 --- /dev/null +++ b/src/lib/components/lnv/HazardMarker.svelte @@ -0,0 +1,16 @@ + + +{hazard} Marker + \ No newline at end of file diff --git a/src/lib/components/lnv/Sidebar.svelte b/src/lib/components/lnv/Sidebar.svelte index 15704a2..b0edf7a 100644 --- a/src/lib/components/lnv/Sidebar.svelte +++ b/src/lib/components/lnv/Sidebar.svelte @@ -25,6 +25,8 @@ import { Tween } from "svelte/motion"; import { quintOut } from "svelte/easing"; import Progressbar from "../Progressbar.svelte"; + import { postHazard } from "$lib/services/lnv"; + import { fetchHazards } from "./hazards.svelte"; const views: Record = { main: "MainSidebar", @@ -282,6 +284,16 @@ > {m["location.join"]()} + diff --git a/src/lib/components/lnv/hazards.svelte.ts b/src/lib/components/lnv/hazards.svelte.ts new file mode 100644 index 0000000..822152e --- /dev/null +++ b/src/lib/components/lnv/hazards.svelte.ts @@ -0,0 +1,22 @@ +import { getHazards, type Hazard } from "$lib/services/lnv"; +import { location } from "./location.svelte"; + +export const hazards: Hazard[] = $state([]); + +export async function fetchHazards() { + if(!location.available) return; + const newHazards = await getHazards({ lat: location.lat, lon: location.lng }, 100); // TODO: get radius from server config + hazards.splice(0, hazards.length, ...newHazards); +} + +setInterval(() => { + fetchHazards(); +}, 1000 * 60 * 5) // Every 5 minutes +// TODO: get from server config + +const initalFetch = setInterval(() => { + if(location.available) { + fetchHazards(); + clearInterval(initalFetch); + } +}) diff --git a/src/lib/services/hosts.ts b/src/lib/services/hosts.ts index ab1f60c..4d4008d 100644 --- a/src/lib/services/hosts.ts +++ b/src/lib/services/hosts.ts @@ -6,7 +6,7 @@ export const SEARCH_SERVER = "https://photon.komoot.io/"; export const OVERPASS_SERVER = "https://overpass-api.de/api/interpreter"; export const LNV_SERVER = location.hostname == "localhost" && location.protocol == "http:" - ? "http://localhost:3000/api" + ? "https://staging-trafficcue-api.picoscratch.de/api" : location.hostname.includes("staging") ? "https://staging-trafficcue-api.picoscratch.de/api" : "https://trafficcue-api.picoscratch.de/api"; diff --git a/src/lib/services/lnv.ts b/src/lib/services/lnv.ts index 30165cc..50f69b8 100644 --- a/src/lib/services/lnv.ts +++ b/src/lib/services/lnv.ts @@ -108,20 +108,20 @@ export async function authFetch( params?: RequestInit, ): ReturnType { let res = await fetch(url, { + ...params, headers: { Authorization: "Bearer " + localStorage.getItem("lnv-token"), }, - ...params, }); if (res.status != 401) { return res; } await refreshToken(); res = await fetch(url, { + ...params, headers: { Authorization: "Bearer " + localStorage.getItem("lnv-token"), }, - ...params, }); if (res.status == 401) { console.error("Server is misconfigured."); @@ -230,3 +230,45 @@ export async function isSaved(data: Trip) { if (filtered.length == 0) return false; return filtered[0].name; } + +export interface Hazard { + user_id: string; + latitude: number; + longitude: number; + type: string; +} + +export async function getHazards(location: WorldLocation, radius = 50) { + // if (!(await hasCapability("hazards"))) { + // throw new Error("Hazards capability is not available"); + // } + const res = await fetch( + LNV_SERVER + `/hazards?lat=${location.lat}&lon=${location.lon}&radius=${radius}`, + ); + if (!res.ok) { + throw new Error(`Failed to fetch hazards: ${res.statusText}`); + } + const data = await res.json(); + return data as Hazard[]; +} + +export async function postHazard(location: WorldLocation, type: string) { + // if (!(await hasCapability("hazards"))) { + // throw new Error("Hazards capability is not available"); + // } + const res = await authFetch(LNV_SERVER + `/hazards`, { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ + lat: location.lat, + lon: location.lon, + type, + }), + }); + if (!res.ok) { + throw new Error(`Failed to post hazard: ${res.statusText}`); + } + return await res.json(); +}