Use direct scan rows for model summary
This commit is contained in:
@@ -250,9 +250,12 @@ export function runTests() {
|
||||
);
|
||||
assert(
|
||||
dashboardSource.includes("<ModelSummaryDashboard") &&
|
||||
dashboardSource.includes("rows={rows}") &&
|
||||
dashboardSource.includes("generatedText={generatedText}"),
|
||||
"terminal model summary view must use existing scan rows instead of fetching city detail",
|
||||
dashboardSource.includes("rows={modelSummaryRows}") &&
|
||||
dashboardSource.includes("cacheScope: \"model-summary\"") &&
|
||||
dashboardSource.includes("modelSummary: true") &&
|
||||
dashboardSource.includes("hasTerminalForecastRows(rows)") &&
|
||||
dashboardSource.includes("generatedText={modelSummaryGeneratedText}"),
|
||||
"terminal model summary view must use direct scan terminal rows without city fallback rows",
|
||||
);
|
||||
assert(
|
||||
modelSummarySource.includes("MODEL_SUMMARY_MODEL_COLUMNS") &&
|
||||
|
||||
@@ -178,7 +178,7 @@ export function runTests() {
|
||||
assert(
|
||||
scanQuerySource.includes("MAX_STALE_SCAN_CACHE_MS") &&
|
||||
scanQuerySource.includes("allowStale") &&
|
||||
scanQuerySource.includes("setCachedRows(readScanCache(tradingRegion || \"\", { allowStale: true }))"),
|
||||
scanQuerySource.includes("setCachedRows(readScanCache(tradingRegion || \"\", cacheScope, { allowStale: true }))"),
|
||||
"terminal data hook must render stale scan rows immediately while revalidating the first-screen API",
|
||||
);
|
||||
assert(
|
||||
|
||||
@@ -26,6 +26,7 @@ const SCAN_TERMINAL_PAYLOAD_VERSION = "runway-slim-v1";
|
||||
|
||||
type TerminalQueryOptions = {
|
||||
forceRefresh?: boolean;
|
||||
modelSummary?: boolean;
|
||||
signal?: AbortSignal;
|
||||
sinceSnapshotId?: string | null;
|
||||
timezoneOffsetSeconds?: number | null;
|
||||
@@ -123,6 +124,7 @@ async function readJsonOrThrow<T>(path: string, init?: RequestInit): Promise<T>
|
||||
|
||||
async function getTerminal({
|
||||
forceRefresh = false,
|
||||
modelSummary = false,
|
||||
signal,
|
||||
sinceSnapshotId,
|
||||
timezoneOffsetSeconds,
|
||||
@@ -139,14 +141,14 @@ async function getTerminal({
|
||||
limit: "180",
|
||||
force_refresh: String(forceRefresh),
|
||||
});
|
||||
if (tradingRegion && tradingRegion !== "all") {
|
||||
if (!modelSummary && tradingRegion && tradingRegion !== "all") {
|
||||
params.set("trading_region", tradingRegion);
|
||||
}
|
||||
if (Number.isFinite(timezoneOffsetSeconds)) {
|
||||
if (!modelSummary && Number.isFinite(timezoneOffsetSeconds)) {
|
||||
params.set("timezone_offset_seconds", String(Math.trunc(Number(timezoneOffsetSeconds))));
|
||||
}
|
||||
const trimmedSnapshotId = String(sinceSnapshotId || "").trim();
|
||||
if (!forceRefresh && trimmedSnapshotId) {
|
||||
if (!modelSummary && !forceRefresh && trimmedSnapshotId) {
|
||||
params.set("diff", "true");
|
||||
params.set("since_snapshot_id", trimmedSnapshotId);
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ import type {
|
||||
ScanTerminalResponse,
|
||||
} from "@/lib/dashboard-types";
|
||||
|
||||
const SCAN_CACHE_PREFIX = "polyweather_scan_v2";
|
||||
const SCAN_CACHE_PREFIX = "polyweather_scan_v3";
|
||||
const SCAN_CACHE_TTL_MS = DASHBOARD_REFRESH_POLICY_MS.scanRows;
|
||||
const FOREGROUND_SCAN_REFRESH_AFTER_SUCCESS_MS = 60_000;
|
||||
const MAX_STALE_SCAN_CACHE_MS = 6 * 60 * 60 * 1000;
|
||||
@@ -32,16 +32,17 @@ const DERIVED_SCAN_PATCH_NUMBER_FIELDS = [
|
||||
"deb_prediction",
|
||||
] as const;
|
||||
|
||||
function scanCacheKey(tradingRegion: string): string {
|
||||
return `${SCAN_CACHE_PREFIX}:${tradingRegion || "all"}`;
|
||||
function scanCacheKey(tradingRegion: string, cacheScope: string): string {
|
||||
return `${SCAN_CACHE_PREFIX}:${cacheScope || "terminal"}:${tradingRegion || "all"}`;
|
||||
}
|
||||
|
||||
function readScanCache(
|
||||
tradingRegion: string,
|
||||
cacheScope: string,
|
||||
options?: { allowStale?: boolean },
|
||||
): ScanTerminalResponse | null {
|
||||
try {
|
||||
const raw = localStorage.getItem(scanCacheKey(tradingRegion));
|
||||
const raw = localStorage.getItem(scanCacheKey(tradingRegion, cacheScope));
|
||||
if (!raw) return null;
|
||||
const cached = JSON.parse(raw);
|
||||
const age = Date.now() - Number(cached.ts || 0);
|
||||
@@ -53,8 +54,8 @@ function readScanCache(
|
||||
return null;
|
||||
}
|
||||
|
||||
function writeScanCache(data: ScanTerminalResponse, tradingRegion: string) {
|
||||
try { localStorage.setItem(scanCacheKey(tradingRegion), JSON.stringify({ ts: Date.now(), data })); } catch { /* ignore */ }
|
||||
function writeScanCache(data: ScanTerminalResponse, tradingRegion: string, cacheScope: string) {
|
||||
try { localStorage.setItem(scanCacheKey(tradingRegion, cacheScope), JSON.stringify({ ts: Date.now(), data })); } catch { /* ignore */ }
|
||||
}
|
||||
|
||||
function normalizeCityKey(city: string | null | undefined) {
|
||||
@@ -225,13 +226,17 @@ function mergeScanTerminalIncrementalResponse(
|
||||
}
|
||||
|
||||
export function useScanTerminalQuery({
|
||||
cacheScope = "terminal",
|
||||
isPro,
|
||||
modelSummary = false,
|
||||
proAccessLoading,
|
||||
terminalActivationRefreshKey = 0,
|
||||
timezoneOffsetSeconds,
|
||||
tradingRegion,
|
||||
}: {
|
||||
cacheScope?: string;
|
||||
isPro: boolean;
|
||||
modelSummary?: boolean;
|
||||
proAccessLoading: boolean;
|
||||
terminalActivationRefreshKey?: number;
|
||||
timezoneOffsetSeconds?: number | null;
|
||||
@@ -252,7 +257,7 @@ export function useScanTerminalQuery({
|
||||
const patchVersion = useSsePatchVersion();
|
||||
const [cachedRows, setCachedRows] = useState<ScanTerminalResponse | null>(() => {
|
||||
if (typeof window !== "undefined") {
|
||||
return readScanCache(tradingRegion || "", { allowStale: true });
|
||||
return readScanCache(tradingRegion || "", cacheScope, { allowStale: true });
|
||||
}
|
||||
return null;
|
||||
});
|
||||
@@ -264,8 +269,8 @@ export function useScanTerminalQuery({
|
||||
|
||||
useEffect(() => {
|
||||
if (typeof window === "undefined") return;
|
||||
setCachedRows(readScanCache(tradingRegion || "", { allowStale: true }));
|
||||
}, [tradingRegion]);
|
||||
setCachedRows(readScanCache(tradingRegion || "", cacheScope, { allowStale: true }));
|
||||
}, [cacheScope, tradingRegion]);
|
||||
|
||||
const fetchScanTerminal = useCallback(
|
||||
async ({
|
||||
@@ -287,6 +292,7 @@ export function useScanTerminalQuery({
|
||||
const previous = latestScanDataRef.current;
|
||||
const next = await scanTerminalClient.getTerminal({
|
||||
forceRefresh,
|
||||
modelSummary,
|
||||
signal,
|
||||
sinceSnapshotId: !forceRefresh ? previous?.snapshot_id : null,
|
||||
timezoneOffsetSeconds,
|
||||
@@ -297,12 +303,12 @@ export function useScanTerminalQuery({
|
||||
showLoading,
|
||||
onSuccess: (data) => {
|
||||
lastScanSuccessAtRef.current = Date.now();
|
||||
writeScanCache(data, tradingRegion || "");
|
||||
writeScanCache(data, tradingRegion || "", cacheScope);
|
||||
setCachedRows(data);
|
||||
},
|
||||
});
|
||||
},
|
||||
[isPro, proAccessLoading, run, timezoneOffsetSeconds, tradingRegion],
|
||||
[cacheScope, isPro, modelSummary, proAccessLoading, run, timezoneOffsetSeconds, tradingRegion],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -392,14 +398,14 @@ export function useScanTerminalQuery({
|
||||
if (!neighbors.length) return;
|
||||
|
||||
const preloadOne = (region: string) => {
|
||||
if (readScanCache(region)) return; // already cached
|
||||
if (readScanCache(region, cacheScope)) return; // already cached
|
||||
const idleFn = (window as any).requestIdleCallback || ((cb: () => void) => setTimeout(cb, 5000));
|
||||
const handle = idleFn(() => {
|
||||
void scanTerminalClient.getTerminal({
|
||||
forceRefresh: false,
|
||||
signal: new AbortController().signal,
|
||||
tradingRegion: region,
|
||||
}).then((data) => { writeScanCache(data, region); });
|
||||
}).then((data) => { writeScanCache(data, region, cacheScope); });
|
||||
});
|
||||
return handle;
|
||||
};
|
||||
@@ -409,7 +415,7 @@ export function useScanTerminalQuery({
|
||||
const cancelFn = (window as any).cancelIdleCallback || clearTimeout;
|
||||
handles.forEach((h) => { try { cancelFn(h); } catch { /* */ } });
|
||||
};
|
||||
}, [tradingRegion, isPro]);
|
||||
}, [cacheScope, tradingRegion, isPro]);
|
||||
|
||||
return {
|
||||
refreshScanTerminalManually,
|
||||
|
||||
Reference in New Issue
Block a user