Remove dashboard prewarm requests
This commit is contained in:
@@ -5,7 +5,7 @@ import clsx from "clsx";
|
|||||||
import { useRouter } from "next/navigation";
|
import { useRouter } from "next/navigation";
|
||||||
import { useEffect, useMemo, useRef, useState } from "react";
|
import { useEffect, useMemo, useRef, useState } from "react";
|
||||||
import { ForecastTable } from "@/components/dashboard/PanelSections";
|
import { ForecastTable } from "@/components/dashboard/PanelSections";
|
||||||
import { preloadChartJs, useChart } from "@/hooks/useChart";
|
import { useChart } from "@/hooks/useChart";
|
||||||
import { useDashboardStore } from "@/hooks/useDashboardStore";
|
import { useDashboardStore } from "@/hooks/useDashboardStore";
|
||||||
import { useI18n } from "@/hooks/useI18n";
|
import { useI18n } from "@/hooks/useI18n";
|
||||||
import { getOfficialSourceLinks } from "@/lib/dashboard-official-sources";
|
import { getOfficialSourceLinks } from "@/lib/dashboard-official-sources";
|
||||||
@@ -360,12 +360,6 @@ export function DetailPanel() {
|
|||||||
: `${t("detail.todayAnalysis")} (Pro)`
|
: `${t("detail.todayAnalysis")} (Pro)`
|
||||||
}
|
}
|
||||||
onClick={() => handleFeatureAccess("today")}
|
onClick={() => handleFeatureAccess("today")}
|
||||||
onFocus={() => {
|
|
||||||
void preloadChartJs();
|
|
||||||
}}
|
|
||||||
onMouseEnter={() => {
|
|
||||||
void preloadChartJs();
|
|
||||||
}}
|
|
||||||
disabled={!store.selectedCity}
|
disabled={!store.selectedCity}
|
||||||
>
|
>
|
||||||
{isPro
|
{isPro
|
||||||
@@ -379,12 +373,6 @@ export function DetailPanel() {
|
|||||||
isPro ? t("detail.history") : `${t("detail.history")} (Pro)`
|
isPro ? t("detail.history") : `${t("detail.history")} (Pro)`
|
||||||
}
|
}
|
||||||
onClick={() => handleFeatureAccess("history")}
|
onClick={() => handleFeatureAccess("history")}
|
||||||
onFocus={() => {
|
|
||||||
void preloadChartJs();
|
|
||||||
}}
|
|
||||||
onMouseEnter={() => {
|
|
||||||
void preloadChartJs();
|
|
||||||
}}
|
|
||||||
disabled={!store.selectedCity}
|
disabled={!store.selectedCity}
|
||||||
>
|
>
|
||||||
{isPro ? t("detail.history") : `${t("detail.history")} · Pro`}
|
{isPro ? t("detail.history") : `${t("detail.history")} · Pro`}
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import {
|
|||||||
DashboardStoreProvider,
|
DashboardStoreProvider,
|
||||||
useDashboardStore,
|
useDashboardStore,
|
||||||
} from "@/hooks/useDashboardStore";
|
} from "@/hooks/useDashboardStore";
|
||||||
import { preloadChartJs } from "@/hooks/useChart";
|
|
||||||
import { I18nProvider, useI18n } from "@/hooks/useI18n";
|
import { I18nProvider, useI18n } from "@/hooks/useI18n";
|
||||||
import { CitySidebar } from "@/components/dashboard/CitySidebar";
|
import { CitySidebar } from "@/components/dashboard/CitySidebar";
|
||||||
import { DetailPanel } from "@/components/dashboard/DetailPanel";
|
import { DetailPanel } from "@/components/dashboard/DetailPanel";
|
||||||
@@ -82,42 +81,6 @@ function DashboardScreen() {
|
|||||||
};
|
};
|
||||||
}, [store]);
|
}, [store]);
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const browserWindow = window as Window & {
|
|
||||||
requestIdleCallback?: (
|
|
||||||
cb: IdleRequestCallback,
|
|
||||||
options?: IdleRequestOptions,
|
|
||||||
) => number;
|
|
||||||
cancelIdleCallback?: (handle: number) => void;
|
|
||||||
};
|
|
||||||
if (typeof browserWindow.requestIdleCallback === "function") {
|
|
||||||
const handle = browserWindow.requestIdleCallback(() => {
|
|
||||||
void loadHistoryModal();
|
|
||||||
void loadFutureForecastModal();
|
|
||||||
}, { timeout: 1200 });
|
|
||||||
return () => {
|
|
||||||
if (typeof browserWindow.cancelIdleCallback === "function") {
|
|
||||||
browserWindow.cancelIdleCallback(handle);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
const timeoutId = window.setTimeout(() => {
|
|
||||||
void loadHistoryModal();
|
|
||||||
void loadFutureForecastModal();
|
|
||||||
}, 500);
|
|
||||||
return () => {
|
|
||||||
window.clearTimeout(timeoutId);
|
|
||||||
};
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (!store.selectedCity) return;
|
|
||||||
void preloadChartJs();
|
|
||||||
void loadHistoryModal();
|
|
||||||
void loadFutureForecastModal();
|
|
||||||
}, [store.selectedCity]);
|
|
||||||
|
|
||||||
// Avoid full-page flashing on initial load; only show this overlay for manual refresh.
|
// Avoid full-page flashing on initial load; only show this overlay for manual refresh.
|
||||||
const showLoading =
|
const showLoading =
|
||||||
store.loadingState.cities ||
|
store.loadingState.cities ||
|
||||||
|
|||||||
@@ -101,38 +101,6 @@ function getMarketScanCacheKey(cityName: string, targetDate?: string | null) {
|
|||||||
|
|
||||||
const SELECTED_CITY_STORAGE_KEY = "polyWeather_selected_city_v1";
|
const SELECTED_CITY_STORAGE_KEY = "polyWeather_selected_city_v1";
|
||||||
const BACKGROUND_SUMMARY_REFRESH_MS = 30_000;
|
const BACKGROUND_SUMMARY_REFRESH_MS = 30_000;
|
||||||
const EAGER_CITY_SUMMARIES_DISABLED =
|
|
||||||
process.env.NEXT_PUBLIC_POLYWEATHER_DISABLE_EAGER_SUMMARIES === "true";
|
|
||||||
const EAGER_SUMMARY_PRIORITY_CONCURRENCY = 3;
|
|
||||||
const EAGER_SUMMARY_BACKGROUND_CONCURRENCY = 2;
|
|
||||||
const EAGER_SUMMARY_BACKGROUND_IDLE_TIMEOUT_MS = 1200;
|
|
||||||
const EAGER_SUMMARY_PRIORITY_CITY_ORDER = [
|
|
||||||
"hong kong",
|
|
||||||
"taipei",
|
|
||||||
"shanghai",
|
|
||||||
"beijing",
|
|
||||||
"wuhan",
|
|
||||||
"shenzhen",
|
|
||||||
"chengdu",
|
|
||||||
"chongqing",
|
|
||||||
"seoul",
|
|
||||||
"busan",
|
|
||||||
"tokyo",
|
|
||||||
"singapore",
|
|
||||||
"kuala lumpur",
|
|
||||||
"jakarta",
|
|
||||||
"tel aviv",
|
|
||||||
"jeddah",
|
|
||||||
"istanbul",
|
|
||||||
"moscow",
|
|
||||||
"helsinki",
|
|
||||||
"warsaw",
|
|
||||||
"amsterdam",
|
|
||||||
"london",
|
|
||||||
"paris",
|
|
||||||
"milan",
|
|
||||||
"madrid",
|
|
||||||
] as const;
|
|
||||||
type CityDetailDepth = "panel" | "market" | "nearby" | "full";
|
type CityDetailDepth = "panel" | "market" | "nearby" | "full";
|
||||||
|
|
||||||
function countAvailableModels(
|
function countAvailableModels(
|
||||||
@@ -291,57 +259,6 @@ function mergeCityDetail(
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function getStoredSelectedCityName(cities: CityListItem[]) {
|
|
||||||
if (typeof window === "undefined") return null;
|
|
||||||
const stored = String(
|
|
||||||
window.localStorage.getItem(SELECTED_CITY_STORAGE_KEY) || "",
|
|
||||||
)
|
|
||||||
.trim()
|
|
||||||
.toLowerCase();
|
|
||||||
if (!stored) return null;
|
|
||||||
if (!cities.some((city) => city.name === stored)) return null;
|
|
||||||
return stored;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getPriorityPreloadCities(cities: CityListItem[]) {
|
|
||||||
const citySet = new Set(cities.map((city) => city.name));
|
|
||||||
return EAGER_SUMMARY_PRIORITY_CITY_ORDER.filter((cityName) =>
|
|
||||||
citySet.has(cityName)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function scheduleWhenBrowserIdle(callback: () => void) {
|
|
||||||
if (typeof window === "undefined") {
|
|
||||||
callback();
|
|
||||||
return () => {};
|
|
||||||
}
|
|
||||||
|
|
||||||
const browserWindow = window as Window & {
|
|
||||||
requestIdleCallback?: (
|
|
||||||
cb: IdleRequestCallback,
|
|
||||||
options?: IdleRequestOptions,
|
|
||||||
) => number;
|
|
||||||
cancelIdleCallback?: (handle: number) => void;
|
|
||||||
};
|
|
||||||
|
|
||||||
if (typeof browserWindow.requestIdleCallback === "function") {
|
|
||||||
const handle = browserWindow.requestIdleCallback(
|
|
||||||
() => callback(),
|
|
||||||
{ timeout: EAGER_SUMMARY_BACKGROUND_IDLE_TIMEOUT_MS },
|
|
||||||
);
|
|
||||||
return () => {
|
|
||||||
if (typeof browserWindow.cancelIdleCallback === "function") {
|
|
||||||
browserWindow.cancelIdleCallback(handle);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
const timeoutId = window.setTimeout(callback, 400);
|
|
||||||
return () => {
|
|
||||||
window.clearTimeout(timeoutId);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function toHistoryMeta(payload: HistoryPayload): HistoryPayloadMeta {
|
function toHistoryMeta(payload: HistoryPayload): HistoryPayloadMeta {
|
||||||
const history = Array.isArray(payload.history) ? payload.history : [];
|
const history = Array.isArray(payload.history) ? payload.history : [];
|
||||||
const previewCount = Number(payload.preview_count || history.length || 0);
|
const previewCount = Number(payload.preview_count || history.length || 0);
|
||||||
@@ -844,75 +761,6 @@ export function DashboardStoreProvider({
|
|||||||
proAccess.userId,
|
proAccess.userId,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (EAGER_CITY_SUMMARIES_DISABLED) return;
|
|
||||||
if (!cities.length) return;
|
|
||||||
if (loadingState.refresh) return;
|
|
||||||
|
|
||||||
const priorityQueue: string[] = [];
|
|
||||||
const backgroundQueue: string[] = [];
|
|
||||||
const queued = new Set<string>();
|
|
||||||
const enqueue = (cityName: string | null, queue: string[]) => {
|
|
||||||
if (!cityName || queued.has(cityName) || citySummariesRef.current[cityName]) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
queued.add(cityName);
|
|
||||||
queue.push(cityName);
|
|
||||||
};
|
|
||||||
|
|
||||||
enqueue(getStoredSelectedCityName(cities), priorityQueue);
|
|
||||||
getPriorityPreloadCities(cities).forEach((cityName) => {
|
|
||||||
enqueue(cityName, priorityQueue);
|
|
||||||
});
|
|
||||||
cities.forEach((city) => {
|
|
||||||
enqueue(city.name, backgroundQueue);
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!priorityQueue.length && !backgroundQueue.length) return;
|
|
||||||
|
|
||||||
let active = true;
|
|
||||||
let cancelIdleSchedule = () => {};
|
|
||||||
const runQueue = async (queue: string[], concurrency: number) => {
|
|
||||||
if (!queue.length) return;
|
|
||||||
let cursor = 0;
|
|
||||||
const worker = async () => {
|
|
||||||
while (active && cursor < queue.length) {
|
|
||||||
const cityName = queue[cursor];
|
|
||||||
cursor += 1;
|
|
||||||
if (citySummariesRef.current[cityName]) continue;
|
|
||||||
|
|
||||||
try {
|
|
||||||
await ensureCitySummary(cityName);
|
|
||||||
} catch {}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
await Promise.all(
|
|
||||||
Array.from({ length: Math.min(concurrency, queue.length) }, () =>
|
|
||||||
worker()
|
|
||||||
),
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
void (async () => {
|
|
||||||
await runQueue(priorityQueue, EAGER_SUMMARY_PRIORITY_CONCURRENCY);
|
|
||||||
if (!active) return;
|
|
||||||
if (isMapInteracting) return;
|
|
||||||
cancelIdleSchedule = scheduleWhenBrowserIdle(() => {
|
|
||||||
if (!active || isMapInteracting) return;
|
|
||||||
void runQueue(
|
|
||||||
backgroundQueue,
|
|
||||||
EAGER_SUMMARY_BACKGROUND_CONCURRENCY,
|
|
||||||
);
|
|
||||||
});
|
|
||||||
})();
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
active = false;
|
|
||||||
cancelIdleSchedule();
|
|
||||||
};
|
|
||||||
}, [cities, isMapInteracting, loadingState.refresh]);
|
|
||||||
|
|
||||||
const selectCity = async (cityName: string) => {
|
const selectCity = async (cityName: string) => {
|
||||||
setSelectedCity(cityName);
|
setSelectedCity(cityName);
|
||||||
setIsPanelOpen(true);
|
setIsPanelOpen(true);
|
||||||
@@ -961,16 +809,6 @@ export function DashboardStoreProvider({
|
|||||||
.then((detail) => {
|
.then((detail) => {
|
||||||
if (selectedCityRef.current !== cityName) return;
|
if (selectedCityRef.current !== cityName) return;
|
||||||
setSelectedForecastDate(detail.local_date);
|
setSelectedForecastDate(detail.local_date);
|
||||||
if (access.authenticated && access.subscriptionActive) {
|
|
||||||
void ensureCityDetail(cityName, false, "market").catch(() => {});
|
|
||||||
// 预热市场数据,不做 await 阻塞,后台静默拉取
|
|
||||||
void ensureCityMarketScan(
|
|
||||||
cityName,
|
|
||||||
false,
|
|
||||||
null,
|
|
||||||
detail.local_date,
|
|
||||||
).catch(() => {});
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
.finally(() => {
|
.finally(() => {
|
||||||
if (selectedCityRef.current !== cityName) return;
|
if (selectedCityRef.current !== cityName) return;
|
||||||
@@ -1002,8 +840,12 @@ export function DashboardStoreProvider({
|
|||||||
)
|
)
|
||||||
.trim()
|
.trim()
|
||||||
.toLowerCase();
|
.toLowerCase();
|
||||||
if (!stored) return;
|
if (!stored) {
|
||||||
if (!cities.some((city) => city.name === stored)) return;
|
return;
|
||||||
|
}
|
||||||
|
if (!cities.some((city) => city.name === stored)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
void selectCity(stored);
|
void selectCity(stored);
|
||||||
}, [cities, selectedCity, selectCity]);
|
}, [cities, selectedCity, selectCity]);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user