From 93514ae5b43fc3df54ffb2e3d2ff24a60e4a60b2 Mon Sep 17 00:00:00 2001 From: Jannik Date: Thu, 18 Sep 2025 13:04:34 +0200 Subject: [PATCH] feat: add hazards --- public/img/hazards/bumpy-road.png | Bin 0 -> 2685 bytes src/lib/components/lnv/FullscreenMap.svelte | 7 +++ src/lib/components/lnv/HazardMarker.svelte | 16 +++++++ src/lib/components/lnv/Sidebar.svelte | 12 +++++ src/lib/components/lnv/hazards.svelte.ts | 22 ++++++++++ src/lib/services/hosts.ts | 2 +- src/lib/services/lnv.ts | 46 +++++++++++++++++++- 7 files changed, 102 insertions(+), 3 deletions(-) create mode 100644 public/img/hazards/bumpy-road.png create mode 100644 src/lib/components/lnv/HazardMarker.svelte create mode 100644 src/lib/components/lnv/hazards.svelte.ts diff --git a/public/img/hazards/bumpy-road.png b/public/img/hazards/bumpy-road.png new file mode 100644 index 0000000000000000000000000000000000000000..f2a085995145f0a6eefe34236425ce367e4575ca GIT binary patch literal 2685 zcmV-@3WD{CP)xQH7&pqa1kF6-Rs8FZ3+?>YCg>dyH=Ad{{; z-Rb&Q)%jPQb1#4(2!bF8f*=TjAP9mW2!bF8MMl9IVzJQ}1*LXtfMN4vz5mC23#tuT z&zLV`Mt3^zS3mr42G$5`LIBwDTB!;1*HPjnGe!_^Rh!L8C^&@{fF2vGb=I$+FeCmY zh=Nj+mD{&pR#Q`LC@6&$z-*&YFDvyCEKT=uXmz!Ie0{Uof&x;40)ivobpEFhzw;4K zNccOUfD}>yIPJr7fR_xzoqO(?fC5suC18Ghe9Zi(K7thJF9p+3APP4Fn9hF_Qo<|+ zsr~M}D3kz9;I|)=o%&ZZJU2cb%~4oNVU~b?)Q^E*9nz&;nVsES6l*vD1)*FjO_^zv)^)=#&5!8x4BkXV&o^ zZv@{Jt}R>`8glBi&=~<>;HQ;(joRMZ+Kn;#SHOUfX$!p|)e)Q#0C&H?o51K_hHDF1 zpEYNk5P;e78|K=+L@~2KC*Ta>4$NnD#TL3T187aQeLb_6AI0{Cegr%xD4;F0UI5%Q zuilUjc0)Luj_!LTd%UPV-73@biOh3oT9@u~Gn7-IJ^BBfzqS z7N?I{DF9A&kC*0^;pkr`f-SVNs>nJ4ux(!kfuA2;-Yj5cRgqN!VB5Z=_WL8EnOn{_ z+Cr-YkY<`!T6mv zI{gKKbY;lk2)k~z5uz@{{>w7?mF6A*&=PVxu{ zGZz5M_GOgjl@`qOv0Q9nrUGDB_dNQjlcRqH2dauP6+oWVJ$b>kg{6!WM=}#Y?$tec z!K^C6Gm$K9VRkcs1=T%yVRKbcCIYajx+gDettv`g0Jc^4mY{2aG^ zVc1wzl&Szc+sAlfmo;f#X~C~5nn>FgrYZoNt9$aoOCMt-w4_~Cl$rq8H*w|#NlwEJ z2-CEMsR@966K6&`mFq;Xg{jQ~sR+QO>Ylvts)`ur1ZmmA#09{%eU_(rr3E*AEG=7@ z_%nl8&+ds4_}#j73(lWE57)0>_kP~CZ5!;^u>&@2*Z^sQ?|0$C1!%Y1P^;Bo*REZ# zapOja0tSTTix&ZI-=;KA>e`>MiBsZ{GmgKyiWKtn>KBNBS((FzJ2>3 zX;5&-jva&3r%&JU8ou9&6DJ~xuY?b*diPy&`-M(t{&>fojZ5lwet~tKYSfU!iAeYmULB7Vgm32Kcz;g?W0up+_-VW z8~I@zK780~n-uu_u3Whi*alzU6Y&fq&#I!t1dv5_&w&F6?mF^UgCh~!CIOCazmdP1 z%a<>E%LEthO{tTtDk?()cxhfrk!$gA7@SNI0N9CynXw2hzkA=TyEy( z=E&Ee2rw4Gpp=CPBrzU<#YIZ%TDMxAf8*-aX^2Ve8Nl1`FO?F1cF&)|5g27;p=iRU zZa=W|ab2g(IIE#78LP>wswlPq`nHeuL6~V?IF;jJ&(C}N1y)hzn|KCx_a*1;}DU^O8|Gj|DQ2O@>=5PwQHQ_v8sw92>^o+ zj_v#4#~T@B!JG~AUVwoJD#a%0hVTgHq8$qn}TEVF6Bk_Z_)?)T$!3061x0*zv5XO!x~p3or&xq?=N+1h9yE(2O*% zqgStj5RdL_Qf7e87P45@Sz-TUA6B0N3_O;Flc#{qjpr zb5>OmNdVk5FR^{e!i}Xk>0=}TaH@O6_N7BwwlGux-0B_-{ABbe58A?`j6*GIRZ*w_ zxVBFMzw|iu!w=q>3Kfbi3={yjx`%05gb2Ly%{QFpp|&tk0NlV2EsKyYN!vnS0W2;l zrw&9U@XHRgg^YkOa2D_tfM(?QF}Ey*n0U63aZXSU=*yrb;4w(y!wAWM0U>!7P|CM2 zg9Jdjl`Gml#&jek1lqz^zW$olIG|K7c2cO=zWg}<^Uu_#zPp041yHM0K*$H21>)i_ z0HsI?@y|Z^APD*J?B>l77k>e0-2wvt-?|n4Wj=_^1lfV#_5ZePfs+qCM7uyK-)iEZ z`vY78LLgYHR^gq89|pl(l5fFETGeyse1nBR z7tjTG9R#=0Io6Il1%hj|Dsyw8H { 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(); +}