Skip to content

Settings: theme preference synced to the account (theme) #36

Description

@Adron

Part of #31 — Settings & preferences parity — most of PATCH /api/user/update is unreachable from Android

Dark mode already works locally via ThemeSettingsStore/ThemeMode in :core:datastore,
but the choice is device-local. The account carries a theme field on PATCH /api/user/update,
so a user who sets dark on the web still lands in light on a fresh Android install.

  • Two-way sync: push local changes to theme, and adopt the account value on first sign-in.
  • Keep the local store as the source of truth while offline, reconciling on next sync.

Tests: local → PATCH; account value applied at sign-in; offline change survives and syncs.


Filed from the parity review of feature/parity-wip @ 14d7795 against interlinedlist.com (OpenAPI /api/openapi.json, 233 paths) and the public help centre.

Activity

  1. added
    parityClosing a gap against interlinedlist.com
    P2Completeness / settings surface
    on Sep 15, 2026
  2. Adron commented on Sep 16, 2026

    @Adron
    MemberAuthor

    Implemented in the PR above (branch issue/36-settings-theme). Closing here — review and any follow-up happens on the PR.

    Server-side finding worth acting on independently: theme is not validated at all. PATCH /api/user/update accepted and stored "not-a-theme", "sepia", "SYSTEM" and "", each answering 200 {"message":"User updated successfully"}. Compare viewingPreference, which returns a proper allow-list in its 400. Any client can put the account into a theme no client can render. The app is therefore the conservative side — it sends only light/dark/system and refuses to adopt an unrenderable stored value rather than silently overwriting it. The test account was left as found (light).

    The vocabulary came from /help/settings instead: "Theme: Light, dark, or system (follows your device preference)" — so the account does have a follow-the-system option, and ThemeMode.SYSTEM maps straight to "system" with no coercion.

    The reconciliation rule the issue asked me to define and test: an unsynced local choice wins; otherwise the account wins. Neither side has a modification timestamp, so "newest wins" was unavailable — but a persisted unsynced flag is happened-after evidence, since a change that never reached the account means the account still holds whatever preceded it.

  3. added a commit that references this issue on Sep 16, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    P2Completeness / settings surfacearea:settingsSettings & preferencesparityClosing a gap against interlinedlist.com

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions