Lock stale intraday analysis during refresh
This commit is contained in:
@@ -3183,12 +3183,59 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.root :global(.future-modal-body) {
|
.root :global(.future-modal-body) {
|
||||||
|
position: relative;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 14px;
|
gap: 14px;
|
||||||
padding-right: 14px;
|
padding-right: 14px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.root :global(.future-modal-body-refreshing > :not(.future-v2-refresh-lock)) {
|
||||||
|
opacity: 0.18;
|
||||||
|
filter: blur(1px);
|
||||||
|
pointer-events: none;
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.root :global(.future-v2-refresh-lock) {
|
||||||
|
position: sticky;
|
||||||
|
top: 0;
|
||||||
|
z-index: 20;
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
gap: 12px;
|
||||||
|
padding: 14px 16px;
|
||||||
|
border: 1px solid rgba(34, 211, 238, 0.34);
|
||||||
|
border-radius: 8px;
|
||||||
|
background: rgba(8, 13, 24, 0.96);
|
||||||
|
box-shadow: 0 18px 44px rgba(0, 0, 0, 0.34);
|
||||||
|
}
|
||||||
|
|
||||||
|
.root :global(.future-v2-refresh-lock strong) {
|
||||||
|
display: block;
|
||||||
|
color: #e0faff;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 900;
|
||||||
|
}
|
||||||
|
|
||||||
|
.root :global(.future-v2-refresh-lock p) {
|
||||||
|
margin: 5px 0 0;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
font-size: 12px;
|
||||||
|
line-height: 1.55;
|
||||||
|
}
|
||||||
|
|
||||||
|
.root :global(.future-v2-refresh-spinner) {
|
||||||
|
width: 18px;
|
||||||
|
height: 18px;
|
||||||
|
flex: 0 0 auto;
|
||||||
|
margin-top: 1px;
|
||||||
|
border: 2px solid rgba(34, 211, 238, 0.18);
|
||||||
|
border-top-color: #22d3ee;
|
||||||
|
border-radius: 999px;
|
||||||
|
animation: spin 0.8s linear infinite;
|
||||||
|
}
|
||||||
|
|
||||||
.root :global(.future-v2-sync-strip) {
|
.root :global(.future-v2-sync-strip) {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
|
grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
|
||||||
|
|||||||
@@ -724,6 +724,7 @@ export function FutureForecastModal() {
|
|||||||
const isFullDetailReady = detailDepth === "full";
|
const isFullDetailReady = detailDepth === "full";
|
||||||
const isStructureSyncing = store.loadingState.futureDeep || !isFullDetailReady;
|
const isStructureSyncing = store.loadingState.futureDeep || !isFullDetailReady;
|
||||||
const isAnyLayerSyncing = isStructureSyncing;
|
const isAnyLayerSyncing = isStructureSyncing;
|
||||||
|
const isTodayBlockingRefresh = isToday && isStructureSyncing;
|
||||||
const view = getFutureModalView(detail, dateStr, locale);
|
const view = getFutureModalView(detail, dateStr, locale);
|
||||||
const scorePosition = `${50 + view.front.score / 2}%`;
|
const scorePosition = `${50 + view.front.score / 2}%`;
|
||||||
const barStyle = {
|
const barStyle = {
|
||||||
@@ -1261,25 +1262,41 @@ export function FutureForecastModal() {
|
|||||||
const syncStatusItems = [
|
const syncStatusItems = [
|
||||||
{
|
{
|
||||||
key: "base",
|
key: "base",
|
||||||
state: "ready",
|
state: isAnyLayerSyncing ? "syncing" : "ready",
|
||||||
label:
|
label:
|
||||||
locale === "en-US" ? "Base analysis ready" : "基础分析已加载",
|
isAnyLayerSyncing
|
||||||
|
? locale === "en-US"
|
||||||
|
? "Refreshing base analysis"
|
||||||
|
: "正在刷新基础分析"
|
||||||
|
: locale === "en-US" ? "Base analysis ready" : "基础分析已加载",
|
||||||
note:
|
note:
|
||||||
locale === "en-US"
|
isAnyLayerSyncing
|
||||||
? "Forecast curve, anchor state, and the core intraday view are available."
|
? locale === "en-US"
|
||||||
: "预测曲线、锚点状态和核心日内视图已经可用。",
|
? "Latest anchor readings and forecast curve are being rebuilt."
|
||||||
|
: "正在重建最新锚点读数和预测曲线。"
|
||||||
|
: locale === "en-US"
|
||||||
|
? "Forecast curve, anchor state, and the core intraday view are available."
|
||||||
|
: "预测曲线、锚点状态和核心日内视图已经可用。",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "market",
|
key: "market",
|
||||||
state: "ready",
|
state: isAnyLayerSyncing ? "syncing" : "ready",
|
||||||
label:
|
label:
|
||||||
locale === "en-US"
|
isAnyLayerSyncing
|
||||||
? "Probability layer ready"
|
? locale === "en-US"
|
||||||
: "概率层已加载",
|
? "Refreshing probability layer"
|
||||||
|
: "正在刷新概率层"
|
||||||
|
: locale === "en-US"
|
||||||
|
? "Probability layer ready"
|
||||||
|
: "概率层已加载",
|
||||||
note:
|
note:
|
||||||
locale === "en-US"
|
isAnyLayerSyncing
|
||||||
? "Probability buckets are derived from the local model stack."
|
? locale === "en-US"
|
||||||
: "概率桶当前由本地模型栈推导。",
|
? "Model spread and calibrated buckets are updating."
|
||||||
|
: "模型分歧和校准概率桶正在更新。"
|
||||||
|
: locale === "en-US"
|
||||||
|
? "Probability buckets are derived from the local model stack."
|
||||||
|
: "概率桶当前由本地模型栈推导。",
|
||||||
},
|
},
|
||||||
] as const;
|
] as const;
|
||||||
|
|
||||||
@@ -1391,7 +1408,29 @@ export function FutureForecastModal() {
|
|||||||
×
|
×
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div className="modal-body future-modal-body">
|
<div
|
||||||
|
className={clsx(
|
||||||
|
"modal-body future-modal-body",
|
||||||
|
isTodayBlockingRefresh && "future-modal-body-refreshing",
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{isTodayBlockingRefresh && (
|
||||||
|
<div className="future-v2-refresh-lock" role="status" aria-live="assertive">
|
||||||
|
<span className="future-v2-refresh-spinner" aria-hidden="true" />
|
||||||
|
<div>
|
||||||
|
<strong>
|
||||||
|
{locale === "en-US"
|
||||||
|
? "Refreshing latest intraday data"
|
||||||
|
: "正在刷新最新日内数据"}
|
||||||
|
</strong>
|
||||||
|
<p>
|
||||||
|
{locale === "en-US"
|
||||||
|
? "Old cached readings are temporarily locked to prevent misjudgement. The analysis will unlock after the latest anchor observation, model layer, and probability layer are ready."
|
||||||
|
: "旧缓存读数已临时锁定,避免误判。最新锚点观测、模型层和概率层就绪后会自动解锁。"}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
{isToday && (
|
{isToday && (
|
||||||
<section className="future-v2-meteorology-brief">
|
<section className="future-v2-meteorology-brief">
|
||||||
<div className="future-v2-meteorology-copy">
|
<div className="future-v2-meteorology-copy">
|
||||||
@@ -1453,7 +1492,10 @@ export function FutureForecastModal() {
|
|||||||
{syncStatusItems.map((item) => (
|
{syncStatusItems.map((item) => (
|
||||||
<div
|
<div
|
||||||
key={item.key}
|
key={item.key}
|
||||||
className="future-v2-sync-chip"
|
className={clsx(
|
||||||
|
"future-v2-sync-chip",
|
||||||
|
item.state === "syncing" && "syncing",
|
||||||
|
)}
|
||||||
>
|
>
|
||||||
<span className="future-v2-sync-dot" aria-hidden="true" />
|
<span className="future-v2-sync-dot" aria-hidden="true" />
|
||||||
<div className="future-v2-sync-copy">
|
<div className="future-v2-sync-copy">
|
||||||
|
|||||||
Reference in New Issue
Block a user