Skip to content

Conversation

@ceviixx
Copy link

@ceviixx ceviixx commented Nov 12, 2025

Changes

  • Add database columns for user preferences (language, timezone, theme, dateRange)
  • Store preferences in database when user changes settings
  • Load and sync preferences to localStorage on login/refresh
  • Remove preferences from localStorage on logout

Benefits

  • User settings persist across devices and browsers
  • Consistent experience after clearing browser data
  • Settings are backed up and recoverable

Migration

  • New migration 15_add_user_preferences adds columns to user table
  • Existing users: preferences will be null until they change settings

@vercel
Copy link

vercel bot commented Nov 12, 2025

@ceviixx is attempting to deploy a commit to the umami-software Team on Vercel.

A member of the Team first needs to authorize it.

@greptile-apps
Copy link
Contributor

greptile-apps bot commented Nov 12, 2025

Greptile Overview

Greptile Summary

This PR implements persistent user preferences by migrating them from localStorage-only storage to database-backed persistence. The changes add four new nullable columns (dateRange, timezone, language, theme) to the user table and create the infrastructure to sync these preferences between the database and client-side storage.

The implementation introduces a new usePreferences hook that provides a unified interface for updating preferences, and modifies all settings components (DateRangeSetting, LanguageSetting, ThemeSetting, TimezoneSetting) to call both local storage functions and the new database persistence layer. Authentication flows (login and token verification) are enhanced to load and apply stored preferences, ensuring users get consistent settings across devices and browsers. The logout process is updated to clear preferences from localStorage, maintaining proper state hygiene.

The solution maintains backward compatibility through nullable database columns and graceful fallbacks, allowing existing users to continue using the application while new preference updates get persisted to the database.

Important Files Changed

Filename Score Overview
prisma/migrations/15_add_user_preferences/migration.sql 4/5 Adds four nullable columns for user preferences to the user table
src/queries/prisma/user.ts 5/5 Adds database functions for getting and updating user preferences
src/components/hooks/usePreferences.ts 4/5 New hook providing interface for updating user preferences via API
src/app/api/users/[userId]/preferences/route.ts 4/5 New REST API endpoint for managing user preferences with validation
src/lib/client.ts 4/5 Adds client-side preference sync functions for localStorage and state management
src/app/login/LoginForm.tsx 5/5 Integrates preference loading and theme application on successful login
src/app/api/auth/login/route.ts 4/5 Includes user preferences in login response for immediate sync
src/app/api/auth/verify/route.ts 4/5 Adds preferences to auth verification response for token refresh scenarios
src/components/hooks/queries/useLoginQuery.ts 4/5 Handles preference synchronization and theme application during login flow
src/app/(main)/settings/preferences/ThemeSetting.tsx 4/5 Enhanced with database persistence and reset functionality for theme preferences
src/app/(main)/settings/preferences/DateRangeSetting.tsx 5/5 Integrates database persistence while maintaining localStorage compatibility
src/app/(main)/settings/preferences/TimezoneSetting.tsx 4/5 Updated to sync timezone changes to database alongside localStorage
src/app/(main)/settings/preferences/LanguageSetting.tsx 5/5 Adds database persistence for language settings with proper reset handling
src/app/logout/LogoutPage.tsx 5/5 Ensures preferences are cleared from localStorage during logout
src/lib/constants.ts 3/5 Changes theme configuration key from 'umami.theme' to 'zen.theme'

Confidence score: 4/5

  • This PR introduces comprehensive preference persistence with good error handling and backward compatibility, though some edge cases need attention
  • Score reflects solid architecture and implementation patterns, but concerns about silent error handling in the usePreferences hook and potential inconsistencies in preference management lower the confidence
  • Pay close attention to the usePreferences hook for silent error handling and the removeClientPreferences function which doesn't clear LOCALE_CONFIG consistently

Sequence Diagram

sequenceDiagram
    participant User
    participant LoginForm as "Login Form"
    participant AuthAPI as "Auth API"
    participant UserDB as "User Database"
    participant ClientStorage as "Client Storage"
    participant AppStore as "App Store"

    User->>LoginForm: "Enter credentials and submit"
    LoginForm->>AuthAPI: "POST /auth/login with username/password"
    AuthAPI->>UserDB: "getUserByUsername() and checkPassword()"
    UserDB-->>AuthAPI: "User data with preferences"
    AuthAPI->>UserDB: "getUserPreferences(userId)"
    UserDB-->>AuthAPI: "User preferences (theme, language, timezone, dateRange)"
    AuthAPI-->>LoginForm: "Return token and user data with preferences"
    LoginForm->>ClientStorage: "setClientAuthToken(token)"
    LoginForm->>ClientStorage: "setClientPreferences(preferences)"
    LoginForm->>AppStore: "setTheme(preferences.theme)"
    LoginForm->>AppStore: "setUser(user)"
    LoginForm->>User: "Redirect to /websites"

    Note over User,AppStore: User changes preferences in settings
    User->>LanguageSetting: "Select new language"
    LanguageSetting->>ClientStorage: "saveLocale(language)"
    LanguageSetting->>AuthAPI: "POST /users/{userId}/preferences with language"
    AuthAPI->>UserDB: "updateUserPreferences(userId, {language})"
    UserDB-->>AuthAPI: "Updated preferences"

    User->>ThemeSetting: "Select new theme"
    ThemeSetting->>AppStore: "setTheme(theme)"
    ThemeSetting->>AuthAPI: "POST /users/{userId}/preferences with theme"
    AuthAPI->>UserDB: "updateUserPreferences(userId, {theme})"
    UserDB-->>AuthAPI: "Updated preferences"

    User->>TimezoneSetting: "Select new timezone"
    TimezoneSetting->>ClientStorage: "saveTimezone(timezone)"
    TimezoneSetting->>AuthAPI: "POST /users/{userId}/preferences with timezone"
    AuthAPI->>UserDB: "updateUserPreferences(userId, {timezone})"
    UserDB-->>AuthAPI: "Updated preferences"

    User->>DateRangeSetting: "Select new date range"
    DateRangeSetting->>ClientStorage: "setItem(DATE_RANGE_CONFIG, dateRange)"
    DateRangeSetting->>AuthAPI: "POST /users/{userId}/preferences with dateRange"
    AuthAPI->>UserDB: "updateUserPreferences(userId, {dateRange})"
    UserDB-->>AuthAPI: "Updated preferences"

    Note over User,AppStore: User logs out
    User->>LogoutPage: "Navigate to /logout"
    LogoutPage->>ClientStorage: "removeClientAuthToken()"
    LogoutPage->>ClientStorage: "removeClientPreferences()"
    LogoutPage->>AppStore: "setUser(null)"
    LogoutPage->>AuthAPI: "POST /auth/logout"
    LogoutPage->>User: "Redirect to /login"

    Note over User,AppStore: User logs in on different device
    User->>LoginForm: "Enter credentials and submit"
    LoginForm->>AuthAPI: "POST /auth/login with username/password"
    AuthAPI->>UserDB: "getUserByUsername() and getUserPreferences()"
    UserDB-->>AuthAPI: "User data with saved preferences from database"
    AuthAPI-->>LoginForm: "Return token and user data with preferences"
    LoginForm->>ClientStorage: "setClientPreferences(preferences) - sync from database"
    LoginForm->>AppStore: "setTheme(preferences.theme) - restore user's theme"
    LoginForm->>User: "User sees consistent settings across devices"
Loading

Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

17 files reviewed, 4 comments

Edit Code Review Agent Settings | Greptile

- Use DEFAULT_THEME constant for theme reset
- Type user selector properly
- Include LOCALE_CONFIG in preferences cleanup
- Add newline to migration file
@ceviixx
Copy link
Author

ceviixx commented Nov 14, 2025

@mikecao @franciscao633 is it possible to merge or should I create an new PR to dev?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant