fix(presenter,app-iced): 删除确认绑定到对话框身份 + 快照不再吃掉目标(M247 r2 复审)
复审 r1 P1-1(安全):`SavesState::confirm` 的「关闭即清」在 Esc 这条路径上没有落实, 而主按钮分支无条件先取目标 —— 「删存档 → Esc → 崩溃卡片 → 导出诊断包」会静默删掉那个世界。 两层修,各自有独立用例钉住: 1. **认框不认人**:`confirm` 改成 `Option<(DialogId, String)>`,`take_confirm(current)` 只在 id 相等时交付;`DialogMessage::Primary` 传 `dialogs.current().id`。即使别的路径漏清, 也核销不了别人那个框。用例 `a_leaked_target_cannot_be_redeemed_by_another_dialog` (去掉 id 校验即红:实测「漏清的目标被别的框兑现了:世界被静默删除」)。 2. **关闭路径补全**:`update/dialog.rs::escape()` 补 `clear_confirm()`(次按钮/遮罩早就清了, Esc 漏了)。用例 `escaping_the_delete_dialog_disarms_it_for_the_next_one` 复现整条事故链, 断言没有下发 `DeleteWorld`、框走自己的路关掉(去掉这行即红)。 P2-1:`changed()` 不再无条件清 `confirm`(一拍无关快照会让主按钮变成静默空操作)—— 只在「被确认的世界已经从快照里消失」时清,并登记 `closed_confirm` 让 app 层**收起那个框** (`refresh_view → settle_saves_confirm`),避免留一个点了没反应的按钮。`failed()` 同理不动 confirm。 用例:`an_unrelated_snapshot_keeps_the_confirmation_redeemable`、`a_vanished_world_closes_its_confirmation`、 `an_unrelated_snapshot_no_longer_drops_the_confirmation`、`the_confirmation_is_dropped_and_reported_when_the_world_vanishes`。 P2-2:`catalog/instances.rs` 那段「这一版只读 / InstancesSavesReadOnly」的陈旧说明改成现状 (写操作已接上,边界由 core 的 `write::boundary` 守)。 拆分(400 行):`presenter/src/state/saves.rs` 的交互态方法 → `state/saves/interaction.rs`、 判据 → `state/saves/tests.rs`;`app/update/dialog.rs` 的接线用例内联在该文件(5 条)。
This commit is contained in:
1 parent
8c67e4eca6
commit
6c87e2ad8a
8 files changed
+682
-229
No files matched your search
@@ -68,6 +68,9 @@ impl App {
|
||||
self.sync_saves();
|
||||
// M224:启停失败的提示语(presenter 只发结构,文案在这里按 i18n 现求)。
|
||||
self.note_mods_failure();
|
||||
// M247(复审 r1 P2-1):被确认的世界已经从快照里消失 ⇒ 收起那个确认框。
|
||||
// 不这样的话主按钮就是一次静默空操作(点了没删、也不说一句)。
|
||||
self.settle_saves_confirm();
|
||||
|
||||
// 分类是**页面自己持有的过滤条件**(真 host 丢弃 `ListVersions` 的分类参数):
|
||||
// 重建视图模型时带回去,否则每次事件都会把 chip 打回「全部」(复审 P2-1)。
|
||||
@@ -100,6 +103,27 @@ impl App {
|
||||
self.refreshes += 1;
|
||||
}
|
||||
|
||||
/// 收掉「被确认的世界已经不在快照里」的那个删除确认框(M247,复审 r1 P2-1)。
|
||||
///
|
||||
/// 判据由 presenter 给(`SavesState::take_closed_confirm`:那个世界从快照里消失了),
|
||||
/// **框的 id 也由它带着**——所以只收我们自己那个框,崩溃卡片/进度框一概不碰。
|
||||
///
|
||||
/// 为什么必须收:目标已经作废,主按钮就成了一次**静默空操作**(用户点了「删除世界」,
|
||||
/// 框关了、什么也没发生、也没一句解释)。少删是安全的,但"看起来坏了"不是可接受的结果。
|
||||
fn settle_saves_confirm(&mut self) {
|
||||
let Some(dialog) = self.state.saves.take_closed_confirm() else {
|
||||
return;
|
||||
};
|
||||
if self
|
||||
.state
|
||||
.dialogs
|
||||
.current()
|
||||
.is_some_and(|current| current.id == dialog)
|
||||
{
|
||||
self.close_dialog();
|
||||
}
|
||||
}
|
||||
|
||||
/// 把详情页当前实例的启动参数快照灌进表单(唯一的重播种点,见 `pages/instances/state.rs`)。
|
||||
///
|
||||
/// 不在详情页(或实例已不在快照里)时解绑:下次进详情页会重新播种,
|
||||
|
||||
Reference in new issue
Block a user