fixed map behaviur with searched locations and dev

This commit is contained in:
Hendrik Behme committed 2026-08-17 02:49:53 +02:00
1 parent 7f6b100506
commit 55e28fd0af
5 files changed
+151 -50

No files matched your search

+134 -42
View File
@@ -12,6 +12,35 @@
// so this only matters if that ever gets forgotten.
var DEFAULT_CENTER = [50.9375, 6.9603];
// Remembers the last place a user searched/geolocated to, shared between
// the home hero map and /map, so switching between them (or coming back
// later) doesn't reset to Köln and make them search again.
var LAST_LOCATION_KEY = 'vindn:lastLocation';
function loadLastLocation() {
try {
var raw = window.localStorage && localStorage.getItem(LAST_LOCATION_KEY);
if (!raw) return null;
var parsed = JSON.parse(raw);
if (typeof parsed.lat === 'number' && typeof parsed.lon === 'number' && isFinite(parsed.lat) && isFinite(parsed.lon)) {
return parsed;
}
} catch (e) { /* localStorage disabled/unavailable, or corrupt value — just skip it */ }
return null;
}
function saveLastLocation(lat, lon) {
try {
if (window.localStorage) localStorage.setItem(LAST_LOCATION_KEY, JSON.stringify({ lat: lat, lon: lon }));
} catch (e) { /* localStorage disabled/unavailable — nothing to persist to */ }
}
function setLocateStatus(statusEl, text, isError) {
if (!statusEl) return;
statusEl.textContent = text;
statusEl.classList.toggle('is-error', !!isError);
}
// Leaflet.awesome-markers only ships a fixed palette of named colors (no
// arbitrary hex/hsl) — curated to the ones that read well against the
// grayscale-filtered basemap. Approximate hex values below are only used
@@ -311,6 +340,10 @@
var defaultCenter = opts.defaultCenter || DEFAULT_CENTER;
var containerId = opts.containerId || 'mapCanvas';
var state = { cat: 'all', userLoc: null };
// getCurrentPosition() can take a few seconds; if the user searches for
// a place before it resolves, its result must not overwrite whatever
// they deliberately chose in the meantime.
var manualLocationSet = false;
var shown = limit ? nearest(defaultCenter, allEvents.slice(), limit) : allEvents;
@@ -449,48 +482,86 @@
});
}
function requestLocation() {
function setUserLocation(lat, lon, statusText) {
state.userLoc = [lat, lon];
saveLastLocation(lat, lon);
if (limit) rebuildMarkers(nearest([lat, lon], allEvents.slice(), limit));
var statusEl = document.getElementById('locateStatus');
setLocateStatus(statusEl, statusText, false);
if (map) {
if (userMarker) map.removeLayer(userMarker);
userMarker = L.marker([lat, lon], { icon: dotIcon('#2440FF', true), zIndexOffset: 1000 }).addTo(map);
if (limit) fitToMarkers(map, markers);
else map.setView([lat, lon], 13);
}
renderList();
}
function requestLocation(isAutoCheck) {
var statusEl = document.getElementById('locateStatus');
if (!navigator.geolocation) {
if (statusEl) statusEl.textContent = 'Location not supported by your browser';
setLocateStatus(statusEl, 'Location not supported by your browser', true);
return;
}
if (statusEl) statusEl.textContent = 'Finding you…';
setLocateStatus(statusEl, 'Finding you…', false);
navigator.geolocation.getCurrentPosition(function (pos) {
var lat = pos.coords.latitude;
var lon = pos.coords.longitude;
state.userLoc = [lat, lon];
if (limit) {
rebuildMarkers(nearest([lat, lon], allEvents.slice(), limit));
if (statusEl) statusEl.textContent = 'Showing the ' + limit + ' nearest events';
} else if (statusEl) {
statusEl.textContent = 'Showing events near you';
}
if (map) {
if (userMarker) map.removeLayer(userMarker);
userMarker = L.marker([lat, lon], { icon: dotIcon('#2440FF', true), zIndexOffset: 1000 }).addTo(map);
if (limit) fitToMarkers(map, markers);
else map.setView([lat, lon], 13);
}
renderList();
// A manual search/click may have already happened while this
// (silent, permission-check-triggered) lookup was still resolving.
if (isAutoCheck && manualLocationSet) return;
setUserLocation(pos.coords.latitude, pos.coords.longitude, limit ? ('Showing the ' + limit + ' nearest events') : 'Showing events near you');
}, function () {
if (statusEl) statusEl.textContent = "Couldn't get your location";
setLocateStatus(statusEl, "Couldn't get your location", true);
}, { timeout: 8000 });
}
var locateBtn = document.getElementById('locateBtn');
if (locateBtn) locateBtn.addEventListener('click', requestLocation);
if (locateBtn) locateBtn.addEventListener('click', function () { manualLocationSet = true; requestLocation(false); });
// If the user already granted location access (on this or an earlier
// visit), center on it automatically — this only *reads* the current
// permission state, it never triggers the browser's permission prompt
// itself, so it's safe to check unconditionally on load.
if (navigator.permissions && navigator.permissions.query) {
// Nominatim: explicit submit only, never as-you-type — required by its
// usage policy (max ~1 req/s, no autocomplete-style querying).
var locationForm = document.getElementById('locationSearchForm');
if (locationForm) {
locationForm.addEventListener('submit', function (e) {
e.preventDefault();
var input = document.getElementById('locationSearch');
var query = (input.value || '').trim();
if (!query) return;
manualLocationSet = true;
var statusEl = document.getElementById('locateStatus');
setLocateStatus(statusEl, 'Looking up "' + query + '"…', false);
fetch('https://nominatim.openstreetmap.org/search?format=json&limit=1&q=' + encodeURIComponent(query))
.then(function (res) { return res.json(); })
.then(function (results) {
if (!results || !results.length) {
setLocateStatus(statusEl, 'Could not find "' + query + '"', true);
return;
}
setUserLocation(parseFloat(results[0].lat), parseFloat(results[0].lon), limit ? ('Showing the ' + limit + ' nearest events') : 'Showing events near you');
})
.catch(function () {
setLocateStatus(statusEl, 'Location search failed', true);
});
});
}
// A remembered search/geolocation takes priority over asking the
// browser again — restoring it counts as the user's own manual choice,
// so a slower-to-resolve auto-geolocation check below can't clobber it.
var lastLocation = loadLastLocation();
if (lastLocation) {
manualLocationSet = true;
setUserLocation(lastLocation.lat, lastLocation.lon, limit ? ('Showing the ' + limit + ' nearest events') : 'Showing events near you');
} else if (navigator.permissions && navigator.permissions.query) {
// If the user already granted location access (on this or an earlier
// visit), center on it automatically — this only *reads* the current
// permission state, it never triggers the browser's permission prompt
// itself, so it's safe to check unconditionally on load.
navigator.permissions.query({ name: 'geolocation' }).then(function (status) {
if (status.state === 'granted') requestLocation();
if (status.state === 'granted') requestLocation(true);
}).catch(function () {});
}
@@ -522,6 +593,10 @@
var containerId = opts.containerId || 'mapCanvas';
var state = { source: originalCenter.slice(), rangeKm: opts.rangeKm || 25, cat: 'all' };
// getCurrentPosition() can take a few seconds; if the user searches or
// clicks the map before it resolves, its result must not overwrite
// whatever they deliberately chose in the meantime.
var manualLocationSet = false;
var result = renderPinMap(containerId, [], {
zoomControl: true,
@@ -601,7 +676,7 @@
}
var statusEl = document.getElementById('locateStatus');
if (statusEl) statusEl.textContent = 'Showing events within ' + state.rangeKm + ' km';
setLocateStatus(statusEl, 'Showing events within ' + state.rangeKm + ' km', false);
var countEl = document.getElementById('resultCount');
if (countEl) countEl.textContent = visible.length + (visible.length === 1 ? ' event' : ' events');
@@ -650,6 +725,7 @@
// conservative: reload whenever full coverage isn't guaranteed,
// rather than risk silently showing an incomplete result.
function setSource(lat, lon) {
saveLastLocation(lat, lon);
var distFromOriginal = distanceKm(originalCenter[0], originalCenter[1], lat, lon);
if (distFromOriginal + state.rangeKm > maxRangeKm) {
var url = new URL(window.location.href);
@@ -667,10 +743,11 @@
}
if (map) {
map.on('click', function (e) { setSource(e.latlng.lat, e.latlng.lng); });
map.on('click', function (e) { manualLocationSet = true; setSource(e.latlng.lat, e.latlng.lng); });
}
if (sourceMarker) {
sourceMarker.on('dragend', function () {
manualLocationSet = true;
var ll = sourceMarker.getLatLng();
setSource(ll.lat, ll.lng);
});
@@ -705,43 +782,58 @@
var input = document.getElementById('locationSearch');
var query = (input.value || '').trim();
if (!query) return;
manualLocationSet = true;
var statusEl = document.getElementById('locateStatus');
if (statusEl) statusEl.textContent = 'Looking up "' + query + '"…';
setLocateStatus(statusEl, 'Looking up "' + query + '"…', false);
fetch('https://nominatim.openstreetmap.org/search?format=json&limit=1&q=' + encodeURIComponent(query))
.then(function (res) { return res.json(); })
.then(function (results) {
if (!results || !results.length) {
if (statusEl) statusEl.textContent = 'Could not find "' + query + '"';
setLocateStatus(statusEl, 'Could not find "' + query + '"', true);
return;
}
setSource(parseFloat(results[0].lat), parseFloat(results[0].lon));
})
.catch(function () {
if (statusEl) statusEl.textContent = 'Location search failed';
setLocateStatus(statusEl, 'Location search failed', true);
});
});
}
function requestLocation() {
function requestLocation(isAutoCheck) {
var statusEl = document.getElementById('locateStatus');
if (!navigator.geolocation) {
if (statusEl) statusEl.textContent = 'Location not supported by your browser';
setLocateStatus(statusEl, 'Location not supported by your browser', true);
return;
}
if (statusEl) statusEl.textContent = 'Finding you…';
setLocateStatus(statusEl, 'Finding you…', false);
navigator.geolocation.getCurrentPosition(function (pos) {
// A manual search/click/drag may have already happened while this
// (silent, permission-check-triggered) lookup was still resolving.
if (isAutoCheck && manualLocationSet) return;
setSource(pos.coords.latitude, pos.coords.longitude);
}, function () {
if (statusEl) statusEl.textContent = "Couldn't get your location";
setLocateStatus(statusEl, "Couldn't get your location", true);
}, { timeout: 8000 });
}
var locateBtn = document.getElementById('locateBtn');
if (locateBtn) locateBtn.addEventListener('click', requestLocation);
if (locateBtn) locateBtn.addEventListener('click', function () { manualLocationSet = true; requestLocation(false); });
if (navigator.permissions && navigator.permissions.query) {
// An explicit ?lat=/&lon= in the URL (e.g. a shared link) always wins.
// Otherwise, a remembered search/geolocation takes priority over asking
// the browser again — restoring it counts as the user's own manual
// choice, so a slower-to-resolve auto-geolocation check below can't
// clobber it. If it's far enough from this page's own server-resolved
// center, setSource's own reload-threshold logic kicks in as usual.
var urlHasExplicitLocation = /[?&]lat=/.test(window.location.search);
var lastLocation = urlHasExplicitLocation ? null : loadLastLocation();
if (lastLocation) {
manualLocationSet = true;
setSource(lastLocation.lat, lastLocation.lon);
} else if (navigator.permissions && navigator.permissions.query) {
navigator.permissions.query({ name: 'geolocation' }).then(function (status) {
if (status.state === 'granted') requestLocation();
if (status.state === 'granted') requestLocation(true);
}).catch(function () {});
}