Each browser keeps its own theme, in a cookie (#69)

The theme was kept on the account, so every browser signed in as the
same person got the same one: no Glass on the phone with Dracula on the
desktop. It is now the ipx_theme cookie (<theme>.<mode>), written by
theme.ts, and read by the server to draw the page in it from the first
frame as before. /api/me no longer reports or takes a theme, and
set_theme is gone.

A browser with no cookie yet is sent the theme the account kept, and
takes it as its cookie on that first load, so nobody loses their choice
in the move. users.theme and theme_mode are only read now, for that.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-09-28 20:27:20 +00:00
parent 7490a3a9ac
commit f57f824535
5 changed files with 62 additions and 75 deletions

View File

@@ -1517,7 +1517,11 @@ impl Db {
Ok(())
}
/// The theme this person chose, and light, dark or auto; None for either until they choose.
/// The theme this person chose when it was kept on the account, and light, dark or auto; None
/// for either if they never did. Only read now, to give a browser with no theme cookie its
/// first one (issue #69).
// ponytail: users.theme and theme_mode are never written any more; drop them once every
// browser in use has its own cookie.
pub async fn theme(&self, user_id: i64) -> Result<(Option<String>, Option<String>)> {
Ok(users::Entity::find_by_id(user_id)
.one(&self.orm)
@@ -1526,18 +1530,6 @@ impl Db {
.unwrap_or_default())
}
pub async fn set_theme(&self, user_id: i64, theme: &str, mode: &str) -> Result<()> {
self.update_user(
user_id,
users::ActiveModel {
theme: Set(Some(theme.to_owned())),
theme_mode: Set(Some(mode.to_owned())),
..Default::default()
},
)
.await
}
/// The user behind a session cookie, if it is still live. Idle sessions expire after
/// `max_idle_secs`; touching `seen` is what keeps a session in daily use alive.
pub async fn session_user(&self, token: &str, max_idle_secs: i64) -> Result<Option<User>> {