Optimize homepage dashboard render paths

This commit is contained in:
2569718930@qq.com
2026-04-23 01:48:08 +08:00
parent 1ed45ded9f
commit a0ea4740b9
@@ -476,11 +476,14 @@ function HomeIntelligencePanel({ snapshots }: { snapshots: CitySnapshot[] }) {
label: string; label: string;
temperatureText: string; temperatureText: string;
} | null>(null); } | null>(null);
const selectedSnapshot = store.selectedCity const spotlight = useMemo(
? snapshots.find((snapshot) => snapshot.city.name === store.selectedCity) () =>
: null; store.selectedCity
const spotlight = selectedSnapshot || null; ? snapshots.find((snapshot) => snapshot.city.name === store.selectedCity) || null
: null,
[snapshots, store.selectedCity],
);
const spotlightView = useMemo(() => {
if (!spotlight) return null; if (!spotlight) return null;
const { city, detail, summary } = spotlight; const { city, detail, summary } = spotlight;
@@ -488,7 +491,8 @@ function HomeIntelligencePanel({ snapshots }: { snapshots: CitySnapshot[] }) {
const currentTemp = summary?.current?.temp ?? detail?.current?.temp; const currentTemp = summary?.current?.temp ?? detail?.current?.temp;
const debPrediction = summary?.deb?.prediction ?? detail?.deb?.prediction; const debPrediction = summary?.deb?.prediction ?? detail?.deb?.prediction;
const maxSoFar = detail?.current?.max_so_far ?? detail?.airport_current?.max_so_far; const maxSoFar = detail?.current?.max_so_far ?? detail?.airport_current?.max_so_far;
const maxTime = detail?.current?.max_temp_time || detail?.airport_current?.max_temp_time || "--"; const maxTime =
detail?.current?.max_temp_time || detail?.airport_current?.max_temp_time || "--";
const localTime = summary?.local_time || detail?.local_time || "--"; const localTime = summary?.local_time || detail?.local_time || "--";
const riskLevel = const riskLevel =
city.deb_recent_tier || city.deb_recent_tier ||
@@ -496,8 +500,7 @@ function HomeIntelligencePanel({ snapshots }: { snapshots: CitySnapshot[] }) {
summary?.risk?.level || summary?.risk?.level ||
detail?.risk?.level; detail?.risk?.level;
const deviation = summary?.deviation_monitor || detail?.deviation_monitor; const deviation = summary?.deviation_monitor || detail?.deviation_monitor;
const observationSource = const observationSource = normalizeObservationSourceLabel(
normalizeObservationSourceLabel(
summary?.current?.settlement_source_label || summary?.current?.settlement_source_label ||
detail?.current?.settlement_source_label || detail?.current?.settlement_source_label ||
city.settlement_source_label, city.settlement_source_label,
@@ -512,9 +515,8 @@ function HomeIntelligencePanel({ snapshots }: { snapshots: CitySnapshot[] }) {
const displayedProbabilities = probabilityBuckets.slice(0, 5); const displayedProbabilities = probabilityBuckets.slice(0, 5);
const modelLabels = getModelLabels(detail); const modelLabels = getModelLabels(detail);
const marketScan = detail?.market_scan; const marketScan = detail?.market_scan;
const hasMarketScan = Boolean(marketScan); const marketBucket =
const showOpportunityLabel = !hasMarketScan || spotlight.tradableOpportunity; marketScan?.temperature_bucket || marketScan?.top_buckets?.[0] || null;
const marketBucket = marketScan?.temperature_bucket || marketScan?.top_buckets?.[0] || null;
const yesPrice = const yesPrice =
marketScan?.yes_buy ?? marketScan?.yes_buy ??
readNumericField(marketBucket, "yes_buy") ?? readNumericField(marketBucket, "yes_buy") ??
@@ -533,49 +535,69 @@ function HomeIntelligencePanel({ snapshots }: { snapshots: CitySnapshot[] }) {
marketScan?.price_analysis?.yes?.edge ?? marketScan?.price_analysis?.yes?.edge ??
marketScan?.price_analysis?.no?.edge_percent ?? marketScan?.price_analysis?.no?.edge_percent ??
marketScan?.price_analysis?.no?.edge; marketScan?.price_analysis?.no?.edge;
const marketLabel = marketBucket
return {
city,
detail,
tradableOpportunity: spotlight.tradableOpportunity,
symbol,
currentTemp,
debPrediction,
maxSoFar,
maxTime,
localTime,
riskLevel,
observationSource,
displayedProbabilities,
modelLabels,
marketScan,
showOpportunityLabel: !marketScan || spotlight.tradableOpportunity,
marketLabel: marketBucket
? getProbabilityLabel(marketBucket, symbol) ? getProbabilityLabel(marketBucket, symbol)
: debPrediction != null : debPrediction != null
? `> ${formatTemperature(debPrediction, symbol)}` ? `> ${formatTemperature(debPrediction, symbol)}`
: "--"; : "--",
const marketProbability = yesPrice,
noPrice,
marketEdge,
marketProbability:
marketScan?.market_price ?? marketScan?.market_price ??
readNumericField(marketBucket, "market_price") ?? readNumericField(marketBucket, "market_price") ??
readNumericField(marketBucket, "yes_buy") ?? readNumericField(marketBucket, "yes_buy") ??
yesPrice ?? yesPrice ??
marketScan?.model_probability; marketScan?.model_probability,
const marketModelProbability = marketScan?.model_probability ?? marketBucket?.probability; marketModelProbability: marketScan?.model_probability ?? marketBucket?.probability,
const sparklinePoints = buildSparklinePoints(marketScan?.sparkline); sparklinePoints: buildSparklinePoints(marketScan?.sparkline),
const isLoading = store.loadingState.cityDetail && store.selectedCity === city.name; isLoading: store.loadingState.cityDetail && store.selectedCity === city.name,
const isPro = store.proAccess.subscriptionActive; isPro: store.proAccess.subscriptionActive,
const cityCode = city.icao || detail?.risk?.icao || city.airport; cityCode: city.icao || detail?.risk?.icao || city.airport,
const localizedCityName = getLocalizedCityDisplay(city, locale, summary, detail); localizedCityName: getLocalizedCityDisplay(city, locale, summary, detail),
const localizedAirportName = getLocalizedAirportDisplay(city, locale, detail); localizedAirportName: getLocalizedAirportDisplay(city, locale, detail),
const subtitle = `${cityCode} · ${localizedAirportName}`; highRiskLabel:
const highRiskLabel =
riskLevel === "high" riskLevel === "high"
? locale === "en-US" ? locale === "en-US"
? "High risk" ? "High risk"
: "高风险" : "高风险"
: getRiskCopy(riskLevel, locale); : getRiskCopy(riskLevel, locale),
const weatherIconKind = getHomeWeatherIconKind(detail, locale); weatherIconKind: getHomeWeatherIconKind(detail, locale),
const trendChart = buildHomeTrendChart(detail, locale); trendChart: buildHomeTrendChart(detail, locale),
const debLabel = locale === "en-US" ? "DEB forecast" : "DEB 预测"; debLabel: locale === "en-US" ? "DEB forecast" : "DEB 预测",
const dayMaxLabel = locale === "en-US" ? "24h max" : "24 小时最高"; dayMaxLabel: locale === "en-US" ? "24h max" : "24 小时最高",
const probabilityTitle = locale === "en-US" ? "EMOS probability" : "EMOS 概率"; probabilityTitle: locale === "en-US" ? "EMOS probability" : "EMOS 概率",
const marketTitle = locale === "en-US" ? "Market edge" : "市场优势"; marketTitle: locale === "en-US" ? "Market edge" : "市场优势",
const marketEdgeLabel = locale === "en-US" ? "Edge" : "优势"; marketEdgeLabel: locale === "en-US" ? "Edge" : "优势",
const marketImpliedLabel = locale === "en-US" ? "Implied" : "市场隐含"; marketImpliedLabel: locale === "en-US" ? "Implied" : "市场隐含",
const marketModelLabel = locale === "en-US" ? "Model prob" : "模型概率"; marketModelLabel: locale === "en-US" ? "Model prob" : "模型概率",
const proLabel = isPro proLabel:
store.proAccess.subscriptionActive
? locale === "en-US" ? locale === "en-US"
? "Pro signal" ? "Pro signal"
: "PRO 信号" : "PRO 信号"
: locale === "en-US" : locale === "en-US"
? "Pro locked" ? "Pro locked"
: "PRO 锁定"; : "PRO 锁定",
const forecastDays = buildHomeForecastDays(detail, locale); forecastDays: buildHomeForecastDays(detail, locale),
const keySignals = [ keySignals: [
{ {
active: Number(marketEdge) > 0, active: Number(marketEdge) > 0,
label: label:
@@ -599,7 +621,61 @@ function HomeIntelligencePanel({ snapshots }: { snapshots: CitySnapshot[] }) {
: "高影响窗口待确认", : "高影响窗口待确认",
tone: "amber", tone: "amber",
}, },
]; ],
};
}, [
locale,
spotlight,
store.loadingState.cityDetail,
store.proAccess.subscriptionActive,
store.selectedCity,
]);
if (!spotlightView) return null;
const {
city,
detail,
symbol,
currentTemp,
debPrediction,
maxSoFar,
maxTime,
localTime,
riskLevel,
observationSource,
displayedProbabilities,
modelLabels,
marketScan,
tradableOpportunity,
showOpportunityLabel,
marketLabel,
yesPrice,
noPrice,
marketEdge,
marketProbability,
marketModelProbability,
sparklinePoints,
isLoading,
isPro,
cityCode,
localizedCityName,
localizedAirportName,
highRiskLabel,
weatherIconKind,
trendChart,
debLabel,
dayMaxLabel,
probabilityTitle,
marketTitle,
marketEdgeLabel,
marketImpliedLabel,
marketModelLabel,
proLabel,
forecastDays,
keySignals,
} = spotlightView;
const subtitle = `${cityCode} · ${localizedAirportName}`;
return ( return (
<aside className="home-intelligence-panel full" aria-label={localizedCityName}> <aside className="home-intelligence-panel full" aria-label={localizedCityName}>
@@ -815,7 +891,7 @@ function HomeIntelligencePanel({ snapshots }: { snapshots: CitySnapshot[] }) {
{marketTitle} <small></small> {marketTitle} <small></small>
</h3> </h3>
<span> <span>
{hasMarketScan && !spotlight.tradableOpportunity {marketScan && !tradableOpportunity
? locale === "en-US" ? locale === "en-US"
? "Market closed" ? "Market closed"
: "市场已结束" : "市场已结束"
@@ -902,12 +978,18 @@ function OpportunityStrip({
}) { }) {
const store = useDashboardStore(); const store = useDashboardStore();
const { locale } = useI18n(); const { locale } = useI18n();
const items = recentCityNames const snapshotByName = useMemo(
.map((cityName) => () => new Map(snapshots.map((snapshot) => [snapshot.city.name, snapshot])),
snapshots.find((snapshot) => snapshot.city.name === cityName) || null, [snapshots],
) );
const items = useMemo(
() =>
recentCityNames
.map((cityName) => snapshotByName.get(cityName) || null)
.filter((snapshot): snapshot is CitySnapshot => snapshot != null) .filter((snapshot): snapshot is CitySnapshot => snapshot != null)
.slice(0, 4); .slice(0, 4),
[recentCityNames, snapshotByName],
);
if (!items.length) return null; if (!items.length) return null;
@@ -1022,6 +1104,10 @@ function DashboardScreen() {
), ),
[store.cities, store.cityDetailsByName, store.citySummariesByName], [store.cities, store.cityDetailsByName, store.citySummariesByName],
); );
const fallbackRecentCityNames = useMemo(
() => homepageSnapshots.slice(0, 4).map((snapshot) => snapshot.city.name),
[homepageSnapshots],
);
const showHomepageChrome = const showHomepageChrome =
!store.historyState.isOpen && !store.futureModalDate; !store.historyState.isOpen && !store.futureModalDate;
@@ -1097,11 +1183,7 @@ function DashboardScreen() {
<HomeIntelligencePanel snapshots={homepageSnapshots} /> <HomeIntelligencePanel snapshots={homepageSnapshots} />
<OpportunityStrip <OpportunityStrip
snapshots={homepageSnapshots} snapshots={homepageSnapshots}
recentCityNames={ recentCityNames={recentCityNames.length ? recentCityNames : fallbackRecentCityNames}
recentCityNames.length
? recentCityNames
: homepageSnapshots.slice(0, 4).map((snapshot) => snapshot.city.name)
}
/> />
</> </>
) : null} ) : null}