Skip to main content

orbok_ui/
notice.rs

1//! User-facing notices (UX review §7): friendly, actionable messages that
2//! replace silent failures and raw error strings.
3//!
4//! Lower layers (download, scanner, search) produce technical errors. The UI
5//! must never show those directly. Instead they are mapped to a [`UserNotice`]
6//! with a plain title, an explanation, and a suggested next action.
7
8use crate::i18n::{tr, Locale, MessageKey};
9
10/// A friendly, actionable message shown to the user. Covers both problems
11/// (download failed) and confirmations (folder added).
12#[derive(Debug, Clone, PartialEq, Eq)]
13pub enum UserNotice {
14    // ── Problems ──────────────────────────────────────────────────────
15    DownloadDidNotFinish,
16    FolderCouldNotBeAdded,
17    SearchDidNotFinish,
18    FilesMovedOrMissing,
19    // ── Confirmations ─────────────────────────────────────────────────
20    FolderAdded,
21    SearchReady,
22    PreviewsCleared,
23}
24
25impl UserNotice {
26    /// Whether this notice reports a problem (vs. a success confirmation).
27    /// The view can use this to choose tone, but never relies on colour alone.
28    pub fn is_problem(&self) -> bool {
29        matches!(
30            self,
31            Self::DownloadDidNotFinish
32                | Self::FolderCouldNotBeAdded
33                | Self::SearchDidNotFinish
34                | Self::FilesMovedOrMissing
35        )
36    }
37
38    pub fn title(&self, locale: Locale) -> &'static str {
39        let key = match self {
40            Self::DownloadDidNotFinish => MessageKey::NoticeDownloadFailTitle,
41            Self::FolderCouldNotBeAdded => MessageKey::NoticeFolderFailTitle,
42            Self::SearchDidNotFinish => MessageKey::NoticeSearchFailTitle,
43            Self::FilesMovedOrMissing => MessageKey::NoticeFilesMissingTitle,
44            Self::FolderAdded => MessageKey::NoticeFolderAddedTitle,
45            Self::SearchReady => MessageKey::NoticeSearchReadyTitle,
46            Self::PreviewsCleared => MessageKey::NoticePreviewsClearedTitle,
47        };
48        tr(locale, key)
49    }
50
51    pub fn body(&self, locale: Locale) -> &'static str {
52        let key = match self {
53            Self::DownloadDidNotFinish => MessageKey::NoticeDownloadFailBody,
54            Self::FolderCouldNotBeAdded => MessageKey::NoticeFolderFailBody,
55            Self::SearchDidNotFinish => MessageKey::NoticeSearchFailBody,
56            Self::FilesMovedOrMissing => MessageKey::NoticeFilesMissingBody,
57            Self::FolderAdded => MessageKey::NoticeFolderAddedBody,
58            Self::SearchReady => MessageKey::NoticeSearchReadyBody,
59            Self::PreviewsCleared => MessageKey::NoticePreviewsClearedBody,
60        };
61        tr(locale, key)
62    }
63
64    /// Suggested next-action label, if the notice offers a recovery action.
65    /// Confirmations return `None` (they are dismissed, not acted upon).
66    pub fn action(&self, locale: Locale) -> Option<&'static str> {
67        let key = match self {
68            Self::DownloadDidNotFinish | Self::SearchDidNotFinish => MessageKey::NoticeActionTryAgain,
69            Self::FolderCouldNotBeAdded => MessageKey::NoticeActionChooseFolder,
70            Self::FilesMovedOrMissing => MessageKey::NoticeActionChooseFolder,
71            Self::FolderAdded | Self::SearchReady | Self::PreviewsCleared => return None,
72        };
73        Some(tr(locale, key))
74    }
75}