Skip to content

fix: smooth out iOS core-loop friction (401 re-auth, player exit, HQ swap)#33

Merged
windoze95 merged 1 commit into
mainfrom
fix/ios-core-loop-friction
Jun 27, 2026
Merged

fix: smooth out iOS core-loop friction (401 re-auth, player exit, HQ swap)#33
windoze95 merged 1 commit into
mainfrom
fix/ios-core-loop-friction

Conversation

@windoze95

Copy link
Copy Markdown
Owner

Three core play/auth-loop friction fixes from the roadmap audit (NOW #8/#9/#10). Fixes #11, #12, #15, #19.

#11 — Graceful 401 recovery (no more dead-ends)

An expired/revoked session previously trapped the user on "Failed to load" across every tab with a Retry that could never succeed. Now a real 401 on a protected endpoint resets auth and bounces to the profile picker with a "Session expired — sign in again" snackbar.

  • ApiService exposes an onUnauthorized callback fired from the Dio onError interceptor only when statusCode == 401 and the request isn't an /auth/ endpoint (those return 401/403 for bad credentials and are handled inline). Connection failures carry no response, so they're correctly excluded.
  • AuthNotifier.handleSessionExpired() clears state synchronously (router redirects via the existing refreshListenable), disconnects the WebSocket, and clears stored session — returning true only if a session existed, so a burst of concurrent 401s resets and notifies exactly once.
  • Added a context-free scaffoldMessengerKey (wired into MaterialApp.router) so the API layer can surface a snackbar.

#12 + #15 — Instant player exit, exactly one progress save

_navigateBack no longer awaits the final save (instant exit on slow/dead networks), and a _progressSavedOnExit guard stops dispose() from saving a second time — so teardown sends exactly one /progress PUT.

#19 — Preview→HQ disposal crash

The old preview controller is now disposed in a post-frame callback instead of synchronously mid-frame, so the outgoing VideoPlayer never reads a disposed controller.

Verification

dart format, flutter analyze, and flutter test (incl. a new auth_notifier_test.dart covering the session-expiry reset/dedup) all pass locally.

Supersedes community PRs #28/#29/#31 (cleaner implementations of the same fixes).

🤖 Generated with Claude Code

https://claude.ai/code/session_01RXMKM1rDWn8wNh93MMUtxY

…swap)

Three friction fixes in the core watch/auth loop.

#11 - Global 401 -> graceful re-auth. The Dio onError interceptor now
detects a real 401 on any protected (non-auth) endpoint and hands off to
AuthNotifier.handleSessionExpired(), which resets to the profile picker
(currentUser -> null drives the GoRouter redirect to '/'), clears the
stored session, disconnects the websocket, and surfaces a
"Session expired - sign in again" SnackBar via a new app-wide
ScaffoldMessenger key. Auth/credential endpoints (everything under
/api/auth/) are excluded so bad-PIN 401/403s still surface inline. A
burst of concurrent 401s resets and notifies exactly once; connection
errors (no response) and non-auth 403s do not trigger it.

#12 + #15 - Instant player exit + single progress save. _navigateBack
now fires the final /progress save unawaited and pops immediately, so
leaving is instant regardless of network. A _progressSavedOnExit guard
stops dispose() from sending a second save: teardown is exactly one PUT.

#19 - Preview->HQ disposal crash. _switchToHq now defers the old preview
controller's pause/dispose to a post-frame callback, so the outgoing
VideoPlayer never reads a disposed controller in the same frame.

Verified: dart format clean, flutter analyze (--fatal-infos
--fatal-warnings) clean, flutter test green (72 tests, +3 for
handleSessionExpired).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RXMKM1rDWn8wNh93MMUtxY
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@windoze95 windoze95 merged commit 908f2e9 into main Jun 27, 2026
6 checks passed
@windoze95 windoze95 deleted the fix/ios-core-loop-friction branch June 27, 2026 18:40
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.

Bug: Missing 401 Unauthorized handling leaves user trapped when session expires

1 participant