crossmate

A collaborative crossword app for iOS
Log | Files | Refs | LICENSE

commit 39fd61588114933c4d45b3a32990b6ade0da0450
parent 1fe11dc6378937ab46cb33c85622247e0523dd3f
Author: Michael Camilleri <[email protected]>
Date:   Sat, 27 Jun 2026 07:33:38 +0900

Reserve accountSeen horizons before publishing

Opening a shared puzzle could still send duplicate accountSeen pushes
when the delivered-notification dismissal and active read lease
overlapped. Both callers checked the coalescing state before either
worker request had returned, so the second path could miss the first
in-flight publish and send the same open-time horizon.

This commit records the per-game accountSeen horizon before awaiting the
worker publish, after the local push-client and author preconditions
have passed. Overlapping open-time callers now see the reserved horizon
and log the existing coalesced diagnostic instead of making a second
worker request.

Co-Authored-By: Codex GPT 5.5 <[email protected]>

Diffstat:
MCrossmate/Services/AccountPushCoordinator.swift | 19++++++++++++++++---
1 file changed, 16 insertions(+), 3 deletions(-)

diff --git a/Crossmate/Services/AccountPushCoordinator.swift b/Crossmate/Services/AccountPushCoordinator.swift @@ -325,6 +325,14 @@ final class AccountPushCoordinator { } func publishAccountSeenPush(gameID: UUID, readAt: Date) async { + guard let pushClient else { + syncMonitor.note("push(\(Self.accountSeenPushKind)): skipped (no pushClient)") + return + } + guard let authorID = identity.currentID, !authorID.isEmpty else { + syncMonitor.note("push(\(Self.accountSeenPushKind)): skipped (no authorID)") + return + } if shouldCoalesceAccountSeen(gameID: gameID, readAt: readAt) { syncMonitor.note( "push(accountSeen): coalesced \(gameID.uuidString.prefix(8)) " + @@ -332,9 +340,14 @@ final class AccountPushCoordinator { ) return } - if await publishAccountEvent(kind: Self.accountSeenPushKind, gameID: gameID, readAt: readAt) { - lastAccountSeenReadAt[gameID] = readAt - } + let address = ensureAccountPushAddress(authorID: authorID) + lastAccountSeenReadAt[gameID] = readAt + await pushClient.publishAccountEvent( + kind: Self.accountSeenPushKind, + gameID: gameID, + address: address, + readAt: readAt + ) } private func shouldCoalesceAccountSeen(gameID: UUID, readAt: Date) -> Bool {