Repository navigation
feat(settings): keep settings with the account so they follow it across devices - #436
Merged
Merged
Conversation
…ss devices
Theme, language, units, the dashboard layout and the map's layers and filters were stored per
browser, so every phone and computer had to be set up on its own. They now belong to the
signed-in account.
- Server: a UserSettings table (migration AddUserSettings) holds one JSON object per account per
section (ui, dashboard, map), which the server does not interpret beyond the section list, key
names and a 16 KB cap. GET /api/settings and PATCH /api/settings/{section}; a save merges key
by key with a version column and a randomised retry, so devices saving at the same moment keep
each other's keys, and answers 409 settings.conflict when it keeps losing. Accounts are keyed by
a SHA-256 of the sign-in method and subject, so no name or email is stored and a password
account never shares settings with a provider account of the same name.
- Browser: persistSettings hands each store to the new settingsSync store. The router guard reads
the account's copy alongside the session check and mounts no protected view before it is taken
in. Each local write sends only the keys that changed; the page reads again when it comes back
into view (at most every 30 s) or back online. An account with no copy yet is filled from the
first browser that signs in. Each browser keeps its own copy, so pages open instantly and the
login page keeps the theme and language.
- The notification checklist stays per device: it decides whether that browser is subscribed to
push, so unticking everything on one computer must not unsubscribe a phone.
- Applying the vehicle type's card defaults moved from a dashboard watcher into
VehicleTypeSetting, so an override arriving from another device cannot reapply defaults over
its cards.
Checked on PostgreSQL 18: eight devices saving one section at the same moment all succeed, both
before the account has a row and after. Two-browser check in demo mode: theme and units follow,
the notification checklist does not. The entry chunk grows by 1 KB gzip.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
joszz
force-pushed
the
feat/synced-settings
branch
from
October 5, 2026 12:05
1d92cee to
ace370b
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Pull Request
Summary
Theme, language, units, the dashboard layout and the map's layers and filters were stored per browser, so every phone and computer had to be set up separately. They now belong to the signed-in account. Built on 2026-09-27 and recovered from a stash: it never reached main.
UserSettingstable (migrationAddUserSettings), one JSON object per account per section (ui,dashboard,map).GET /api/settings,PATCH /api/settings/{section}. Saves merge key by key with a version column and a randomised retry, so devices saving at once keep each other's keys;409 settings.conflictif one keeps losing. Accounts are keyed by a SHA-256 of sign-in method + subject: no name or email stored, and password and provider accounts of the same name stay apart. Server accepts only the known sections, plain keys and 16 KB per section.persistSettingshands each store to the newsettingsSyncstore. The router guard reads the account's copy alongside the session check, and no protected view mounts before it is taken in. Each local change sends only the changed keys; other devices pick changes up when they come back into view (at most every 30 s) or back online. An account with no copy is filled from the first browser that signs in, so upgrading loses nothing.VehicleTypeSettinginstead of a dashboard watcher, so an override synced from another device cannot reapply defaults over its cards.Changes since the stash: ported onto main (one conflict in
settingsDashboard.ts, now using main'sstoredCards()), migration regenerated so it sorts afterDropRawTopicIndex, the per-device notification checklist (localKeys) with a test, and three.env.examplelines that still said "per browser".Type of Change
Related Issues
None.
Checklist
AuthenticationFlowTests, 12 frontend sync tests plus router and vehicle-type setting tests)pnpm lint,pnpm type-check,dotnet build,dotnet format --verify-no-changes, no pending model changes)unraid/garagestack.xmlchanged (it did not)Verification
Merge note
Rebased onto main after #435. The only conflict was the two repository registrations in
ServiceCollectionExtensions.cs.AddUserSettingswas regenerated (20261005120400) so it followsAddTelemetryCompactedUntil; its body is unchanged.🤖 Generated with Claude Code