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    /// The added folder may contain sensitive files (SSH keys, browser profiles, etc.).
20    SensitiveSourceAdded,
21    // ── Confirmations ─────────────────────────────────────────────────
22    FolderAdded,
23    SearchReady,
24    PreviewsCleared,
25}
26
27impl UserNotice {
28    /// Whether this notice reports a problem (vs. a success confirmation).
29    /// The view can use this to choose tone, but never relies on colour alone.
30    pub fn is_problem(&self) -> bool {
31        matches!(
32            self,
33            Self::SensitiveSourceAdded | Self::DownloadDidNotFinish
34                | Self::FolderCouldNotBeAdded
35                | Self::SearchDidNotFinish
36                | Self::FilesMovedOrMissing
37        )
38    }
39
40    pub fn title(&self, locale: Locale) -> &'static str {
41        let key = match self {
42            Self::DownloadDidNotFinish => MessageKey::NoticeDownloadFailTitle,
43            Self::FolderCouldNotBeAdded => MessageKey::NoticeFolderFailTitle,
44            Self::SearchDidNotFinish => MessageKey::NoticeSearchFailTitle,
45            Self::FilesMovedOrMissing => MessageKey::NoticeFilesMissingTitle,
46            Self::SensitiveSourceAdded => MessageKey::NoticeSensitiveSourceTitle,
47            Self::FolderAdded => MessageKey::NoticeFolderAddedTitle,
48            Self::SearchReady => MessageKey::NoticeSearchReadyTitle,
49            Self::PreviewsCleared => MessageKey::NoticePreviewsClearedTitle,
50        };
51        tr(locale, key)
52    }
53
54    pub fn body(&self, locale: Locale) -> &'static str {
55        let key = match self {
56            Self::DownloadDidNotFinish => MessageKey::NoticeDownloadFailBody,
57            Self::FolderCouldNotBeAdded => MessageKey::NoticeFolderFailBody,
58            Self::SearchDidNotFinish => MessageKey::NoticeSearchFailBody,
59            Self::FilesMovedOrMissing => MessageKey::NoticeFilesMissingBody,
60            Self::SensitiveSourceAdded => MessageKey::NoticeSensitiveSourceBody,
61            Self::FolderAdded => MessageKey::NoticeFolderAddedBody,
62            Self::SearchReady => MessageKey::NoticeSearchReadyBody,
63            Self::PreviewsCleared => MessageKey::NoticePreviewsClearedBody,
64        };
65        tr(locale, key)
66    }
67
68    /// Suggested next-action label, if the notice offers a recovery action.
69    /// Confirmations return `None` (they are dismissed, not acted upon).
70    pub fn action(&self, locale: Locale) -> Option<&'static str> {
71        let key = match self {
72            Self::DownloadDidNotFinish | Self::SearchDidNotFinish => MessageKey::NoticeActionTryAgain,
73            Self::FolderCouldNotBeAdded => MessageKey::NoticeActionChooseFolder,
74            Self::FilesMovedOrMissing => MessageKey::NoticeActionChooseFolder,
75            Self::SensitiveSourceAdded => return None, // informational only
76            Self::FolderAdded | Self::SearchReady | Self::PreviewsCleared => return None,
77        };
78        Some(tr(locale, key))
79    }
80}