fix(app-iced): 点选共享档立刻显示后果原文,登记在飞时不吃关闭(M223 自检)
两处自己复审抓到的问题: 1. 契约 §5.2 要求 Shared/VersionDir 被**点选**就展示 target_notes() 原文,而上一版要等 用户再点一次「预检」才出现。现在点选这两档会顺手发一次**零写盘**的预检,结果落在 Pick 段当提示读(不把用户甩到确认页:确认仍由显式的「预检」按钮触发,Form::checked)。 改档之后旧 plan 因目标档不一致而不再显示(Form::fresh_plan),避免画上一档的后果原文。 2. 「关闭」在登记在飞时被忽略:core 那条线程已经开跑,关面板会让 Done 事件被代次丢掉, 用户看到的就是「实例写进去了,界面什么都没说」。 用例:a_foreign_target_shows_its_notes_without_jumping_to_confirm / choosing_a_foreign_target_previews_but_waits_for_the_user / closing_is_ignored_while_the_registration_is_in_flight。
This commit is contained in:
1 parent
9ca5ee8ae7
commit
a36e17a5d3
4 files changed
+163
-19
No files matched your search
@@ -48,7 +48,9 @@ use unlucky_design::shape::ShapeScale;
|
||||
use unlucky_design::spacing::Space as Sp;
|
||||
use unlucky_design::typography::TypeRole;
|
||||
use unlucky_i18n::{Catalog, Key, Lang};
|
||||
use unlucky_presenter::dto::{AdoptCandidateView, AdoptStage, AdoptState, AdoptTargetView};
|
||||
use unlucky_presenter::dto::{
|
||||
AdoptCandidateView, AdoptPlanView, AdoptStage, AdoptState, AdoptTargetView,
|
||||
};
|
||||
|
||||
use crate::app::components::buttons::{self, Button};
|
||||
use crate::app::components::feedback::dialog::scrim;
|
||||
@@ -102,6 +104,13 @@ pub struct Form {
|
||||
pub target: AdoptTargetView,
|
||||
/// 在等在飞的那一条命令
|
||||
pub busy: Busy,
|
||||
/// 用户已经点过「预检」(没点过就留在 Pick 段,哪怕后台已经顺手算过一次落点)。
|
||||
///
|
||||
/// 为什么要有这一位:共享档 / 版本目录档一旦被点选,界面就要立刻显示 `target_notes()` 的
|
||||
/// **后果原文**(契约 §5.2)——那是 core 唯一会产出这段文字的地方,所以点选时顺手发一次
|
||||
/// **零写盘**的预检。但那一次结果不该把用户甩到「可以导入」的确认页:确认必须由显式的
|
||||
/// `Check` 触发。于是 `checked` 记住「这次预检是不是用户要的」。
|
||||
pub checked: bool,
|
||||
}
|
||||
|
||||
impl Form {
|
||||
@@ -124,6 +133,30 @@ impl Form {
|
||||
self.busy = Busy::Idle;
|
||||
}
|
||||
|
||||
/// 当前该画哪一段:`Preview` 只在用户**点过预检**之后才算数,其余按 `AdoptState::stage()`。
|
||||
///
|
||||
/// 这条收窄是为了「点选共享档 ⇒ 立刻看到后果原文」而不把用户直接甩到确认页:
|
||||
/// 自动预检的结果落在 Pick 段里当**提示**读,用户点「预检」之后才进确认段。
|
||||
#[must_use]
|
||||
pub fn stage(&self, adopt_state: &AdoptState) -> AdoptStage {
|
||||
match adopt_state.stage() {
|
||||
AdoptStage::Preview if !self.checked => AdoptStage::Pick,
|
||||
other => other,
|
||||
}
|
||||
}
|
||||
|
||||
/// 后台算出来、且**对得上当前选择**的预检(对不上 = 上一档的过期结果,一律不画)。
|
||||
///
|
||||
/// 判据是「目标档一致」:用户改了目标档之后,旧 plan 的 `notes` 说的是别的目录,
|
||||
/// 拿它当后果原文就是撒谎。版本一致性由 [`Self::candidate`] 那一路保证(选中即重扫)。
|
||||
#[must_use]
|
||||
pub fn fresh_plan<'a>(&self, adopt_state: &'a AdoptState) -> Option<&'a AdoptPlanView> {
|
||||
adopt_state
|
||||
.plan
|
||||
.as_ref()
|
||||
.filter(|plan| plan.target == self.target)
|
||||
}
|
||||
|
||||
/// 当前选中的候选(选中项已不在列表里时返回 `None`,界面自动退回「请选择」)。
|
||||
#[must_use]
|
||||
pub fn candidate<'a>(&self, adopt_state: &'a AdoptState) -> Option<&'a AdoptCandidateView> {
|
||||
@@ -206,24 +239,25 @@ fn card<'a>(
|
||||
adopt_state: &'a AdoptState,
|
||||
catalog: Catalog,
|
||||
) -> Element<'a, Message> {
|
||||
let stage = form.stage(adopt_state);
|
||||
let body = column![
|
||||
frame::text_role(
|
||||
ui,
|
||||
TypeRole::HeadlineSmall,
|
||||
true,
|
||||
title_text(adopt_state, catalog),
|
||||
title_text(stage, catalog),
|
||||
ui.color(Role::OnSurface),
|
||||
)
|
||||
.map(Message::Page),
|
||||
wrapped(
|
||||
ui,
|
||||
subtitle(adopt_state, catalog),
|
||||
subtitle(stage, adopt_state, catalog),
|
||||
ui.color(Role::OnSurfaceVariant)
|
||||
),
|
||||
frame::divider(ui).map(Message::Page),
|
||||
middle(ui, form, adopt_state, catalog),
|
||||
middle(ui, form, adopt_state, stage, catalog),
|
||||
frame::divider(ui).map(Message::Page),
|
||||
footer(ui, form, adopt_state, catalog),
|
||||
footer(ui, form, adopt_state, stage, catalog),
|
||||
]
|
||||
.spacing(ui.space(Sp::X3))
|
||||
.width(Length::Fill);
|
||||
@@ -241,8 +275,8 @@ fn card<'a>(
|
||||
}
|
||||
|
||||
/// 段标题(四段各一句)。
|
||||
fn title_text(adopt_state: &AdoptState, catalog: Catalog) -> &'static str {
|
||||
match adopt_state.stage() {
|
||||
fn title_text(stage: AdoptStage, catalog: Catalog) -> &'static str {
|
||||
match stage {
|
||||
AdoptStage::Pick => catalog.text(Key::AdoptTitle),
|
||||
AdoptStage::Preview => catalog.text(Key::AdoptPlanTitle),
|
||||
AdoptStage::Failed => catalog.text(Key::AdoptFailureTitle),
|
||||
@@ -251,8 +285,8 @@ fn title_text(adopt_state: &AdoptState, catalog: Catalog) -> &'static str {
|
||||
}
|
||||
|
||||
/// 标题下那句说明(Pick 段说清「什么都不复制」;Done 段说清「去实例列表启动」)。
|
||||
fn subtitle(adopt_state: &AdoptState, catalog: Catalog) -> String {
|
||||
match adopt_state.stage() {
|
||||
fn subtitle(stage: AdoptStage, adopt_state: &AdoptState, catalog: Catalog) -> String {
|
||||
match stage {
|
||||
AdoptStage::Pick => catalog.text(Key::AdoptSubtitle).to_string(),
|
||||
AdoptStage::Preview => adopt_state
|
||||
.plan
|
||||
@@ -279,11 +313,18 @@ fn middle<'a>(
|
||||
ui: &'a Ui,
|
||||
form: &'a Form,
|
||||
adopt_state: &'a AdoptState,
|
||||
stage: AdoptStage,
|
||||
catalog: Catalog,
|
||||
) -> Element<'a, Message> {
|
||||
match adopt_state.stage() {
|
||||
AdoptStage::Pick => pick::pick(ui, form, adopt_state.scan.as_ref(), catalog),
|
||||
AdoptStage::Preview => panes::preview(ui, adopt_state.plan.as_ref(), catalog),
|
||||
match stage {
|
||||
AdoptStage::Pick => pick::pick(
|
||||
ui,
|
||||
form,
|
||||
adopt_state.scan.as_ref(),
|
||||
form.fresh_plan(adopt_state),
|
||||
catalog,
|
||||
),
|
||||
AdoptStage::Preview => panes::preview(ui, form.fresh_plan(adopt_state), catalog),
|
||||
AdoptStage::Failed => panes::failed(ui, adopt_state.failure.as_ref(), catalog),
|
||||
AdoptStage::Done => panes::done(ui, adopt_state.done.as_ref(), catalog),
|
||||
}
|
||||
@@ -294,9 +335,9 @@ fn footer<'a>(
|
||||
ui: &'a Ui,
|
||||
form: &'a Form,
|
||||
adopt_state: &'a AdoptState,
|
||||
stage: AdoptStage,
|
||||
catalog: Catalog,
|
||||
) -> Element<'a, Message> {
|
||||
let stage = adopt_state.stage();
|
||||
let busy = form.busy.waiting();
|
||||
|
||||
let primary: Element<'a, Message> = match stage {
|
||||
|
||||
Reference in new issue
Block a user