fix(web): 次级页稳定外壳与缓存预取,跨部署导航自愈(BUG-966/967)
This commit is contained in:
@@ -6,6 +6,12 @@ import { fetchChartView } from "@/lib/chart-view-client";
|
||||
import type { ChartViewResponse } from "@/lib/chart-view-contract";
|
||||
import type { ChartViewLayer } from "@/lib/chart-view-engine";
|
||||
import { CHART_VIEW_COPY } from "@/lib/chart-view-labels";
|
||||
import {
|
||||
peekChartPage,
|
||||
refreshChartPage,
|
||||
writeChartPage,
|
||||
type ChartPageSnapshot,
|
||||
} from "@/lib/secondary-page-data";
|
||||
|
||||
function unavailableBody(): ChartViewResponse {
|
||||
return { status: "chart_unavailable", billed: false, message: CHART_VIEW_COPY.unavailable };
|
||||
@@ -19,29 +25,43 @@ function warnClientFailure(label: string, error: unknown, extra?: Record<string,
|
||||
});
|
||||
}
|
||||
|
||||
function viewFromSnapshot(snapshot: ChartPageSnapshot | null): ChartViewResponse | null {
|
||||
return snapshot?.kind === "view" ? snapshot.view : null;
|
||||
}
|
||||
|
||||
export function useChartPage() {
|
||||
const [view, setView] = useState<ChartViewResponse | null>(null);
|
||||
const [view, setView] = useState<ChartViewResponse | null>(() => viewFromSnapshot(peekChartPage()));
|
||||
const [pendingLayers, setPendingLayers] = useState<ReadonlySet<ChartViewLayer>>(new Set());
|
||||
const loadedLayers = useRef(new Set<ChartViewLayer>());
|
||||
const inflightLayers = useRef(new Set<ChartViewLayer>());
|
||||
|
||||
useEffect(() => {
|
||||
const controller = new AbortController();
|
||||
void fetchChartView({ signal: controller.signal })
|
||||
.then((result) => {
|
||||
if (controller.signal.aborted) return;
|
||||
if (result.httpStatus === 401) {
|
||||
const cached = peekChartPage();
|
||||
if (cached?.kind === "unauthenticated") {
|
||||
window.location.assign("/login");
|
||||
return;
|
||||
}
|
||||
let cancelled = false;
|
||||
void refreshChartPage()
|
||||
.then((snapshot) => {
|
||||
if (cancelled) return;
|
||||
if (snapshot.kind === "unauthenticated") {
|
||||
window.location.assign("/login");
|
||||
return;
|
||||
}
|
||||
setView(result.body ?? unavailableBody());
|
||||
setView(snapshot.view);
|
||||
})
|
||||
.catch((error: unknown) => {
|
||||
if (controller.signal.aborted) return;
|
||||
if (cancelled) return;
|
||||
warnClientFailure("chart_page_fetch_failed", error);
|
||||
setView(unavailableBody());
|
||||
if (peekChartPage()?.kind === "view") return;
|
||||
const fallback = unavailableBody();
|
||||
writeChartPage({ kind: "view", view: fallback });
|
||||
setView(fallback);
|
||||
});
|
||||
return () => controller.abort();
|
||||
return () => {
|
||||
cancelled = true;
|
||||
};
|
||||
}, []);
|
||||
|
||||
const requestLayer = useCallback((layer: ChartViewLayer) => {
|
||||
@@ -57,6 +77,7 @@ export function useChartPage() {
|
||||
.then((result) => {
|
||||
if (result.body?.status === "ok") {
|
||||
loadedLayers.current.add(layer);
|
||||
writeChartPage({ kind: "view", view: result.body });
|
||||
setView(result.body);
|
||||
}
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user