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
This commit is contained in:
David Adegoke
2026-09-02 18:30:28 +08:00
committed by GitHub
parent 5a497bf05a
commit 1f3b261e3d
4 changed files with 99 additions and 59 deletions
+4
View File
@@ -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<NewDashboard> {
setState(() {
_selectedPage = index;
});
if (widget.dashboardPageWidgets[_selectedPage] case PageOpenListener page) {
page.onPageOpen();
}
},
)
],
+3
View File
@@ -0,0 +1,3 @@
abstract interface class PageOpenListener {
void onPageOpen();
}
@@ -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);
}
@@ -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<void> _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<void> _lastWalletContactsLoad = Future.value();
@action
Future<void> loadWalletContacts() async {
final waitFor = _lastWalletContactsLoad;
final done = Completer<void>();
_lastWalletContactsLoad = done.future;
await waitFor;
try {
final loadedContacts = <WalletContact>[];
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}) {