From 1f3b261e3d97300b6a1307756b306648c74bfba7 Mon Sep 17 00:00:00 2001 From: David Adegoke <64401859+Blazebrain@users.noreply.github.com> Date: Wed, 2 Sep 2026 11:30:28 +0100 Subject: [PATCH] CW 1515 Contacts List Out Of Sync (#3550) * fix android CI * fix: address book wallet contacts tab not updating after wallet changes * feat: add page open listener for contact list and handle concurrent calls to fetch contact list --- lib/new-ui/new_dashboard.dart | 4 + lib/new-ui/page_open_listener.dart | 3 + .../screens/contact/contact_list_page.dart | 10 +- .../contact_list/contact_list_view_model.dart | 141 +++++++++++------- 4 files changed, 99 insertions(+), 59 deletions(-) create mode 100644 lib/new-ui/page_open_listener.dart diff --git a/lib/new-ui/new_dashboard.dart b/lib/new-ui/new_dashboard.dart index 9c9294648..76917cce5 100644 --- a/lib/new-ui/new_dashboard.dart +++ b/lib/new-ui/new_dashboard.dart @@ -2,6 +2,7 @@ import 'dart:io'; import 'package:cake_wallet/di.dart'; import 'package:cake_wallet/entities/preferences_key.dart'; +import "package:cake_wallet/new-ui/page_open_listener.dart"; import 'package:cake_wallet/new-ui/pages/home_page.dart'; import 'package:cake_wallet/new-ui/widgets/changelog_modal.dart'; import 'package:cake_wallet/src/screens/contact/contact_list_page.dart'; @@ -112,6 +113,9 @@ class _NewDashboardState extends State { setState(() { _selectedPage = index; }); + if (widget.dashboardPageWidgets[_selectedPage] case PageOpenListener page) { + page.onPageOpen(); + } }, ) ], diff --git a/lib/new-ui/page_open_listener.dart b/lib/new-ui/page_open_listener.dart new file mode 100644 index 000000000..7914869ee --- /dev/null +++ b/lib/new-ui/page_open_listener.dart @@ -0,0 +1,3 @@ +abstract interface class PageOpenListener { + void onPageOpen(); +} diff --git a/lib/src/screens/contact/contact_list_page.dart b/lib/src/screens/contact/contact_list_page.dart index 79892062e..e8a336fa8 100644 --- a/lib/src/screens/contact/contact_list_page.dart +++ b/lib/src/screens/contact/contact_list_page.dart @@ -1,9 +1,12 @@ +import "dart:async"; + import 'package:cake_wallet/core/auth_service.dart'; import 'package:cake_wallet/entities/contact_base.dart'; import 'package:cake_wallet/entities/contact_record.dart'; import 'package:cake_wallet/entities/wallet_contact.dart'; import 'package:cake_wallet/entities/wallet_list_order_types.dart'; import 'package:cake_wallet/generated/i18n.dart'; +import "package:cake_wallet/new-ui/page_open_listener.dart"; import 'package:cake_wallet/routes.dart'; import 'package:cake_wallet/src/screens/base_page.dart'; import 'package:cake_wallet/src/screens/dashboard/widgets/filter_list_widget.dart'; @@ -26,7 +29,7 @@ import 'package:flutter/services.dart'; import 'package:flutter_mobx/flutter_mobx.dart'; import 'package:flutter_slidable/flutter_slidable.dart'; -class ContactListPage extends BasePage { +class ContactListPage extends BasePage implements PageOpenListener { ContactListPage(this.contactListViewModel, this.authService, {this.showAddButton = false}); final ContactListViewModel contactListViewModel; @@ -89,6 +92,11 @@ class ContactListPage extends BasePage { ); } + @override + void onPageOpen() { + unawaited(contactListViewModel.loadWalletContacts()); + } + @override Widget body(BuildContext context) => ContactPageBody(contactListViewModel: contactListViewModel); } diff --git a/lib/view_model/contact_list/contact_list_view_model.dart b/lib/view_model/contact_list/contact_list_view_model.dart index 62db91143..19425242b 100644 --- a/lib/view_model/contact_list/contact_list_view_model.dart +++ b/lib/view_model/contact_list/contact_list_view_model.dart @@ -14,6 +14,7 @@ import 'package:cake_wallet/utils/mobx.dart'; import 'package:cake_wallet/utils/token_utilities.dart'; import 'package:cw_core/crypto_currency.dart'; import 'package:cw_core/currency_for_wallet_type.dart'; +import "package:cw_core/utils/print_verbose.dart"; import 'package:cw_core/wallet_info.dart'; import 'package:cw_core/wallet_type.dart'; import 'package:hive/hive.dart'; @@ -33,79 +34,103 @@ abstract class ContactListViewModelBase with Store { } Future _init() async { - final walletInfos = await WalletInfo.getAll(); - for (final info in walletInfos) { - final addressInfos = await info.getAddressInfos(); - final addresses = await info.getAddresses(); - if ([WalletType.monero, WalletType.wownero, WalletType.haven].contains(info.type) && - addressInfos.isNotEmpty) { - for (var key in addressInfos.keys) { - final value = addressInfos[key]; - final address = value?.first; - if (address != null) { - final name = _createName(info.name, address.label, key: key); - walletContacts.add(WalletContact( - address.address, + await loadWalletContacts(); + + _subscription = contactSource.bindToListWithTransform( + contacts, + (contact) => ContactRecord(contactSource, contact), + initialFire: true, + ); + + setOrderType(settingsStore.contactListOrder); + } + + Future _lastWalletContactsLoad = Future.value(); + + @action + Future loadWalletContacts() async { + final waitFor = _lastWalletContactsLoad; + final done = Completer(); + _lastWalletContactsLoad = done.future; + + await waitFor; + + try { + final loadedContacts = []; + final walletInfos = await WalletInfo.getAll(); + for (final info in walletInfos) { + final addressInfos = await info.getAddressInfos(); + final addresses = await info.getAddresses(); + if ([WalletType.monero, WalletType.wownero, WalletType.haven].contains(info.type) && + addressInfos.isNotEmpty) { + for (var key in addressInfos.keys) { + final value = addressInfos[key]; + final address = value?.first; + if (address != null) { + final name = _createName(info.name, address.label, key: key); + loadedContacts.add(WalletContact( + address.address, + name, + getCryptoCurrencyForWalletListItem( + info.type, + ), + walletType: info.type, + )); + } + } + } else if (addresses.isNotEmpty == true && addresses.length > 1) { + if ([WalletType.monero, WalletType.wownero, WalletType.haven, WalletType.decred] + .contains(info.type)) { + final address = info.address; + final name = _createName(info.name, "", key: 0); + loadedContacts.add(WalletContact( + address, name, getCryptoCurrencyForWalletListItem( info.type, ), walletType: info.type, )); + } else { + addresses.forEach((address, label) { + if (label.isEmpty) { + return; + } + final name = _createName(info.name, label, key: null); + loadedContacts.add(WalletContact( + address, + name, + getCryptoCurrencyForWalletListItem( + info.type, + isTestnet: info.network == null + ? false + : info.network!.toLowerCase().contains("testnet"), + ), + walletType: info.type, + )); + }); } - } - } else if (addresses.isNotEmpty == true && addresses.length > 1) { - if ([WalletType.monero, WalletType.wownero, WalletType.haven, WalletType.decred] - .contains(info.type)) { - final address = info.address; - final name = _createName(info.name, "", key: 0); - walletContacts.add(WalletContact( - address, - name, + } else { + loadedContacts.add(WalletContact( + info.address, + _createName(info.name, "", + key: [WalletType.monero, WalletType.wownero, WalletType.haven].contains(info.type) + ? 0 + : null), getCryptoCurrencyForWalletListItem( info.type, ), walletType: info.type, )); - } else { - addresses.forEach((address, label) { - if (label.isEmpty) { - return; - } - final name = _createName(info.name, label, key: null); - walletContacts.add(WalletContact( - address, - name, - getCryptoCurrencyForWalletListItem( - info.type, - isTestnet: - info.network == null ? false : info.network!.toLowerCase().contains("testnet"), - ), - walletType: info.type, - )); - }); } - } else { - walletContacts.add(WalletContact( - info.address, - _createName(info.name, "", - key: [WalletType.monero, WalletType.wownero, WalletType.haven].contains(info.type) - ? 0 - : null), - getCryptoCurrencyForWalletListItem( - info.type, - ), - walletType: info.type, - )); } + + walletContacts = loadedContacts; + } catch (e) { + printV("loadWalletContacts failed: $e"); + } finally { + done.complete(); } - - _subscription = contactSource.bindToListWithTransform( - contacts, (Contact contact) => ContactRecord(contactSource, contact), - initialFire: true); - - setOrderType(settingsStore.contactListOrder); - walletContacts = walletContacts.toList(); // rebuild } String _createName(String walletName, String label, {int? key = null}) {