127 lines
3.4 KiB
Dart
127 lines
3.4 KiB
Dart
import 'dart:convert';
|
||
|
||
import 'package:autos/data/models/user_model.dart';
|
||
import 'package:autos/data/repositories/user_repository_impl.dart';
|
||
import 'package:autos/data/sources/remote/api_service.dart';
|
||
import 'package:autos/domain/entities/user.dart';
|
||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||
import 'package:flutter_riverpod/legacy.dart';
|
||
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
||
|
||
/// Provide a single ApiService instance across the app
|
||
final apiServiceProvider = Provider<ApiService>((ref) => ApiService());
|
||
|
||
/// Provide repository that depends on ApiService
|
||
final userRepositoryProvider = Provider<UserRepositoryImpl>(
|
||
(ref) => UserRepositoryImpl(ref.read(apiServiceProvider)),
|
||
);
|
||
|
||
/// SINGLE GLOBAL USER PROVIDER
|
||
final userProvider =
|
||
StateNotifierProvider<UserNotifier, AsyncValue<User?>>((ref) {
|
||
final repo = ref.read(userRepositoryProvider);
|
||
return UserNotifier(ref, repo);
|
||
});
|
||
|
||
enum AuthAction { idle, login, signup }
|
||
|
||
class UserNotifier extends StateNotifier<AsyncValue<User?>> {
|
||
final Ref ref;
|
||
final UserRepositoryImpl repository;
|
||
|
||
final _storage = const FlutterSecureStorage();
|
||
static const _userKey = 'logged_in_user';
|
||
|
||
AuthAction lastAction = AuthAction.idle;
|
||
|
||
UserNotifier(this.ref, this.repository)
|
||
: super(const AsyncValue.data(null));
|
||
|
||
// AUTO LOGIN ON APP START
|
||
Future<void> loadUserFromStorage() async {
|
||
final jsonString = await _storage.read(key: _userKey);
|
||
|
||
if (jsonString == null) {
|
||
state = const AsyncValue.data(null);
|
||
return;
|
||
}
|
||
|
||
final jsonData = jsonDecode(jsonString);
|
||
final user = UserModel.fromJson(jsonData);
|
||
|
||
state = AsyncValue.data(user);
|
||
}
|
||
|
||
// LOGIN FLOW (FULLY SYNCHRONIZED)
|
||
Future<void> login(String email, String password) async {
|
||
lastAction = AuthAction.login;
|
||
|
||
try {
|
||
state = const AsyncValue.loading();
|
||
|
||
// 1️⃣ Login API (partial user)
|
||
final partialUser = await repository.login(email, password);
|
||
|
||
// 2️⃣ Fetch FULL user details
|
||
final fullUser = await repository.getUserDetails(partialUser.id);
|
||
|
||
// 3️⃣ Store FULL user in secure storage
|
||
await _storage.write(key: _userKey, value: fullUser.toRawJson());
|
||
|
||
// 4️⃣ Update provider ONCE with FULL data
|
||
state = AsyncValue.data(fullUser);
|
||
} catch (e, st) {
|
||
state = AsyncValue.error(e, st);
|
||
}
|
||
}
|
||
|
||
// SIGNUP
|
||
Future<void> signup(
|
||
String name,
|
||
String email,
|
||
String password,
|
||
String phone,
|
||
) async {
|
||
lastAction = AuthAction.signup;
|
||
|
||
try {
|
||
state = const AsyncValue.loading();
|
||
|
||
// ✅ 1️⃣ Signup → returns USER ENTITY
|
||
final user = await repository.signup(name, email, password, phone);
|
||
|
||
// ✅ 2️⃣ Convert User → UserModel for storage
|
||
final userModel = UserModel.fromEntity(user);
|
||
|
||
// ✅ 3️⃣ Store safely
|
||
await _storage.write(
|
||
key: _userKey,
|
||
value: userModel.toRawJson(),
|
||
);
|
||
|
||
// ✅ 4️⃣ Update provider state
|
||
state = AsyncValue.data(user);
|
||
} catch (e, st) {
|
||
state = AsyncValue.error(e, st);
|
||
}
|
||
}
|
||
|
||
|
||
// ✅ ✅ LOGOUT
|
||
Future<void> logout() async {
|
||
await _storage.delete(key: _userKey);
|
||
state = const AsyncValue.data(null);
|
||
}
|
||
|
||
// PASSWORD RESET
|
||
Future<void> sendPasswordResetLink(String email) async {
|
||
try {
|
||
state = const AsyncValue.loading();
|
||
await repository.sendPasswordResetLink(email);
|
||
state = const AsyncValue.data(null);
|
||
} catch (e, st) {
|
||
state = AsyncValue.error(e, st);
|
||
}
|
||
}
|
||
}
|