Files
retail/lib/features/auth/presentation/providers/auth_provider.dart
2025-10-10 22:49:05 +07:00

254 lines
6.4 KiB
Dart

import 'package:riverpod_annotation/riverpod_annotation.dart';
import '../../../../core/providers/providers.dart';
import '../../data/datasources/auth_remote_datasource.dart';
import '../../data/repositories/auth_repository_impl.dart';
import '../../domain/entities/user.dart';
import '../../domain/repositories/auth_repository.dart';
part 'auth_provider.g.dart';
/// Provider for AuthRemoteDataSource
@Riverpod(keepAlive: true)
AuthRemoteDataSource authRemoteDataSource(Ref ref) {
final dioClient = ref.watch(dioClientProvider);
return AuthRemoteDataSourceImpl(dioClient: dioClient);
}
/// Provider for AuthRepository
@Riverpod(keepAlive: true)
AuthRepository authRepository(Ref ref) {
final remoteDataSource = ref.watch(authRemoteDataSourceProvider);
final secureStorage = ref.watch(secureStorageProvider);
final dioClient = ref.watch(dioClientProvider);
return AuthRepositoryImpl(
remoteDataSource: remoteDataSource,
secureStorage: secureStorage,
dioClient: dioClient,
);
}
/// Auth state class
class AuthState {
final User? user;
final bool isAuthenticated;
final bool isLoading;
final String? errorMessage;
const AuthState({
this.user,
this.isAuthenticated = false,
this.isLoading = false,
this.errorMessage,
});
AuthState copyWith({
User? user,
bool? isAuthenticated,
bool? isLoading,
String? errorMessage,
bool clearUser = false,
bool clearError = false,
}) {
return AuthState(
user: clearUser ? null : (user ?? this.user),
isAuthenticated: isAuthenticated ?? this.isAuthenticated,
isLoading: isLoading ?? this.isLoading,
errorMessage: clearError ? null : (errorMessage ?? this.errorMessage),
);
}
}
/// Auth state notifier provider
@Riverpod(keepAlive: true)
class Auth extends _$Auth {
@override
AuthState build() {
// Don't call async operations in build
// Use a separate method to initialize auth state
return const AuthState();
}
AuthRepository get _repository => ref.read(authRepositoryProvider);
/// Initialize auth state - call this on app start
Future<void> initialize() async {
print('🚀 Initializing auth state...');
state = state.copyWith(isLoading: true);
final isAuthenticated = await _repository.isAuthenticated();
print('🚀 isAuthenticated result: $isAuthenticated');
if (isAuthenticated) {
print('🚀 Token found, fetching user profile...');
// Get user profile
final result = await _repository.getProfile();
result.fold(
(failure) {
print('❌ Failed to get profile: ${failure.message}');
state = const AuthState(
isAuthenticated: false,
isLoading: false,
);
},
(user) {
print('✅ Profile loaded: ${user.name}');
state = AuthState(
user: user,
isAuthenticated: true,
isLoading: false,
);
print('✅ Initialize complete: isAuthenticated=${state.isAuthenticated}');
},
);
} else {
print('❌ No token found, user needs to login');
state = const AuthState(
isAuthenticated: false,
isLoading: false,
);
}
}
/// Login user
Future<bool> login({
required String email,
required String password,
bool rememberMe = false,
}) async {
state = state.copyWith(isLoading: true, clearError: true);
final result = await _repository.login(
email: email,
password: password,
rememberMe: rememberMe,
);
return result.fold(
(failure) {
print('❌ Login FAILED: ${failure.message}');
state = state.copyWith(
isAuthenticated: false,
isLoading: false,
errorMessage: failure.message,
);
return false;
},
(authResponse) {
print('✅ Login SUCCESS: user=${authResponse.user.name}, token length=${authResponse.accessToken.length}');
state = AuthState(
user: authResponse.user,
isAuthenticated: true,
isLoading: false,
errorMessage: null,
);
print('✅ State updated: isAuthenticated=${state.isAuthenticated}');
return true;
},
);
}
/// Register new user
Future<bool> register({
required String name,
required String email,
required String password,
List<String> roles = const ['user'],
}) async {
state = state.copyWith(isLoading: true, clearError: true);
final result = await _repository.register(
name: name,
email: email,
password: password,
roles: roles,
);
return result.fold(
(failure) {
state = state.copyWith(
isAuthenticated: false,
isLoading: false,
errorMessage: failure.message,
);
return false;
},
(authResponse) {
state = AuthState(
user: authResponse.user,
isAuthenticated: true,
isLoading: false,
errorMessage: null,
);
return true;
},
);
}
/// Get user profile (refresh user data)
Future<void> getProfile() async {
state = state.copyWith(isLoading: true, clearError: true);
final result = await _repository.getProfile();
result.fold(
(failure) {
state = state.copyWith(
isLoading: false,
errorMessage: failure.message,
);
},
(user) {
state = state.copyWith(
user: user,
isAuthenticated: true,
isLoading: false,
);
},
);
}
/// Refresh access token
Future<bool> refreshToken() async {
final result = await _repository.refreshToken();
return result.fold(
(failure) {
// If token refresh fails, logout user
logout();
return false;
},
(authResponse) {
state = state.copyWith(user: authResponse.user);
return true;
},
);
}
/// Logout user
Future<void> logout() async {
state = state.copyWith(isLoading: true);
await _repository.logout();
state = const AuthState(
isAuthenticated: false,
isLoading: false,
);
}
}
/// Current authenticated user provider
@riverpod
User? currentUser(Ref ref) {
final authState = ref.watch(authProvider);
return authState.user;
}
/// Is authenticated provider
@riverpod
bool isAuthenticated(Ref ref) {
final authState = ref.watch(authProvider);
return authState.isAuthenticated;
}