Refresh terminal charts on activation
This commit is contained in:
@@ -542,6 +542,7 @@ export function LiveTemperatureThresholdChart({
|
||||
isMaximized = false,
|
||||
disableClose = false,
|
||||
isActive = !compact,
|
||||
activationRefreshKey = 0,
|
||||
slotIndex = 0,
|
||||
}: {
|
||||
isEn: boolean;
|
||||
@@ -555,6 +556,7 @@ export function LiveTemperatureThresholdChart({
|
||||
isMaximized?: boolean;
|
||||
disableClose?: boolean;
|
||||
isActive?: boolean;
|
||||
activationRefreshKey?: number;
|
||||
slotIndex?: number;
|
||||
}) {
|
||||
const [hourly, setHourly] = useState<HourlyForecast>(null);
|
||||
@@ -986,6 +988,56 @@ export function LiveTemperatureThresholdChart({
|
||||
};
|
||||
}, [city, compact, isActive, isMaximized, targetResolution, markDetailDegraded, markDetailRequest, applySuccessfulHourlyDetail]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!activationRefreshKey) return;
|
||||
if (!shouldPollLiveChart({ city, compact, isActive, isMaximized })) return;
|
||||
if (typeof document !== "undefined" && document.visibilityState !== "visible") return;
|
||||
|
||||
let cancelled = false;
|
||||
|
||||
const refreshActivatedCachedDetail = () => {
|
||||
const now = Date.now();
|
||||
if (now - lastForegroundRefreshAtRef.current < 10_000) return;
|
||||
|
||||
lastForegroundRefreshAtRef.current = now;
|
||||
lastPatchAtRef.current = now;
|
||||
markDetailRequest("network");
|
||||
|
||||
fetchHourlyForecastForCity(city, { bypassLocalCache: true, resolution: targetResolution })
|
||||
.then((data) => {
|
||||
if (cancelled) return;
|
||||
if (!data) {
|
||||
markDetailDegraded();
|
||||
return;
|
||||
}
|
||||
applySuccessfulHourlyDetail(data, { updateLiveTemp: true });
|
||||
})
|
||||
.catch(() => {
|
||||
if (!cancelled) {
|
||||
markDetailDegraded();
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
if (!cancelled) setIsHourlyLoading(false);
|
||||
});
|
||||
};
|
||||
|
||||
refreshActivatedCachedDetail();
|
||||
return () => {
|
||||
cancelled = true;
|
||||
};
|
||||
}, [
|
||||
activationRefreshKey,
|
||||
city,
|
||||
compact,
|
||||
isActive,
|
||||
isMaximized,
|
||||
targetResolution,
|
||||
markDetailDegraded,
|
||||
markDetailRequest,
|
||||
applySuccessfulHourlyDetail,
|
||||
]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!shouldPollLiveChart({ city, compact, isActive, isMaximized })) return;
|
||||
let cancelled = false;
|
||||
@@ -1004,9 +1056,9 @@ export function LiveTemperatureThresholdChart({
|
||||
|
||||
lastForegroundRefreshAtRef.current = now;
|
||||
lastPatchAtRef.current = now;
|
||||
markDetailRequest("force_refresh");
|
||||
markDetailRequest("network");
|
||||
|
||||
fetchHourlyForecastForCity(city, { ignoreCache: true, resolution: targetResolution })
|
||||
fetchHourlyForecastForCity(city, { bypassLocalCache: true, resolution: targetResolution })
|
||||
.then((data) => {
|
||||
if (cancelled) return;
|
||||
if (!data) {
|
||||
|
||||
@@ -108,6 +108,20 @@ export async function runTests() {
|
||||
dashboardSource.includes('activeNavKey !== "thresholds"'),
|
||||
"terminal screen should preload the chart chunk once access is confirmed on the chart tab",
|
||||
);
|
||||
assert(
|
||||
dashboardSource.includes("terminalActivationRefreshKey") &&
|
||||
dashboardSource.includes("setTerminalActivationRefreshKey") &&
|
||||
querySource.includes("terminalActivationRefreshKey") &&
|
||||
querySource.includes("handleTerminalActivationRefresh") &&
|
||||
querySource.includes("fetchScanTerminal({ forceRefresh: false, showLoading: false })"),
|
||||
"switching back to the terminal tab should trigger a lightweight scan refresh without waiting for browser focus events",
|
||||
);
|
||||
assert(
|
||||
chartSource.includes("activationRefreshKey") &&
|
||||
chartSource.includes("refreshActivatedCachedDetail") &&
|
||||
chartSource.includes("fetchHourlyForecastForCity(city, { bypassLocalCache: true, resolution: targetResolution })"),
|
||||
"switching back to the terminal tab should refresh visible chart detail through cached backend data without forcing external sources",
|
||||
);
|
||||
assert(
|
||||
chartSource.includes("fetchHourlyForecastForCity(city, { ignoreCache: true, resolution: targetResolution })") &&
|
||||
chartSource.includes("setHourly((prev) => mergeHourlyWithLiveObservations(dataWithCurrentRow, prev, row))"),
|
||||
|
||||
@@ -249,11 +249,12 @@ export function runTests() {
|
||||
);
|
||||
const foregroundRefreshBlock = chart.match(/const refreshForegroundFullDetail = \(\) => \{[\s\S]*?\n \};/)?.[0] || "";
|
||||
assert(
|
||||
foregroundRefreshBlock.includes("ignoreCache: true") &&
|
||||
foregroundRefreshBlock.includes("fetchHourlyForecastForCity") &&
|
||||
foregroundRefreshBlock.includes("FOREGROUND_FULL_DETAIL_REFRESH_DEDUP_MS") &&
|
||||
!foregroundRefreshBlock.includes("setIsHourlyLoading(true)"),
|
||||
"foreground resume refresh should update full detail in the background without showing the loading overlay or refetching fresh detail",
|
||||
foregroundRefreshBlock.includes("bypassLocalCache: true") &&
|
||||
foregroundRefreshBlock.includes("fetchHourlyForecastForCity") &&
|
||||
foregroundRefreshBlock.includes("FOREGROUND_FULL_DETAIL_REFRESH_DEDUP_MS") &&
|
||||
!foregroundRefreshBlock.includes("ignoreCache: true") &&
|
||||
!foregroundRefreshBlock.includes("setIsHourlyLoading(true)"),
|
||||
"foreground resume refresh should revalidate cached detail in the background without showing the loading overlay or forcing external sources",
|
||||
);
|
||||
assert(
|
||||
!chart.includes("/api/city/${encodeURIComponent(city)}/summary"),
|
||||
|
||||
@@ -85,11 +85,13 @@ function applyTerminalPatches(
|
||||
export function useScanTerminalQuery({
|
||||
isPro,
|
||||
proAccessLoading,
|
||||
terminalActivationRefreshKey = 0,
|
||||
timezoneOffsetSeconds,
|
||||
tradingRegion,
|
||||
}: {
|
||||
isPro: boolean;
|
||||
proAccessLoading: boolean;
|
||||
terminalActivationRefreshKey?: number;
|
||||
timezoneOffsetSeconds?: number | null;
|
||||
tradingRegion?: string;
|
||||
}) {
|
||||
@@ -206,6 +208,27 @@ export function useScanTerminalQuery({
|
||||
};
|
||||
}, [fetchScanTerminal, isPro, proAccessLoading, scanRemote.status]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!terminalActivationRefreshKey) return;
|
||||
if (typeof document !== "undefined" && document.visibilityState !== "visible") return;
|
||||
if (proAccessLoading || !isPro || scanRemote.status === "loading") return;
|
||||
|
||||
const handleTerminalActivationRefresh = () => {
|
||||
const now = Date.now();
|
||||
if (now - lastForegroundScanRefreshAtRef.current < 10_000) return;
|
||||
lastForegroundScanRefreshAtRef.current = now;
|
||||
void fetchScanTerminal({ forceRefresh: false, showLoading: false });
|
||||
};
|
||||
|
||||
handleTerminalActivationRefresh();
|
||||
}, [
|
||||
fetchScanTerminal,
|
||||
isPro,
|
||||
proAccessLoading,
|
||||
scanRemote.status,
|
||||
terminalActivationRefreshKey,
|
||||
]);
|
||||
|
||||
// Preload adjacent regions in idle time for instant tab switches
|
||||
useEffect(() => {
|
||||
if (typeof window === "undefined" || !tradingRegion || !isPro) return;
|
||||
|
||||
Reference in New Issue
Block a user