- Shipped
- September 7, 2026 at 1:55 AM UTC
- Author
- Kamo
- Commit
- 542fc90
Reported as "when I enable repeating sound it does not save". It saved. Then the next preference the member touched turned it off again — and the settings page PUTs on load through the quiet-hours reconcile, so opening the page was enough. The body was bound to MemberNotificationPreference, and an entity cannot express "absent". Its fields carry initializers and Lombok's @NoArgsConstructor applies the @Builder.Default values, so a body of {"soundEnabled":true} arrives with soundRepeatEnabled=false, toastPosition="bottom-right" and every other default already filled in. The guard "apply it if it is not null" therefore fired for every field on every write, and could never tell absent from set-to-the-default. It was never specific to the new field. Every defaulted setting was being reset on every write, and quiet hours were assigned unconditionally so any unrelated patch cleared the member's window. The others default to TRUE or to a sane value, so the damage read as nothing having happened. So the controller takes the raw JSON and the service merges it with readerForUpdating: only the keys the body carries are written, an explicit null still clears (which is how the UI turns quiet hours and DND off), and the entity can grow without a line per field going stale. Identity keys are dropped before the merge — the merge is what makes the patch work, and it is also what would let a caller re-address someone else's row. Also lowers the repeat-interval floor to one second, as asked. A member who wants to be nagged every second for the one call they cannot miss is entitled to be; the floor exists only to stop a zero, which is not a fast repeat but a tone with no gap at all.