现象
openless-all/app/src/pages/History.tsx 的三个数据动作都没有 try/catch/finally,IPC 一旦 reject,UI 不会回到可用状态,且用户看不到任何错误提示。
完整链路(UI → IPC → Rust handler)
| 动作 |
UI |
IPC 包装 |
Rust handler |
| 刷新 / 初次加载 |
History.tsx:46-56 refresh() → Btn[refresh] History.tsx:95 |
listHistory src/lib/ipc.ts:188 → invokeOrMock('list_history', …) |
commands.rs:539 pub fn list_history(…) -> Result<Vec<DictationSession>, String> |
| 清空 |
History.tsx:68-74 onClear() → Btn[clear] History.tsx:96 |
clearHistory src/lib/ipc.ts:196 → invokeOrMock('clear_history', …) |
commands.rs:549 pub fn clear_history(…) -> Result<(), String> |
| 单条删除 |
History.tsx:76-80 onDelete() → Btn[trash] History.tsx:178 |
deleteHistoryEntry src/lib/ipc.ts:192 → invokeOrMock('delete_history_entry', { id }, …) |
commands.rs:544 pub fn delete_history_entry(…) -> Result<(), String> |
失败路径
refresh() 当前实现:
const refresh = async () => {
const data = await listHistory();
setItems(data);
setLoading(false); // ← listHistory reject 时永远不执行
if (data.length > 0 && !selectedId) setSelectedId(data[0].id);
};
listHistory reject(如 history.json 损坏 / IO 错误,handler 返回 Err(String))→ setLoading(false) 永不执行 → 列表区永远显示 t('common.loading') 文案,主面板也永远显示"加载中"
onClear / onDelete reject → 后端清/删失败,但前端 setItems([…]) 已经把项移走 → UI 与磁盘不一致;用户不知道
- 三处都没有 user-visible error toast / inline message
用户可见后果
- 列表加载失败 = 永久"加载中…"卡死状态,无重试入口
- 删除/清空表面上"成功",下次重启或 refresh 后条目又出现,用户不知道刚才点的删除其实没生效
建议接受标准
不在范围
- 不重写错误展示组件 / 不引入新 toast 库
- 不修改 Rust 端 handler 行为(handler 已经返回
Result<_, String>,前端只是不用)
- 其他页类似问题分别在 Overview / Style / Translation / SelectionAsk 单独跟踪
现象
openless-all/app/src/pages/History.tsx的三个数据动作都没有try/catch/finally,IPC 一旦 reject,UI 不会回到可用状态,且用户看不到任何错误提示。完整链路(UI → IPC → Rust handler)
History.tsx:46-56refresh()→Btn[refresh]History.tsx:95listHistorysrc/lib/ipc.ts:188→invokeOrMock('list_history', …)commands.rs:539pub fn list_history(…) -> Result<Vec<DictationSession>, String>History.tsx:68-74onClear()→Btn[clear]History.tsx:96clearHistorysrc/lib/ipc.ts:196→invokeOrMock('clear_history', …)commands.rs:549pub fn clear_history(…) -> Result<(), String>History.tsx:76-80onDelete()→Btn[trash]History.tsx:178deleteHistoryEntrysrc/lib/ipc.ts:192→invokeOrMock('delete_history_entry', { id }, …)commands.rs:544pub fn delete_history_entry(…) -> Result<(), String>失败路径
refresh()当前实现:listHistoryreject(如history.json损坏 / IO 错误,handler 返回Err(String))→setLoading(false)永不执行 → 列表区永远显示t('common.loading')文案,主面板也永远显示"加载中"onClear/onDeletereject → 后端清/删失败,但前端setItems([…])已经把项移走 → UI 与磁盘不一致;用户不知道用户可见后果
建议接受标准
refresh()失败时把loading置回 false 并显示可点的"加载失败,重试"占位onClear/onDelete失败时回滚items状态 + 显示错误提示Err(String),避免泄露内部细节但要可读Card内 inline 显示不在范围
Result<_, String>,前端只是不用)