aboutsummaryrefslogtreecommitdiffstats
path: root/src/components
diff options
context:
space:
mode:
Diffstat (limited to 'src/components')
-rw-r--r--src/components/AppHeader.svelte4
-rw-r--r--src/components/LocationsMap.svelte147
2 files changed, 134 insertions, 17 deletions
diff --git a/src/components/AppHeader.svelte b/src/components/AppHeader.svelte
index 4ccd2d03..3bf1e893 100644
--- a/src/components/AppHeader.svelte
+++ b/src/components/AppHeader.svelte
@@ -17,6 +17,8 @@
menuButtons = [
{ key: 'nav.settings', icon: 'settings', href: `/${user.username}/settings` },
{ key: 'nav.support', icon: 'support', href: `/support` },
+ { key: 'nav.map', icon: 'map', href: `/${user.username}/map` },
+ { key: 'nav.merch', icon: 'shop', href: `https://coinosmerch.com` },
{ key: 'nav.signOut', icon: 'logout', href: `/logout` },
];
@@ -53,7 +55,7 @@
.url.pathname === `/${user.username}/receive`
? 'opacity-100'
: 'opacity-70 hover:opacity-80'}"
- ><Icon icon="receive" style="mx-auto w-6 md:w-8" />
+ ><Icon icon="numpad" style="mx-auto w-6 md:w-8" />
</button>
</a>
<a href={`/${user.username}/payments`}>
diff --git a/src/components/LocationsMap.svelte b/src/components/LocationsMap.svelte
index 7876659a..edd20632 100644
--- a/src/components/LocationsMap.svelte
+++ b/src/components/LocationsMap.svelte
@@ -2,6 +2,7 @@
import { onMount, onDestroy } from 'svelte';
import { browser } from '$app/environment';
import html2canvas from 'html2canvas';
+ import { Icon } from '$comp';
import 'maplibre-gl/dist/maplibre-gl.css';
@@ -10,6 +11,71 @@
let mapContainer;
let markers = [];
+ let currentIndex = -1;
+ let timeout;
+
+ function select(m) {
+ stopFlying();
+ currentIndex = inview.indexOf(m);
+ flyToMarker(m);
+ }
+
+ async function flyToMarker(marker) {
+ marker.getElement().click();
+
+ map.flyTo({
+ zoom: 14,
+ center: marker.getLngLat(),
+ essential: true,
+ easing: function (t) {
+ return 1 - Math.pow(1 - t, 3);
+ },
+ duration: 5000
+ });
+
+ await new Promise((r) => setTimeout(r, 5000));
+
+ if (!timeout) return;
+
+ map.flyTo({
+ zoom: 10.5,
+ center: marker.getLngLat(),
+ essential: true,
+ easing: function (t) {
+ return 1 - Math.pow(1 - t, 3);
+ },
+ duration: 2000
+ });
+ }
+
+ let toggle = () => {
+ if (timeout) stopFlying();
+ else startFlying();
+ };
+
+ let currentPopup;
+ function startFlying() {
+ currentIndex++;
+
+ for (let p in popups) popups[p].remove();
+ if (currentIndex >= inview.length) currentIndex = 0;
+ flyToMarker(inview[currentIndex]);
+
+ timeout = setTimeout(startFlying, 7000);
+ }
+
+ function stopFlying() {
+ if (!timeout) return;
+ timeout = clearTimeout(timeout);
+
+ if (currentPopup) {
+ currentPopup.remove();
+ }
+
+ map.jumpTo({ center: map.getCenter(), zoom: map.getZoom() });
+ map.off('moveend'); // Remove moveend listener if set
+ }
+
let checkAddress = (location) => {
if (location['addr:housenumber'] && location['addr:street'] && location['addr:city']) {
return `${
@@ -24,23 +90,24 @@
}
};
+ let inview = [];
+
function updateLabelVisibility() {
+ inview = [];
if (!map) return;
let zoom = map.getZoom();
let bounds = map.getBounds();
for (let marker of markers) {
let isInView = bounds.contains(marker.getLngLat());
- let popup = marker.getPopup();
let { tags } = marker;
- if (zoom > 13 && isInView) {
- popup.setHTML(`<span class='block font-bold'>${tags.name}</span>`).addTo(map);
- } else {
- popup.remove();
- popup.setHTML(html(tags));
+ if (isInView) {
+ inview.push(marker);
}
}
+
+ inview = inview.sort((a, b) => a.tags.name.localeCompare(b.tags.name));
}
let html = (tags) => `${
@@ -53,10 +120,13 @@
${
tags && tags.website
- ? `<a href=${tags.website} target="_blank" rel="noreferrer" class='block text-blue-400'>${tags.website}</a>`
+ ? `<a href=${tags.website} target="_blank" rel="noreferrer" class='block text-blue-400 break-all'>${tags.website}</a>`
: ''
}`;
+ let popups = {};
+ let counter = 0;
+
onMount(() => {
if (browser) {
import('maplibre-gl').then(({ default: maplibre }) => {
@@ -82,10 +152,16 @@
.setPopup(new maplibre.Popup().setHTML(html(tags)))
.addTo(map);
+ marker.id = counter++;
+
marker.getElement().addEventListener('click', () => {
let h = html(marker.tags);
- let popup = marker.getPopup();
- popup.setHTML(h);
+ let p = marker.getPopup();
+ popups[marker.id] = p;
+ p.setHTML(h);
+ p.on('close', () => {
+ delete popups[marker.id];
+ });
});
marker.tags = tags;
@@ -93,18 +169,31 @@
markers.push(marker);
});
- // updateLabelVisibility();
- // map.on('moveend', updateLabelVisibility);
- // map.on('zoomend', updateLabelVisibility);
+ updateLabelVisibility();
+
+ let observer = new IntersectionObserver(
+ (entries, observer) => {
+ entries.forEach((entry) => {
+ if (entry.isIntersecting) startFlying();
+ else stopFlying();
+ });
+ },
+ { threshold: 0.5 }
+ );
+
+ observer.observe(mapContainer);
});
+
+ map.on('mousedown', stopFlying);
+ map.on('touchstart', stopFlying);
+ map.on('wheel', stopFlying);
+ map.on('dragstart', stopFlying);
});
}
});
onDestroy(() => {
if (map) {
- // map.off('moveend', updateLabelVisibility);
- // map.off('zoomend', updateLabelVisibility);
map.remove();
}
});
@@ -113,12 +202,37 @@
<div class="container mx-auto max-w-4xl">
<div class="flex w-full">
<div class="mx-auto h-[550px] w-full z-0" bind:this={mapContainer} />
+ <div class="relative">
+ <button
+ class="absolute flex top-2 right-2 rounded-full border-2 border-black bg-white w-10 h-10"
+ on:click={toggle}
+ >
+ {#if timeout}
+ <Icon icon="pause" style="w-4 m-auto" />
+ {:else}
+ <Icon icon="play" style="w-4 m-auto" />
+ {/if}
+ </button>
+
+ <div class="absolute bottom-2 right-2 w-60 h-60 bg-white overflow-y-scroll p-4 bg-opacity-80">
+ {#each inview as m}
+ <button
+ on:click={() => select(m)}
+ class="text-left"
+ class:font-bold={m.id === inview[currentIndex]?.id}
+ id={`marker-${m.id}`}>{m.tags.name}</button
+ >
+ {/each}
+ </div>
+ </div>
</div>
+
+ {inview.length}
</div>
<style>
:global(.maplibregl-popup-close-button) {
- font-size: 18px;
+ font-size: 24px;
padding: 8px;
}
@@ -131,11 +245,12 @@
}
:global(.maplibregl-popup-content) {
- padding: 0.5em 1em;
+ padding: 1em 2em;
border-radius: 4px;
font-size: 14px;
color: black;
background-color: white;
overflow: hidden;
+ width: 280px;
}
</style>