From de40b2f9aa9f92d88305516d6f2acf3ae613c66b Mon Sep 17 00:00:00 2001 From: Matthew Fosse Date: Mon, 3 Mar 2025 17:36:20 -0800 Subject: [PATCH] Address list fixes CW-883 (#1995) * fix show/hide buttons not updating address list * fix label not updating * cleanup * minor fix --- .../screens/receive/widgets/address_list.dart | 40 +++---- .../wallet_address_list_view_model.dart | 104 ++++++++---------- 2 files changed, 68 insertions(+), 76 deletions(-) diff --git a/lib/src/screens/receive/widgets/address_list.dart b/lib/src/screens/receive/widgets/address_list.dart index 45891d03a..8d22d81ef 100644 --- a/lib/src/screens/receive/widgets/address_list.dart +++ b/lib/src/screens/receive/widgets/address_list.dart @@ -1,4 +1,3 @@ - import 'dart:math'; import 'package:cake_wallet/di.dart'; @@ -21,6 +20,7 @@ import 'package:cw_core/wallet_type.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_mobx/flutter_mobx.dart'; +import 'package:mobx/mobx.dart'; class AddressList extends StatefulWidget { const AddressList({ @@ -37,7 +37,6 @@ class AddressList extends StatefulWidget { } class _AddressListState extends State { - bool showHiddenAddresses = false; void _toggleHiddenAddresses() { @@ -62,7 +61,7 @@ class _AddressListState extends State { void updateItems() { setState(() { - items = getItems(widget.addressListViewModel.items, showHiddenAddresses); + items = getItems(widget.addressListViewModel.forceRecomputeItems, showHiddenAddresses); }); } @@ -132,9 +131,10 @@ class _AddressListState extends State { showTrailingButton: widget.addressListViewModel.showAddManualAddresses, showSearchButton: true, onSearchCallback: updateItems, - trailingButtonTap: () => Navigator.of(context).pushNamed(Routes.newSubaddress).then((value) { - updateItems(); // refresh the new address - }), + trailingButtonTap: () => + Navigator.of(context).pushNamed(Routes.newSubaddress).then((value) { + updateItems(); // refresh the new address + }), trailingIcon: Icon( Icons.add, size: 20, @@ -149,7 +149,8 @@ class _AddressListState extends State { cell = Container(); } else { cell = Observer(builder: (_) { - final isCurrent = item.address == widget.addressListViewModel.address.address && editable; + final isCurrent = + item.address == widget.addressListViewModel.address.address && editable; final backgroundColor = isCurrent ? Theme.of(context).extension()!.currentTileBackgroundColor : Theme.of(context).extension()!.tilesBackgroundColor; @@ -157,17 +158,17 @@ class _AddressListState extends State { ? Theme.of(context).extension()!.currentTileTextColor : Theme.of(context).extension()!.tilesTextColor; - return AddressCell.fromItem( item, isCurrent: isCurrent, hasBalance: widget.addressListViewModel.isBalanceAvailable, hasReceived: widget.addressListViewModel.isReceivedAvailable, - // hasReceived: - backgroundColor: (kDebugMode && item.isHidden) ? - Theme.of(context).colorScheme.error : - (kDebugMode && item.isManual) ? Theme.of(context).colorScheme.error.withBlue(255) : - backgroundColor, + // hasReceived: + backgroundColor: (kDebugMode && item.isHidden) + ? Theme.of(context).colorScheme.error + : (kDebugMode && item.isManual) + ? Theme.of(context).colorScheme.error.withBlue(255) + : backgroundColor, textColor: textColor, onTap: (_) { if (widget.onSelect != null) { @@ -177,9 +178,11 @@ class _AddressListState extends State { widget.addressListViewModel.setAddress(item); }, onEdit: editable - ? () => Navigator.of(context).pushNamed(Routes.newSubaddress, arguments: item).then((value) { - updateItems(); // refresh the new address - }) + ? () => Navigator.of(context) + .pushNamed(Routes.newSubaddress, arguments: item) + .then((value) { + updateItems(); // refresh the new address + }) : null, isHidden: item.isHidden, onHide: () => _hideAddress(item), @@ -191,8 +194,8 @@ class _AddressListState extends State { return index != 0 ? cell : ClipRRect( - borderRadius: BorderRadius.only( - topLeft: Radius.circular(30), topRight: Radius.circular(30)), + borderRadius: + BorderRadius.only(topLeft: Radius.circular(30), topRight: Radius.circular(30)), child: cell, ); }, @@ -203,5 +206,4 @@ class _AddressListState extends State { await widget.addressListViewModel.toggleHideAddress(item); updateItems(); } - } diff --git a/lib/view_model/wallet_address_list/wallet_address_list_view_model.dart b/lib/view_model/wallet_address_list/wallet_address_list_view_model.dart index 773d8335c..ef1c52f31 100644 --- a/lib/view_model/wallet_address_list/wallet_address_list_view_model.dart +++ b/lib/view_model/wallet_address_list/wallet_address_list_view_model.dart @@ -26,14 +26,14 @@ import 'package:cake_wallet/view_model/wallet_address_list/wallet_address_list_i import 'package:cake_wallet/wownero/wownero.dart'; import 'package:cw_core/amount_converter.dart'; import 'package:cw_core/currency.dart'; +import 'package:cw_core/utils/print_verbose.dart'; import 'package:cw_core/wallet_type.dart'; import 'package:intl/intl.dart'; import 'package:mobx/mobx.dart'; part 'wallet_address_list_view_model.g.dart'; -class WalletAddressListViewModel = WalletAddressListViewModelBase - with _$WalletAddressListViewModel; +class WalletAddressListViewModel = WalletAddressListViewModelBase with _$WalletAddressListViewModel; abstract class PaymentURI { PaymentURI({required this.amount, required this.address}); @@ -222,9 +222,7 @@ class ZanoURI extends PaymentURI { } } - -abstract class WalletAddressListViewModelBase - extends WalletChangeListenerViewModel with Store { +abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewModel with Store { WalletAddressListViewModelBase({ required AppStore appStore, required this.yatStore, @@ -245,8 +243,7 @@ abstract class WalletAddressListViewModelBase _init(); selectedCurrency = walletTypeToCryptoCurrency(wallet.type); - hasAccounts = [WalletType.monero, WalletType.wownero, WalletType.haven] - .contains(wallet.type); + hasAccounts = [WalletType.monero, WalletType.wownero, WalletType.haven].contains(wallet.type); } static const String _cryptoNumberPattern = '0.00000000'; @@ -259,8 +256,7 @@ abstract class WalletAddressListViewModelBase double? _fiatRate; String _rawAmount = ''; - List get currencies => - [walletTypeToCryptoCurrency(wallet.type), ...FiatCurrency.all]; + List get currencies => [walletTypeToCryptoCurrency(wallet.type), ...FiatCurrency.all]; String get buttonTitle { if (isElectrumWallet) { @@ -286,8 +282,8 @@ abstract class WalletAddressListViewModelBase WalletType get type => wallet.type; @computed - WalletAddressListItem get address => WalletAddressListItem( - address: wallet.walletAddresses.address, isPrimary: false); + WalletAddressListItem get address => + WalletAddressListItem(address: wallet.walletAddresses.address, isPrimary: false); @computed PaymentURI get uri { @@ -317,25 +313,23 @@ abstract class WalletAddressListViewModelBase case WalletType.wownero: return WowneroURI(amount: amount, address: address.address); case WalletType.zano: - return ZanoURI(amount: amount, address: address.address); + return ZanoURI(amount: amount, address: address.address); case WalletType.none: throw Exception('Unexpected type: ${type.toString()}'); } } + @computed ObservableList get items => ObservableList() ..addAll(_baseItems) ..addAll(addressList); - @computed - ObservableList get addressList { + ObservableList _computeAddressList() { final addressList = ObservableList(); if (wallet.type == WalletType.monero) { - final primaryAddress = - monero!.getSubaddressList(wallet).subaddresses.first; - final addressItems = - monero!.getSubaddressList(wallet).subaddresses.map((subaddress) { + final primaryAddress = monero!.getSubaddressList(wallet).subaddresses.first; + final addressItems = monero!.getSubaddressList(wallet).subaddresses.map((subaddress) { final isPrimary = subaddress == primaryAddress; return WalletAddressListItem( @@ -351,10 +345,8 @@ abstract class WalletAddressListViewModelBase } if (wallet.type == WalletType.wownero) { - final primaryAddress = - wownero!.getSubaddressList(wallet).subaddresses.first; - final addressItems = - wownero!.getSubaddressList(wallet).subaddresses.map((subaddress) { + final primaryAddress = wownero!.getSubaddressList(wallet).subaddresses.first; + final addressItems = wownero!.getSubaddressList(wallet).subaddresses.map((subaddress) { final isPrimary = subaddress == primaryAddress; return WalletAddressListItem( @@ -367,10 +359,8 @@ abstract class WalletAddressListViewModelBase } if (wallet.type == WalletType.haven) { - final primaryAddress = - haven!.getSubaddressList(wallet).subaddresses.first; - final addressItems = - haven!.getSubaddressList(wallet).subaddresses.map((subaddress) { + final primaryAddress = haven!.getSubaddressList(wallet).subaddresses.first; + final addressItems = haven!.getSubaddressList(wallet).subaddresses.map((subaddress) { final isPrimary = subaddress == primaryAddress; return WalletAddressListItem( @@ -384,8 +374,7 @@ abstract class WalletAddressListViewModelBase if (isElectrumWallet) { if (bitcoin!.hasSelectedSilentPayments(wallet)) { - final addressItems = - bitcoin!.getSilentPaymentAddresses(wallet).map((address) { + final addressItems = bitcoin!.getSilentPaymentAddresses(wallet).map((address) { final isPrimary = address.id == 0; return WalletAddressListItem( @@ -436,8 +425,7 @@ abstract class WalletAddressListViewModelBase if (wallet.type == WalletType.litecoin && addressItems.length >= 1000) { // find the index of the last item with a txCount > 0 final addressItemsList = addressItems.toList(); - int index = addressItemsList - .lastIndexWhere((item) => (item.txCount ?? 0) > 0); + int index = addressItemsList.lastIndexWhere((item) => (item.txCount ?? 0) > 0); if (index == -1) { index = 0; } @@ -451,22 +439,19 @@ abstract class WalletAddressListViewModelBase if (wallet.type == WalletType.ethereum) { final primaryAddress = ethereum!.getAddress(wallet); - addressList.add(WalletAddressListItem( - isPrimary: true, name: null, address: primaryAddress)); + addressList.add(WalletAddressListItem(isPrimary: true, name: null, address: primaryAddress)); } if (wallet.type == WalletType.polygon) { final primaryAddress = polygon!.getAddress(wallet); - addressList.add(WalletAddressListItem( - isPrimary: true, name: null, address: primaryAddress)); + addressList.add(WalletAddressListItem(isPrimary: true, name: null, address: primaryAddress)); } if (wallet.type == WalletType.solana) { final primaryAddress = solana!.getAddress(wallet); - addressList.add(WalletAddressListItem( - isPrimary: true, name: null, address: primaryAddress)); + addressList.add(WalletAddressListItem(isPrimary: true, name: null, address: primaryAddress)); } if (wallet.type == WalletType.nano) { @@ -480,21 +465,18 @@ abstract class WalletAddressListViewModelBase if (wallet.type == WalletType.tron) { final primaryAddress = tron!.getAddress(wallet); - addressList.add(WalletAddressListItem( - isPrimary: true, name: null, address: primaryAddress)); + addressList.add(WalletAddressListItem(isPrimary: true, name: null, address: primaryAddress)); } for (var i = 0; i < addressList.length; i++) { if (!(addressList[i] is WalletAddressListItem)) continue; - (addressList[i] as WalletAddressListItem).isHidden = wallet - .walletAddresses.hiddenAddresses + (addressList[i] as WalletAddressListItem).isHidden = wallet.walletAddresses.hiddenAddresses .contains((addressList[i] as WalletAddressListItem).address); } for (var i = 0; i < addressList.length; i++) { if (!(addressList[i] is WalletAddressListItem)) continue; - (addressList[i] as WalletAddressListItem).isManual = wallet - .walletAddresses.manualAddresses + (addressList[i] as WalletAddressListItem).isManual = wallet.walletAddresses.manualAddresses .contains((addressList[i] as WalletAddressListItem).address); } @@ -516,13 +498,28 @@ abstract class WalletAddressListViewModelBase return addressList; } + @computed + ObservableList get addressList { + return _computeAddressList(); + } + + List get forceRecomputeItems { + // necessary because the addressList contains non-observable items + List recomputed = []; + recomputed.addAll(_baseItems); + recomputed.addAll(_computeAddressList()); + return recomputed; + } + Future toggleHideAddress(WalletAddressListItem item) async { if (item.isHidden) { - wallet.walletAddresses.hiddenAddresses - .removeWhere((element) => element == item.address); + item.isHidden = false; + wallet.walletAddresses.hiddenAddresses.removeWhere((element) => element == item.address); } else { + item.isHidden = true; wallet.walletAddresses.hiddenAddresses.add(item.address); } + // update the address list: await wallet.walletAddresses.saveAddressesInBox(); if (wallet.type == WalletType.monero) { monero! @@ -568,28 +565,22 @@ abstract class WalletAddressListViewModelBase ].contains(wallet.type); @computed - bool get isElectrumWallet => [ - WalletType.bitcoin, - WalletType.litecoin, - WalletType.bitcoinCash - ].contains(wallet.type); + bool get isElectrumWallet => + [WalletType.bitcoin, WalletType.litecoin, WalletType.bitcoinCash].contains(wallet.type); @computed bool get isBalanceAvailable => isElectrumWallet; @computed - bool get isReceivedAvailable => - [WalletType.monero, WalletType.wownero].contains(wallet.type); + bool get isReceivedAvailable => [WalletType.monero, WalletType.wownero].contains(wallet.type); @computed bool get isSilentPayments => - wallet.type == WalletType.bitcoin && - bitcoin!.hasSelectedSilentPayments(wallet); + wallet.type == WalletType.bitcoin && bitcoin!.hasSelectedSilentPayments(wallet); @computed bool get isAutoGenerateSubaddressEnabled => - _settingsStore.autoGenerateSubaddressStatus != - AutoGenerateSubaddressStatus.disabled && + _settingsStore.autoGenerateSubaddressStatus != AutoGenerateSubaddressStatus.disabled && !isSilentPayments; @computed @@ -672,8 +663,7 @@ abstract class WalletAddressListViewModelBase @action void _convertAmountToCrypto() { final cryptoCurrency = walletTypeToCryptoCurrency(wallet.type); - final fiatRate = - _fiatRate ?? (fiatConversionStore.prices[cryptoCurrency] ?? 0.0); + final fiatRate = _fiatRate ?? (fiatConversionStore.prices[cryptoCurrency] ?? 0.0); if (fiatRate <= 0.0) { dev.log("Invalid Fiat Rate $fiatRate");