Address list fixes CW-883 (#1995)
* fix show/hide buttons not updating address list * fix label not updating * cleanup * minor fix
Matthew Fosse committed
Mar 3, 2025 at 17:36 UTC
de40b2f9aa9f92d88305516d6f2acf3ae613c66b
2 files changed
+68
-76
lib/src/screens/receive/widgets/address_list.dart
+21
-19
@@ -1,4 +1,3 @@
1
-
1
import 'dart:math';
2
3
import 'package:cake_wallet/di.dart';
@@ -21,6 +20,7 @@ import 'package:cw_core/wallet_type.dart';
20
import 'package:flutter/foundation.dart';
21
import 'package:flutter/material.dart';
22
import 'package:flutter_mobx/flutter_mobx.dart';
23
+import 'package:mobx/mobx.dart';
24
25
class AddressList extends StatefulWidget {
26
const AddressList({
@@ -37,7 +37,6 @@ class AddressList extends StatefulWidget {
37
}
38
39
class _AddressListState extends State<AddressList> {
40
-
40
bool showHiddenAddresses = false;
41
42
void _toggleHiddenAddresses() {
@@ -62,7 +61,7 @@ class _AddressListState extends State<AddressList> {
61
62
void updateItems() {
63
setState(() {
65
- items = getItems(widget.addressListViewModel.items, showHiddenAddresses);
64
+ items = getItems(widget.addressListViewModel.forceRecomputeItems, showHiddenAddresses);
65
});
66
}
67
@@ -132,9 +131,10 @@ class _AddressListState extends State<AddressList> {
131
showTrailingButton: widget.addressListViewModel.showAddManualAddresses,
132
showSearchButton: true,
133
onSearchCallback: updateItems,
135
- trailingButtonTap: () => Navigator.of(context).pushNamed(Routes.newSubaddress).then((value) {
136
- updateItems(); // refresh the new address
137
- }),
134
+ trailingButtonTap: () =>
135
+ Navigator.of(context).pushNamed(Routes.newSubaddress).then((value) {
136
+ updateItems(); // refresh the new address
137
+ }),
138
trailingIcon: Icon(
139
Icons.add,
140
size: 20,
@@ -149,7 +149,8 @@ class _AddressListState extends State<AddressList> {
149
cell = Container();
150
} else {
151
cell = Observer(builder: (_) {
152
- final isCurrent = item.address == widget.addressListViewModel.address.address && editable;
152
+ final isCurrent =
153
+ item.address == widget.addressListViewModel.address.address && editable;
154
final backgroundColor = isCurrent
155
? Theme.of(context).extension<ReceivePageTheme>()!.currentTileBackgroundColor
156
: Theme.of(context).extension<ReceivePageTheme>()!.tilesBackgroundColor;
@@ -157,17 +158,17 @@ class _AddressListState extends State<AddressList> {
158
? Theme.of(context).extension<ReceivePageTheme>()!.currentTileTextColor
159
: Theme.of(context).extension<ReceivePageTheme>()!.tilesTextColor;
160
160
-
161
return AddressCell.fromItem(
162
item,
163
isCurrent: isCurrent,
164
hasBalance: widget.addressListViewModel.isBalanceAvailable,
165
hasReceived: widget.addressListViewModel.isReceivedAvailable,
166
- // hasReceived:
167
- backgroundColor: (kDebugMode && item.isHidden) ?
168
- Theme.of(context).colorScheme.error :
169
- (kDebugMode && item.isManual) ? Theme.of(context).colorScheme.error.withBlue(255) :
170
- backgroundColor,
166
+ // hasReceived:
167
+ backgroundColor: (kDebugMode && item.isHidden)
168
+ ? Theme.of(context).colorScheme.error
169
+ : (kDebugMode && item.isManual)
170
+ ? Theme.of(context).colorScheme.error.withBlue(255)
171
+ : backgroundColor,
172
textColor: textColor,
173
onTap: (_) {
174
if (widget.onSelect != null) {
@@ -177,9 +178,11 @@ class _AddressListState extends State<AddressList> {
178
widget.addressListViewModel.setAddress(item);
179
},
180
onEdit: editable
180
- ? () => Navigator.of(context).pushNamed(Routes.newSubaddress, arguments: item).then((value) {
181
- updateItems(); // refresh the new address
182
- })
181
+ ? () => Navigator.of(context)
182
+ .pushNamed(Routes.newSubaddress, arguments: item)
183
+ .then((value) {
184
+ updateItems(); // refresh the new address
185
+ })
186
: null,
187
isHidden: item.isHidden,
188
onHide: () => _hideAddress(item),
@@ -191,8 +194,8 @@ class _AddressListState extends State<AddressList> {
194
return index != 0
195
? cell
196
: ClipRRect(
194
- borderRadius: BorderRadius.only(
195
- topLeft: Radius.circular(30), topRight: Radius.circular(30)),
197
+ borderRadius:
198
+ BorderRadius.only(topLeft: Radius.circular(30), topRight: Radius.circular(30)),
199
child: cell,
200
);
201
},
@@ -203,5 +206,4 @@ class _AddressListState extends State<AddressList> {
206
await widget.addressListViewModel.toggleHideAddress(item);
207
updateItems();
208
}
206
-
209
}
lib/view_model/wallet_address_list/wallet_address_list_view_model.dart
+47
-57
@@ -26,14 +26,14 @@ import 'package:cake_wallet/view_model/wallet_address_list/wallet_address_list_i
26
import 'package:cake_wallet/wownero/wownero.dart';
27
import 'package:cw_core/amount_converter.dart';
28
import 'package:cw_core/currency.dart';
29
+import 'package:cw_core/utils/print_verbose.dart';
30
import 'package:cw_core/wallet_type.dart';
31
import 'package:intl/intl.dart';
32
import 'package:mobx/mobx.dart';
33
34
part 'wallet_address_list_view_model.g.dart';
35
35
-class WalletAddressListViewModel = WalletAddressListViewModelBase
36
- with _$WalletAddressListViewModel;
36
+class WalletAddressListViewModel = WalletAddressListViewModelBase with _$WalletAddressListViewModel;
37
38
abstract class PaymentURI {
39
PaymentURI({required this.amount, required this.address});
@@ -222,9 +222,7 @@ class ZanoURI extends PaymentURI {
222
}
223
}
224
225
-
226
-abstract class WalletAddressListViewModelBase
227
- extends WalletChangeListenerViewModel with Store {
225
+abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewModel with Store {
226
WalletAddressListViewModelBase({
227
required AppStore appStore,
228
required this.yatStore,
@@ -245,8 +243,7 @@ abstract class WalletAddressListViewModelBase
243
_init();
244
245
selectedCurrency = walletTypeToCryptoCurrency(wallet.type);
248
- hasAccounts = [WalletType.monero, WalletType.wownero, WalletType.haven]
249
- .contains(wallet.type);
246
+ hasAccounts = [WalletType.monero, WalletType.wownero, WalletType.haven].contains(wallet.type);
247
}
248
249
static const String _cryptoNumberPattern = '0.00000000';
@@ -259,8 +256,7 @@ abstract class WalletAddressListViewModelBase
256
double? _fiatRate;
257
String _rawAmount = '';
258
262
- List<Currency> get currencies =>
263
- [walletTypeToCryptoCurrency(wallet.type), ...FiatCurrency.all];
259
+ List<Currency> get currencies => [walletTypeToCryptoCurrency(wallet.type), ...FiatCurrency.all];
260
261
String get buttonTitle {
262
if (isElectrumWallet) {
@@ -286,8 +282,8 @@ abstract class WalletAddressListViewModelBase
282
WalletType get type => wallet.type;
283
284
@computed
289
- WalletAddressListItem get address => WalletAddressListItem(
290
- address: wallet.walletAddresses.address, isPrimary: false);
285
+ WalletAddressListItem get address =>
286
+ WalletAddressListItem(address: wallet.walletAddresses.address, isPrimary: false);
287
288
@computed
289
PaymentURI get uri {
@@ -317,25 +313,23 @@ abstract class WalletAddressListViewModelBase
313
case WalletType.wownero:
314
return WowneroURI(amount: amount, address: address.address);
315
case WalletType.zano:
320
- return ZanoURI(amount: amount, address: address.address);
316
+ return ZanoURI(amount: amount, address: address.address);
317
case WalletType.none:
318
throw Exception('Unexpected type: ${type.toString()}');
319
}
320
}
321
+
322
@computed
323
ObservableList<ListItem> get items => ObservableList<ListItem>()
324
..addAll(_baseItems)
325
..addAll(addressList);
326
330
- @computed
331
- ObservableList<ListItem> get addressList {
327
+ ObservableList<ListItem> _computeAddressList() {
328
final addressList = ObservableList<ListItem>();
329
330
if (wallet.type == WalletType.monero) {
335
- final primaryAddress =
336
- monero!.getSubaddressList(wallet).subaddresses.first;
337
- final addressItems =
338
- monero!.getSubaddressList(wallet).subaddresses.map((subaddress) {
331
+ final primaryAddress = monero!.getSubaddressList(wallet).subaddresses.first;
332
+ final addressItems = monero!.getSubaddressList(wallet).subaddresses.map((subaddress) {
333
final isPrimary = subaddress == primaryAddress;
334
335
return WalletAddressListItem(
@@ -351,10 +345,8 @@ abstract class WalletAddressListViewModelBase
345
}
346
347
if (wallet.type == WalletType.wownero) {
354
- final primaryAddress =
355
- wownero!.getSubaddressList(wallet).subaddresses.first;
356
- final addressItems =
357
- wownero!.getSubaddressList(wallet).subaddresses.map((subaddress) {
348
+ final primaryAddress = wownero!.getSubaddressList(wallet).subaddresses.first;
349
+ final addressItems = wownero!.getSubaddressList(wallet).subaddresses.map((subaddress) {
350
final isPrimary = subaddress == primaryAddress;
351
352
return WalletAddressListItem(
@@ -367,10 +359,8 @@ abstract class WalletAddressListViewModelBase
359
}
360
361
if (wallet.type == WalletType.haven) {
370
- final primaryAddress =
371
- haven!.getSubaddressList(wallet).subaddresses.first;
372
- final addressItems =
373
- haven!.getSubaddressList(wallet).subaddresses.map((subaddress) {
362
+ final primaryAddress = haven!.getSubaddressList(wallet).subaddresses.first;
363
+ final addressItems = haven!.getSubaddressList(wallet).subaddresses.map((subaddress) {
364
final isPrimary = subaddress == primaryAddress;
365
366
return WalletAddressListItem(
@@ -384,8 +374,7 @@ abstract class WalletAddressListViewModelBase
374
375
if (isElectrumWallet) {
376
if (bitcoin!.hasSelectedSilentPayments(wallet)) {
387
- final addressItems =
388
- bitcoin!.getSilentPaymentAddresses(wallet).map((address) {
377
+ final addressItems = bitcoin!.getSilentPaymentAddresses(wallet).map((address) {
378
final isPrimary = address.id == 0;
379
380
return WalletAddressListItem(
@@ -436,8 +425,7 @@ abstract class WalletAddressListViewModelBase
425
if (wallet.type == WalletType.litecoin && addressItems.length >= 1000) {
426
// find the index of the last item with a txCount > 0
427
final addressItemsList = addressItems.toList();
439
- int index = addressItemsList
440
- .lastIndexWhere((item) => (item.txCount ?? 0) > 0);
428
+ int index = addressItemsList.lastIndexWhere((item) => (item.txCount ?? 0) > 0);
429
if (index == -1) {
430
index = 0;
431
}
@@ -451,22 +439,19 @@ abstract class WalletAddressListViewModelBase
439
if (wallet.type == WalletType.ethereum) {
440
final primaryAddress = ethereum!.getAddress(wallet);
441
454
- addressList.add(WalletAddressListItem(
455
- isPrimary: true, name: null, address: primaryAddress));
442
+ addressList.add(WalletAddressListItem(isPrimary: true, name: null, address: primaryAddress));
443
}
444
445
if (wallet.type == WalletType.polygon) {
446
final primaryAddress = polygon!.getAddress(wallet);
447
461
- addressList.add(WalletAddressListItem(
462
- isPrimary: true, name: null, address: primaryAddress));
448
+ addressList.add(WalletAddressListItem(isPrimary: true, name: null, address: primaryAddress));
449
}
450
451
if (wallet.type == WalletType.solana) {
452
final primaryAddress = solana!.getAddress(wallet);
453
468
- addressList.add(WalletAddressListItem(
469
- isPrimary: true, name: null, address: primaryAddress));
454
+ addressList.add(WalletAddressListItem(isPrimary: true, name: null, address: primaryAddress));
455
}
456
457
if (wallet.type == WalletType.nano) {
@@ -480,21 +465,18 @@ abstract class WalletAddressListViewModelBase
465
if (wallet.type == WalletType.tron) {
466
final primaryAddress = tron!.getAddress(wallet);
467
483
- addressList.add(WalletAddressListItem(
484
- isPrimary: true, name: null, address: primaryAddress));
468
+ addressList.add(WalletAddressListItem(isPrimary: true, name: null, address: primaryAddress));
469
}
470
471
for (var i = 0; i < addressList.length; i++) {
472
if (!(addressList[i] is WalletAddressListItem)) continue;
489
- (addressList[i] as WalletAddressListItem).isHidden = wallet
490
- .walletAddresses.hiddenAddresses
473
+ (addressList[i] as WalletAddressListItem).isHidden = wallet.walletAddresses.hiddenAddresses
474
.contains((addressList[i] as WalletAddressListItem).address);
475
}
476
477
for (var i = 0; i < addressList.length; i++) {
478
if (!(addressList[i] is WalletAddressListItem)) continue;
496
- (addressList[i] as WalletAddressListItem).isManual = wallet
497
- .walletAddresses.manualAddresses
479
+ (addressList[i] as WalletAddressListItem).isManual = wallet.walletAddresses.manualAddresses
480
.contains((addressList[i] as WalletAddressListItem).address);
481
}
482
@@ -516,13 +498,28 @@ abstract class WalletAddressListViewModelBase
498
return addressList;
499
}
500
501
+ @computed
502
+ ObservableList<ListItem> get addressList {
503
+ return _computeAddressList();
504
+ }
505
+
506
+ List<ListItem> get forceRecomputeItems {
507
+ // necessary because the addressList contains non-observable items
508
+ List<ListItem> recomputed = [];
509
+ recomputed.addAll(_baseItems);
510
+ recomputed.addAll(_computeAddressList());
511
+ return recomputed;
512
+ }
513
+
514
Future<void> toggleHideAddress(WalletAddressListItem item) async {
515
if (item.isHidden) {
521
- wallet.walletAddresses.hiddenAddresses
522
- .removeWhere((element) => element == item.address);
516
+ item.isHidden = false;
517
+ wallet.walletAddresses.hiddenAddresses.removeWhere((element) => element == item.address);
518
} else {
519
+ item.isHidden = true;
520
wallet.walletAddresses.hiddenAddresses.add(item.address);
521
}
522
+ // update the address list:
523
await wallet.walletAddresses.saveAddressesInBox();
524
if (wallet.type == WalletType.monero) {
525
monero!
@@ -568,28 +565,22 @@ abstract class WalletAddressListViewModelBase
565
].contains(wallet.type);
566
567
@computed
571
- bool get isElectrumWallet => [
572
- WalletType.bitcoin,
573
- WalletType.litecoin,
574
- WalletType.bitcoinCash
575
- ].contains(wallet.type);
568
+ bool get isElectrumWallet =>
569
+ [WalletType.bitcoin, WalletType.litecoin, WalletType.bitcoinCash].contains(wallet.type);
570
571
@computed
572
bool get isBalanceAvailable => isElectrumWallet;
573
574
@computed
581
- bool get isReceivedAvailable =>
582
- [WalletType.monero, WalletType.wownero].contains(wallet.type);
575
+ bool get isReceivedAvailable => [WalletType.monero, WalletType.wownero].contains(wallet.type);
576
577
@computed
578
bool get isSilentPayments =>
586
- wallet.type == WalletType.bitcoin &&
587
- bitcoin!.hasSelectedSilentPayments(wallet);
579
+ wallet.type == WalletType.bitcoin && bitcoin!.hasSelectedSilentPayments(wallet);
580
581
@computed
582
bool get isAutoGenerateSubaddressEnabled =>
591
- _settingsStore.autoGenerateSubaddressStatus !=
592
- AutoGenerateSubaddressStatus.disabled &&
583
+ _settingsStore.autoGenerateSubaddressStatus != AutoGenerateSubaddressStatus.disabled &&
584
!isSilentPayments;
585
586
@computed
@@ -672,8 +663,7 @@ abstract class WalletAddressListViewModelBase
663
@action
664
void _convertAmountToCrypto() {
665
final cryptoCurrency = walletTypeToCryptoCurrency(wallet.type);
675
- final fiatRate =
676
- _fiatRate ?? (fiatConversionStore.prices[cryptoCurrency] ?? 0.0);
666
+ final fiatRate = _fiatRate ?? (fiatConversionStore.prices[cryptoCurrency] ?? 0.0);
667
668
if (fiatRate <= 0.0) {
669
dev.log("Invalid Fiat Rate $fiatRate");