package main
import (
"net/http"
"strings"
"time"
)
const themeCookieName = "gitfed_theme"
// theme resolves the UI theme for a request from its cookie (set via the
// nav switcher) — "light" or "dark" force that theme regardless of the
// browser's own preference, "" (unset, or any other value) means "system":
// no data-theme attribute is rendered at all, so the page's
// prefers-color-scheme media query decides instead. There's no
// Accept-Language-style fallback here on purpose — unlike language, "system"
// is a legitimate, common default, not a missing signal to guess around.
func (s *server) theme(r *http.Request) string {
if c, err := r.Cookie(themeCookieName); err == nil && (c.Value == "light" || c.Value == "dark") {
return c.Value
}
return ""
}
// themeToggleNext returns the icon to show for the nav's compact theme
// button given the current theme value ("" meaning system), and the theme
// clicking it switches to — a three-state cycle (system -> light -> dark
// -> system) exposed as one small icon rather than the full spelled-out
// choice, which lives in Settings instead for anyone who wants it there.
func themeToggleNext(current string) (icon, next string) {
switch current {
case "light":
return "sun", "dark"
case "dark":
return "moon", "system"
default:
return "auto", "light"
}
}
// handleSetTheme stores the chosen theme in a cookie (or clears it for
// "system") and bounces back to wherever the switcher was clicked from —
// same shape as handleSetLang.
func (s *server) handleSetTheme(w http.ResponseWriter, r *http.Request) {
choice := r.PathValue("theme")
cookie := &http.Cookie{
Name: themeCookieName,
Path: "/",
HttpOnly: true,
Secure: true,
SameSite: http.SameSiteLaxMode,
}
if choice == "light" || choice == "dark" {
cookie.Value = choice
cookie.Expires = time.Now().Add(365 * 24 * time.Hour)
} else {
cookie.MaxAge = -1 // "system" — clear any previous explicit choice
}
http.SetCookie(w, cookie)
next := r.URL.Query().Get("next")
if next == "" || !strings.HasPrefix(next, "/") || strings.HasPrefix(next, "//") {
next = "/"
}
http.Redirect(w, r, next, http.StatusSeeOther)
}