From 9883965cb3a5afe6bcaaaba9e85fc093e6866c46 Mon Sep 17 00:00:00 2001 From: Serhii Date: Wed, 20 Dec 2023 13:01:30 +0200 Subject: [PATCH] add list of used electrum addresses --- cw_bitcoin/lib/electrum_wallet_addresses.dart | 4 + lib/di.dart | 4 + .../screens/dashboard/pages/address_page.dart | 34 +++-- .../widgets/electrum_address_list_page.dart | 119 ++++++++++++++++++ .../widgets/electrum_address_tile.dart | 71 +++++++++++ .../wallet_address_list_view_model.dart | 5 + 6 files changed, 226 insertions(+), 11 deletions(-) create mode 100644 lib/src/screens/receive/widgets/electrum_address_list_page.dart create mode 100644 lib/src/screens/receive/widgets/electrum_address_tile.dart diff --git a/cw_bitcoin/lib/electrum_wallet_addresses.dart b/cw_bitcoin/lib/electrum_wallet_addresses.dart index ab99a875c..caa619d58 100644 --- a/cw_bitcoin/lib/electrum_wallet_addresses.dart +++ b/cw_bitcoin/lib/electrum_wallet_addresses.dart @@ -86,6 +86,10 @@ abstract class ElectrumWalletAddressesBase extends WalletAddresses with Store { return acc; }); + List get usedAddressList => + addresses.where((element) => element.isUsed).toList(); + + Future discoverAddresses() async { await _discoverAddresses(mainHd, false); await _discoverAddresses(sideHd, true); diff --git a/lib/di.dart b/lib/di.dart index f3a03f03b..f32e2fcf6 100644 --- a/lib/di.dart +++ b/lib/di.dart @@ -35,6 +35,7 @@ import 'package:cake_wallet/src/screens/nano_accounts/nano_account_list_page.dar import 'package:cake_wallet/src/screens/nodes/pow_node_create_or_edit_page.dart'; import 'package:cake_wallet/src/screens/receive/anonpay_invoice_page.dart'; import 'package:cake_wallet/src/screens/receive/anonpay_receive_page.dart'; +import 'package:cake_wallet/src/screens/receive/widgets/electrum_address_list_page.dart'; import 'package:cake_wallet/src/screens/restore/wallet_restore_choose_derivation.dart'; import 'package:cake_wallet/src/screens/settings/display_settings_page.dart'; import 'package:cake_wallet/src/screens/settings/domain_lookups_page.dart'; @@ -660,6 +661,9 @@ Future setup({ getIt.registerFactory( () => NanoAccountListPage(accountListViewModel: getIt.get())); + getIt.registerFactory( + () => ElectrumAddressListPage(wallet: getIt.get().wallet!)); + /*getIt.registerFactory(() { final wallet = getIt.get().wallet; diff --git a/lib/src/screens/dashboard/pages/address_page.dart b/lib/src/screens/dashboard/pages/address_page.dart index ff21d4aad..72f7c1595 100644 --- a/lib/src/screens/dashboard/pages/address_page.dart +++ b/lib/src/screens/dashboard/pages/address_page.dart @@ -1,3 +1,4 @@ +import 'package:cake_wallet/src/screens/receive/widgets/electrum_address_list_page.dart'; import 'package:cake_wallet/themes/extensions/keyboard_theme.dart'; import 'package:cake_wallet/di.dart'; import 'package:cake_wallet/src/screens/base_page.dart'; @@ -15,6 +16,7 @@ import 'package:cake_wallet/utils/share_util.dart'; import 'package:cake_wallet/utils/show_pop_up.dart'; import 'package:cake_wallet/view_model/dashboard/receive_option_view_model.dart'; import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart'; +import 'package:cw_core/wallet_type.dart'; import 'package:flutter/material.dart'; import 'package:cake_wallet/view_model/wallet_address_list/wallet_address_list_view_model.dart'; import 'package:cake_wallet/src/screens/receive/widgets/qr_widget.dart'; @@ -155,12 +157,29 @@ class AddressPage extends BasePage { amountController: _amountController, isLight: dashboardViewModel.settingsStore.currentTheme.type == ThemeType.light))), + if (dashboardViewModel.isAutoGenerateSubaddressesEnabled || + addressListViewModel.showElectrumAddressDisclaimer) + Padding( + padding: const EdgeInsets.all(8.0), + child: Text(S.of(context).electrum_address_disclaimer, + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 15, + color: Theme.of(context).extension()!.labelTextColor)), + ), Observer(builder: (_) { - if (addressListViewModel.hasAddressList) { + if (addressListViewModel.hasAddressList || addressListViewModel.hasElectrumAddressList) { return GestureDetector( - onTap: () async => dashboardViewModel.isAutoGenerateSubaddressesEnabled + onTap: () async => + addressListViewModel.hasElectrumAddressList ? + await showPopUp( + context: context, + builder: (_) => getIt.get()) + : + dashboardViewModel.isAutoGenerateSubaddressesEnabled ? await showPopUp( - context: context, builder: (_) => getIt.get()) + context: context, + builder: (_) => getIt.get()) : Navigator.of(context).pushNamed(Routes.receive), child: Container( height: 50, @@ -210,14 +229,7 @@ class AddressPage extends BasePage { ), ), ); - } else if (dashboardViewModel.isAutoGenerateSubaddressesEnabled || - addressListViewModel.showElectrumAddressDisclaimer) { - return Text(S.of(context).electrum_address_disclaimer, - textAlign: TextAlign.center, - style: TextStyle( - fontSize: 15, - color: Theme.of(context).extension()!.labelTextColor)); - } else { + } else { return const SizedBox(); } }) diff --git a/lib/src/screens/receive/widgets/electrum_address_list_page.dart b/lib/src/screens/receive/widgets/electrum_address_list_page.dart new file mode 100644 index 000000000..a4784624e --- /dev/null +++ b/lib/src/screens/receive/widgets/electrum_address_list_page.dart @@ -0,0 +1,119 @@ +import 'package:cake_wallet/generated/i18n.dart'; +import 'package:cake_wallet/src/screens/receive/widgets/electrum_address_tile.dart'; +import 'package:cake_wallet/src/widgets/alert_background.dart'; +import 'package:cake_wallet/src/widgets/picker_inner_wrapper_widget.dart'; +import 'package:cake_wallet/src/widgets/search_bar_widget.dart'; +import 'package:cake_wallet/src/widgets/section_divider.dart'; +import 'package:cake_wallet/utils/show_bar.dart'; +import 'package:cw_bitcoin/bitcoin_address_record.dart'; +import 'package:cw_bitcoin/electrum_wallet.dart'; +import 'package:cw_core/wallet_base.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; + +class ElectrumAddressListPage extends StatefulWidget { + const ElectrumAddressListPage({required WalletBase wallet}) + : this._wallet = wallet; + + final WalletBase _wallet; + + @override + State createState() => + _ElectrumAddressListPageState(wallet: _wallet); +} + +class _ElectrumAddressListPageState extends State { + _ElectrumAddressListPageState({required WalletBase wallet}) + : this._wallet = wallet is ElectrumWallet ? wallet : null; + + final ElectrumWallet? _wallet; + final ScrollController controller = ScrollController(); + late TextEditingController searchController; + late List filteredAddresses; + + @override + void initState() { + super.initState(); + searchController = TextEditingController(); + filteredAddresses = getAddresses(); + searchController.addListener(() { + filterAddresses(); + }); + } + + List getAddresses() => + _wallet?.walletAddresses.usedAddressList ?? []; + + void filterAddresses() { + String searchText = searchController.text.toLowerCase(); + setState(() { + filteredAddresses = getAddresses().where((address) { + return address.address.toLowerCase().contains(searchText); + }).toList(); + }); + } + + @override + void dispose() { + searchController.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + double itemHeight = 65; + double buttonHeight = 62; + + return getAddresses().isEmpty + ? GestureDetector( + onTap: () => Navigator.of(context).pop(), + child: AlertBackground(child: PlaceholderWidget(context))) + : PickerInnerWrapperWidget( + title: 'Address List', + itemsHeight: (itemHeight * filteredAddresses.length) + buttonHeight, + children: [ + Padding( + padding: const EdgeInsets.only(top: 8, left: 12, right: 12), + child: SearchBarWidget( + searchController: searchController, + borderRadius: 12, + hintText: 'Search address'), + ), + Expanded( + child: Scrollbar( + controller: controller, + child: ListView.separated( + padding: EdgeInsets.zero, + controller: controller, + separatorBuilder: (context, index) => + const HorizontalSectionDivider(), + itemCount: filteredAddresses.length, + itemBuilder: (context, index) { + final item = filteredAddresses[index]; + return ElectrumAddressTile( + address: item.address, + isChange: item.isHidden, + onTap: () { + Clipboard.setData(ClipboardData(text: item.address)); + showBar( + context, + S.of(context).transaction_details_copied( + S.of(context).address)); + }, + ); + }, + ), + )), + ], + ); + } +} + +Widget PlaceholderWidget(BuildContext context) { + return Center( + child: Text( + 'Your previous used addresses will appear here', + style: TextStyle(fontSize: 14, color: Colors.white), + ), + ); +} diff --git a/lib/src/screens/receive/widgets/electrum_address_tile.dart b/lib/src/screens/receive/widgets/electrum_address_tile.dart new file mode 100644 index 000000000..4da7f6c20 --- /dev/null +++ b/lib/src/screens/receive/widgets/electrum_address_tile.dart @@ -0,0 +1,71 @@ +import 'package:cake_wallet/generated/i18n.dart'; +import 'package:cake_wallet/themes/extensions/account_list_theme.dart'; +import 'package:flutter/material.dart'; + +class ElectrumAddressTile extends StatelessWidget { + ElectrumAddressTile({ + required this.address, + required this.isChange, + required this.onTap, + }); + + final String address; + final bool isChange; + final Function() onTap; + + @override + Widget build(BuildContext context) { + final color = + Theme.of(context).extension()!.tilesBackgroundColor; + final textColor = + Theme.of(context).extension()!.tilesTextColor; + + return GestureDetector( + onTap: onTap, + child: Container( + height: 60, + width: double.infinity, + padding: EdgeInsets.symmetric(horizontal: 24), + color: color, + child: Column( + children: [ + Expanded(child: SizedBox()), + Expanded( + child: FittedBox( + fit: BoxFit.fitWidth, + child: Text( + address, + style: TextStyle( + fontSize: 15, + fontWeight: FontWeight.w600, + fontFamily: 'Lato', + color: Theme.of(context) + .extension()! + .tilesAmountColor, + decoration: TextDecoration.none, + ), + ), + ), + ), + Expanded( + child: Container( + width: double.infinity, + child: Text( + isChange ? S.of(context).unspent_change : '', + textAlign: TextAlign.right, + style: TextStyle( + fontSize: 12, + fontWeight: FontWeight.w600, + fontFamily: 'Lato', + color: textColor, + decoration: TextDecoration.none, + ), + ), + ), + ), + ], + ), + ), + ); + } +} 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 ade279124..8688e48e3 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 @@ -331,6 +331,11 @@ abstract class WalletAddressListViewModelBase extends WalletChangeListenerViewMo wallet.type == WalletType.litecoin || wallet.type == WalletType.bitcoinCash; + bool get hasElectrumAddressList => + wallet.type == WalletType.litecoin || + wallet.type == WalletType.bitcoinCash || + wallet.type == WalletType.bitcoin; + List _baseItems; final YatStore yatStore;