use crate::app::Message; use crate::io::recovery::{RecoveryReport, RecoveryStatus}; use iced::widget::{button, column, container, row, scrollable, text, Space}; use iced::{Background, Border, Element, Fill, Theme}; fn muted_style(theme: &Theme) -> iced::widget::text::Style { iced::widget::text::Style { color: Some(theme.palette().background.base.text.scale_alpha(0.68)), } } fn status_style( status: RecoveryStatus, ) -> impl Fn(&Theme) -> iced::widget::text::Style + Copy { move |theme: &Theme| iced::widget::text::Style { color: Some(match status { RecoveryStatus::Recovered => theme.palette().warning.base.color, RecoveryStatus::Failed => theme.palette().danger.base.color, }), } } fn metric<'a>(label: String, value: String) -> Element<'a, Message> { container( column![ text(label).size(10).style(muted_style), text(value).size(18), ] .spacing(3), ) .padding([10, 12]) .width(Fill) .style(|theme: &Theme| container::Style { background: Some(Background::Color(theme.palette().background.weak.color)), border: Border { color: theme.palette().background.neutral.color, width: 1.0, radius: 5.0.into(), }, ..Default::default() }) .into() } pub fn view_window<'a>( report: &'a RecoveryReport, allow_save_copy: bool, sizing: crate::ui::modal::ModalSizing, ) -> Element<'a, Message> { let recovered = report.status == RecoveryStatus::Recovered; let heading = if recovered { crate::tr!("recovery-opened-with-repairs") } else { crate::tr!("recovery-open-failed") }; let description = if recovered { crate::tr!("recovery-repaired-description") } else { crate::tr!("recovery-failed-description") }; let status = report.status; let metrics = row![ metric( crate::tr!("recovery-entities-checked"), report.entities_scanned.to_string(), ), metric( crate::tr!("recovery-issues-found"), report.issues_found().to_string(), ), metric( crate::tr!("recovery-entities-removed"), report.removed_total().to_string(), ), metric( crate::tr!("recovery-references-checked"), report.references_checked.to_string(), ), ] .spacing(8) .width(Fill); let mut details = column![].spacing(6); if report.referenced_entities_removed > 0 { details = details.push( text(format!( "{}: {}", crate::tr!("recovery-referenced-entities-removed"), report.referenced_entities_removed )) .size(11), ); } if report.references_missing > 0 || report.references_failed > 0 { details = details.push( text(format!( "{}: {}", crate::tr!("recovery-references-unavailable"), report .references_missing .saturating_add(report.references_failed) )) .size(11), ); } if let Some(error) = &report.error { details = details.push(text(error).size(11)); } for (kind, message) in report.diagnostics.iter().take(100) { details = details.push(text(format!("[{kind}] {message}")).size(10)); } if let Some(path) = &report.log_path { details = details.push( text(format!("{}: {}", crate::tr!("recovery-log-path"), path.display())) .size(10) .style(muted_style), ); } else if let Some(error) = &report.log_error { details = details.push( text(format!( "{}: {error}", crate::tr!("recovery-log-write-failed") )) .size(10) .style(status_style(RecoveryStatus::Failed)), ); } else if cfg!(target_arch = "wasm32") { details = details.push( text(crate::tr!("recovery-log-download-ready")) .size(10) .style(muted_style), ); } let detail_panel = container(scrollable(details).height(Fill)) .padding([10, 12]) .width(Fill) .height(Fill) .style(container::bordered_box); let mut actions = row![Space::new().width(Fill)].spacing(8); if recovered && report.save_as_required && allow_save_copy { actions = actions.push( button(text(crate::tr!("recovery-save-copy")).size(12)) .on_press(Message::RecoverySaveAs) .style(button::primary) .padding([6, 14]), ); } if report.log_path.is_some() || cfg!(target_arch = "wasm32") { actions = actions.push( button(text(crate::tr!("recovery-show-log")).size(12)) .on_press(Message::RecoveryShowLog) .style(button::secondary) .padding([6, 14]), ); } actions = actions.push( button(text(crate::tr!("action-close")).size(12)) .on_press(Message::RecoveryClose) .style(button::secondary) .padding([6, 14]), ); container( column![ text(heading).size(20).style(status_style(status)), text(&report.file_name).size(13), text(description).size(11).style(muted_style), Space::new().height(4), metrics, detail_panel, actions, ] .spacing(10) .width(sizing.width) .height(sizing.height), ) .padding([12, 16]) .width(sizing.width) .height(sizing.height) .into() } pub fn view_prompt<'a>( file_name: &'a str, error: &'a str, sizing: crate::ui::modal::ModalSizing, ) -> Element<'a, Message> { let actions = row![ Space::new().width(Fill), button(text(crate::tr!("recovery-decline")).size(12)) .on_press(Message::RecoveryDecline) .style(button::secondary) .padding([6, 14]), button(text(crate::tr!("recovery-attempt")).size(12)) .on_press(Message::RecoveryAttempt) .style(button::primary) .padding([6, 14]), ] .spacing(8); container( column![ text(crate::tr!("recovery-prompt-heading")).size(20), text(file_name).size(13), text(crate::tr!("recovery-prompt-description")) .size(11) .style(muted_style), container(scrollable(text(error).size(10)).height(Fill)) .padding([10, 12]) .width(Fill) .height(Fill) .style(container::bordered_box), actions, ] .spacing(10) .width(sizing.width) .height(sizing.height), ) .padding([12, 16]) .into() }