Repository navigation
Settings: edit every server-side preference, and honor theme for real (#46) - #182
Merged
Merged
Conversation
…for real #131 modelled the 14 missing `GET /api/user` fields; this makes them editable and makes the one consumption point that isn't in an in-flight file actually obey its value. Theme precedence (#46's acceptance criterion) now works: an explicit server `theme` of light/dark wins over the OS, and `system` — or null, or anything else — falls back to the existing HKCU/`UserPreferenceChanged` behaviour, which stays wired. `App` re-resolves on every `CurrentUser` change, so login, session restore and a settings save all take effect without a restart. The fallback is not defensive padding: the server does NOT validate `theme` (it accepted and stored `"zzz_bogus"` in a live probe), so an unrecognized value has to degrade to the OS setting. Fixes a live 500 along the way. `UpdateProfileAsync` built its body from an anonymous object, and `JsonSerializerDefaults.Web` does not drop nulls, so every "Save profile" shipped `"theme": null` — which the server answers with `500 internal_error`, writing nothing. Both writers now build a sparse dictionary and omit untouched fields, which also stops a profile save from stomping preferences set elsewhere. Verified live 2026-09-16 against the shared test account with a throwaway `issue-46-probe` sync-token. All 15 `PATCH /api/user/update` field names accept native JSON numbers/booleans (the OpenAPI *request* schema types them as `"string"` — that's wrong). The server named the `viewingPreference` enum itself in a 400, and enforces maxMessageLength 1–10000, messagesPerPage 10–30, notificationTrayLimit 10–40; those ranges are now validated client-side so a typo is an inline message rather than a raw 400. Every value changed was captured first and restored; a follow-up `GET /api/user` diffed byte-identical. The PATCH 200 does return a `user`, but a narrower one than `GET /api/user` (no accountStatus/cleared/pendingEmail/isAdministrator), so callers re-fetch rather than trust it — house read-after-write rule. 33 assertions covering the theme truth table, the normalizers, the sparse-body serialization and the captured live payload were run in a throwaway net10.0 console project outside the repo; all pass. Debug and Release both build clean. Closes #46 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Single trivial conflict in SettingsView.xaml's resource dictionary: this branch adds StringEqualsToBoolConverter (for the viewingPreference radio group) and main has InverseBooleanConverter (from #132's notification-channel toggles). Different converters, both wanted — kept both. SettingsViewModel.cs auto-merged. Debug and Release both build clean. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
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.
Closes #46
What this does
#131 modelled the 14 missing preference fields; this makes them editable and effective. All 15
PATCH /api/user/updatefield names verified live and round-tripping:displayName,bio,avatar,isPrivateAccount,githubDefaultRepo,theme,maxMessageLength,messagesPerPage,viewingPreference,showPreviews,notificationTrayLimit,defaultPubliclyVisible,showAdvancedPostSettings,latitude,longitude.It also fixes a live bug — see #171
"Save profile" has never worked.
UpdateProfileAsyncbuilt its body from an anonymous object, andJsonSerializerDefaults.Webdoesn't drop nulls, so every save shipped"theme": null:Both writers now build a sparse dictionary. The generalisation is on #171: any PATCH body built from an anonymous object with optional parameters is a latent 500 on this API.
Four findings that shaped the implementation
"string". (The response schema uses native types.)viewingPreferenceis enum-validated, and the server named its own values in a 400:my_messages,all_messages,followers_only,following_only.themeis not validated — it accepted and stored"zzz_bogus". SoResolveDarktreats anything unrecognized as "follow the OS", and the OS listener is retained.useris narrower thanGET /api/user(noaccountStatus/cleared/pendingEmail/isAdministrator), so deserializing it would blank Settings: account-status banner and gated-feature messaging #50's banner fields — callers re-fetch.Live ranges from 400s, now validated client-side:
maxMessageLength1–10000,messagesPerPage10–30,notificationTrayLimit10–40.Consumers still needing wiring
Six consumption points sit in files held by other PRs, each spelled out with its exact one-line change:
messagesPerPage,notificationTrayLimit,maxMessageLength,showAdvancedPostSettings,showPreviews,viewingPreference.defaultPubliclyVisiblewas already honored;themeis done here.Two of those are not one-liners:
viewingPreferenceneeds a product decision (followers_only/following_onlyhave no server-side equivalent — see #36), andshowPreviewshas nothing to gate yet since nothing calls the metadata endpoint.Shared-account hygiene
Nine fields were probed off their real values and all nine restored, confirmed by a
GET /api/userthat diffed byte-identical to the pre-probe capture. No identifying field touched.Builds green in Debug and Release.
🤖 Generated with Claude Code