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,130 +476,206 @@ 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 symbol = getTempSymbol(city, summary, detail);
const { city, detail, summary } = spotlight; const currentTemp = summary?.current?.temp ?? detail?.current?.temp;
const symbol = getTempSymbol(city, summary, detail); const debPrediction = summary?.deb?.prediction ?? detail?.deb?.prediction;
const currentTemp = summary?.current?.temp ?? detail?.current?.temp; const maxSoFar = detail?.current?.max_so_far ?? detail?.airport_current?.max_so_far;
const debPrediction = summary?.deb?.prediction ?? detail?.deb?.prediction; const maxTime =
const maxSoFar = detail?.current?.max_so_far ?? detail?.airport_current?.max_so_far; 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 || city.risk_level ||
city.risk_level || 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 = normalizeObservationSourceLabel(
const observationSource =
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,
"METAR", "METAR",
); );
const probabilityBuckets = const probabilityBuckets =
detail?.probabilities?.distribution || detail?.probabilities?.distribution ||
(detail?.local_date (detail?.local_date
? detail?.multi_model_daily?.[detail.local_date]?.probabilities ? detail?.multi_model_daily?.[detail.local_date]?.probabilities
: undefined) || : undefined) ||
[]; [];
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") ?? marketScan?.yes_token?.buy_price ??
marketScan?.yes_token?.buy_price ?? marketScan?.yes_token?.midpoint ??
marketScan?.yes_token?.midpoint ?? marketScan?.price_analysis?.yes?.ask;
marketScan?.price_analysis?.yes?.ask; const noPrice =
const noPrice = marketScan?.no_buy ??
marketScan?.no_buy ?? readNumericField(marketBucket, "no_buy") ??
readNumericField(marketBucket, "no_buy") ?? marketScan?.no_token?.buy_price ??
marketScan?.no_token?.buy_price ?? marketScan?.no_token?.midpoint ??
marketScan?.no_token?.midpoint ?? marketScan?.price_analysis?.no?.ask;
marketScan?.price_analysis?.no?.ask; const marketEdge =
const marketEdge = marketScan?.edge_percent ??
marketScan?.edge_percent ?? marketScan?.price_analysis?.yes?.edge_percent ??
marketScan?.price_analysis?.yes?.edge_percent ?? 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 {
? getProbabilityLabel(marketBucket, symbol) city,
: debPrediction != null detail,
? `> ${formatTemperature(debPrediction, symbol)}` tradableOpportunity: spotlight.tradableOpportunity,
: "--"; symbol,
const marketProbability = currentTemp,
marketScan?.market_price ?? debPrediction,
readNumericField(marketBucket, "market_price") ?? maxSoFar,
readNumericField(marketBucket, "yes_buy") ?? maxTime,
yesPrice ?? localTime,
marketScan?.model_probability; riskLevel,
const marketModelProbability = marketScan?.model_probability ?? marketBucket?.probability; observationSource,
const sparklinePoints = buildSparklinePoints(marketScan?.sparkline); displayedProbabilities,
const isLoading = store.loadingState.cityDetail && store.selectedCity === city.name; modelLabels,
const isPro = store.proAccess.subscriptionActive; marketScan,
const cityCode = city.icao || detail?.risk?.icao || city.airport; showOpportunityLabel: !marketScan || spotlight.tradableOpportunity,
const localizedCityName = getLocalizedCityDisplay(city, locale, summary, detail); marketLabel: marketBucket
const localizedAirportName = getLocalizedAirportDisplay(city, locale, detail); ? getProbabilityLabel(marketBucket, symbol)
const subtitle = `${cityCode} · ${localizedAirportName}`; : debPrediction != null
const highRiskLabel = ? `> ${formatTemperature(debPrediction, symbol)}`
riskLevel === "high" : "--",
? locale === "en-US" yesPrice,
? "High risk" noPrice,
: "高风险" marketEdge,
: getRiskCopy(riskLevel, locale); marketProbability:
const weatherIconKind = getHomeWeatherIconKind(detail, locale); marketScan?.market_price ??
const trendChart = buildHomeTrendChart(detail, locale); readNumericField(marketBucket, "market_price") ??
const debLabel = locale === "en-US" ? "DEB forecast" : "DEB 预测"; readNumericField(marketBucket, "yes_buy") ??
const dayMaxLabel = locale === "en-US" ? "24h max" : "24 小时最高"; yesPrice ??
const probabilityTitle = locale === "en-US" ? "EMOS probability" : "EMOS 概率"; marketScan?.model_probability,
const marketTitle = locale === "en-US" ? "Market edge" : "市场优势"; marketModelProbability: marketScan?.model_probability ?? marketBucket?.probability,
const marketEdgeLabel = locale === "en-US" ? "Edge" : "优势"; sparklinePoints: buildSparklinePoints(marketScan?.sparkline),
const marketImpliedLabel = locale === "en-US" ? "Implied" : "市场隐含"; isLoading: store.loadingState.cityDetail && store.selectedCity === city.name,
const marketModelLabel = locale === "en-US" ? "Model prob" : "模型概率"; isPro: store.proAccess.subscriptionActive,
const proLabel = isPro cityCode: city.icao || detail?.risk?.icao || city.airport,
? locale === "en-US" localizedCityName: getLocalizedCityDisplay(city, locale, summary, detail),
? "Pro signal" localizedAirportName: getLocalizedAirportDisplay(city, locale, detail),
: "PRO 信号" highRiskLabel:
: locale === "en-US" riskLevel === "high"
? "Pro locked" ? locale === "en-US"
: "PRO 锁定"; ? "High risk"
const forecastDays = buildHomeForecastDays(detail, locale); : "高风险"
const keySignals = [ : getRiskCopy(riskLevel, locale),
{ weatherIconKind: getHomeWeatherIconKind(detail, locale),
active: Number(marketEdge) > 0, trendChart: buildHomeTrendChart(detail, locale),
label: debLabel: locale === "en-US" ? "DEB forecast" : "DEB 预测",
locale === "en-US" dayMaxLabel: locale === "en-US" ? "24h max" : "24 小时最高",
? "DEB > Market implied" probabilityTitle: locale === "en-US" ? "EMOS probability" : "EMOS 概率",
: "DEB 高于市场隐含", marketTitle: locale === "en-US" ? "Market edge" : "市场优势",
tone: "green", marketEdgeLabel: locale === "en-US" ? "Edge" : "优势",
}, marketImpliedLabel: locale === "en-US" ? "Implied" : "市场隐含",
{ marketModelLabel: locale === "en-US" ? "Model prob" : "模型概率",
active: deviation?.trend === "expanding" || deviation?.direction === "hot", proLabel:
label: locale === "en-US" ? "Rising temps trend" : "升温趋势", store.proAccess.subscriptionActive
tone: "green", ? locale === "en-US"
}, ? "Pro signal"
{ : "PRO 信号"
active: Boolean(detail?.peak?.hours?.length),
label:
detail?.peak?.hours?.length
? `${locale === "en-US" ? "High impact window" : "高影响窗口"} ${detail.peak.hours[0]}-${detail.peak.hours[detail.peak.hours.length - 1]}`
: locale === "en-US" : locale === "en-US"
? "High impact window pending" ? "Pro locked"
: "高影响窗口待确认", : "PRO 锁定",
tone: "amber", forecastDays: buildHomeForecastDays(detail, locale),
}, keySignals: [
]; {
active: Number(marketEdge) > 0,
label:
locale === "en-US"
? "DEB > Market implied"
: "DEB 高于市场隐含",
tone: "green",
},
{
active: deviation?.trend === "expanding" || deviation?.direction === "hot",
label: locale === "en-US" ? "Rising temps trend" : "升温趋势",
tone: "green",
},
{
active: Boolean(detail?.peak?.hours?.length),
label:
detail?.peak?.hours?.length
? `${locale === "en-US" ? "High impact window" : "高影响窗口"} ${detail.peak.hours[0]}-${detail.peak.hours[detail.peak.hours.length - 1]}`
: locale === "en-US"
? "High impact window pending"
: "高影响窗口待确认",
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],
) );
.filter((snapshot): snapshot is CitySnapshot => snapshot != null) const items = useMemo(
.slice(0, 4); () =>
recentCityNames
.map((cityName) => snapshotByName.get(cityName) || null)
.filter((snapshot): snapshot is CitySnapshot => snapshot != null)
.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}