File size: 2,819 Bytes
b24b684 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 | import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../../../core/providers.dart';
import '../data/auth_repository.dart';
import '../domain/user_model.dart';
enum AuthStatus { unknown, unauthenticated, authenticated }
class AuthState {
final AuthStatus status;
final AppUser? user;
const AuthState({this.status = AuthStatus.unknown, this.user});
AuthState copyWith({AuthStatus? status, AppUser? user, bool clearUser = false}) {
return AuthState(
status: status ?? this.status,
user: clearUser ? null : (user ?? this.user),
);
}
String? get role => user?.role;
}
final authControllerProvider =
NotifierProvider<AuthController, AuthState>(AuthController.new);
class AuthController extends Notifier<AuthState> {
AuthRepository get _repo => ref.read(authRepositoryProvider);
@override
AuthState build() {
// Wire global 401 handling to force logout.
ref.read(dioClientProvider).onUnauthorized = () {
// ignore: discarded_futures
logout();
};
return const AuthState();
}
/// Restore session on app start.
Future<void> bootstrap() async {
final token = await ref.read(tokenStorageProvider).readToken();
if (token == null || token.isEmpty) {
state = state.copyWith(status: AuthStatus.unauthenticated);
return;
}
try {
final user = await _repo.me();
state = AuthState(status: AuthStatus.authenticated, user: user);
} catch (_) {
await ref.read(tokenStorageProvider).clear();
state = state.copyWith(status: AuthStatus.unauthenticated, clearUser: true);
}
}
Future<String?> sendOtp(String phone) => _repo.sendOtp(phone);
/// Verifies OTP, persists the session and loads the user.
Future<void> verifyOtp({
required String phone,
required String otp,
required String role,
String? name,
}) async {
final result = await _repo.verifyOtp(
phone: phone,
otp: otp,
role: role,
name: name,
);
await ref.read(tokenStorageProvider).save(
token: result.accessToken,
refreshToken: result.refreshToken,
role: result.role,
userId: result.userId,
);
final user = await _repo.me();
state = AuthState(status: AuthStatus.authenticated, user: user);
}
void setUser(AppUser user) {
state = state.copyWith(user: user);
}
Future<void> logout() async {
final refresh = await ref.read(tokenStorageProvider).readRefresh();
if (refresh != null && refresh.isNotEmpty) {
await _repo.logout(refresh); // best-effort server revoke
}
await ref.read(tokenStorageProvider).clear();
state = const AuthState(status: AuthStatus.unauthenticated);
}
}
|