guru_sdk/guru_app/lib/account/account_manager.dart

162 lines
5.8 KiB
Dart

import 'dart:async';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:guru_app/account/account_data_store.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/firebase/firebase.dart';
import 'package:guru_app/firebase/firestore/firestore_manager.dart';
import 'package:guru_app/property/app_property.dart';
import 'package:guru_app/property/settings/guru_settings.dart';
import 'package:guru_utils/collection/collectionutils.dart';
import 'package:guru_utils/core/ext.dart';
import 'package:guru_utils/datetime/datetime_utils.dart';
import 'package:guru_utils/device/device_info.dart';
import 'package:guru_utils/device/device_utils.dart';
import 'package:guru_utils/log/log.dart';
import 'package:guru_utils/network/network_utils.dart';
/// Created by Haoyi on 6/3/21
///
///
part "account_service_extension.dart";
part "account_auth_extension.dart";
class ModifyNicknameException implements Exception {
final String? message;
final dynamic cause;
ModifyNicknameException(this.message, {this.cause});
@override
String toString() {
return "ModifyNicknameException: $message cause:$cause";
}
}
class ModifyLevelException implements Exception {
final String? message;
final dynamic cause;
ModifyLevelException(this.message, {this.cause});
@override
String toString() {
return "ModifyLevelException: $message cause:$cause";
}
}
class AccountManager {
final AccountDataStore accountDataStore;
// final FirestoreService firestoreService;
Timer? retryTimer;
static AccountManager instance = AccountManager();
AccountManager() : accountDataStore = AccountDataStore.instance;
Future init({Completer<dynamic>? completer}) async {
try {
final result = accountDataStore.transitionTo(AccountDataStatus.initializing);
if (!result) {
Log.w(
"init account error, current initializing! please wait result! retry[${accountDataStore.initRetryCount}]",
tag: "Account");
return;
}
retryTimer?.cancel();
final account = await AppProperty.getInstance().loadAccount();
final restoreResult = await _restoreAccount(account);
if (!restoreResult) {
Log.v("init account error: restoreAccount error! retry[${accountDataStore.initRetryCount}]",
tag: "Account");
_retry();
} else {
accountDataStore.initRetryCount = 0;
accountDataStore.transitionTo(AccountDataStatus.initialized);
Log.v("init account success!", tag: "Account");
}
} catch (error, stacktrace) {
completer?.complete(error);
Log.v("init account error retry[${accountDataStore.initRetryCount}]:$error $stacktrace",
tag: "Account");
_retry();
}
completer?.complete(true);
}
void _retry() {
final intervalSeconds = (accountDataStore.initRetryCount * 2 + 8).clamp(8, 30);
retryTimer?.cancel();
accountDataStore.transitionTo(AccountDataStatus.waiting);
retryTimer = Timer(Duration(seconds: intervalSeconds), () {
init();
accountDataStore.initRetryCount++;
});
}
Future updateLocalProfile(Map<String, dynamic> modifiedJson) async {
modifiedJson[AccountProfile.dirtyField] = true;
final dirtyAccountProfile = accountDataStore.accountProfile?.merge(modifiedJson) ??
AccountProfile.fromJson(modifiedJson);
AppProperty.getInstance().setAccountProfile(dirtyAccountProfile);
accountDataStore.updateAccountProfile(dirtyAccountProfile);
}
Future<bool> modifyProfile(
{String? nickname,
String? avatar,
String? countryCode,
Map<String, dynamic> userData = const <String, dynamic>{}}) async {
int retryCount = 2;
Log.i("modifyProfile $nickname $avatar $countryCode", syncFirebase: true, tag: "Account");
if (nickname == null && avatar == null && countryCode == null && userData.isEmpty) {
return false;
}
final now = DateTimeUtils.currentTimeInMillis();
final modifiedJson = CollectionUtils.filterOutNulls(<String, dynamic>{
AccountProfile.uidField: accountDataStore.uid,
AccountProfile.nicknameField: nickname,
AccountProfile.countryField: countryCode?.toLowerCase(),
AccountProfile.avatarField: avatar,
AccountProfile.updateAtField: now,
AccountProfile.versionField: GuruSettings.instance.version.get(),
AccountProfile.roleField:
GuruSettings.instance.debugMode.get() == true ? UserAttr.tester : UserAttr.real,
AccountProfile.dirtyField: true,
...userData
});
await updateLocalProfile(modifiedJson);
while ((await NetworkUtils.isNetworkConnected()) && retryCount-- > 0) {
final accountProfile =
await FirestoreManager.instance.modifyProfile(modifiedJson).onError((error, stackTrace) {
Log.i("modifyProfile error!:$error");
GuruAnalytics.instance.logException(ModifyLevelException("modifyProfile error!:$error"),
stacktrace: stackTrace);
return null;
});
if (accountProfile != null) {
Log.i("modifyProfile success! $accountProfile", tag: "Account");
AppProperty.getInstance().setAccountProfile(accountProfile);
accountDataStore.updateAccountProfile(accountProfile);
return true;
} else {
Log.i("[$retryCount] modify profile error!", tag: "Account");
await authenticate().timeout(const Duration(seconds: 15)).catchError((error, stackTrace) {
Log.i("re-authenticate error:$error", stackTrace: stackTrace, tag: "Account");
});
await Future.delayed(const Duration(seconds: 1));
}
}
return false;
}
}