From 4e35e3d17676a42028fcfe9358756153f837c271 Mon Sep 17 00:00:00 2001 From: klockky Date: Sun, 5 Jul 2026 23:06:16 +0300 Subject: [PATCH] Fix password login profile handling --- lib/backend/modules/account.dart | 47 +++++++++++++++---- .../modules/account/account_models.dart | 3 +- .../modules/account/profile_module.dart | 6 +-- lib/core/storage/app_database.dart | 34 ++++++++++---- .../screens/auth/password_2fa_screen.dart | 5 +- pubspec.yaml | 2 +- 6 files changed, 73 insertions(+), 24 deletions(-) diff --git a/lib/backend/modules/account.dart b/lib/backend/modules/account.dart index 5112b4e..395e0c3 100644 --- a/lib/backend/modules/account.dart +++ b/lib/backend/modules/account.dart @@ -228,7 +228,9 @@ class AccountModule { throw Exception('completeRegistration: отсутствует id аккаунта'); } - final profile = ProfileData.fromServerMap(contact.cast()); + final profile = ProfileData.fromServerProfile( + profileMap.cast(), + ); await AppDatabase.saveProfile(profile, isActive: true); await TokenStorage.setActiveAccount(accountId); await SpoofingService.commitPendingSpoof(accountId); @@ -446,8 +448,25 @@ class AccountModule { throw Exception('checkPassword: отсутствует токен в ответе'); } + final accountId = extractAccountId(data); + if (accountId == null) { + throw Exception('checkPassword: отсутствует accountId в ответе'); + } + + final profileMap = data['profile']; + if (profileMap is Map) { + final profile = ProfileData.fromServerProfile( + profileMap.cast(), + ); + await AppDatabase.saveProfile(profile, isActive: true); + } + + await TokenStorage.saveToken(loginToken, accountId); + await TokenStorage.setActiveAccount(accountId); + await SpoofingService.commitPendingSpoof(accountId); + logger.i('2FA пройдена, получен login-токен'); - return TwoFactorResult(loginToken: loginToken); + return TwoFactorResult(loginToken: loginToken, accountId: accountId); } Map buildLoginPayload( @@ -500,16 +519,24 @@ class AccountModule { await TokenStorage.saveToken(updatedToken, accountId); } + ProfileData profile; final profileMap = data['profile']; - if (profileMap is! Map) { - throw Exception('login: отсутствует profile в ответе'); + if (profileMap is Map) { + final contact = profileMap['contact']; + if (contact is! Map) { + throw Exception('login: отсутствует profile.contact в ответе'); + } + profile = ProfileData.fromServerProfile( + profileMap.cast(), + ); + await AppDatabase.saveProfile(profile, isActive: true); + } else { + final cachedProfile = await AppDatabase.loadProfile(accountId); + if (cachedProfile == null) { + throw Exception('login: отсутствует profile в ответе'); + } + profile = cachedProfile; } - final contact = profileMap['contact']; - if (contact is! Map) { - throw Exception('login: отсутствует profile.contact в ответе'); - } - final profile = ProfileData.fromServerMap(contact.cast()); - await AppDatabase.saveProfile(profile, isActive: true); await AppDatabase.setActiveAccount(profile.id); await _saveSyncState(data, serverTime, profile.id); diff --git a/lib/backend/modules/account/account_models.dart b/lib/backend/modules/account/account_models.dart index 844e6b8..a5b2d47 100644 --- a/lib/backend/modules/account/account_models.dart +++ b/lib/backend/modules/account/account_models.dart @@ -372,8 +372,9 @@ class VerifyCodeResult { class TwoFactorResult { final String loginToken; + final int accountId; - const TwoFactorResult({required this.loginToken}); + const TwoFactorResult({required this.loginToken, required this.accountId}); } class LoginSyncParams { diff --git a/lib/backend/modules/account/profile_module.dart b/lib/backend/modules/account/profile_module.dart index 28deb0b..87fe5a2 100644 --- a/lib/backend/modules/account/profile_module.dart +++ b/lib/backend/modules/account/profile_module.dart @@ -18,8 +18,8 @@ class ProfileModule extends AccountApiBase { if (profile == null) throw Exception('No profile in response'); final contact = profile['contact'] as Map?; if (contact == null) throw Exception('No contact in response'); - final newProfile = ProfileData.fromServerMap( - contact.cast(), + final newProfile = ProfileData.fromServerProfile( + profile.cast(), ); await AppDatabase.saveProfile(newProfile, isActive: true); return newProfile; @@ -88,7 +88,7 @@ class ProfileModule extends AccountApiBase { final contact = profile['contact']; if (contact is! Map) return; completer.complete( - ProfileData.fromServerMap(contact.cast()), + ProfileData.fromServerProfile(profile.cast()), ); }); final timer = Timer(const Duration(seconds: 15), () { diff --git a/lib/core/storage/app_database.dart b/lib/core/storage/app_database.dart index 287b7e2..52731c2 100644 --- a/lib/core/storage/app_database.dart +++ b/lib/core/storage/app_database.dart @@ -36,7 +36,21 @@ class ProfileData { this.profileOptions, }); - factory ProfileData.fromServerMap(Map contact) { + factory ProfileData.fromServerProfile(Map profile) { + final contact = profile['contact']; + if (contact is! Map) { + throw const FormatException('No contact in profile'); + } + return ProfileData.fromServerMap( + contact.cast(), + profileOptions: _parseProfileOptions(profile['profileOptions']), + ); + } + + factory ProfileData.fromServerMap( + Map contact, { + List? profileOptions, + }) { final names = contact['names']; String firstName = ''; String? lastName; @@ -52,12 +66,6 @@ class ProfileData { lastName = name['lastName'] as String?; } - final profileOptionsRaw = contact['profileOptions']; - List? profileOptions; - if (profileOptionsRaw is List) { - profileOptions = profileOptionsRaw.map((e) => e as int).toList(); - } - return ProfileData( id: contact['id'] as int, firstName: firstName, @@ -69,10 +77,20 @@ class ProfileData { country: (contact['country'] as String?) ?? '', accountStatus: (contact['accountStatus'] as int?) ?? 0, updateTime: (contact['updateTime'] as int?) ?? 0, - profileOptions: profileOptions, + profileOptions: + profileOptions ?? _parseProfileOptions(contact['profileOptions']), ); } + static List? _parseProfileOptions(dynamic raw) { + if (raw is! List) return null; + final options = raw + .map((e) => e is int ? e : int.tryParse(e.toString())) + .whereType() + .toList(); + return options.isEmpty ? null : options; + } + factory ProfileData.fromDbRow(Map row) { final profileOptionsStr = row['profile_options'] as String?; List? profileOptions; diff --git a/lib/frontend/screens/auth/password_2fa_screen.dart b/lib/frontend/screens/auth/password_2fa_screen.dart index 8ab80c5..dae30dd 100644 --- a/lib/frontend/screens/auth/password_2fa_screen.dart +++ b/lib/frontend/screens/auth/password_2fa_screen.dart @@ -67,7 +67,10 @@ class _Password2FAScreenState extends State if (!mounted) return; - final loginResult = await accountModule.login(token: result.loginToken); + final loginResult = await accountModule.login( + accountId: result.accountId, + token: result.loginToken, + ); if (!mounted) return; diff --git a/pubspec.yaml b/pubspec.yaml index fd2f641..4f29a8c 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -16,7 +16,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html # In Windows, build-name is used as the major, minor, and patch parts # of the product and file versions while build-number is used as the build suffix. -version: 0.5.0+13 +version: 0.5.0+14 environment: sdk: ^3.10.4