tallyweb_frontend/
preferences.rs

1use leptos::*;
2use serde::{Deserialize, Serialize};
3
4use super::*;
5
6pub type PrefResource = Resource<UserSession, Result<Preferences, ServerFnError>>;
7
8#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
9pub struct AccountAccentColor(pub String);
10
11impl AccountAccentColor {
12    fn new(user: &UserSession) -> Self {
13        let mut this = Self(String::new());
14        this.set_user(user);
15        this
16    }
17
18    pub fn set_user(&mut self, user: &UserSession) {
19        let letter = user
20            .username
21            .to_uppercase()
22            .chars()
23            .next()
24            .unwrap_or_default();
25        let color_hex = elements::letter_to_three_digit_hash(letter);
26        self.0 = format!("#{color_hex}")
27    }
28}
29
30impl std::fmt::Display for AccountAccentColor {
31    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
32        write!(f, "{}", self.0)
33    }
34}
35
36#[derive(Debug, Default, Clone, PartialEq, Serialize, Deserialize)]
37pub struct Preferences {
38    pub use_default_accent_color: bool,
39    pub accent_color: AccountAccentColor,
40    pub show_separator: bool,
41    pub multi_select: bool,
42    pub save_on_pause: bool,
43}
44
45impl Preferences {
46    pub fn new(user: &UserSession) -> Self {
47        let accent_color = AccountAccentColor::new(user);
48        Self {
49            use_default_accent_color: true,
50            accent_color,
51            show_separator: false,
52            multi_select: false,
53            save_on_pause: true,
54        }
55    }
56}
57
58#[cfg(feature = "ssr")]
59impl Preferences {
60    pub fn from_db(user: &UserSession, value: backend::DbPreferences) -> Self {
61        Self {
62            use_default_accent_color: value.use_default_accent_color,
63            accent_color: value
64                .accent_color
65                .map(|c| AccountAccentColor(c))
66                .unwrap_or(AccountAccentColor::new(user)),
67            show_separator: value.show_separator,
68            multi_select: value.multi_select,
69            save_on_pause: value.save_on_pause,
70        }
71    }
72}
73
74#[component(transparent)]
75pub fn ProvidePreferences(children: ChildrenFn) -> impl IntoView {
76    let user = expect_context::<RwSignal<UserSession>>();
77
78    let data = create_blocking_resource(user, api::get_user_preferences);
79    provide_context(data);
80
81    let pref_signal = create_rw_signal(Preferences::new(&user.get_untracked()));
82    provide_context(pref_signal);
83
84    let accent_color = create_read_slice(pref_signal, |p| p.accent_color.clone().0);
85
86    view! {
87        <Transition>
88
89            {if let Some(Ok(p)) = data.get() {
90                pref_signal.set(p.clone())
91            }} <div style=move || { format!("--accent: {}", accent_color()) }>{children()}</div>
92        </Transition>
93    }
94}