From d8368172ff36890b0acb6b0710dd1a7fe4ae6b3d Mon Sep 17 00:00:00 2001 From: Serhii Date: Fri, 15 Dec 2023 15:31:03 +0200 Subject: [PATCH] list of used electrum addresses --- cw_bitcoin/lib/electrum_wallet_addresses.dart | 3 + lib/di.dart | 3 + lib/router.dart | 4 + lib/routes.dart | 1 + .../dashboard/pages/address_list_page.dart | 94 +++++++++++++++++++ .../screens/dashboard/pages/address_page.dart | 49 ++++++---- .../receive/widgets/address_list_item.dart | 59 ++++++++++++ lib/src/widgets/search_bar_widget.dart | 4 +- 8 files changed, 200 insertions(+), 17 deletions(-) create mode 100644 lib/src/screens/dashboard/pages/address_list_page.dart create mode 100644 lib/src/screens/receive/widgets/address_list_item.dart diff --git a/cw_bitcoin/lib/electrum_wallet_addresses.dart b/cw_bitcoin/lib/electrum_wallet_addresses.dart index ab99a875c..f1009ceee 100644 --- a/cw_bitcoin/lib/electrum_wallet_addresses.dart +++ b/cw_bitcoin/lib/electrum_wallet_addresses.dart @@ -86,6 +86,9 @@ 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..659a640ec 100644 --- a/lib/di.dart +++ b/lib/di.dart @@ -29,6 +29,7 @@ import 'package:cake_wallet/src/screens/dashboard/desktop_widgets/desktop_wallet import 'package:cake_wallet/src/screens/dashboard/edit_token_page.dart'; import 'package:cake_wallet/src/screens/dashboard/home_settings_page.dart'; import 'package:cake_wallet/src/screens/dashboard/pages/transactions_page.dart'; +import 'package:cake_wallet/src/screens/dashboard/pages/address_list_page.dart'; import 'package:cake_wallet/src/screens/nano/nano_change_rep_page.dart'; import 'package:cake_wallet/src/screens/nano_accounts/nano_account_edit_or_create_page.dart'; import 'package:cake_wallet/src/screens/nano_accounts/nano_account_list_page.dart'; @@ -1183,5 +1184,7 @@ Future setup({ getIt.registerFactory(() => NFTViewModel(appStore, getIt.get())); getIt.registerFactory(() => TorPage(getIt.get())); + getIt.registerFactory(() => AddressListPage(wallet: getIt.get().wallet!)); + _isSetupFinished = true; } diff --git a/lib/router.dart b/lib/router.dart index 11eeb582b..671dfe361 100644 --- a/lib/router.dart +++ b/lib/router.dart @@ -15,6 +15,7 @@ import 'package:cake_wallet/src/screens/buy/webview_page.dart'; import 'package:cake_wallet/src/screens/dashboard/edit_token_page.dart'; import 'package:cake_wallet/src/screens/dashboard/home_settings_page.dart'; import 'package:cake_wallet/src/screens/dashboard/pages/nft_details_page.dart'; +import 'package:cake_wallet/src/screens/dashboard/pages/address_list_page.dart'; import 'package:cake_wallet/src/screens/nano/nano_change_rep_page.dart'; import 'package:cake_wallet/src/screens/nano_accounts/nano_account_edit_or_create_page.dart'; import 'package:cake_wallet/src/screens/nodes/pow_node_create_or_edit_page.dart'; @@ -630,6 +631,9 @@ Route createRoute(RouteSettings settings) { case Routes.torPage: return MaterialPageRoute(builder: (_) => getIt.get()); + case Routes.addressListPage: + return MaterialPageRoute(builder: (_) => getIt.get()); + default: return MaterialPageRoute( builder: (_) => Scaffold( diff --git a/lib/routes.dart b/lib/routes.dart index 60801297a..024baa1da 100644 --- a/lib/routes.dart +++ b/lib/routes.dart @@ -105,4 +105,5 @@ class Routes { static const nftDetailsPage = '/nft_details_page'; static const importNFTPage = '/import_nft_page'; static const torPage = '/tor_page'; + static const addressListPage = '/address_list_page'; } diff --git a/lib/src/screens/dashboard/pages/address_list_page.dart b/lib/src/screens/dashboard/pages/address_list_page.dart new file mode 100644 index 000000000..a0a5dffef --- /dev/null +++ b/lib/src/screens/dashboard/pages/address_list_page.dart @@ -0,0 +1,94 @@ +import 'package:cake_wallet/src/screens/base_page.dart'; +import 'package:cake_wallet/src/screens/receive/widgets/address_list_item.dart'; +import 'package:cake_wallet/src/widgets/search_bar_widget.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'; + +class AddressListPage extends BasePage { + AddressListPage({required WalletBase wallet}) : this._wallet = wallet; + + @override + String get title => 'Address list'; + + final WalletBase _wallet; + + Widget body(BuildContext context) => AddressListBody(wallet: _wallet); +} + +class AddressListBody extends StatefulWidget { + const AddressListBody({required this.wallet}); + + final WalletBase wallet; + + @override + State createState() => _AddressListBodyState(wallet: wallet); +} + +class _AddressListBodyState extends State { + _AddressListBodyState({required this.wallet}); + + final WalletBase wallet; + late TextEditingController searchController; + late List filteredAddresses; + + @override + void initState() { + super.initState(); + searchController = TextEditingController(); + filteredAddresses = getAddresses(); + searchController.addListener(() { + filterAddresses(); + }); + } + + List getAddresses() { + return (widget.wallet as ElectrumWallet).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) => (wallet is ElectrumWallet) + ? Column( + children: [ + Padding( + padding: const EdgeInsets.symmetric(horizontal: 24), + child: SearchBarWidget( + searchController: searchController, + borderRadius: 12, + searchIconColor: Theme.of(context).primaryColor, + hintText: 'Search address'), + ), + Expanded( + child: Padding( + padding: EdgeInsets.fromLTRB(24, 12, 24, 24), + child: ListView.separated( + itemCount: filteredAddresses.length, + separatorBuilder: (_, __) => SizedBox(height: 15), + itemBuilder: (_, int index) { + final item = filteredAddresses[index]; + return AddressListItem( + address: item.address, isChange: item.isHidden); + }, + ), + ), + ), + ], + ) + : Container(); +} diff --git a/lib/src/screens/dashboard/pages/address_page.dart b/lib/src/screens/dashboard/pages/address_page.dart index ff21d4aad..a8e791574 100644 --- a/lib/src/screens/dashboard/pages/address_page.dart +++ b/lib/src/screens/dashboard/pages/address_page.dart @@ -15,6 +15,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_bitcoin/electrum_wallet.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'; @@ -104,22 +105,38 @@ class AddressPage extends BasePage { Widget? trailing(BuildContext context) { return Material( color: Colors.transparent, - child: Semantics( - label: S.of(context).share, - child: IconButton( - padding: EdgeInsets.zero, - constraints: BoxConstraints(), - highlightColor: Colors.transparent, - splashColor: Colors.transparent, - iconSize: 25, - onPressed: () { - ShareUtil.share( - text: addressListViewModel.uri.toString(), - context: context, - ); - }, - icon: Icon(Icons.share, size: 20, color: pageIconColor(context)), - ), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + if(dashboardViewModel.wallet is ElectrumWallet) + TextButton( + onPressed: () => Navigator.of(context).pushNamed(Routes.addressListPage), + child: Text( + 'address list', + style: TextStyle( + fontSize: 14, + fontWeight: FontWeight.w500, + color: titleColor(context)), + ), + ), + Semantics( + label: S.of(context).share, + child: IconButton( + padding: EdgeInsets.zero, + constraints: BoxConstraints(), + highlightColor: Colors.transparent, + splashColor: Colors.transparent, + iconSize: 25, + onPressed: () { + ShareUtil.share( + text: addressListViewModel.uri.toString(), + context: context, + ); + }, + icon: Icon(Icons.share, size: 20, color: pageIconColor(context)), + ), + ), + ], ), ); } diff --git a/lib/src/screens/receive/widgets/address_list_item.dart b/lib/src/screens/receive/widgets/address_list_item.dart new file mode 100644 index 000000000..b324f8fc0 --- /dev/null +++ b/lib/src/screens/receive/widgets/address_list_item.dart @@ -0,0 +1,59 @@ +import 'package:auto_size_text/auto_size_text.dart'; +import 'package:cake_wallet/generated/i18n.dart'; +import 'package:flutter/material.dart'; + +class AddressListItem extends StatelessWidget { + AddressListItem({required this.address, required this.isChange}); + + final String address; + final bool isChange; + + @override + Widget build(BuildContext context) { + return ConstrainedBox( + constraints: BoxConstraints(maxHeight: 70, minHeight: 50), + child: Container( + padding: EdgeInsets.symmetric(vertical: 6, horizontal: 12), + decoration: BoxDecoration( + borderRadius: BorderRadius.all(Radius.circular(12)), + color: Theme.of(context).primaryColor), + child: Column( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + AutoSizeText( + address, + style: TextStyle( + color: Colors.white, + fontSize: 15, + fontWeight: FontWeight.w600), + maxLines: 1, + ), + if (isChange) + Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + Container( + height: 17, + padding: EdgeInsets.only(left: 6, right: 6), + decoration: BoxDecoration( + borderRadius: + BorderRadius.all(Radius.circular(8.5)), + color: Colors.white), + alignment: Alignment.center, + child: Text( + S.of(context).unspent_change, + style: TextStyle( + color: Theme.of(context).primaryColor, + fontSize: 7, + fontWeight: FontWeight.w600, + ), + ), + ), + ], + ), + ])), + ); + } +} diff --git a/lib/src/widgets/search_bar_widget.dart b/lib/src/widgets/search_bar_widget.dart index dc604934f..b480d0dd7 100644 --- a/lib/src/widgets/search_bar_widget.dart +++ b/lib/src/widgets/search_bar_widget.dart @@ -7,11 +7,13 @@ class SearchBarWidget extends StatelessWidget { required this.searchController, this.hintText, this.borderRadius = 14, + this.searchIconColor }); final TextEditingController searchController; final String? hintText; final double borderRadius; + final Color? searchIconColor; @override Widget build(BuildContext context) { @@ -22,7 +24,7 @@ class SearchBarWidget extends StatelessWidget { hintText: hintText ?? S.of(context).search_currency, hintStyle: TextStyle(color: Theme.of(context).extension()!.searchHintColor), prefixIcon: Image.asset("assets/images/search_icon.png", - color: Theme.of(context).extension()!.searchIconColor), + color: searchIconColor ?? Theme.of(context).extension()!.searchIconColor), filled: true, fillColor: Theme.of(context).extension()!.searchBackgroundFillColor, alignLabelWithHint: false,