Files
MatchLiveTv/mobile/lib/providers/auth_provider.dart
Emiliano Frascaro 854738b46d Stabilizza live YouTube/RTMP e fix crash rotazione mobile.
Pipeline YouTube con relay, overlay e publisher sync lato backend; fix GL pauseGlDuringRotation su Android per evitare crash in rotazione durante lo streaming Flutter.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-06 15:30:55 +02:00

142 lines
3.3 KiB
Dart

import 'package:dio/dio.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../core/auth_storage.dart';
import '../core/team_selection_storage.dart';
import '../shared/api_client.dart';
import '../shared/models/user.dart';
class AuthState {
const AuthState({
this.user,
this.accessToken,
this.refreshToken,
this.loading = false,
this.error,
});
final User? user;
final String? accessToken;
final String? refreshToken;
final bool loading;
final String? error;
bool get isAuthenticated =>
accessToken != null && accessToken!.isNotEmpty && user != null;
AuthState copyWith({
User? user,
String? accessToken,
String? refreshToken,
bool? loading,
String? error,
bool clearError = false,
}) {
return AuthState(
user: user ?? this.user,
accessToken: accessToken ?? this.accessToken,
refreshToken: refreshToken ?? this.refreshToken,
loading: loading ?? this.loading,
error: clearError ? null : (error ?? this.error),
);
}
}
class AuthNotifier extends StateNotifier<AuthState> {
AuthNotifier() : super(const AuthState());
void setSession({
required User user,
required String accessToken,
required String refreshToken,
}) {
state = AuthState(
user: user,
accessToken: accessToken,
refreshToken: refreshToken,
loading: false,
);
AuthStorage.save(
user: user,
accessToken: accessToken,
refreshToken: refreshToken,
);
}
Future<void> restoreSession() async {
final stored = await AuthStorage.load();
if (stored == null) return;
state = AuthState(
user: stored.user,
accessToken: stored.accessToken,
refreshToken: stored.refreshToken,
loading: false,
);
}
/// Dopo restore: verifica token o rinnova con refresh (evita 401 su /teams).
Future<bool> validateOrRefreshSession() async {
if (!state.isAuthenticated) return false;
final client = ApiClient(accessToken: state.accessToken);
try {
final user = await client.me();
state = state.copyWith(user: user);
await AuthStorage.save(
user: user,
accessToken: state.accessToken!,
refreshToken: state.refreshToken!,
);
return true;
} on DioException catch (e) {
if (e.response?.statusCode != 401) {
return true;
}
} catch (_) {
return true;
}
return refreshSession();
}
Future<bool> refreshSession() async {
final refreshToken = state.refreshToken;
if (refreshToken == null || refreshToken.isEmpty) {
logout();
return false;
}
try {
final client = ApiClient();
final result = await client.refreshSession(refreshToken);
setSession(
user: result.user,
accessToken: result.tokens.accessToken,
refreshToken: result.tokens.refreshToken,
);
return true;
} catch (_) {
logout();
return false;
}
}
void setLoading(bool loading) {
state = state.copyWith(loading: loading, clearError: true);
}
void setError(String message) {
state = state.copyWith(loading: false, error: message);
}
void logout() {
state = const AuthState();
AuthStorage.clear();
TeamSelectionStorage.clear();
}
}
final authProvider = StateNotifierProvider<AuthNotifier, AuthState>(
(ref) => AuthNotifier(),
);