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