using System.Text.Json; using System.Text.Json.Serialization; using DeepDrftModels.DTOs; using DeepDrftModels.Enums; using Microsoft.AspNetCore.Components; namespace DeepDrftPublic.Client.Services; /// /// Production (Phase 16 §2.2): serializes the play classification and fires /// it via navigator.sendBeacon to the proxied api/event/play route. Fire-and-forget by /// design — is synchronous (it is called from the player's close /// path and the unload handler, neither of which can await), so the beacon is dispatched without /// awaiting and its failure is irrelevant. The current anonId (wave 16.3) is read synchronously /// from the warmed cache and omitted when null (storage unavailable / not /// yet warmed) — an anonId-less play still counts, it just doesn't contribute to the listener tally. /// public sealed class BeaconPlayEventSink : IPlayEventSink { // Omit a null anonId from the wire payload (§2.2 — "omitted entirely" when absent) rather than // sending "anonId":null. The API treats absent and null identically, so this is cosmetic minimalism; // it does not change the integer enum encoding the 16.1 contract already relies on. private static readonly JsonSerializerOptions BeaconJson = new() { DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull }; private readonly BeaconInterop _beacon; private readonly IAnonIdProvider _anonId; private readonly string _playUrl; public BeaconPlayEventSink(BeaconInterop beacon, IAnonIdProvider anonId, NavigationManager navigation) { _beacon = beacon; _anonId = anonId; // The WASM client posts to its own host, which proxies to DeepDrftAPI. BaseUri carries a // trailing slash; the route does not lead with one. _playUrl = $"{navigation.BaseUri}api/event/play"; } public void EmitPlay(string trackEntryKey, PlayBucket bucket) { var json = JsonSerializer.Serialize(new PlayEventDto { TrackEntryKey = trackEntryKey, Bucket = bucket, AnonId = _anonId.Current, }, BeaconJson); // Fire-and-forget: do not await. The beacon survives unload; the C# task may not, and we do not // act on the result either way. _ = _beacon.SendAsync(_playUrl, json); } }