//! 弹窗消息的落点:关闭 / 展开 / 导出 / 打开文件夹 / 复制路径。 //! //! 只有两类副作用:**剪贴板**(返回 `Task`,必须交回 runtime)与**弹 Toast**(就地推)。 //! 导出与打开文件夹是同步的本地动作(写一个 zip / 拉起一个 `explorer`),耗时都在毫秒级, //! 不为它们单开线程——真要慢(网络盘)也是用户能感知的一次点击。 use iced::Task; use unlucky_presenter::toast::ToastKind; use crate::app::dialog::{export, Message as DialogMessage}; use crate::app::state::App; pub(super) fn on_dialog(app: &mut App, message: DialogMessage) -> Task { match message { DialogMessage::Primary => { // 崩溃卡片的主按钮是「导出诊断包」;普通确认框的主按钮就是「关掉」。 let Some(card) = app.dialog_card.clone() else { app.close_dialog(); return Task::none(); }; let done = export::export(&card.report, app.lang()); // 结论**画在卡片里**:导出这种「必须看见落到哪」的结果不能只靠 Toast // (app-iced 的 Toast 至今没有渲染点)。成败按 `write_bundle` 的真实结果判, // 不看「launcher_logs 有没有值」——那只是入参齐全度(r1 复审 P3-2)。 app.state.toasts.push( if done.ok { ToastKind::Success } else { ToastKind::Error }, done.text.clone(), ); if let Some(state) = &mut app.dialog_card { state.outcome = Some(done.text); } app.view_dirty = true; app.refresh_view(); Task::none() } DialogMessage::Secondary | DialogMessage::Dismiss => { app.close_dialog(); // 立即重画:空闲态只有 1 Hz 心跳,只置 `view_dirty` 会让弹窗再留最多 1 秒 // (r1 复审 P3-1)。 app.refresh_view(); Task::none() } DialogMessage::Toggle => { app.toggle_dialog_detail(); Task::none() } DialogMessage::OpenFolder => { let Some(card) = app.dialog_card.clone() else { return Task::none(); }; let problem = export::open_folder(&card.report, app.lang()); if !problem.is_empty() { if let Some(state) = &mut app.dialog_card { state.outcome = Some(problem.clone()); } app.state.toasts.push(ToastKind::Error, problem); app.view_dirty = true; app.refresh_view(); } Task::none() } DialogMessage::CopyPaths => { let Some(card) = app.dialog_card.clone() else { return Task::none(); }; app.state.toasts.push( ToastKind::Info, app.lang() .catalog() .text(unlucky_i18n::catalog::Key::CrashDialogCopied), ); iced::clipboard::write(export::paths_text(&card.report)) } DialogMessage::CopyPath(index) => { let Some(card) = app.dialog_card.clone() else { return Task::none(); }; let Some((_, path)) = card.report.path_lines().into_iter().nth(index) else { return Task::none(); }; app.state.toasts.push( ToastKind::Info, app.lang() .catalog() .text(unlucky_i18n::catalog::Key::CrashDialogCopied), ); iced::clipboard::write(path.display().to_string()) } } } /// `Esc` 关弹窗(有弹窗时**先吃这一条**,不让它落到 AI 栏的 Esc 收起上)。 pub(super) fn escape(app: &mut App) -> bool { if app.state.dialogs.current().is_none() { return false; } app.close_dialog(); app.refresh_view(); true }