234 lines
7.1 KiB
Dart
234 lines
7.1 KiB
Dart
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? 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.accessToken,
|
|
this.refreshToken,
|
|
this.isLoggedIn = false,
|
|
this.isLoading = false,
|
|
this.error,
|
|
});
|
|
|
|
UserState copyWith({
|
|
String? accountSequence,
|
|
String? nickname,
|
|
String? phone,
|
|
String? kycStatus,
|
|
String? source,
|
|
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,
|
|
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,
|
|
);
|
|
}
|
|
}
|
|
|
|
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,
|
|
);
|
|
} 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,
|
|
);
|
|
} 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) return;
|
|
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) {
|
|
await logout();
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
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;
|
|
});
|