2023-12-21 09:14:40 +00:00
|
|
|
import 'dart:convert';
|
|
|
|
|
import 'package:firebase_auth/firebase_auth.dart';
|
2024-03-07 03:46:50 +00:00
|
|
|
import 'package:guru_app/account/model/account.dart';
|
2023-12-21 09:14:40 +00:00
|
|
|
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';
|
2024-03-07 03:46:50 +00:00
|
|
|
import 'package:guru_utils/auth/auth_credential_manager.dart';
|
2023-12-21 09:14:40 +00:00
|
|
|
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);
|
2024-03-07 03:46:50 +00:00
|
|
|
final BehaviorSubject<GuruUser?> _guruUserSubject = BehaviorSubject.seeded(null);
|
2023-12-21 09:14:40 +00:00
|
|
|
final BehaviorSubject<User?> _firebaseUser = BehaviorSubject.seeded(null);
|
|
|
|
|
final BehaviorSubject<AccountProfile?> _accountProfile = BehaviorSubject.seeded(null);
|
|
|
|
|
final BehaviorSubject<AccountDataStatus> _accountDataStatus =
|
|
|
|
|
BehaviorSubject<AccountDataStatus>.seeded(AccountDataStatus.idle);
|
2024-03-07 03:46:50 +00:00
|
|
|
|
|
|
|
|
final BehaviorSubject<Map<AuthType, Credential>> _credentials =
|
|
|
|
|
BehaviorSubject.seeded(<AuthType, Credential>{});
|
|
|
|
|
|
2023-12-21 09:14:40 +00:00
|
|
|
int initRetryCount = 0;
|
|
|
|
|
|
|
|
|
|
Stream<AccountProfile?> get observableAccountProfile => _accountProfile.stream;
|
|
|
|
|
|
|
|
|
|
// final EnvConfig envConfig;
|
|
|
|
|
|
|
|
|
|
AccountDataStore._();
|
|
|
|
|
|
2024-03-07 03:46:50 +00:00
|
|
|
@Deprecated("use guruToken instead")
|
|
|
|
|
String? get saasToken => _guruUserSubject.value?.token;
|
|
|
|
|
|
|
|
|
|
String? get guruToken => _guruUserSubject.value?.token;
|
2023-12-21 09:14:40 +00:00
|
|
|
|
2024-03-07 03:46:50 +00:00
|
|
|
String? get uid => _guruUserSubject.value?.uid;
|
2023-12-21 09:14:40 +00:00
|
|
|
|
|
|
|
|
AccountProfile? get accountProfile => _accountProfile.value;
|
|
|
|
|
|
|
|
|
|
String? get nickname => _accountProfile.value?.nickname;
|
|
|
|
|
|
|
|
|
|
String? get countryCode => _accountProfile.value?.countryCode;
|
|
|
|
|
|
2024-03-07 03:46:50 +00:00
|
|
|
GuruUser? get user => _guruUserSubject.value;
|
2023-12-21 09:14:40 +00:00
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
|
2024-03-07 03:46:50 +00:00
|
|
|
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;
|
2023-12-21 09:14:40 +00:00
|
|
|
|
|
|
|
|
void dispose() {
|
|
|
|
|
_deviceInfoSubject.close();
|
2024-03-07 03:46:50 +00:00
|
|
|
_guruUserSubject.close();
|
2023-12-21 09:14:40 +00:00
|
|
|
_firebaseUser.close();
|
|
|
|
|
_accountProfile.close();
|
2024-03-07 03:46:50 +00:00
|
|
|
_credentials.close();
|
2023-12-21 09:14:40 +00:00
|
|
|
}
|
|
|
|
|
|
2024-03-07 03:46:50 +00:00
|
|
|
Future<GuruUser?> signInAnonymousInLocked() async {
|
2023-12-21 09:14:40 +00:00
|
|
|
// 这里需要使用原始的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);
|
2024-03-07 03:46:50 +00:00
|
|
|
return GuruUser.fromJson(result["data"]);
|
2023-12-21 09:14:40 +00:00
|
|
|
}
|
|
|
|
|
} catch (error, stacktrace) {
|
|
|
|
|
Log.v("signInAnonymousInLocked error:$error", tag: "Account");
|
|
|
|
|
}
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Future refreshAuth() async {
|
2024-03-07 03:46:50 +00:00
|
|
|
final guruUser = await signInAnonymousInLocked();
|
|
|
|
|
if (guruUser != null) {
|
|
|
|
|
updateGuruUser(guruUser);
|
2023-12-21 09:14:40 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void updateDeviceInfo(DeviceInfo deviceInfo) {
|
|
|
|
|
_deviceInfoSubject.addEx(deviceInfo);
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-07 03:46:50 +00:00
|
|
|
@Deprecated("use updateGuruUser instead")
|
|
|
|
|
void updateSaasUser(GuruUser saasUser) {
|
|
|
|
|
updateGuruUser(saasUser);
|
|
|
|
|
}
|
2023-12-21 09:14:40 +00:00
|
|
|
|
2024-03-07 03:46:50 +00:00
|
|
|
void updateGuruUser(GuruUser guruUser) {
|
|
|
|
|
_guruUserSubject.addEx(guruUser);
|
|
|
|
|
if (guruUser.createAtTimestamp > 0) {
|
2023-12-21 09:14:40 +00:00
|
|
|
GuruAnalytics.instance
|
2024-03-07 03:46:50 +00:00
|
|
|
.setUserProperty("user_created_timestamp", guruUser.createAtTimestamp.toString());
|
2023-12-21 09:14:40 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void updateFirebaseUser(User user) {
|
|
|
|
|
_firebaseUser.addEx(user);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void updateAccountProfile(AccountProfile profile) {
|
|
|
|
|
_accountProfile.addEx(profile);
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-07 03:46:50 +00:00
|
|
|
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));
|
|
|
|
|
}
|
|
|
|
|
|
2023-12-21 09:14:40 +00:00
|
|
|
bool transitionTo(AccountDataStatus status, {AccountDataStatus? expectStatus}) {
|
|
|
|
|
return _accountDataStatus.addIfChanged(status);
|
|
|
|
|
}
|
2024-03-07 03:46:50 +00:00
|
|
|
|
|
|
|
|
logout() {
|
|
|
|
|
_guruUserSubject.addEx(null);
|
|
|
|
|
_firebaseUser.addEx(null);
|
|
|
|
|
_deviceInfoSubject.addEx(null);
|
|
|
|
|
_accountProfile.addEx(null);
|
|
|
|
|
_accountDataStatus.addIfChanged(AccountDataStatus.idle);
|
|
|
|
|
}
|
2023-12-21 09:14:40 +00:00
|
|
|
}
|