Fix LFS
Some checks failed
Build and publish Docker image / build-and-push (push) Failing after 10s
CI / Check (push) Failing after 4m35s

This commit is contained in:
Andras Schmelczer 2026-06-15 20:50:57 +01:00
parent 408d3f87f0
commit 8c679fcf96
3 changed files with 83 additions and 27 deletions

View file

@ -209,6 +209,11 @@ export default function App() {
}, []);
const initialRoute = useMemo(() => pathToPage(window.location.pathname), []);
const [mapUrlState, setMapUrlState] = useState(urlState);
// IP-derived initial map centre (fetched from /api/geo on a fresh visit). Null
// until resolved; `geoChecked` flips once the lookup settles (or times out) so
// the dashboard map mounts with the right centre instead of a London flash.
const [ipViewState, setIpViewState] = useState<typeof INITIAL_VIEW_STATE | null>(null);
const [geoChecked, setGeoChecked] = useState(() => urlState.hasExplicitView);
const [dashboardRouteKey, setDashboardRouteKey] = useState(() =>
window.location.pathname === '/dashboard' ? window.location.search : ''
);
@ -220,8 +225,11 @@ export default function App() {
);
const activePageRef = useRef<Page>('home');
const initialViewState = useMemo(
() => mapUrlState.viewState || INITIAL_VIEW_STATE,
[mapUrlState.viewState]
() =>
mapUrlState.hasExplicitView
? mapUrlState.viewState
: (ipViewState ?? INITIAL_VIEW_STATE),
[mapUrlState.hasExplicitView, mapUrlState.viewState, ipViewState]
);
const isScreenshotMode = useMemo(() => {
@ -416,6 +424,44 @@ export default function App() {
return () => controller.abort();
}, []);
// On a fresh visit (no view in the URL), centre the initial map on the visitor's
// approximate location from their IP. Falls back to INITIAL_VIEW_STATE (inner
// London) when geolocation is unavailable. Skipped when the URL already carries a
// view (shared/dashboard links) so we never override it. `geoChecked` gates the
// dashboard map render so it mounts once with the resolved centre.
useEffect(() => {
if (urlState.hasExplicitView) return; // geoChecked already initialised true
let cancelled = false;
const controller = new AbortController();
const finish = () => {
if (!cancelled) setGeoChecked(true);
};
// Never block the map for more than this if the lookup is slow/hangs.
const timeout = window.setTimeout(finish, 1500);
fetch(apiUrl('geo'), { signal: controller.signal })
.then((res) => (res.ok ? res.json() : null))
.then((data: { center?: { lat: number; lon: number } } | null) => {
if (cancelled || !data?.center) return;
setIpViewState({
...INITIAL_VIEW_STATE,
latitude: data.center.lat,
longitude: data.center.lon,
});
})
.catch(() => {})
.finally(() => {
window.clearTimeout(timeout);
finish();
});
return () => {
cancelled = true;
window.clearTimeout(timeout);
controller.abort();
};
// Mount-only: resolve the IP-based initial view exactly once.
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
const navigateTo = useCallback(
(page: Page, hash?: string, infoFeature?: string) => {
const targetHash = normalizeHash(hash);
@ -723,6 +769,8 @@ export default function App() {
refreshAuth();
}}
/>
) : !geoChecked ? (
<PageFallback />
) : (
<MapPage
key={dashboardRouteKey}