| 1 | import 'package:cake_wallet/di.dart'; |
| 2 | import 'package:cake_wallet/generated/i18n.dart'; |
| 3 | import 'package:cake_wallet/routes.dart'; |
| 4 | import 'package:cake_wallet/src/screens/monero_accounts/monero_account_list_page.dart'; |
| 5 | import 'package:cake_wallet/src/screens/nano_accounts/nano_account_list_page.dart'; |
| 6 | import 'package:cake_wallet/src/screens/receive/widgets/address_cell.dart'; |
| 7 | import 'package:cake_wallet/src/screens/receive/widgets/header_tile.dart'; |
| 8 | import 'package:cake_wallet/src/widgets/section_divider.dart'; |
| 9 | import 'package:cake_wallet/themes/core/material_base_theme.dart'; |
| 10 | import 'package:cake_wallet/utils/list_item.dart'; |
| 11 | import 'package:cake_wallet/utils/show_pop_up.dart'; |
| 12 | import 'package:cake_wallet/view_model/wallet_address_list/wallet_account_list_header.dart'; |
| 13 | import 'package:cake_wallet/view_model/wallet_address_list/wallet_address_hidden_list_header.dart'; |
| 14 | import 'package:cake_wallet/view_model/wallet_address_list/wallet_address_list_header.dart'; |
| 15 | import 'package:cake_wallet/view_model/wallet_address_list/wallet_address_list_item.dart'; |
| 16 | import 'package:cake_wallet/view_model/wallet_address_list/wallet_address_list_view_model.dart'; |
| 17 | import 'package:cw_core/wallet_type.dart'; |
| 18 | import 'package:flutter/material.dart'; |
| 19 | import 'package:flutter_mobx/flutter_mobx.dart'; |
| 20 | |
| 21 | class AddressList extends StatefulWidget { |
| 22 | const AddressList({super.key, required this.addressListViewModel, this.onSelect}); |
| 23 | |
| 24 | final WalletAddressListViewModel addressListViewModel; |
| 25 | final Function(String)? onSelect; |
| 26 | |
| 27 | @override |
| 28 | State<AddressList> createState() => _AddressListState(); |
| 29 | } |
| 30 | |
| 31 | class _AddressListState extends State<AddressList> { |
| 32 | bool showHiddenAddresses = false; |
| 33 | |
| 34 | void _toggleHiddenAddresses() { |
| 35 | setState(() { |
| 36 | showHiddenAddresses = !showHiddenAddresses; |
| 37 | }); |
| 38 | updateItems(); |
| 39 | } |
| 40 | |
| 41 | List<ListItem> getItems(List<ListItem> list, bool showHidden) { |
| 42 | return list.where((element) { |
| 43 | if (element is WalletAddressListItem) { |
| 44 | if (showHidden && element.isHidden) return true; |
| 45 | if (!showHidden && !element.isHidden) return true; |
| 46 | return false; |
| 47 | } |
| 48 | return true; |
| 49 | }).toList(); |
| 50 | } |
| 51 | |
| 52 | List<ListItem> items = []; |
| 53 | |
| 54 | void updateItems() { |
| 55 | setState(() { |
| 56 | items = getItems(widget.addressListViewModel.forceRecomputeItems, showHiddenAddresses); |
| 57 | }); |
| 58 | } |
| 59 | |
| 60 | @override |
| 61 | void initState() { |
| 62 | super.initState(); |
| 63 | |
| 64 | items = getItems(widget.addressListViewModel.items, showHiddenAddresses); |
| 65 | } |
| 66 | |
| 67 | @override |
| 68 | Widget build(BuildContext context) { |
| 69 | bool editable = widget.onSelect == null; |
| 70 | return ListView.separated( |
| 71 | padding: EdgeInsets.all(0), |
| 72 | separatorBuilder: (context, _) => const HorizontalSectionDivider(), |
| 73 | shrinkWrap: true, |
| 74 | physics: NeverScrollableScrollPhysics(), |
| 75 | itemCount: items.length, |
| 76 | itemBuilder: (context, index) { |
| 77 | final item = items[index]; |
| 78 | Widget cell = Container(); |
| 79 | |
| 80 | if (item is WalletAccountListHeader) { |
| 81 | cell = HeaderTile( |
| 82 | showTrailingButton: true, |
| 83 | walletAddressListViewModel: widget.addressListViewModel, |
| 84 | trailingButtonTap: () async { |
| 85 | if (widget.addressListViewModel.type == WalletType.monero || |
| 86 | widget.addressListViewModel.type == WalletType.wownero || |
| 87 | widget.addressListViewModel.type == WalletType.haven) { |
| 88 | await showPopUp<void>( |
| 89 | context: context, |
| 90 | builder: (_) => getIt.get<MoneroAccountListPage>(), |
| 91 | ); |
| 92 | updateItems(); |
| 93 | } else { |
| 94 | await showPopUp<void>( |
| 95 | context: context, |
| 96 | builder: (_) => getIt.get<NanoAccountListPage>(), |
| 97 | ); |
| 98 | updateItems(); |
| 99 | } |
| 100 | }, |
| 101 | title: S.of(context).accounts, |
| 102 | trailingIcon: Icon( |
| 103 | Icons.arrow_forward_ios, |
| 104 | size: 14, |
| 105 | color: Theme.of(context).colorScheme.onSurfaceVariant, |
| 106 | ), |
| 107 | ); |
| 108 | } |
| 109 | |
| 110 | if (item is WalletAddressHiddenListHeader) { |
| 111 | cell = HeaderTile( |
| 112 | title: S.of(context).hidden_addresses, |
| 113 | walletAddressListViewModel: widget.addressListViewModel, |
| 114 | showTrailingButton: true, |
| 115 | showSearchButton: false, |
| 116 | trailingButtonTap: _toggleHiddenAddresses, |
| 117 | trailingIcon: Icon( |
| 118 | showHiddenAddresses ? Icons.toggle_on : Icons.toggle_off, |
| 119 | size: 20, |
| 120 | color: Theme.of(context).colorScheme.onSurfaceVariant, |
| 121 | ), |
| 122 | ); |
| 123 | } |
| 124 | |
| 125 | if (item is WalletAddressListHeader) { |
| 126 | cell = HeaderTile( |
| 127 | title: S.of(context).addresses, |
| 128 | walletAddressListViewModel: widget.addressListViewModel, |
| 129 | showTrailingButton: widget.addressListViewModel.showAddManualAddresses, |
| 130 | showSearchButton: true, |
| 131 | onSearchCallback: updateItems, |
| 132 | trailingButtonTap: () => |
| 133 | Navigator.of(context).pushNamed(Routes.newSubaddress).then((value) { |
| 134 | updateItems(); // refresh the new address |
| 135 | }), |
| 136 | trailingIcon: Icon( |
| 137 | Icons.add, |
| 138 | size: 20, |
| 139 | color: Theme.of(context).colorScheme.onSurfaceVariant, |
| 140 | ), |
| 141 | ); |
| 142 | } |
| 143 | |
| 144 | if (item is WalletAddressListItem) { |
| 145 | if (item.isHidden && !showHiddenAddresses) { |
| 146 | cell = Container(); |
| 147 | } else if (!item.isHidden && showHiddenAddresses) { |
| 148 | cell = Container(); |
| 149 | } else { |
| 150 | cell = Observer( |
| 151 | builder: (_) { |
| 152 | final isCurrent = |
| 153 | item.address == widget.addressListViewModel.address.address && editable; |
| 154 | final backgroundColor = isCurrent |
| 155 | ? Theme.of(context).colorScheme.primary |
| 156 | : Theme.of(context).colorScheme.surface; |
| 157 | final textColor = isCurrent |
| 158 | ? Theme.of(context).colorScheme.onPrimary |
| 159 | : Theme.of(context).colorScheme.onSurface; |
| 160 | |
| 161 | return AddressCell.fromItem( |
| 162 | item, |
| 163 | isCurrent: isCurrent, |
| 164 | walletType: widget.addressListViewModel.type, |
| 165 | hasBalance: widget.addressListViewModel.isBalanceAvailable, |
| 166 | hasReceived: widget.addressListViewModel.isReceivedAvailable, |
| 167 | // hasReceived: |
| 168 | backgroundColor: backgroundColor, |
| 169 | textColor: textColor, |
| 170 | onTap: (_) { |
| 171 | if (widget.onSelect != null) { |
| 172 | widget.onSelect!(item.address); |
| 173 | return; |
| 174 | } |
| 175 | widget.addressListViewModel.setAddress(item); |
| 176 | }, |
| 177 | onEdit: editable |
| 178 | ? () => Navigator.of(context) |
| 179 | .pushNamed(Routes.newSubaddress, arguments: item) |
| 180 | .then((value) { |
| 181 | updateItems(); // refresh the new address |
| 182 | }) |
| 183 | : null, |
| 184 | isHidden: item.isHidden, |
| 185 | onHide: () => _hideAddress(item), |
| 186 | ); |
| 187 | }, |
| 188 | ); |
| 189 | } |
| 190 | } |
| 191 | |
| 192 | return index != 0 |
| 193 | ? cell |
| 194 | : ClipRRect( |
| 195 | borderRadius: BorderRadius.only( |
| 196 | topLeft: Radius.circular(30), |
| 197 | topRight: Radius.circular(30), |
| 198 | ), |
| 199 | child: cell, |
| 200 | ); |
| 201 | }, |
| 202 | ); |
| 203 | } |
| 204 | |
| 205 | void _hideAddress(WalletAddressListItem item) async { |
| 206 | await widget.addressListViewModel.toggleHideAddress(item); |
| 207 | updateItems(); |
| 208 | } |
| 209 | } |