import 'package:shuffle/features/auth/domain/auth_status.dart'; import 'package:shuffle/features/auth/domain/auth_user.dart'; // `AuthUser` heißt auch in supabase_flutter; hier ist die Domänenklasse gemeint. import 'package:supabase_flutter/supabase_flutter.dart' hide AuthUser; /// Bildet `AuthChangeEvent`s auf [AuthStatus] ab (Spec #1 § 5.5). /// /// Abweichung von der Wortlaut-Zuordnung in § 5.5: Die Recovery bleibt /// bestehen, bis [finishRecovery] oder `signedOut` eintrifft. Ein /// `tokenRefreshed` oder `signedIn` während der Recovery hebt den Guard nicht /// auf. `userUpdated` (Passwort geändert) beendet ihn ebenfalls **nicht** /// mehr, sondern setzt nur `passwordChanged`: Sonst würde der Router den /// Nutzer von `/reset-password` wegleiten, bevor die Erfolgsmeldung /// (KTO-01 Kriterium 9) zu sehen ist. Erst die ausdrückliche Bestätigung des /// Nutzers beendet den Guard. Der Zustand lebt nur im Prozess /// (Spec #1 § 5.4, R12): Aus dem `amr`-Claim lässt er sich nicht ableiten. class AuthStatusTracker { bool _recovery = false; bool _passwordChanged = false; /// Status für [session] unter Berücksichtigung der laufenden Recovery. AuthStatus statusFor(Session? session) { final user = session?.user; if (user == null) { return const AuthStatus.signedOut(); } final authUser = AuthUser( id: user.id, email: user.email, emailConfirmed: user.emailConfirmedAt != null, ); return _recovery ? AuthStatus.passwordRecovery( authUser, passwordChanged: _passwordChanged, ) : AuthStatus.signedIn(authUser); } /// Verarbeitet ein Ereignis und liefert den neuen Status. AuthStatus handle(AuthChangeEvent event, Session? session) { switch (event) { case AuthChangeEvent.passwordRecovery: _recovery = session != null; _passwordChanged = false; case AuthChangeEvent.userUpdated: // The password change itself: stay in the guard (see class doc). if (_recovery) { _passwordChanged = true; } case AuthChangeEvent.signedOut: _end(); default: break; } if (session == null) { _end(); } return statusFor(session); } /// The password was set during a running recovery (successful /// `updateUser`). Idempotent to the later `userUpdated` event, which gotrue /// delivers asynchronously, possibly after `updateUser` returned. Without a /// running recovery nothing changes. AuthStatus markPasswordChanged(Session? session) { if (_recovery) { _passwordChanged = true; } return statusFor(session); } /// Ends the recovery after the user confirmed the success message. Only /// possible once the password is changed; before that the status stays /// unchanged (the guard cannot be skipped). AuthStatus finishRecovery(Session? session) { if (_recovery && _passwordChanged) { _end(); } return statusFor(session); } void _end() { _recovery = false; _passwordChanged = false; } }