rwadurian/frontend/mining-app/lib/presentation/providers/user_providers.dart

281 lines
8.5 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:shared_preferences/shared_preferences.dart';
import '../../data/datasources/remote/auth_remote_datasource.dart';
import '../../core/di/injection.dart';
class UserState {
final String? accountSequence;
final String? nickname;
final String? phone;
final String? kycStatus;
final String? source;
final String? status;
final String? realName;
final DateTime? createdAt;
final DateTime? lastLoginAt;
final String? accessToken;
final String? refreshToken;
final bool isLoggedIn;
final bool isLoading;
final String? error;
UserState({
this.accountSequence,
this.nickname,
this.phone,
this.kycStatus,
this.source,
this.status,
this.realName,
this.createdAt,
this.lastLoginAt,
this.accessToken,
this.refreshToken,
this.isLoggedIn = false,
this.isLoading = false,
this.error,
});
bool get isKycVerified => kycStatus == 'VERIFIED';
UserState copyWith({
String? accountSequence,
String? nickname,
String? phone,
String? kycStatus,
String? source,
String? status,
String? realName,
DateTime? createdAt,
DateTime? lastLoginAt,
String? accessToken,
String? refreshToken,
bool? isLoggedIn,
bool? isLoading,
String? error,
}) {
return UserState(
accountSequence: accountSequence ?? this.accountSequence,
nickname: nickname ?? this.nickname,
phone: phone ?? this.phone,
kycStatus: kycStatus ?? this.kycStatus,
source: source ?? this.source,
status: status ?? this.status,
realName: realName ?? this.realName,
createdAt: createdAt ?? this.createdAt,
lastLoginAt: lastLoginAt ?? this.lastLoginAt,
accessToken: accessToken ?? this.accessToken,
refreshToken: refreshToken ?? this.refreshToken,
isLoggedIn: isLoggedIn ?? this.isLoggedIn,
isLoading: isLoading ?? this.isLoading,
error: error,
);
}
}
class UserNotifier extends StateNotifier<UserState> {
final AuthRemoteDataSource _authDataSource;
UserNotifier(this._authDataSource) : super(UserState()) {
_loadFromStorage();
}
Future<void> _loadFromStorage() async {
final prefs = await SharedPreferences.getInstance();
final accessToken = prefs.getString('access_token');
final refreshToken = prefs.getString('refresh_token');
final accountSequence = prefs.getString('account_sequence');
final phone = prefs.getString('phone');
if (accessToken != null && refreshToken != null && accountSequence != null) {
state = state.copyWith(
accessToken: accessToken,
refreshToken: refreshToken,
accountSequence: accountSequence,
phone: phone,
isLoggedIn: true,
);
// 登录后自动获取用户详情
await fetchProfile();
}
}
Future<void> _saveToStorage(AuthResult result) async {
final prefs = await SharedPreferences.getInstance();
await prefs.setString('access_token', result.accessToken);
await prefs.setString('refresh_token', result.refreshToken);
await prefs.setString('account_sequence', result.user.accountSequence);
await prefs.setString('phone', result.user.phone);
}
Future<void> _clearStorage() async {
final prefs = await SharedPreferences.getInstance();
await prefs.remove('access_token');
await prefs.remove('refresh_token');
await prefs.remove('account_sequence');
await prefs.remove('phone');
}
Future<void> sendSmsCode(String phone, String type) async {
state = state.copyWith(isLoading: true, error: null);
try {
await _authDataSource.sendSmsCode(phone, type);
state = state.copyWith(isLoading: false);
} catch (e) {
state = state.copyWith(isLoading: false, error: e.toString());
rethrow;
}
}
Future<void> register(String phone, String password, String smsCode) async {
state = state.copyWith(isLoading: true, error: null);
try {
final result = await _authDataSource.register(phone, password, smsCode);
await _saveToStorage(result);
state = state.copyWith(
accessToken: result.accessToken,
refreshToken: result.refreshToken,
accountSequence: result.user.accountSequence,
phone: result.user.phone,
kycStatus: result.user.kycStatus,
source: result.user.source,
isLoggedIn: true,
isLoading: false,
);
} catch (e) {
state = state.copyWith(isLoading: false, error: e.toString());
rethrow;
}
}
Future<void> loginWithPassword(String phone, String password) async {
state = state.copyWith(isLoading: true, error: null);
try {
final result = await _authDataSource.loginWithPassword(phone, password);
await _saveToStorage(result);
state = state.copyWith(
accessToken: result.accessToken,
refreshToken: result.refreshToken,
accountSequence: result.user.accountSequence,
phone: result.user.phone,
kycStatus: result.user.kycStatus,
source: result.user.source,
isLoggedIn: true,
isLoading: false,
);
// 登录后获取详细信息
await fetchProfile();
} catch (e) {
state = state.copyWith(isLoading: false, error: e.toString());
rethrow;
}
}
Future<void> loginWithSms(String phone, String smsCode) async {
state = state.copyWith(isLoading: true, error: null);
try {
final result = await _authDataSource.loginWithSms(phone, smsCode);
await _saveToStorage(result);
state = state.copyWith(
accessToken: result.accessToken,
refreshToken: result.refreshToken,
accountSequence: result.user.accountSequence,
phone: result.user.phone,
kycStatus: result.user.kycStatus,
source: result.user.source,
isLoggedIn: true,
isLoading: false,
);
// 登录后获取详细信息
await fetchProfile();
} catch (e) {
state = state.copyWith(isLoading: false, error: e.toString());
rethrow;
}
}
Future<void> logout() async {
if (state.refreshToken != null) {
try {
await _authDataSource.logout(state.refreshToken!);
} catch (_) {}
}
await _clearStorage();
state = UserState();
}
Future<void> refreshTokenIfNeeded() async {
if (state.refreshToken == null) {
throw Exception('No refresh token available');
}
try {
final newAccessToken = await _authDataSource.refreshToken(state.refreshToken!);
final prefs = await SharedPreferences.getInstance();
await prefs.setString('access_token', newAccessToken);
state = state.copyWith(accessToken: newAccessToken);
} catch (e) {
// 清除本地存储不调用远程logout API可能网络不可用
await _clearStorage();
state = UserState();
rethrow;
}
}
Future<void> resetPassword(String phone, String smsCode, String newPassword) async {
state = state.copyWith(isLoading: true, error: null);
try {
await _authDataSource.resetPassword(phone, smsCode, newPassword);
state = state.copyWith(isLoading: false);
} catch (e) {
state = state.copyWith(isLoading: false, error: e.toString());
rethrow;
}
}
Future<void> changePassword(String oldPassword, String newPassword) async {
state = state.copyWith(isLoading: true, error: null);
try {
await _authDataSource.changePassword(oldPassword, newPassword);
state = state.copyWith(isLoading: false);
} catch (e) {
state = state.copyWith(isLoading: false, error: e.toString());
rethrow;
}
}
/// 获取用户详细信息
Future<void> fetchProfile() async {
if (!state.isLoggedIn) return;
try {
final profile = await _authDataSource.getProfile();
state = state.copyWith(
phone: profile.phone,
kycStatus: profile.kycStatus,
source: profile.source,
status: profile.status,
realName: profile.realName,
createdAt: profile.createdAt,
lastLoginAt: profile.lastLoginAt,
);
} catch (e) {
// 静默失败,不影响用户体验
}
}
}
final userNotifierProvider = StateNotifierProvider<UserNotifier, UserState>(
(ref) => UserNotifier(getIt<AuthRemoteDataSource>()),
);
final currentAccountSequenceProvider = Provider<String?>((ref) {
return ref.watch(userNotifierProvider).accountSequence;
});
final isLoggedInProvider = Provider<bool>((ref) {
return ref.watch(userNotifierProvider).isLoggedIn;
});
final accessTokenProvider = Provider<String?>((ref) {
return ref.watch(userNotifierProvider).accessToken;
});