Files
PolyWeather/frontend/components/dashboard/scan-terminal/use-scan-terminal-query.ts
T

143 lines
4.0 KiB
TypeScript

"use client";
import { useCallback, useEffect, useRef, useState } from "react";
import {
scanTerminalQueryPolicy,
scanTerminalClient,
shouldRunAutoTerminalRefresh,
shouldSkipManualTerminalRefresh,
} from "@/components/dashboard/scan-terminal/scan-terminal-client";
import { useRemoteDataQuery } from "@/components/dashboard/scan-terminal/use-remote-data-query";
import type { ScanTerminalResponse } from "@/lib/dashboard-types";
const SCAN_CACHE_KEY = "polyweather_scan_v1";
const SCAN_CACHE_TTL_MS = 2 * 60 * 1000; // 2 min — cities list instant on revisit
function readScanCache(): ScanTerminalResponse | null {
try {
const raw = localStorage.getItem(SCAN_CACHE_KEY);
if (!raw) return null;
const cached = JSON.parse(raw);
if (cached.ts && Date.now() - cached.ts < SCAN_CACHE_TTL_MS && cached.data?.rows) {
return cached.data;
}
} catch { /* ignore */ }
return null;
}
function writeScanCache(data: ScanTerminalResponse) {
try { localStorage.setItem(SCAN_CACHE_KEY, JSON.stringify({ ts: Date.now(), data })); } catch { /* ignore */ }
}
export function useScanTerminalQuery({
isPro,
proAccessLoading,
timezoneOffsetSeconds,
tradingRegion,
}: {
isPro: boolean;
proAccessLoading: boolean;
timezoneOffsetSeconds?: number | null;
tradingRegion?: string;
}) {
const {
data: terminalData,
error: scanError,
isLoading,
loading: scanLoading,
remote: scanRemote,
reset,
run,
} = useRemoteDataQuery<ScanTerminalResponse>();
const lastForcedScanRefreshAtRef = useRef(0);
const [cachedRows, setCachedRows] = useState<ScanTerminalResponse | null>(() => {
if (typeof window !== "undefined") return readScanCache();
return null;
});
const fetchScanTerminal = useCallback(
async ({
forceRefresh = false,
showLoading = false,
}: {
forceRefresh?: boolean;
showLoading?: boolean;
} = {}) => {
if (proAccessLoading || !isPro) return;
if (typeof fetch !== "function" || typeof AbortController === "undefined") {
return;
}
if (forceRefresh) {
lastForcedScanRefreshAtRef.current = Date.now();
}
await run({
request: (signal) =>
scanTerminalClient.getTerminal({
forceRefresh,
signal,
timezoneOffsetSeconds,
tradingRegion,
}),
showLoading,
onSuccess: (data) => { writeScanCache(data); setCachedRows(data); },
});
},
[isPro, proAccessLoading, run, timezoneOffsetSeconds, tradingRegion],
);
useEffect(() => {
if (proAccessLoading) return;
if (!isPro) {
reset();
return;
}
void fetchScanTerminal({ forceRefresh: false, showLoading: true });
}, [fetchScanTerminal, isPro, proAccessLoading, reset, timezoneOffsetSeconds, tradingRegion]);
const effectiveData = terminalData || cachedRows;
const refreshScanTerminalManually = useCallback(() => {
if (
shouldSkipManualTerminalRefresh({
hasCurrentData: Boolean(terminalData),
lastForcedRefreshAt: lastForcedScanRefreshAtRef.current,
})
) {
return;
}
void fetchScanTerminal({ forceRefresh: true, showLoading: true });
}, [fetchScanTerminal, terminalData]);
useEffect(() => {
if (proAccessLoading || !isPro) return;
if (
typeof window === "undefined" ||
typeof window.setInterval !== "function" ||
typeof window.clearInterval !== "function"
) {
return;
}
const intervalId = window.setInterval(() => {
if (
!shouldRunAutoTerminalRefresh({
documentHidden: document.hidden,
isLoading: isLoading(),
})
) {
return;
}
void fetchScanTerminal({ forceRefresh: false, showLoading: false });
}, scanTerminalQueryPolicy.autoRefreshMs);
return () => window.clearInterval(intervalId);
}, [fetchScanTerminal, isLoading, isPro, proAccessLoading]);
return {
refreshScanTerminalManually,
scanError,
scanLoading,
scanRemote,
terminalData: effectiveData,
};
}