Skip to content

Fix stale overlay tap: redesign .dismissed state machine#585

Merged
bjorkert merged 1 commit intoloopandlearn:live-activityfrom
achkars-org:live-activity
Mar 29, 2026
Merged

Fix stale overlay tap: redesign .dismissed state machine#585
bjorkert merged 1 commit intoloopandlearn:live-activityfrom
achkars-org:live-activity

Conversation

@MtlPhil
Copy link
Copy Markdown

@MtlPhil MtlPhil commented Mar 29, 2026

Summary

  • Redesign .dismissed state machine: Three explicitly separated sources of .dismissed — our own planned restart (endingForRestart), iOS system force-dismiss (renewalFailed or pastDeadline), and user decision (explicit swipe). Only a true user swipe sets dismissedByUser = true and blocks auto-restart.

  • Remove staleDatePassed: Stale date expiry fires .ended, not .dismissed, so it was the wrong discriminator. Replaced with pastDeadline = now >= laRenewBy for the system-dismiss check.

  • Preserve laRenewBy on .ended and system .dismissed: handleForeground() can now detect the renewal window on the next foreground event and restart automatically — whether iOS ended the LA normally or force-dismissed it. Only a user swipe clears laRenewBy to cancel the renewal intent.

  • Fix handleForeground() nil-current path: When current is nil and the renewal window is open, the LA was ended by iOS (a user swipe would have cleared laRenewBy to 0, making overlayIsShowing false and bypassing this branch entirely). startFromCurrentState() now proceeds correctly in this case.

  • Widen overlay window to 30 minutes (was 20 min) before the 7.5-hour renewal deadline.

🤖 Generated with Claude Code

Three clearly separated .dismissed sources:
(a) endingForRestart — our own end() during planned restart, ignore
(b) iOS system force-dismiss — renewalFailed OR pastDeadline (now >= laRenewBy)
    → auto-restart on next foreground, laRenewBy preserved
(c) User decision — explicit swipe
    → dismissedByUser=true, laRenewBy=0 (renewal intent cancelled)

Remove staleDatePassed: staleDate expiry fires .ended not .dismissed.

Preserve laRenewBy on .ended and system .dismissed so handleForeground()
detects the renewal window and restarts on next foreground. Only the
user-swipe path clears laRenewBy, preventing handleForeground() from
re-entering the renewal path after the user explicitly killed the LA.

Fix handleForeground() nil-current path: reaching it means iOS ended the
LA while the renewal window was open (laRenewBy still set). A user-swipe
would have cleared laRenewBy to 0, so overlayIsShowing would be false
and this branch would never be reached — startFromCurrentState() is safe.

Set renewalWarning to 30 minutes (overlay appears 30 min before 7.5h deadline).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@bjorkert bjorkert merged commit b89d3af into loopandlearn:live-activity Mar 29, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants