From de0e87cd7178c7b19d8574af77de1191e8b7f5b6 Mon Sep 17 00:00:00 2001 From: juancwu Date: Sun, 12 Apr 2026 16:15:22 +0000 Subject: [PATCH] feat: persist sidebar state --- internal/ctxkeys/ctx.go | 12 +++++++++++- internal/middleware/sidebar.go | 21 +++++++++++++++++++++ internal/routes/routes.go | 1 + internal/ui/layouts/app.templ | 4 +++- 4 files changed, 36 insertions(+), 2 deletions(-) create mode 100644 internal/middleware/sidebar.go diff --git a/internal/ctxkeys/ctx.go b/internal/ctxkeys/ctx.go index ed634b9..ee98d82 100644 --- a/internal/ctxkeys/ctx.go +++ b/internal/ctxkeys/ctx.go @@ -13,7 +13,8 @@ const ( URLPathKey string = "url_path" ConfigKey string = "config" CSRFTokenKey string = "csrf_token" - AppVersionKey string = "app_version" + AppVersionKey string = "app_version" + SidebarCollapsedKey string = "sidebar_collapsed" ) func User(ctx context.Context) *model.User { @@ -61,3 +62,12 @@ func AppVersion(ctx context.Context) string { func WithAppVersion(ctx context.Context, version string) context.Context { return context.WithValue(ctx, AppVersionKey, version) } + +func SidebarCollapsed(ctx context.Context) bool { + collapsed, _ := ctx.Value(SidebarCollapsedKey).(bool) + return collapsed +} + +func WithSidebarCollapsed(ctx context.Context, collapsed bool) context.Context { + return context.WithValue(ctx, SidebarCollapsedKey, collapsed) +} diff --git a/internal/middleware/sidebar.go b/internal/middleware/sidebar.go new file mode 100644 index 0000000..e9a75d2 --- /dev/null +++ b/internal/middleware/sidebar.go @@ -0,0 +1,21 @@ +package middleware + +import ( + "net/http" + + "git.juancwu.dev/juancwu/budgit/internal/ctxkeys" +) + +// WithSidebarState reads the sidebar_state cookie and adds the collapsed +// state to the request context so templates can render the sidebar in the +// correct initial state. +func WithSidebarState(next http.Handler) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + collapsed := false + if c, err := r.Cookie("sidebar_state"); err == nil { + collapsed = c.Value == "false" + } + ctx := ctxkeys.WithSidebarCollapsed(r.Context(), collapsed) + next.ServeHTTP(w, r.WithContext(ctx)) + } +} diff --git a/internal/routes/routes.go b/internal/routes/routes.go index ba4823d..75ad0bc 100644 --- a/internal/routes/routes.go +++ b/internal/routes/routes.go @@ -33,6 +33,7 @@ func SetupRoutes(a *app.App) http.Handler { middleware.CSRFProtection, middleware.AuthMiddleware(a.AuthService, a.UserService), middleware.WithURLPath, + middleware.WithSidebarState, ) // Static assets (bypass router groups — registered directly on mux) diff --git a/internal/ui/layouts/app.templ b/internal/ui/layouts/app.templ index 71365d5..7917862 100644 --- a/internal/ui/layouts/app.templ +++ b/internal/ui/layouts/app.templ @@ -22,7 +22,9 @@ templ App(title string) { Path: ctxkeys.URLPath(ctx), }) { @sidebar.Layout() { - @sidebar.Sidebar() { + @sidebar.Sidebar(sidebar.Props{ + Collapsed: ctxkeys.SidebarCollapsed(ctx), + }) { @sidebar.Header() { @sidebar.Menu() { @sidebar.MenuItem() {