import 'dart:convert'; import 'package:firebase_auth/firebase_auth.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/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 _deviceInfoSubject = BehaviorSubject.seeded(null); final BehaviorSubject _saasUserSubject = BehaviorSubject.seeded(null); final BehaviorSubject _firebaseUser = BehaviorSubject.seeded(null); final BehaviorSubject _accountProfile = BehaviorSubject.seeded(null); final BehaviorSubject _accountDataStatus = BehaviorSubject.seeded(AccountDataStatus.idle); int initRetryCount = 0; Stream get observableAccountProfile => _accountProfile.stream; // final EnvConfig envConfig; AccountDataStore._(); String? get saasToken => _saasUserSubject.value?.token; String? get uid => _saasUserSubject.value?.uid; AccountProfile? get accountProfile => _accountProfile.value; String? get nickname => _accountProfile.value?.nickname; String? get countryCode => _accountProfile.value?.countryCode; SaasUser? get user => _saasUserSubject.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 get observableInitialized => _accountDataStatus.stream.map((status) => status == AccountDataStatus.initialized); Stream get observableSaasUser => _saasUserSubject.stream; void dispose() { _deviceInfoSubject.close(); _saasUserSubject.close(); _firebaseUser.close(); _accountProfile.close(); } Future 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 SaasUser.fromJson(result["data"]); } } catch (error, stacktrace) { Log.v("signInAnonymousInLocked error:$error", tag: "Account"); } return null; } Future refreshAuth() async { final saasUser = await signInAnonymousInLocked(); if (saasUser != null) { updateSaasUser(saasUser); } } void updateDeviceInfo(DeviceInfo deviceInfo) { _deviceInfoSubject.addEx(deviceInfo); } void updateSaasUser(SaasUser saasUser) { _saasUserSubject.addEx(saasUser); if (saasUser.createAtTimestamp > 0) { GuruAnalytics.instance .setUserProperty("user_created_timestamp", saasUser.createAtTimestamp.toString()); } } void updateFirebaseUser(User user) { _firebaseUser.addEx(user); } void updateAccountProfile(AccountProfile profile) { _accountProfile.addEx(profile); } bool transitionTo(AccountDataStatus status, {AccountDataStatus? expectStatus}) { return _accountDataStatus.addIfChanged(status); } }