feat(mira/feed): WikiStatusBadge 加手動重試按鈕
leo 2026-05-17 反饋:「漏了的要怎麼做?會自己慢慢完成還是要手動下令?」 自動行為解釋: mira_feed_watcher cron 每 5 min 掃 tags=[] 的 raw 自動重試。 但若已被 mark wiki-processed (假處理),watcher 永遠跳過 → 需手動。 互動加: - ⚠️ 漏了 變按鈕,click → 清 tag + 立即 trigger wiki_synthesis - ✅ wiki 旁加小 ↻ icon,給「tag 標完成但實際沒 wiki」的情況用 - 點擊後 1 分鐘內顯示「⏳ 重試中」防 spam UX 細節: - title hover 解釋每個狀態跟動作 - retry 同時清 tag + 直接 trigger(不等下個 cron tick) - 失敗 fail silently (catch all),1 min 後可再試 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -1145,6 +1145,7 @@ function DocCard({
|
|||||||
mainBlock={mainBlocksList[0]}
|
mainBlock={mainBlocksList[0]}
|
||||||
createdAt={doc.created_at}
|
createdAt={doc.created_at}
|
||||||
showMira={showMira}
|
showMira={showMira}
|
||||||
|
apiKey={me.api_key}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
return !showMira && mainBlocksList[0]
|
return !showMira && mainBlocksList[0]
|
||||||
@@ -2058,22 +2059,25 @@ function SourceBadge({ source }: { source: string }) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// WikiStatusBadge:顯示這篇 raw 是否已合成 wiki
|
// WikiStatusBadge:顯示這篇 raw 是否已合成 wiki + 「漏了」手動重試按鈕
|
||||||
// - ✅ 已合成(tags 含 wiki-processed)
|
// 對應 leo 2026-05-17 反饋:「漏了的要怎麼做?會自己慢慢完成還是要手動下令?」
|
||||||
// - ⏳ 處理中(< 6 分鐘前貼,可能還沒被 cron 撈到 / wiki_synthesis 正在跑)
|
//
|
||||||
// - ○ 排隊中(> 6 分鐘但 < 30 分鐘,等下一個 cron tick)
|
// 自動行為:mira_feed_watcher cron 每 5 min 掃 tags=[] 的 raw,會無限重試
|
||||||
// - ⚠️ 可能漏了(> 30 分鐘還沒處理)
|
// 手動入口:⚠️ 漏了 變按鈕,點擊 = 清 tag + 立即 trigger wiki_synthesis(不等 cron)
|
||||||
// 對應 leo 2026-05-17 反饋:「沒有符號顯示是否已建立 wiki」
|
|
||||||
function WikiStatusBadge({
|
function WikiStatusBadge({
|
||||||
mainBlock,
|
mainBlock,
|
||||||
createdAt,
|
createdAt,
|
||||||
showMira,
|
showMira,
|
||||||
|
apiKey,
|
||||||
}: {
|
}: {
|
||||||
mainBlock: KBDBBlock | undefined;
|
mainBlock: KBDBBlock | undefined;
|
||||||
createdAt: number | string;
|
createdAt: number | string;
|
||||||
showMira: boolean;
|
showMira: boolean;
|
||||||
|
apiKey: string;
|
||||||
}) {
|
}) {
|
||||||
// Mira 自己貼的(type=wiki-page)就是 wiki,不需要狀態
|
const [retrying, setRetrying] = useState(false);
|
||||||
|
const [retriedAt, setRetriedAt] = useState<number | null>(null);
|
||||||
|
|
||||||
if (showMira) return null;
|
if (showMira) return null;
|
||||||
if (!mainBlock) return null;
|
if (!mainBlock) return null;
|
||||||
|
|
||||||
@@ -2085,51 +2089,83 @@ function WikiStatusBadge({
|
|||||||
// ignore
|
// ignore
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const ageMs = Date.now() - toDate(createdAt).getTime();
|
||||||
|
const minutes = ageMs / 60_000;
|
||||||
|
const recentlyRetried = retriedAt && Date.now() - retriedAt < 60_000;
|
||||||
|
|
||||||
|
// 手動重試:清 wiki-processed tag(保險:watcher 才會重撈)+ 立即 trigger wiki_synthesis
|
||||||
|
const handleRetry = useCallback(async () => {
|
||||||
|
if (retrying) return;
|
||||||
|
setRetrying(true);
|
||||||
|
try {
|
||||||
|
await fetch(`${KBDB_BASE}/blocks/${mainBlock.id}`, {
|
||||||
|
method: 'PATCH',
|
||||||
|
headers: { Authorization: `Bearer ${apiKey}`, 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify({ tags: [] }),
|
||||||
|
}).catch(() => null);
|
||||||
|
await fetch(`${API_BASE}/webhooks/named/wiki_synthesis/trigger`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'X-Arcrun-API-Key': apiKey, 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify({ api_key: apiKey, raw_block_id: mainBlock.id }),
|
||||||
|
}).catch(() => null);
|
||||||
|
setRetriedAt(Date.now());
|
||||||
|
} finally {
|
||||||
|
setRetrying(false);
|
||||||
|
}
|
||||||
|
}, [retrying, mainBlock?.id, apiKey]);
|
||||||
|
|
||||||
if (processed) {
|
if (processed) {
|
||||||
return (
|
return (
|
||||||
<span
|
<span
|
||||||
className="wiki-status wiki-status-done"
|
title="已合成 wiki — 若實際看不到 entity 可能是 fan-out 假處理,按 ↻ 重試"
|
||||||
title="已合成 wiki — click 看詳細 entity"
|
style={{ color: '#3a8a3a', fontSize: '0.85em', display: 'inline-flex', gap: 4, alignItems: 'center' }}
|
||||||
style={{ color: '#3a8a3a', fontSize: '0.85em' }}
|
|
||||||
>
|
>
|
||||||
✅ wiki
|
✅ wiki
|
||||||
|
<button
|
||||||
|
onClick={handleRetry}
|
||||||
|
disabled={retrying || !!recentlyRetried}
|
||||||
|
title="重跑(適用 tag 已標但實際沒 wiki-page 的情況)"
|
||||||
|
style={{
|
||||||
|
background: 'transparent', border: 'none', color: '#888',
|
||||||
|
fontSize: '0.85em', padding: 0, cursor: 'pointer',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{recentlyRetried ? '已重試' : (retrying ? '…' : '↻')}
|
||||||
|
</button>
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const ageMs = Date.now() - toDate(createdAt).getTime();
|
if (recentlyRetried) {
|
||||||
const minutes = ageMs / 60_000;
|
return <span style={{ color: '#888', fontSize: '0.85em' }}>⏳ 重試中</span>;
|
||||||
|
}
|
||||||
|
|
||||||
if (minutes < 6) {
|
if (minutes < 6) {
|
||||||
return (
|
return (
|
||||||
<span
|
<span title="處理中(mira_feed_watcher 每 5 分鐘掃一次)" style={{ color: '#888', fontSize: '0.85em' }}>
|
||||||
className="wiki-status wiki-status-pending"
|
|
||||||
title="處理中(mira_feed_watcher 每 5 分鐘掃一次)"
|
|
||||||
style={{ color: '#888', fontSize: '0.85em' }}
|
|
||||||
>
|
|
||||||
⏳ 處理中
|
⏳ 處理中
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (minutes < 30) {
|
if (minutes < 30) {
|
||||||
return (
|
return (
|
||||||
<span
|
<span title="排隊中等下一個 cron tick (5 分鐘一次)" style={{ color: '#aaa', fontSize: '0.85em' }}>
|
||||||
className="wiki-status wiki-status-queued"
|
|
||||||
title="排隊中等下一個 cron tick"
|
|
||||||
style={{ color: '#aaa', fontSize: '0.85em' }}
|
|
||||||
>
|
|
||||||
○ 排隊
|
○ 排隊
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<span
|
<button
|
||||||
className="wiki-status wiki-status-stuck"
|
onClick={handleRetry}
|
||||||
title={`已 ${Math.floor(minutes)} 分鐘未處理 — 可能失敗,看 wiki/ 確認或 trigger watcher`}
|
disabled={retrying}
|
||||||
style={{ color: '#c66', fontSize: '0.85em' }}
|
title={`已 ${Math.floor(minutes)} 分鐘未處理 — cron 應該已試多次。點擊強制重試一次`}
|
||||||
|
style={{
|
||||||
|
background: 'transparent', border: '1px solid #c66', color: '#c66',
|
||||||
|
fontSize: '0.85em', padding: '0 6px', borderRadius: 4, cursor: 'pointer',
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
⚠️ 漏了?
|
{retrying ? '…' : '⚠️ 漏了 ↻'}
|
||||||
</span>
|
</button>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user