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 @@
+
+
+
+
\ 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();
+}