This commit is contained in:
Andras Schmelczer 2026-05-06 22:40:46 +01:00
parent 28323f145e
commit 94f9c0d594
76 changed files with 3238 additions and 1230 deletions

View file

@ -8,6 +8,7 @@ import type {
ViewState,
PostcodeGeometry,
Property,
MapFlyToOptions,
} from '../../types';
import type { SearchedLocation } from './LocationSearch';
import type { Page } from '../ui/Header';
@ -36,6 +37,7 @@ import { trackEvent } from '../../lib/analytics';
import { canWheelScrollInsideTarget } from '../../lib/dom-scroll';
import { INITIAL_VIEW_STATE } from '../../lib/consts';
import { getSchoolBackendFeatureName } from '../../lib/school-filter';
import { getSpecificCrimeFeatureName } from '../../lib/crime-filter';
import { useLicense } from '../../hooks/useLicense';
import { SpinnerIcon } from '../ui/icons/SpinnerIcon';
import { MapPinIcon } from '../ui/icons/MapPinIcon';
@ -208,7 +210,11 @@ export default function MapPage({
handleToggleBest,
} = useTravelTime(initialTravelTime);
const mapFlyToRef = useRef<((lat: number, lng: number, zoom: number) => void) | null>(null);
const mapFlyToRef = useRef<
((lat: number, lng: number, zoom: number, options?: MapFlyToOptions) => void) | null
>(null);
const pendingCurrentLocationFlyToRef = useRef<{ lat: number; lng: number } | null>(null);
const mobileDrawerPanelRectRef = useRef<DOMRectReadOnly | null>(null);
const mapData = useMapData({
filters,
@ -349,8 +355,11 @@ export default function MapPage({
} = useHexagonSelection({
filters,
features,
hexagonData: mapData.committedHexagonData,
resolution: mapData.resolution,
usePostcodeView: mapData.usePostcodeView,
travelTimeEntries: entries,
shareCode,
journeyDest,
});
@ -379,15 +388,44 @@ export default function MapPage({
[handleLocationSearch, handleCloseSelection, isMobile]
);
const consumePendingCurrentLocationFlyTo = useCallback((rect?: DOMRectReadOnly | null) => {
const pending = pendingCurrentLocationFlyToRef.current;
const panelRect = rect ?? mobileDrawerPanelRectRef.current;
if (!pending || !panelRect) return;
const bottomInset = Math.max(0, window.innerHeight - panelRect.top);
mapFlyToRef.current?.(pending.lat, pending.lng, 17, {
visibleViewportArea: { bottom: bottomInset },
});
pendingCurrentLocationFlyToRef.current = null;
}, []);
const handleCurrentLocationFound = useCallback(
(lat: number, lng: number) => {
if (isMobile) {
pendingCurrentLocationFlyToRef.current = { lat, lng };
consumePendingCurrentLocationFlyTo();
} else {
mapFlyToRef.current?.(lat, lng, 17);
}
setCurrentLocation({ lat, lng });
handleCurrentLocationSearch(lat, lng);
if (isMobile) setMobileDrawerOpen(true);
},
[handleCurrentLocationSearch, isMobile]
[consumePendingCurrentLocationFlyTo, handleCurrentLocationSearch, isMobile]
);
const handleMobileDrawerPanelRectChange = useCallback((rect: DOMRectReadOnly) => {
mobileDrawerPanelRectRef.current = rect;
consumePendingCurrentLocationFlyTo(rect);
}, [consumePendingCurrentLocationFlyTo]);
const handleMobileDrawerClose = useCallback(() => {
pendingCurrentLocationFlyToRef.current = null;
mobileDrawerPanelRectRef.current = null;
setMobileDrawerOpen(false);
}, []);
// For share-link recipients, "Continue with Demo" snaps back to the shared
// coords (the area their link was meant to show), not the central-London
// free-zone demo. Captured once on mount so a later URL rewrite by
@ -557,12 +595,19 @@ export default function MapPage({
const mobileLegendMeta = useMemo(() => {
const featureName = viewFeature
? (getSchoolBackendFeatureName(viewFeature) ?? viewFeature)
? (getSchoolBackendFeatureName(viewFeature) ??
getSpecificCrimeFeatureName(viewFeature) ??
viewFeature)
: null;
return featureName ? features.find((f) => f.name === featureName) || null : null;
}, [viewFeature, features]);
const mapViewFeature = useMemo(
() => (viewFeature ? (getSchoolBackendFeatureName(viewFeature) ?? viewFeature) : null),
() =>
viewFeature
? (getSchoolBackendFeatureName(viewFeature) ??
getSpecificCrimeFeatureName(viewFeature) ??
viewFeature)
: null,
[viewFeature]
);
const mobileDensityRange = useMemo((): [number, number] => {
@ -760,7 +805,7 @@ export default function MapPage({
onLoginRequired={onRegisterClick ?? (() => {})}
isLicensed={user?.subscription === 'licensed'}
onUpgradeClick={() => onNavigateTo('pricing')}
onResetTutorial={tutorial.resetTutorial}
onResetTutorial={!isMobile ? tutorial.resetTutorial : undefined}
filterImpacts={filterCounts.impacts}
onClearAll={handleClearAll}
onSaveSearch={onSaveSearch}
@ -914,10 +959,11 @@ export default function MapPage({
{mobileDrawerOpen && selectedHexagon && (
<Suspense fallback={<PaneFallback />}>
<MobileDrawer
onClose={() => setMobileDrawerOpen(false)}
onClose={handleMobileDrawerClose}
renderArea={renderAreaPane}
renderProperties={renderPropertiesPane}
tab={rightPaneTab}
onPanelRectChange={handleMobileDrawerPanelRectChange}
onTabChange={(t) => {
if (t === 'properties') {
handlePropertiesTabClick();