diff options
| author | Adam Soltys <[email protected]> | 2026-02-27 10:09:16 -0800 |
|---|---|---|
| committer | Adam Soltys <[email protected]> | 2026-02-27 10:09:16 -0800 |
| commit | bf7d8d373c11cd7a6e1487175147c9a84e775a98 (patch) | |
| tree | 82fb58568de073ff03dfca3d55d8ce5adcf51608 /src/components | |
| parent | 2aa2ac29eb241fc8ded3671e728e578e72652056 (diff) | |
| download | coinos-ui-bf7d8d373c11cd7a6e1487175147c9a84e775a98.tar.gz coinos-ui-bf7d8d373c11cd7a6e1487175147c9a84e775a98.zip | |
map updates
Diffstat (limited to 'src/components')
| -rw-r--r-- | src/components/About.svelte | 2 | ||||
| -rw-r--r-- | src/components/Footer.svelte | 25 | ||||
| -rw-r--r-- | src/components/LandingHero.svelte | 4 | ||||
| -rw-r--r-- | src/components/LocationsMap.svelte | 168 |
4 files changed, 124 insertions, 75 deletions
diff --git a/src/components/About.svelte b/src/components/About.svelte index 874ec8db..b2f376ed 100644 --- a/src/components/About.svelte +++ b/src/components/About.svelte @@ -60,7 +60,7 @@ </div> </div> -<div> +<div class="mb-20"> <h3 class="text-5xl font-medium mb-5 text-center">{$t("about.locations.header")}</h3> <p class="text-secondary text-xl w-full md:w-10/12 lg:w-2/3 2xl:w-[800px] mx-auto mb-10"> {@html $t("about.locations.description")} diff --git a/src/components/Footer.svelte b/src/components/Footer.svelte index 8e80151c..0bdee16e 100644 --- a/src/components/Footer.svelte +++ b/src/components/Footer.svelte @@ -9,7 +9,6 @@ titleID: "nostr", }, { url: "https://t.me/coinoswallet", titleID: "telegram" }, - { url: "https://linktr.ee/coinos.io", titleID: "linktree" }, ]; const companyLinks = [ @@ -20,21 +19,18 @@ <footer class="space-y-10 md:space-y-0 text-xl block md:flex justify-around pb-8 px-4"> <div class="space-y-5"> - <div class="text-center w-full"> + <div class="text-center w-full hidden md:block"> <a href="/" aria-label="Home"> <iconify-icon noobserver icon="coinos:logo" width="224"></iconify-icon> </a> </div> - <div class="flex gap-2 justify-center sm:hidden"> - <div> - <a href="/register"> - <button class="btn">{$t("nav.startInSeconds")}</button> - </a> - </div> + <div class="w-full sm:hidden"> + <a href="/register"> + <button class="btn btn-accent w-full text-2xl">{$t("nav.startInSeconds")}</button> + </a> </div> - <div class="w-full flex flex-wrap sm:flex-nowrap gap-2 justify-center items-center"> + <div class="w-full hidden sm:flex gap-2 justify-center items-center"> <DarkToggle /> - <LocaleSelector /> </div> </div> @@ -62,6 +58,11 @@ </ul> </div> - <iconify-icon noobserver icon="coinos:shield" class="flex justify-center" width="96" - ></iconify-icon> + <div class="space-y-5"> + <div class="flex justify-center"> + <LocaleSelector /> + </div> + <iconify-icon noobserver icon="coinos:shield" class="flex justify-center" width="96" + ></iconify-icon> + </div> </footer> diff --git a/src/components/LandingHero.svelte b/src/components/LandingHero.svelte index dae0bf2b..44def229 100644 --- a/src/components/LandingHero.svelte +++ b/src/components/LandingHero.svelte @@ -27,11 +27,11 @@ {$t("landing.subheader")} </h2> - <div class="flex justify-center"> + <div> <a href="/register"> <button use:focus - class="btn !rounded-full bg-primary text-primary-content hover:bg-primary/80" + class="btn !rounded-full bg-primary text-primary-content hover:bg-primary/80 w-full text-2xl" > {$t("nav.startInSeconds")} </button> diff --git a/src/components/LocationsMap.svelte b/src/components/LocationsMap.svelte index 185a6ae5..dbc56375 100644 --- a/src/components/LocationsMap.svelte +++ b/src/components/LocationsMap.svelte @@ -4,11 +4,12 @@ import Popup from "$comp/Popup.svelte"; import { back } from "$lib/utils"; - let { locations } = $props(); let map: any; + let maplibreRef: any; + let mapReady = $state(false); let mapContainer: any = $state(), mapWrapper: any = $state(); - let markers: any[] = []; + let markers: any[] = $state([]); let search: string = $state(""); let clearSearch = (e: any) => (search = ""); @@ -20,13 +21,10 @@ if (mapWrapper.requestFullscreen) { mapWrapper.requestFullscreen(); } else if (mapWrapper.mozRequestFullScreen) { - /* Firefox */ mapWrapper.mozRequestFullScreen(); } else if (mapWrapper.webkitRequestFullscreen) { - /* Chrome, Safari & Opera */ mapWrapper.webkitRequestFullscreen(); } else if (mapWrapper.msRequestFullscreen) { - /* IE/Edge */ mapWrapper.msRequestFullscreen(); } @@ -36,13 +34,10 @@ if (doc.exitFullscreen) { doc.exitFullscreen(); } else if (doc.mozCancelFullScreen) { - /* Firefox */ doc.mozCancelFullScreen(); } else if (doc.webkitExitFullscreen) { - /* Chrome, Safari and Opera */ doc.webkitExitFullscreen(); } else if (doc.msExitFullscreen) { - /* IE/Edge */ doc.msExitFullscreen(); } } @@ -84,16 +79,6 @@ await new Promise((r) => setTimeout(r, 2500)); if (!timeout) return; - // - // map.flyTo({ - // zoom: 10.5, - // center: marker.getLngLat(), - // essential: true, - // easing: function (t) { - // return 1 - Math.pow(1 - t, 3); - // }, - // duration: 1000, - // }); } let toggle = () => { @@ -159,8 +144,6 @@ for (let marker of markers) { let isInView = bounds.contains(marker.getLngLat()); - let { tags } = marker; - if (isInView) { inview.push(marker); } @@ -170,10 +153,98 @@ }, 200); let locationMarkers: any = {}; + let addedIds = new Set(); let popups: any = {}; let counter = 0; let maplibrePromise: any; + function addMarkers(locations: any[]) { + let maplibre = maplibreRef; + if (!map || !maplibre) return; + + locations.forEach((location) => { + if (location["deleted_at"]) return; + if (addedIds.has(location.id)) return; + addedIds.add(location.id); + + let { lat, lon, tags } = location["osm_json"]; + if (!(lat && lon)) return; + + let popupContainer = document.createElement("div"); + + mount(Popup, { + target: popupContainer, + props: { tags }, + }); + + let marker = new maplibre.Marker({ color: "#F7931A", scale: 0.65 }) + .setLngLat([lon, lat]) + .setPopup(new maplibre.Popup().setDOMContent(popupContainer)) + .addTo(map); + + marker.id = counter++; + + marker.getElement().addEventListener("click", () => { + let p = marker.getPopup(); + popups[marker.id] = p; + p.setDOMContent(popupContainer); + setTimeout(() => { + selected = marker; + setTimeout(scroll, 10); + }, 50); + p.on("close", () => { + selected = undefined; + delete popups[marker.id]; + }); + }); + + marker.tags = tags; + locationMarkers[location.id] = marker; + + markers.push(marker); + }); + + updateLabelVisibility(); + } + + let fetchController: AbortController | null = null; + + async function fetchNearby(lat: number, lon: number, radius: number) { + if (fetchController) fetchController.abort(); + fetchController = new AbortController(); + + try { + let params = new URLSearchParams({ + lat: String(lat), + lon: String(lon), + radius: String(Math.ceil(radius)), + count: "100", + }); + let res = await fetch(`/locations?${params}`, { signal: fetchController.signal }); + let data = await res.json(); + if (data.locations) addMarkers(data.locations); + } catch (e: any) { + if (e.name !== "AbortError") console.log("fetch nearby failed", e); + } + } + + function getViewportRadius() { + let bounds = map.getBounds(); + let center = map.getCenter(); + let ne = bounds.getNorthEast(); + + let dlat = ne.lat - center.lat; + let dlng = ne.lng - center.lng; + let km = Math.sqrt(dlat * dlat + dlng * dlng) * 111; + return Math.max(km, 10); + } + + let fetchOnMove = debounce(() => { + if (!map) return; + let center = map.getCenter(); + fetchNearby(center.lat, center.lng, getViewportRadius()); + }, 500); + const loadMaplibre = async () => { if ((window as any).maplibregl) return (window as any).maplibregl; if (maplibrePromise) return maplibrePromise; @@ -210,6 +281,8 @@ await tick(); if (!mapContainer) return; loadMaplibre().then((maplibre) => { + maplibreRef = maplibre; + map = new maplibre.Map({ container: mapContainer, style: "https://tiles.stadiamaps.com/styles/stamen_toner.json", @@ -217,51 +290,26 @@ zoom: 10.5, }); - map.on("load", () => { - locations.forEach((location) => { - if (location["deleted_at"]) return; - - let { lat, lon, tags } = location["osm_json"]; - if (!(lat && lon)) return; - - let element = document.createElement("div"); - element.className = "marker"; - - let popupContainer = document.createElement("div"); + map.scrollZoom.setWheelZoomRate(1 / 60); + map.scrollZoom.setZoomRate(1 / 30); - mount(Popup, { - target: popupContainer, - props: { tags }, - }); + map.on("load", () => { + mapReady = true; - let marker = new maplibre.Marker({ color: "#F7931A", scale: 0.65 }) - .setLngLat([lon, lat]) - .setPopup(new maplibre.Popup().setDOMContent(popupContainer)) - .addTo(map); + // Initial fetch for default view + fetchNearby(49.26, -123.05, 50); - marker.id = counter++; + // Fetch more on pan/zoom + map.on("moveend", fetchOnMove); - marker.getElement().addEventListener("click", () => { - let p = marker.getPopup(); - popups[marker.id] = p; - p.setDOMContent(popupContainer); - setTimeout(() => { - selected = marker; - setTimeout(scroll, 10); - }, 50); - p.on("close", () => { - selected = undefined; - delete popups[marker.id]; - }); + // Center on user if available + if (navigator.geolocation) { + navigator.geolocation.getCurrentPosition((position) => { + let { longitude: lng, latitude: lat } = position.coords; + map.flyTo({ center: [lng, lat], zoom: 11, duration: 1000 }); + fetchNearby(lat, lng, 50); }); - - marker.tags = tags; - locationMarkers[location.id] = marker; - - markers.push(marker); - }); - - updateLabelVisibility(); + } }); map.on("mousedown", stopFlying); |
