aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--common/src/user.rs9
-rw-r--r--locale/en.ini4
-rw-r--r--server/src/ui/account/settings.rs31
-rw-r--r--ui/client-style/src/node_card.css3
-rw-r--r--ui/client-style/src/themes.css59
-rw-r--r--ui/src/components/user.rs11
-rw-r--r--ui/src/lib.rs14
-rw-r--r--ui/src/scaffold.rs7
8 files changed, 66 insertions, 72 deletions
diff --git a/common/src/user.rs b/common/src/user.rs
index ccd9c5c..0a4111d 100644
--- a/common/src/user.rs
+++ b/common/src/user.rs
@@ -4,15 +4,20 @@
Copyright (C) 2026 metamuffin <metamuffin.org>
*/
-use jellyobject::{enums, fields};
+use jellyobject::{Tag, enums, fields};
fields! {
USER_LOGIN: &str = b"Ulgn";
USER_PASSWORD: &[u8] = b"Upwd";
USER_NAME: &str = b"Unam";
- USER_THEME: &str = b"Uthm";
USER_ADMIN: () = b"Uadm";
+ USER_THEME_PRESET: Tag = b"Utpr";
+ USER_THEME_ACCENT: u32 = b"Utac";
UDATA_WATCHED: () = b"Dwat";
UDATA_RATING: f64 = b"Drat";
}
+enums! {
+ THEME_DARK = b"dark";
+ THEME_LIGHT = b"ligh";
+}
diff --git a/locale/en.ini b/locale/en.ini
index 15658ae..8ec8c32 100644
--- a/locale/en.ini
+++ b/locale/en.ini
@@ -148,6 +148,10 @@ tag.Uadm=Admin
tag.Ulgn=Login
tag.Unam=Name
tag.Upwd=Password
+tag.Utac=Accent Color
+tag.Utpr.dark=Dark
+tag.Utpr.ligh=Light
+tag.Utpr=Theme Preset
theme.dark=Dark
theme.light=Light
diff --git a/server/src/ui/account/settings.rs b/server/src/ui/account/settings.rs
index 2e9fcbe..2052c5e 100644
--- a/server/src/ui/account/settings.rs
+++ b/server/src/ui/account/settings.rs
@@ -9,10 +9,9 @@ use crate::{
};
use anyhow::anyhow;
use jellycommon::{
- MESSAGE_KIND, MESSAGE_TEXT, USER_LOGIN, USER_NAME, USER_PASSWORD, VIEW_MESSAGE,
- VIEW_USER_SETTINGS,
- jellyobject::{Object, ObjectBuffer, ObjectBufferBuilder, Path},
+ jellyobject::{Object, ObjectBuffer, ObjectBufferBuilder, Path, Tag},
routes::u_account_settings,
+ *,
};
use jellydb::{Filter, Query, Sort};
use jellyui::tr;
@@ -31,6 +30,8 @@ pub struct SettingsForm {
password: Option<String>,
#[field(validate = option_len(4..32))]
name: Option<String>,
+ theme_accent: Option<u32>,
+ theme_preset: Option<String>,
}
fn option_len<'v>(value: &Option<String>, range: Range<usize>) -> form::Result<'v, ()> {
@@ -87,21 +88,25 @@ pub fn r_account_settings_post(
out += &*tr(ri.lang, "settings.account.display_name.changed");
out += "\n";
}
- // if let Some(theme) = form.theme {
- // update_user_theme(&ri.session, theme.0)?;
- // out += &*tr(ri.lang, "settings.account.theme.changed");
- // out += "\n";
- // }
+ if let Some(preset) = &form.theme_preset {
+ let tag = Tag::new(
+ preset
+ .as_bytes()
+ .try_into()
+ .map_err(|_| anyhow!("invalid theme preset"))?,
+ );
+ update_user(&ri, |user| user.insert(USER_THEME_PRESET, tag))?;
+ out += &*tr(ri.lang, "settings.appearance.theme.changed");
+ out += "\n";
+ }
+ if let Some(accent) = form.theme_accent {
+ update_user(&ri, |user| user.insert(USER_THEME_ACCENT, accent))?;
+ }
// if let Some(player_preference) = form.player_preference {
// update_user_player_preference(&ri.session, player_preference.0)?;
// out += &*tr(ri.lang, "settings.player_preference.changed");
// out += "\n";
// }
- // if let Some(native_secret) = &form.native_secret {
- // update_user_native_secret(&ri.session, native_secret)?;
- // out += &*tr(ri.lang, "settings.native_secret.changed");
- // out += "\n";
- // }
let out = if out.is_empty() {
tr(ri.lang, "settings.no_change").to_string()
} else {
diff --git a/ui/client-style/src/node_card.css b/ui/client-style/src/node_card.css
index 8148845..f9197c5 100644
--- a/ui/client-style/src/node_card.css
+++ b/ui/client-style/src/node_card.css
@@ -158,6 +158,9 @@
transparent 100%
);
}
+.card.highlight .inner {
+ border-radius: 1em;
+}
.card.highlight .overview {
flex-grow: 1;
}
diff --git a/ui/client-style/src/themes.css b/ui/client-style/src/themes.css
index 18a79b5..2e8e2df 100644
--- a/ui/client-style/src/themes.css
+++ b/ui/client-style/src/themes.css
@@ -5,20 +5,20 @@
*/
body {
--video-brackground: black;
- --c-danger: rgb(177, 36, 36);
+ --accent-hue: 277;
}
body.theme-dark {
- --accent-light: rgb(255, 163, 87);
- --accent-dark: rgb(199, 90, 0);
+ --accent-light: hsl(var(--accent-hue), 100%, 67%);
+ --accent-dark: hsl(var(--accent-hue), 65%, 49%);
--c-error: rgb(255, 117, 117);
--c-warn: rgb(252, 255, 78);
--c-success: rgb(117, 255, 117);
--c-nav: #1c1c1c9a;
--c-nav-hover: #ffffff10;
--c-fade: black;
- --overlay-poster: #0005;
- --overlay: #0006;
- --overlay-hover: #0009;
+ --c-danger: rgb(177, 36, 36);
+ --overlay: #0005;
+ --overlay-hover: #0008;
--background-dark: #070707;
--background-light: #1c1c1c;
--background-very-light: #323232;
@@ -30,14 +30,15 @@ body.theme-dark {
--image-loading-placeholder: rgb(50, 50, 50);
}
body.theme-light {
- --accent-light: #e46600;
- --accent-dark: #ff9036;
+ --accent-light: hsl(var(--accent-hue), 100%, 45%);
+ --accent-dark: hsl(var(--accent-hue), 100%, 61%);
--c-error: rgb(255, 117, 117);
--c-warn: rgb(252, 255, 78);
--c-success: rgb(117, 255, 117);
--c-nav: #c4c4c4d7;
--c-nav-hover: #ffffff10;
--c-fade: white;
+ --c-danger: rgb(177, 36, 36);
--overlay: rgba(255, 255, 255, 0.267);
--overlay-hover: rgba(255, 255, 255, 0.533);
--background-dark: #ffffff;
@@ -50,45 +51,3 @@ body.theme-light {
--font-highlight: black;
--image-loading-placeholder: rgb(200, 200, 200);
}
-body.theme-purple {
- --accent-light: rgb(191, 87, 255);
- --accent-dark: rgb(143, 43, 205);
- --c-error: rgb(255, 117, 117);
- --c-warn: rgb(252, 255, 78);
- --c-success: rgb(117, 255, 117);
- --c-nav: #1c1c1c9a;
- --c-nav-hover: #ffffff10;
- --c-fade: black;
- --overlay: #0005;
- --overlay-hover: #0008;
- --background-dark: #070707;
- --background-light: #1c1c1c;
- --background-very-light: #323232;
- --background-disable: rgb(128, 128, 128);
- --background-prop: rgba(50, 50, 50, 0.8);
- --font: #f1f1f1;
- --font-dark: rgb(122, 122, 122);
- --font-highlight: white;
- --image-loading-placeholder: rgb(50, 50, 50);
-}
-body.theme-black {
- --accent-light: hsl(250, 100%, 67%);
- --accent-dark: hsl(250, 60%, 42%);
- --c-error: rgb(255, 117, 117);
- --c-warn: rgb(252, 255, 78);
- --c-success: rgb(117, 255, 117);
- --c-nav: #0000009a;
- --c-nav-hover: #ffffff10;
- --c-fade: black;
- --overlay: rgba(114, 114, 114, 0.333);
- --overlay-hover: rgba(255, 255, 255, 0.533);
- --background-dark: #000000;
- --background-light: #000000;
- --background-very-light: #323232;
- --background-disable: rgb(128, 128, 128);
- --background-prop: rgba(50, 50, 50, 0.8);
- --font: #e8e8e8;
- --font-dark: rgb(102, 102, 102);
- --font-highlight: white;
- --image-loading-placeholder: rgb(20, 20, 20);
-}
diff --git a/ui/src/components/user.rs b/ui/src/components/user.rs
index 600af22..cf1c123 100644
--- a/ui/src/components/user.rs
+++ b/ui/src/components/user.rs
@@ -30,6 +30,17 @@ markup::define! {
input[type="password", id="password", name="password"];
input[type="submit", value=tr(ri.lang, "settings.update")];
}
+ form[method="POST", action=u_account_settings()] {
+ fieldset {
+ legend { @tr(ri.lang, "tag.Utpr") }
+ @for preset in [THEME_DARK, THEME_LIGHT] {
+ label { input[type="radio", name="theme_preset", value=preset.to_string(), checked=user.get(USER_THEME_PRESET) == Some(preset)]; @tr(ri.lang, &format!("tag.Utpr.{preset}")) } br;
+ }
+ }
+ label[for="accent"] { @tr(ri.lang, "tag.Utac") }
+ input[type="range", id="accent", name="theme_accent", min=0, max=360, step=1, value=user.get(USER_THEME_ACCENT)];
+ input[type="submit", value=tr(ri.lang, "settings.update")];
+ }
// h2 { @tr(ri.lang, "settings.appearance") }
// form[method="POST", action=u_account_settings()] {
diff --git a/ui/src/lib.rs b/ui/src/lib.rs
index f32657d..d74df51 100644
--- a/ui/src/lib.rs
+++ b/ui/src/lib.rs
@@ -35,14 +35,20 @@ pub struct RenderInfo<'a> {
}
pub fn render_view(ri: RenderInfo<'_>, view: Object<'_>) -> String {
+ let theme = ri
+ .user
+ .and_then(|u| u.get(USER_THEME_PRESET))
+ .unwrap_or(THEME_DARK);
Scaffold {
ri: &ri,
main: View { ri: &ri, view },
title: view.get(VIEW_TITLE).unwrap_or_default(),
- class: if view.has(VIEW_PLAYER.0) {
- "theme-purple player"
- } else {
- "theme-purple"
+ class: match (theme, view.has(VIEW_PLAYER.0)) {
+ (THEME_DARK, true) => "theme-dark player",
+ (THEME_DARK, false) => "theme-dark",
+ (THEME_LIGHT, true) => "theme-light player",
+ (THEME_LIGHT, false) => "theme-light",
+ _ => "theme-dark",
},
}
.to_string()
diff --git a/ui/src/scaffold.rs b/ui/src/scaffold.rs
index 4fc7d60..c563ee4 100644
--- a/ui/src/scaffold.rs
+++ b/ui/src/scaffold.rs
@@ -6,9 +6,10 @@
use crate::RenderInfo;
use jellycommon::{
+ USER_THEME_ACCENT,
routes::{
- u_account_login, u_account_logout, u_account_settings, u_admin_dashboard, u_home, u_items,
- u_node_slug, u_search, u_stats,
+ u_account_login, u_account_settings, u_admin_dashboard, u_home, u_items, u_node_slug,
+ u_search, u_stats,
},
user::{USER_ADMIN, USER_NAME},
};
@@ -25,7 +26,7 @@ markup::define! {
link[rel="stylesheet", href="/assets/bundle.css"];
script[src="/assets/bundle.js"] {}
}
- body[class=class] {
+ body[class=class, style=format!("--accent-hue: {}", ri.user.and_then(|u|u.get(USER_THEME_ACCENT)).unwrap_or(277))] {
@Navbar { ri }
#main { @main }
footer {