188 lines
		
	
	
		
			6.1 KiB
		
	
	
	
		
			Dart
		
	
	
			
		
		
	
	
			188 lines
		
	
	
		
			6.1 KiB
		
	
	
	
		
			Dart
		
	
	
| import 'dart:convert';
 | |
| import 'package:firebase_auth/firebase_auth.dart';
 | |
| import 'package:guru_app/account/model/account.dart';
 | |
| import 'package:guru_app/account/model/account_profile.dart';
 | |
| import 'package:guru_app/account/model/user.dart';
 | |
| import 'package:guru_app/analytics/guru_analytics.dart';
 | |
| import 'package:guru_app/api/guru_api.dart';
 | |
| import 'package:guru_app/guru_app.dart';
 | |
| import 'package:guru_app/property/app_property.dart';
 | |
| import 'package:guru_utils/auth/auth_credential_manager.dart';
 | |
| import 'package:guru_utils/device/device_info.dart';
 | |
| import 'package:guru_utils/extensions/extensions.dart';
 | |
| 
 | |
| import 'package:http/http.dart' as http;
 | |
| 
 | |
| /// Created by Haoyi on 6/3/21
 | |
| ///
 | |
| enum AccountDataStatus { idle, initializing, initialized, waiting, error }
 | |
| 
 | |
| class AccountDataStore {
 | |
|   static final AccountDataStore instance = AccountDataStore._();
 | |
| 
 | |
|   final BehaviorSubject<DeviceInfo?> _deviceInfoSubject = BehaviorSubject.seeded(null);
 | |
|   final BehaviorSubject<GuruUser?> _guruUserSubject = BehaviorSubject.seeded(null);
 | |
|   final BehaviorSubject<User?> _firebaseUser = BehaviorSubject.seeded(null);
 | |
|   final BehaviorSubject<AccountProfile?> _accountProfile = BehaviorSubject.seeded(null);
 | |
|   final BehaviorSubject<AccountDataStatus> _accountDataStatus =
 | |
|       BehaviorSubject<AccountDataStatus>.seeded(AccountDataStatus.idle);
 | |
| 
 | |
|   final BehaviorSubject<Map<AuthType, Credential>> _credentials =
 | |
|       BehaviorSubject.seeded(<AuthType, Credential>{});
 | |
| 
 | |
|   int initRetryCount = 0;
 | |
| 
 | |
|   Stream<AccountProfile?> get observableAccountProfile => _accountProfile.stream;
 | |
| 
 | |
|   // final EnvConfig envConfig;
 | |
| 
 | |
|   AccountDataStore._();
 | |
| 
 | |
|   @Deprecated("use guruToken instead")
 | |
|   String? get saasToken => _guruUserSubject.value?.token;
 | |
| 
 | |
|   String? get guruToken => _guruUserSubject.value?.token;
 | |
| 
 | |
|   String? get uid => _guruUserSubject.value?.uid;
 | |
| 
 | |
|   AccountProfile? get accountProfile => _accountProfile.value;
 | |
| 
 | |
|   String? get nickname => _accountProfile.value?.nickname;
 | |
| 
 | |
|   String? get countryCode => _accountProfile.value?.countryCode;
 | |
| 
 | |
|   GuruUser? get user => _guruUserSubject.value;
 | |
| 
 | |
|   String? get avatar => _accountProfile.value?.avatar;
 | |
| 
 | |
|   DeviceInfo? get currentDevice => _deviceInfoSubject.value;
 | |
| 
 | |
|   AccountDataStatus get accountDataStatus => _accountDataStatus.value;
 | |
| 
 | |
|   bool get initialized => _accountDataStatus.value == AccountDataStatus.initialized;
 | |
| 
 | |
|   Stream<bool> get observableInitialized =>
 | |
|       _accountDataStatus.stream.map((status) => status == AccountDataStatus.initialized);
 | |
| 
 | |
|   bool get hasUid => uid?.isNotEmpty == true;
 | |
| 
 | |
|   bool get isAnonymous =>
 | |
|       (uid?.isNotEmpty != true) ||
 | |
|       (_credentials.value.containsKey(AuthType.anonymous) && _credentials.value.length == 1);
 | |
| 
 | |
|   Stream<GuruUser?> get observableSaasUser => _guruUserSubject.stream;
 | |
| 
 | |
|   Map<AuthType, Credential> get credentials => _credentials.value;
 | |
| 
 | |
|   Account get account => Account.restore(
 | |
|       guruUser: user,
 | |
|       device: currentDevice,
 | |
|       accountProfile: accountProfile,
 | |
|       firebaseUser: _firebaseUser.value,
 | |
|       credentials: credentials);
 | |
| 
 | |
|   Stream<Account> get observableAccount => Rx.combineLatestList([
 | |
|         _guruUserSubject.stream,
 | |
|         _deviceInfoSubject.stream,
 | |
|         _accountProfile.stream,
 | |
|         _firebaseUser.stream,
 | |
|         _credentials.stream
 | |
|       ]).debounceTime(const Duration(milliseconds: 100)).map((_) => account);
 | |
| 
 | |
|   bool get isSocialLogged => (uid?.isNotEmpty == true) && credentials.isNotEmpty;
 | |
| 
 | |
|   void dispose() {
 | |
|     _deviceInfoSubject.close();
 | |
|     _guruUserSubject.close();
 | |
|     _firebaseUser.close();
 | |
|     _accountProfile.close();
 | |
|     _credentials.close();
 | |
|   }
 | |
| 
 | |
|   Future<GuruUser?> signInAnonymousInLocked() async {
 | |
|     // 这里需要使用原始的http post请求。否则这里将会死锁DIO所有请求
 | |
|     final secret = await AppProperty.getInstance().getAnonymousSecretKey();
 | |
|     final headers = {
 | |
|       "X-APP-ID": GuruApp.instance.details.saasAppId,
 | |
|       "content-type": "application/json"
 | |
|     };
 | |
|     try {
 | |
|       final uri = Uri.parse("${GuruApi.saasApiHost}/auth/api/v1/tokens/provider/secret");
 | |
|       final response = await http
 | |
|           .post(uri,
 | |
|               headers: headers,
 | |
|               body: jsonEncode(AnonymousLoginReqBody(secret: secret)),
 | |
|               encoding: utf8)
 | |
|           .timeout(const Duration(seconds: 30));
 | |
|       final data = const Utf8Decoder().convert(response.bodyBytes);
 | |
|       if (data.isNotEmpty) {
 | |
|         final result = json.decode(data);
 | |
|         return GuruUser.fromJson(result["data"]);
 | |
|       }
 | |
|     } catch (error, stacktrace) {
 | |
|       Log.v("signInAnonymousInLocked error:$error", tag: "Account");
 | |
|     }
 | |
|     return null;
 | |
|   }
 | |
| 
 | |
|   Future refreshAuth() async {
 | |
|     final guruUser = await signInAnonymousInLocked();
 | |
|     if (guruUser != null) {
 | |
|       updateGuruUser(guruUser);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   void updateDeviceInfo(DeviceInfo deviceInfo) {
 | |
|     _deviceInfoSubject.addEx(deviceInfo);
 | |
|   }
 | |
| 
 | |
|   @Deprecated("use updateGuruUser instead")
 | |
|   void updateSaasUser(GuruUser saasUser) {
 | |
|     updateGuruUser(saasUser);
 | |
|   }
 | |
| 
 | |
|   void updateGuruUser(GuruUser guruUser) {
 | |
|     _guruUserSubject.addEx(guruUser);
 | |
|     if (guruUser.createAtTimestamp > 0) {
 | |
|       GuruAnalytics.instance
 | |
|           .setUserProperty("user_created_timestamp", guruUser.createAtTimestamp.toString());
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   void updateFirebaseUser(User user) {
 | |
|     _firebaseUser.addEx(user);
 | |
|   }
 | |
| 
 | |
|   void updateAccountProfile(AccountProfile profile) {
 | |
|     _accountProfile.addEx(profile);
 | |
|   }
 | |
| 
 | |
|   void bindCredential(Credential credential) {
 | |
|     final newCredentials = Map.of(_credentials.value);
 | |
|     newCredentials[credential.authType] = credential;
 | |
|     _credentials.addEx(newCredentials);
 | |
|   }
 | |
| 
 | |
|   void unbindCredential(AuthType authType) {
 | |
|     final newCredentials = Map.of(_credentials.value);
 | |
|     newCredentials.remove(authType);
 | |
|     _credentials.addEx(newCredentials);
 | |
|   }
 | |
| 
 | |
|   void updateCredentials(Map<AuthType, Credential> credentials) {
 | |
|     _credentials.addEx(Map.of(credentials));
 | |
|   }
 | |
| 
 | |
|   bool transitionTo(AccountDataStatus status, {AccountDataStatus? expectStatus}) {
 | |
|     return _accountDataStatus.addIfChanged(status);
 | |
|   }
 | |
| 
 | |
|   logout() {
 | |
|     _guruUserSubject.addEx(null);
 | |
|     _firebaseUser.addEx(null);
 | |
|     _deviceInfoSubject.addEx(null);
 | |
|     _accountProfile.addEx(null);
 | |
|     _accountDataStatus.addIfChanged(AccountDataStatus.idle);
 | |
|   }
 | |
| }
 |