fix(notifications): always serialize streams so clearing the last subscription saves
All checks were successful
PR Checks / android-build (pull_request) Successful in 10m34s

Turning off the final notification subscription (going from one opted-in
stream to zero) failed with "could not save" and the toggle stuck on. The
backend's PUT /auth/me/notifications/subscriptions validator requires the
`streams` field (body('streams').isArray()), but kotlinx.serialization omits a
property equal to its default (encodeDefaults=false). NotificationSubscriptionsDto
defaulted `streams` to emptyList(), so an empty set serialized to `{}` and the
backend rejected it 400 "Validation failed". Any non-empty set included the
field, so only the last toggle-off broke — regardless of which stream it was.

Remove the default from NotificationSubscriptionsDto.streams so kotlinx always
emits the field; an empty set now sends `{"streams":[]}` (200). The one call
site already passes streams explicitly and the server always returns the field,
so response decoding is unaffected. Add a regression test asserting the empty
DTO serializes to `{"streams":[]}` under the production Json config.

Verified on-device (AVD) against the live site and via the live API
(`{}` -> 400, `{"streams":[]}` -> 200).

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2026-07-22 11:36:32 -05:00
parent f0a3b6c03e
commit 9c52a3dafa
2 changed files with 18 additions and 1 deletions

View File

@@ -60,8 +60,15 @@ data class NotificationStreamsDto(
* `GET/PUT /auth/me/notifications/subscriptions` — the user's opted-in stream ids.
* PUT replaces the full set; unknown ids are dropped server-side and the stored set
* echoed back.
*
* [streams] intentionally has NO default: this DTO doubles as the PUT body, and the
* backend validator requires the `streams` field (`body('streams').isArray()`).
* kotlinx omits a property equal to its default (encodeDefaults=false), so a default
* of `emptyList()` would drop the field when the user clears their LAST subscription,
* sending `{}` → 400 "Validation failed" (the "can't turn off the last one" bug). With
* no default the empty list always serializes as `{"streams":[]}`. Do not re-add a default.
*/
@Serializable
data class NotificationSubscriptionsDto(
val streams: List<String> = emptyList(),
val streams: List<String>,
)