162 lines
5.8 KiB
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;
|
||
|
|
}
|
||
|
|
}
|