fix(download): M94 r2 —— 安装终态出口 + 分类真机过滤(复审 P1-1 / P2-1)
P1-1 安装一旦收尾就再也装不了第二个版本(终态面板没有出口)
- 页面侧:`Download::from_snapshot` 把 **Done / Cancelled** 当结束(`InstallView::finished`),
面板让回列表 —— 装完一个版本可以直接选下一个继续装(图:r2-done / r2-done-next)。
不清 `Failed`:失败态带着用户要的东西(停在哪个文件 + 去日志页)。
- 面板加真出口:失败态「Open logs」(实心)+「Dismiss」(文本 → `Message::CancelInstall`,
host 侧对没在跑的任务是安全 no-op),修掉「失败后连重试都不可达」。
- 决策抽成纯函数 `install::panel_state()`(Hint / Ready / Install),`panel()` 只是它的画法,
于是「能不能再装」可以被单测钉死,不用开图形环境。
- 两条新测试**不走 MockBackend**(它从不发 `InstallProgress`,终态永远跑不到 ⇒ 假绿):
直接构造 `AppState` + `apply(CoreEvent::InstallProgress(..))`。
P2-1 分类 chip 在真 host 上是空控件
- `from_snapshot(state, category, catalog)` 收分类并按 `Category::matches` **本地过滤**
(真 host 的 `ListVersions(_)` 丢掉分类参数、回全量清单);
- 分类不再从列表反推(`Category::of` 只留给演示首帧),`app/view.rs::model` 收分类、
`refresh_view` 把上一份视图模型的分类带回去 ⇒ 事件回来 chip 不会跳回「全部」;
- `update.rs` 的 `SetCategory` 写分类后**立刻重建**(chip 与列表同一个谓词),再派真指令。
p3(顺带)
- 失败态与占位态共存:列表空/加载中时若安装还在跑,面板照样出现在底部(r2-installing-empty);
- 修 `install.rs` 的坏 rustdoc 链接;`install.rs` / `model.rs` 的测试模块外置到子文件守住 400 行。
测试文件:`pages/download/{model,install}/tests.rs`(新),`pages/download/tests.rs` 里的
`from_snapshot` 调用补分类参数。
实证(回退法):删掉 `.filter(|view| !view.finished())` → 两条终态测试红并点名
「Done 之后不许再占着安装面板」;删掉 `.filter(|category.matches(..))` → 两条分类测试红
(「只剩正式版」left=3 right=1)。放回修复 → 全绿。
门禁:fmt --check 干净、clippy -D warnings 干净、app-iced 280 + 集成 6 + i18n 23 全绿(-j 4)。
This commit is contained in:
1 parent
9d6ee70301
commit
6453c76750
12 files changed
+432
-74
No files matched your search
@@ -18,13 +18,25 @@
|
||||
//! --screen=download --screenshot=out.png --screenshot-theme=light --software-renderer
|
||||
//! ```
|
||||
//!
|
||||
//! 取值:`ready`(缺省)/ `loading` / `empty` / `selected` / `installing` / `installing-late` / `failed`。
|
||||
//! 取值:`ready`(缺省)/ `loading` / `empty` / `selected` / `category-release` / `installing` /
|
||||
//! `installing-late` / `installing-empty` / `failed` / `done` / `done-next`。
|
||||
//!
|
||||
//! `category-release` / `done` / `done-next` 都**不手搓结果**,而是拿一份 `AppState` 跑
|
||||
//! [`Download::from_snapshot`]:图证明的是产品路径的行为(分类本地过滤、装完后面板让回列表),
|
||||
//! 不是演示的特权。
|
||||
//!
|
||||
//! `done` / `done-next` 是「**刚装完**」的两个采样点:它们不手搓模型,而是拿一份
|
||||
//! `AppState`(版本列表 + `InstallStage::Done`)跑 [`Download::from_snapshot`],所以图上看到的是
|
||||
//! 产品路径的真行为(复审 p2:`Done` 之后面板必须让回列表,否则第二个版本装不了)。
|
||||
//! **只有 harness 与演示走这里**:产品路径由 `app/view.rs` 从 presenter 快照建模型。
|
||||
|
||||
use unlucky_presenter::dto::{
|
||||
AncientKind, InstallProgressView, InstallStage, VersionKind, VersionSummary,
|
||||
};
|
||||
|
||||
use unlucky_presenter::dto::{AppEvent, CoreEvent};
|
||||
use unlucky_presenter::state::AppState;
|
||||
|
||||
use unlucky_i18n::Catalog;
|
||||
|
||||
use super::model::{Category, Download, InstallView, VersionRow};
|
||||
@@ -60,6 +72,17 @@ pub fn demo(catalog: Catalog) -> Download {
|
||||
selected: Some("1.21.9".to_string()),
|
||||
..ready(catalog)
|
||||
},
|
||||
// 装完之后:走**产品那条真转换**(见 `finished`),图里看到的就是页面真会画的东西
|
||||
Some("done") => finished(catalog, None),
|
||||
// 装完之后再点下一个版本:面板必须重新给出实心「安装」
|
||||
Some("done-next") => finished(catalog, Some("1.21.9-pre3")),
|
||||
// 安装进行中、但列表被过滤空:面板要跟着占位态一起在(复审 p3 的边角)
|
||||
// 分类筛选(本页本地过滤:真 host 丢弃 ListVersions 的分类参数)——混合清单里只留正式版
|
||||
Some("category-release") => category(catalog, Category::Release),
|
||||
Some("installing-empty") => Download {
|
||||
install: Some(install(InstallStage::Downloading, 0.42, catalog)),
|
||||
..Download::loading()
|
||||
},
|
||||
_ => ready(catalog),
|
||||
}
|
||||
}
|
||||
@@ -69,6 +92,24 @@ pub fn demo(catalog: Catalog) -> Download {
|
||||
/// 不复用 `mock_data::mock_versions()`:演示数据是**出图的基线**,得比 mock 稳定
|
||||
/// (mock 随 P3 的假数据一起漂)。
|
||||
fn ready(catalog: Catalog) -> Download {
|
||||
let versions = demo_versions();
|
||||
let rows: Vec<VersionRow> = versions
|
||||
.iter()
|
||||
.map(|version| VersionRow::of(version, catalog))
|
||||
.collect();
|
||||
|
||||
Download {
|
||||
// 混合分类 → 没有单个 chip 该亮(这正是 `Category::of` 的反推结果)
|
||||
category: Category::of(&versions),
|
||||
rows,
|
||||
selected: None,
|
||||
install: None,
|
||||
loaded: true,
|
||||
}
|
||||
}
|
||||
|
||||
/// 演示用的版本表(`ready` 与 `finished` 共用;与 presenter 的 `mock_versions` 同形)。
|
||||
fn demo_versions() -> Vec<VersionSummary> {
|
||||
fn summary(id: &str, kind: VersionKind, released: &str, installed: bool) -> VersionSummary {
|
||||
VersionSummary {
|
||||
id: id.to_string(),
|
||||
@@ -78,7 +119,7 @@ fn ready(catalog: Catalog) -> Download {
|
||||
}
|
||||
}
|
||||
|
||||
let versions = vec![
|
||||
vec![
|
||||
summary("1.21.9", VersionKind::Release, "2026-09-20", false),
|
||||
summary("1.21.8", VersionKind::Release, "2026-07-11", true),
|
||||
summary("1.20.4", VersionKind::Release, "2023-12-07", true),
|
||||
@@ -95,21 +136,43 @@ fn ready(catalog: Catalog) -> Download {
|
||||
"2010-08-11",
|
||||
false,
|
||||
),
|
||||
];
|
||||
]
|
||||
}
|
||||
|
||||
let rows: Vec<VersionRow> = versions
|
||||
.iter()
|
||||
.map(|version| VersionRow::of(version, catalog))
|
||||
.collect();
|
||||
/// 「刚装完一个版本」的演示:**走产品那条真转换**(`AppState` → `Download::from_snapshot`)。
|
||||
///
|
||||
/// 为什么不用手搓模型:本轮复审的 bug 正是「`Done` 之后 `install` 从不清」——手搓一个
|
||||
/// `install: None` 的模型会让图**假绿**(图上好看,真路径照样卡死)。这里真的走一遍快照转换,
|
||||
/// 于是图能证明「装完之后面板让回列表」是产品路径的行为,不是演示的特权。
|
||||
///
|
||||
/// `select_next`:演「用户接着点了下一个版本」,面板应当重新给出实心「安装」。
|
||||
fn finished(catalog: Catalog, select_next: Option<&str>) -> Download {
|
||||
let mut state = AppState::new();
|
||||
state.apply(AppEvent::Core(CoreEvent::VersionListChanged(
|
||||
demo_versions(),
|
||||
)));
|
||||
state.apply(AppEvent::Core(CoreEvent::InstallProgress(Some(
|
||||
InstallProgressView {
|
||||
version: "1.21.8".to_string(),
|
||||
stage: InstallStage::Done,
|
||||
files_done: 512,
|
||||
files_total: 512,
|
||||
..InstallProgressView::new()
|
||||
},
|
||||
))));
|
||||
|
||||
Download {
|
||||
// 混合分类 → 没有单个 chip 该亮(这正是 `Category::of` 的反推结果)
|
||||
category: Category::of(&versions),
|
||||
rows,
|
||||
selected: None,
|
||||
install: None,
|
||||
loaded: true,
|
||||
}
|
||||
let mut model = Download::from_snapshot(&state, Category::All, catalog);
|
||||
model.selected = select_next.map(str::to_string);
|
||||
model
|
||||
}
|
||||
|
||||
/// 「用户点了某个分类」的演示:走真转换(`from_snapshot` 的分类过滤),不手搓过滤结果。
|
||||
fn category(catalog: Catalog, wanted: Category) -> Download {
|
||||
let mut state = AppState::new();
|
||||
state.apply(AppEvent::Core(CoreEvent::VersionListChanged(
|
||||
demo_versions(),
|
||||
)));
|
||||
Download::from_snapshot(&state, wanted, catalog)
|
||||
}
|
||||
|
||||
/// 一次安装的演示快照(文件计数 / 字节 / 速度 / 剩余时间都给真值,进度条画得出来)。
|
||||
|
||||
Reference in new issue
Block a user