feat: берсерк. Регенерация profile если сервер его не отдал
This commit is contained in:
@@ -2,6 +2,7 @@ import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:typed_data';
|
||||
import '../api.dart';
|
||||
import '../../core/config/debug_test.dart';
|
||||
import '../../core/config/komet_settings.dart';
|
||||
import '../../core/protocol/chat_cache_fingerprint.dart';
|
||||
import '../../core/protocol/opcode_map.dart';
|
||||
@@ -41,6 +42,7 @@ class AccountModule {
|
||||
late final ProfileModule _profile = ProfileModule(_api);
|
||||
late final TwoFactorModule _twoFactor = TwoFactorModule(_api, _profile);
|
||||
final _loginStatusController = StreamController<LoginStatus>.broadcast();
|
||||
final _noticeController = StreamController<AccountNotice>.broadcast();
|
||||
bool _loggedIn = false;
|
||||
|
||||
AccountModule(this._api) {
|
||||
@@ -51,6 +53,8 @@ class AccountModule {
|
||||
|
||||
Stream<LoginStatus> get loginStatusStream => _loginStatusController.stream;
|
||||
|
||||
Stream<AccountNotice> get noticeStream => _noticeController.stream;
|
||||
|
||||
/// `true`, только когда сервер считает сессию ONLINE — после успешного
|
||||
/// login (opcode 19), а не просто после хэндшейка (opcode 6).
|
||||
bool get isLoggedIn => _loggedIn;
|
||||
@@ -565,22 +569,16 @@ class AccountModule {
|
||||
|
||||
ProfileData profile;
|
||||
final profileMap = data['profile'];
|
||||
if (profileMap is Map) {
|
||||
final contact = profileMap['contact'];
|
||||
if (contact is! Map) {
|
||||
throw Exception('login: отсутствует profile.contact в ответе');
|
||||
}
|
||||
if (!DebugTest.berserk &&
|
||||
profileMap is Map &&
|
||||
profileMap['contact'] is Map) {
|
||||
profile = ProfileData.fromServerProfile(
|
||||
profileMap.cast<dynamic, dynamic>(),
|
||||
);
|
||||
await AppDatabase.saveProfile(profile, isActive: true);
|
||||
} else {
|
||||
final cachedProfile = await AppDatabase.loadProfile(accountId);
|
||||
if (cachedProfile == null) {
|
||||
throw Exception('login: отсутствует profile в ответе');
|
||||
}
|
||||
profile = cachedProfile;
|
||||
profile = await _resurrectProfile(accountId);
|
||||
}
|
||||
await AppDatabase.saveProfile(profile, isActive: true);
|
||||
await AppDatabase.setActiveAccount(profile.id);
|
||||
|
||||
await _saveSyncState(data, serverTime, profile.id);
|
||||
@@ -625,6 +623,31 @@ class AccountModule {
|
||||
);
|
||||
}
|
||||
|
||||
Future<ProfileData> _resurrectProfile(int accountId) async {
|
||||
if (DebugTest.berserk) {
|
||||
await AppDatabase.deleteAccount(accountId);
|
||||
logger.w('login: [BERSERK] профиль удалён из БД, форсирую регенерацию (id=$accountId)');
|
||||
} else {
|
||||
final cached = await AppDatabase.loadProfile(accountId);
|
||||
if (cached != null) return cached;
|
||||
}
|
||||
|
||||
_noticeController.add(AccountNotice.resurrectingProfile);
|
||||
|
||||
try {
|
||||
final fetched = await ContactsModule.fetchSelfProfile(_api, accountId);
|
||||
if (fetched != null) {
|
||||
logger.i('login: профиль восстановлен через CONTACT_INFO (id=$accountId)');
|
||||
return fetched;
|
||||
}
|
||||
} catch (e) {
|
||||
logger.w('login: восстановление профиля через CONTACT_INFO не удалось: $e');
|
||||
}
|
||||
|
||||
logger.w('login: профиль недоступен, использую заглушку (id=$accountId)');
|
||||
return ProfileData.stub(accountId);
|
||||
}
|
||||
|
||||
Future<void> _saveSyncState(
|
||||
Map<dynamic, dynamic> data,
|
||||
int serverTime,
|
||||
|
||||
@@ -282,6 +282,8 @@ enum AuthRequestType {
|
||||
|
||||
enum LoginStatus { idle, loading, success, error }
|
||||
|
||||
enum AccountNotice { resurrectingProfile }
|
||||
|
||||
class WrongDeviceTokenException implements Exception {
|
||||
const WrongDeviceTokenException();
|
||||
@override
|
||||
|
||||
@@ -191,6 +191,21 @@ class ContactsModule {
|
||||
await syncFromLoginPayload(map.cast<dynamic, dynamic>(), accountId);
|
||||
}
|
||||
|
||||
static Future<ProfileData?> fetchSelfProfile(Api api, int accountId) async {
|
||||
final map = await api.sendRequestMap(Opcode.contactInfo, {
|
||||
'contactIds': [accountId],
|
||||
});
|
||||
final contacts = map?['contacts'];
|
||||
if (contacts is! List) return null;
|
||||
for (final raw in contacts.whereType<Map>()) {
|
||||
if (raw['id'] != accountId) continue;
|
||||
final contact = raw.cast<dynamic, dynamic>();
|
||||
_primeContactCache(contact);
|
||||
return ProfileData.fromServerMap(contact);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
static void _primeContactCache(Map<dynamic, dynamic> contact) {
|
||||
final id = contact['id'];
|
||||
if (id is! int) return;
|
||||
|
||||
Reference in New Issue
Block a user