Delay background preload until map interaction stops
This commit is contained in:
@@ -13,6 +13,7 @@ export function MapCanvas() {
|
||||
citySummariesByName: store.citySummariesByName,
|
||||
onClosePanel: store.closePanel,
|
||||
onEnsureCityDetail: store.ensureCityDetail,
|
||||
onMapInteractionChange: store.setMapInteractionActive,
|
||||
onRegisterStopMotion: store.registerMapStopMotion,
|
||||
onSelectCity: (cityName) => {
|
||||
void store.selectCity(cityName);
|
||||
|
||||
@@ -47,6 +47,7 @@ interface DashboardStoreValue extends DashboardState {
|
||||
selectedMarketScan: MarketScan | null;
|
||||
selectedDetail: CityDetail | null;
|
||||
selectCity: (cityName: string) => Promise<void>;
|
||||
setMapInteractionActive: (active: boolean) => void;
|
||||
setForecastDate: (dateStr: string | null) => void;
|
||||
marketScanByCityName: Record<string, MarketScan>;
|
||||
}
|
||||
@@ -254,6 +255,7 @@ export function DashboardStoreProvider({
|
||||
string | null
|
||||
>(null);
|
||||
const [futureModalDate, setFutureModalDate] = useState<string | null>(null);
|
||||
const [isMapInteracting, setIsMapInteracting] = useState(false);
|
||||
const [loadingState, setLoadingState] = useState<LoadingState>(
|
||||
getInitialLoadingState,
|
||||
);
|
||||
@@ -747,8 +749,9 @@ export function DashboardStoreProvider({
|
||||
void (async () => {
|
||||
await runQueue(priorityQueue, EAGER_SUMMARY_PRIORITY_CONCURRENCY);
|
||||
if (!active) return;
|
||||
if (isMapInteracting) return;
|
||||
cancelIdleSchedule = scheduleWhenBrowserIdle(() => {
|
||||
if (!active) return;
|
||||
if (!active || isMapInteracting) return;
|
||||
void runQueue(
|
||||
backgroundQueue,
|
||||
EAGER_SUMMARY_BACKGROUND_CONCURRENCY,
|
||||
@@ -760,7 +763,7 @@ export function DashboardStoreProvider({
|
||||
active = false;
|
||||
cancelIdleSchedule();
|
||||
};
|
||||
}, [cities, loadingState.refresh]);
|
||||
}, [cities, isMapInteracting, loadingState.refresh]);
|
||||
|
||||
const selectCity = async (cityName: string) => {
|
||||
setSelectedCity(cityName);
|
||||
@@ -1066,6 +1069,7 @@ export function DashboardStoreProvider({
|
||||
selectedDetail,
|
||||
selectedForecastDate,
|
||||
selectCity,
|
||||
setMapInteractionActive: setIsMapInteracting,
|
||||
setForecastDate: (dateStr: string | null) =>
|
||||
setSelectedForecastDate(dateStr),
|
||||
marketScanByCityName,
|
||||
|
||||
@@ -19,6 +19,7 @@ interface UseLeafletMapArgs {
|
||||
cityName: string,
|
||||
force?: boolean,
|
||||
) => Promise<CityDetail>;
|
||||
onMapInteractionChange: (active: boolean) => void;
|
||||
onRegisterStopMotion: (stopMotion: () => void) => void;
|
||||
onSelectCity: (cityName: string) => void;
|
||||
selectedCity: string | null;
|
||||
@@ -195,6 +196,7 @@ export function useLeafletMap({
|
||||
citySummariesByName,
|
||||
onClosePanel,
|
||||
onEnsureCityDetail,
|
||||
onMapInteractionChange,
|
||||
onRegisterStopMotion,
|
||||
onSelectCity,
|
||||
selectedCity,
|
||||
@@ -218,6 +220,10 @@ export function useLeafletMap({
|
||||
const onRegisterStopMotionRef = useRef(onRegisterStopMotion);
|
||||
const onSelectCityRef = useRef(onSelectCity);
|
||||
const onEnsureCityDetailRef = useRef(onEnsureCityDetail);
|
||||
const onMapInteractionChangeRef = useRef(onMapInteractionChange);
|
||||
const interactionIdleTimerRef = useRef<ReturnType<typeof setTimeout> | null>(
|
||||
null,
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
onClosePanelRef.current = onClosePanel;
|
||||
@@ -235,6 +241,10 @@ export function useLeafletMap({
|
||||
onEnsureCityDetailRef.current = onEnsureCityDetail;
|
||||
}, [onEnsureCityDetail]);
|
||||
|
||||
useEffect(() => {
|
||||
onMapInteractionChangeRef.current = onMapInteractionChange;
|
||||
}, [onMapInteractionChange]);
|
||||
|
||||
useEffect(() => {
|
||||
suspendMotionRef.current = suspendMotion;
|
||||
}, [suspendMotion]);
|
||||
@@ -278,8 +288,42 @@ export function useLeafletMap({
|
||||
};
|
||||
map.on("click", handleMapClick);
|
||||
|
||||
const markInteracting = () => {
|
||||
if (interactionIdleTimerRef.current) {
|
||||
clearTimeout(interactionIdleTimerRef.current);
|
||||
interactionIdleTimerRef.current = null;
|
||||
}
|
||||
onMapInteractionChangeRef.current(true);
|
||||
};
|
||||
|
||||
const markIdleSoon = () => {
|
||||
if (interactionIdleTimerRef.current) {
|
||||
clearTimeout(interactionIdleTimerRef.current);
|
||||
}
|
||||
interactionIdleTimerRef.current = setTimeout(() => {
|
||||
interactionIdleTimerRef.current = null;
|
||||
onMapInteractionChangeRef.current(false);
|
||||
}, 700);
|
||||
};
|
||||
|
||||
map.on("movestart", markInteracting);
|
||||
map.on("zoomstart", markInteracting);
|
||||
map.on("dragstart", markInteracting);
|
||||
map.on("moveend", markIdleSoon);
|
||||
map.on("zoomend", markIdleSoon);
|
||||
|
||||
return () => {
|
||||
onRegisterStopMotionRef.current(() => {});
|
||||
if (interactionIdleTimerRef.current) {
|
||||
clearTimeout(interactionIdleTimerRef.current);
|
||||
interactionIdleTimerRef.current = null;
|
||||
}
|
||||
onMapInteractionChangeRef.current(false);
|
||||
map.off("movestart", markInteracting);
|
||||
map.off("zoomstart", markInteracting);
|
||||
map.off("dragstart", markInteracting);
|
||||
map.off("moveend", markIdleSoon);
|
||||
map.off("zoomend", markIdleSoon);
|
||||
map.off("click", handleMapClick);
|
||||
map.remove();
|
||||
mapRef.current = null;
|
||||
|
||||
Reference in New Issue
Block a user