fix: restore native Android play-event recording (History was empty) #89
Reference in New Issue
Block a user
Delete Branch "dev"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Fixes listening History being empty/sparse on the native Android client.
Root cause (client)
Every native
/api/eventsrequest omitted thetypediscriminator. The app'sJson is configured
encodeDefaults=false, so atypeleft at its data-classdefault (
play_started, etc.) is never serialized. The server multiplexes ontypeand returns 400 "unknown event type";PlayEventsReporterswallowsthe error and the
play_startedpath has no offline fallback — so the play islost without a trace. Confirmed live in the server access log:
POST /api/events → 400on every play while reads return 200.Net effect: all native Android play events have 400'd since the code was
written. History only ever populated from Flutter/web; as usage moved to the
native app, History went sparse (the 05-31 → 06-01 cliff).
653ed95fforces the discriminator onto the wire with@EncodeDefault(Mode.ALWAYS)on each event request'stype.Server fix (history recovery)
b2c6f6f0—autoClosePriorOpenpreviously hardcodedwas_skipped=truewhenclosing an orphaned play, hiding even fully-listened tracks from History. It
now applies the same skip rule as
RecordPlayEndedto the duration-cappedelapsed estimate, so a track that played long enough is recovered into History
instead of force-hidden. No
skip_eventsrow written (auto-close is anambiguous signal, not a deliberate skip).
Follow-ups (not in this PR)
play_started,onStopclose-by-id (avoid orphan + duplicate), async-start race.
🤖 Generated with Claude Code
typeso plays aren't droppedThe native client's /api/events requests omitted the `type` discriminator entirely. The app's Json is configured with encodeDefaults=false (AppModule), so a `type` left at its data-class default ("play_started" etc.) is never written to the wire. The server multiplexes on `type` and returns 400 "unknown event type" for an empty one, which PlayEventsReporter's catch swallows — and the play_started path has no offline fallback, so the play is lost with no trace. Net effect: EVERY native Android play event (started/ended/skipped/ offline) has 400'd since this code was written. Listening History only ever populated from the Flutter/web clients; as usage moved to the native app, History went sparse. Confirmed live in the server access log: POST /api/events -> 400 on every play, while reads 200. Force the discriminator onto the wire with @EncodeDefault(Mode.ALWAYS) on each request type's `type` field. Surgical (vs flipping encodeDefaults globally), and idiomatic for a constant-valued discriminator. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>