From d6618518ad130a935080db38206a2c8c045fd2f5 Mon Sep 17 00:00:00 2001 From: julian Date: Mon, 5 Sep 2022 19:18:45 -0600 Subject: [PATCH] receive screen redesign and added generate new address button --- assets/svg/share-2.svg | 14 + .../generate_receiving_uri_qr_code_view.dart | 45 +++- lib/pages/receive_view/receive_view.dart | 242 ++++++++++++------ lib/pages/wallet_view/wallet_view.dart | 10 +- lib/route_generator.dart | 2 +- .../coins/bitcoin/bitcoin_wallet.dart | 30 +++ lib/services/coins/coin_service.dart | 2 + .../coins/dogecoin/dogecoin_wallet.dart | 29 +++ .../coins/epiccash/epiccash_wallet.dart | 25 ++ lib/services/coins/firo/firo_wallet.dart | 24 ++ lib/services/coins/manager.dart | 8 + lib/services/coins/monero/monero_wallet.dart | 29 +++ lib/utilities/assets.dart | 1 + pubspec.yaml | 1 + 14 files changed, 368 insertions(+), 94 deletions(-) create mode 100644 assets/svg/share-2.svg diff --git a/assets/svg/share-2.svg b/assets/svg/share-2.svg new file mode 100644 index 000000000..d002d2bff --- /dev/null +++ b/assets/svg/share-2.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/lib/pages/receive_view/generate_receiving_uri_qr_code_view.dart b/lib/pages/receive_view/generate_receiving_uri_qr_code_view.dart index a473d0d45..037677a40 100644 --- a/lib/pages/receive_view/generate_receiving_uri_qr_code_view.dart +++ b/lib/pages/receive_view/generate_receiving_uri_qr_code_view.dart @@ -6,10 +6,12 @@ import 'dart:ui' as ui; import 'package:decimal/decimal.dart'; import 'package:flutter/material.dart'; import 'package:flutter/rendering.dart'; +import 'package:flutter_svg/svg.dart'; import 'package:path_provider/path_provider.dart'; import 'package:qr_flutter/qr_flutter.dart'; import 'package:share_plus/share_plus.dart'; import 'package:stackwallet/notifications/show_flush_bar.dart'; +import 'package:stackwallet/utilities/assets.dart'; import 'package:stackwallet/utilities/cfcolors.dart'; import 'package:stackwallet/utilities/clipboard_interface.dart'; import 'package:stackwallet/utilities/constants.dart'; @@ -318,8 +320,7 @@ class _GenerateUriQrCodeViewState extends State { child: QrImage( data: uriString, size: width, - backgroundColor: - CFColors.almostWhite, + backgroundColor: CFColors.white, foregroundColor: CFColors.stackAccent, ), @@ -344,12 +345,40 @@ class _GenerateUriQrCodeViewState extends State { CFColors.buttonGray, ), ), - child: Text( - "Share", - style: - STextStyles.button.copyWith( - color: CFColors.stackAccent, - ), + child: Row( + mainAxisAlignment: + MainAxisAlignment.center, + crossAxisAlignment: + CrossAxisAlignment.center, + children: [ + Center( + child: SvgPicture.asset( + Assets.svg.share, + width: 14, + height: 14, + ), + ), + const SizedBox( + width: 4, + ), + Column( + children: [ + Text( + "Share", + textAlign: + TextAlign.center, + style: STextStyles.button + .copyWith( + color: CFColors + .stackAccent, + ), + ), + const SizedBox( + height: 2, + ), + ], + ), + ], ), ), ), diff --git a/lib/pages/receive_view/receive_view.dart b/lib/pages/receive_view/receive_view.dart index 4662abac2..b0f49682b 100644 --- a/lib/pages/receive_view/receive_view.dart +++ b/lib/pages/receive_view/receive_view.dart @@ -1,36 +1,114 @@ +import 'dart:async'; + import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:qr_flutter/qr_flutter.dart'; import 'package:stackwallet/notifications/show_flush_bar.dart'; import 'package:stackwallet/pages/receive_view/generate_receiving_uri_qr_code_view.dart'; +import 'package:stackwallet/providers/providers.dart'; import 'package:stackwallet/route_generator.dart'; import 'package:stackwallet/utilities/assets.dart'; import 'package:stackwallet/utilities/cfcolors.dart'; import 'package:stackwallet/utilities/clipboard_interface.dart'; -import 'package:stackwallet/utilities/constants.dart'; import 'package:stackwallet/utilities/enums/coin_enum.dart'; import 'package:stackwallet/utilities/enums/flush_bar_type.dart'; import 'package:stackwallet/utilities/text_styles.dart'; import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart'; +import 'package:stackwallet/widgets/custom_buttons/blue_text_button.dart'; +import 'package:stackwallet/widgets/custom_loading_overlay.dart'; +import 'package:stackwallet/widgets/rounded_white_container.dart'; -class ReceiveView extends StatelessWidget { +class ReceiveView extends ConsumerStatefulWidget { const ReceiveView({ Key? key, required this.coin, - required this.receivingAddress, + required this.walletId, this.clipboard = const ClipboardWrapper(), }) : super(key: key); static const String routeName = "/receiveView"; final Coin coin; - final String receivingAddress; + final String walletId; final ClipboardInterface clipboard; + @override + ConsumerState createState() => _ReceiveViewState(); +} + +class _ReceiveViewState extends ConsumerState { + late final Coin coin; + late final String walletId; + late final ClipboardInterface clipboard; + + Future generateNewAddress() async { + bool shouldPop = false; + unawaited( + showDialog( + context: context, + builder: (_) { + return WillPopScope( + onWillPop: () async => shouldPop, + child: const CustomLoadingOverlay( + message: "Generating address", + eventBus: null, + ), + ); + }, + ), + ); + + await ref + .read(walletsChangeNotifierProvider) + .getManager(walletId) + .generateNewAddress(); + + shouldPop = true; + + if (mounted) { + Navigator.of(context) + .popUntil(ModalRoute.withName(ReceiveView.routeName)); + } + } + + String receivingAddress = ""; + + @override + void initState() { + walletId = widget.walletId; + coin = widget.coin; + clipboard = widget.clipboard; + + WidgetsBinding.instance.addPostFrameCallback((timeStamp) async { + final address = await ref + .read(walletsChangeNotifierProvider) + .getManager(walletId) + .currentReceivingAddress; + setState(() { + receivingAddress = address; + }); + }); + + super.initState(); + } + @override Widget build(BuildContext context) { debugPrint("BUILD: $runtimeType"); + + ref.listen( + ref + .read(walletsChangeNotifierProvider) + .getManagerProvider(walletId) + .select((value) => value.currentReceivingAddress), + (previous, next) { + if (next is Future) { + next.then((value) => setState(() => receivingAddress = value)); + } + }); + return Scaffold( backgroundColor: CFColors.almostWhite, appBar: AppBar( @@ -52,15 +130,19 @@ class ReceiveView extends StatelessWidget { child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ - Container( - decoration: BoxDecoration( - color: CFColors.white, - borderRadius: BorderRadius.circular( - Constants.size.circularBorderRadius, - ), - ), - child: Padding( - padding: const EdgeInsets.all(12.0), + GestureDetector( + onTap: () { + clipboard.setData( + ClipboardData(text: receivingAddress), + ); + showFloatingFlushBar( + type: FlushBarType.info, + message: "Copied to clipboard", + iconAsset: Assets.svg.copy, + context: context, + ); + }, + child: RoundedWhiteContainer( child: Column( children: [ Row( @@ -70,35 +152,22 @@ class ReceiveView extends StatelessWidget { style: STextStyles.itemSubtitle, ), const Spacer(), - GestureDetector( - onTap: () { - clipboard.setData( - ClipboardData(text: receivingAddress), - ); - showFloatingFlushBar( - type: FlushBarType.info, - message: "Copied to clipboard", - iconAsset: Assets.svg.copy, - context: context, - ); - }, - child: Row( - children: [ - SvgPicture.asset( - Assets.svg.copy, - width: 10, - height: 10, - color: CFColors.link2, - ), - const SizedBox( - width: 4, - ), - Text( - "Copy", - style: STextStyles.link2, - ), - ], - ), + Row( + children: [ + SvgPicture.asset( + Assets.svg.copy, + width: 10, + height: 10, + color: CFColors.link2, + ), + const SizedBox( + width: 4, + ), + Text( + "Copy", + style: STextStyles.link2, + ), + ], ), ], ), @@ -119,47 +188,62 @@ class ReceiveView extends StatelessWidget { ), ), ), - const SizedBox( - height: 30, - ), - Center( - child: QrImage( - data: "${coin.uriScheme}:$receivingAddress", - size: MediaQuery.of(context).size.width / 2, - foregroundColor: CFColors.stackAccent, + if (coin != Coin.epicCash) + const SizedBox( + height: 12, ), - ), - const SizedBox( - height: 30, - ), - // Spacer( - // flex: 7, - // ), - TextButton( - onPressed: () { - Navigator.of(context).push( - RouteGenerator.getRoute( - shouldUseMaterialRoute: - RouteGenerator.useMaterialPageRoute, - builder: (_) => GenerateUriQrCodeView( - coin: coin, - receivingAddress: receivingAddress, - ), - settings: const RouteSettings( - name: GenerateUriQrCodeView.routeName, - ), + if (coin != Coin.epicCash) + TextButton( + onPressed: generateNewAddress, + style: ButtonStyle( + backgroundColor: MaterialStateProperty.all( + CFColors.buttonGray, + ), + ), + child: Text( + "Generate new address", + style: STextStyles.button.copyWith( + color: CFColors.stackAccent, ), - ); - }, - style: ButtonStyle( - backgroundColor: MaterialStateProperty.all( - CFColors.buttonGray, ), ), - child: Text( - "Generate QR Code", - style: STextStyles.button.copyWith( - color: CFColors.stackAccent, + const SizedBox( + height: 30, + ), + RoundedWhiteContainer( + child: Padding( + padding: const EdgeInsets.all(8.0), + child: Center( + child: Column( + children: [ + QrImage( + data: "${coin.uriScheme}:$receivingAddress", + size: MediaQuery.of(context).size.width / 2, + foregroundColor: CFColors.stackAccent, + ), + const SizedBox( + height: 20, + ), + BlueTextButton( + text: "Create new QR code", + onTap: () async { + unawaited(Navigator.of(context).push( + RouteGenerator.getRoute( + shouldUseMaterialRoute: + RouteGenerator.useMaterialPageRoute, + builder: (_) => GenerateUriQrCodeView( + coin: coin, + receivingAddress: receivingAddress, + ), + settings: const RouteSettings( + name: GenerateUriQrCodeView.routeName, + ), + ), + )); + }, + ), + ], + ), ), ), ), diff --git a/lib/pages/wallet_view/wallet_view.dart b/lib/pages/wallet_view/wallet_view.dart index 3f097ddf3..198e30904 100644 --- a/lib/pages/wallet_view/wallet_view.dart +++ b/lib/pages/wallet_view/wallet_view.dart @@ -532,19 +532,17 @@ class _WalletViewState extends ConsumerState { onExchangePressed: () => _onExchangePressed(context), onReceivePressed: () async { - final address = await ref - .read(managerProvider) - .currentReceivingAddress; final coin = ref.read(managerProvider).coin; if (mounted) { - Navigator.of(context).pushNamed( + unawaited( + Navigator.of(context).pushNamed( ReceiveView.routeName, arguments: Tuple2( - address, + walletId, coin, ), - ); + )); } }, onSendPressed: () { diff --git a/lib/route_generator.dart b/lib/route_generator.dart index 329018d9a..001c21b0d 100644 --- a/lib/route_generator.dart +++ b/lib/route_generator.dart @@ -681,7 +681,7 @@ class RouteGenerator { return getRoute( shouldUseMaterialRoute: useMaterialPageRoute, builder: (_) => ReceiveView( - receivingAddress: args.item1, + walletId: args.item1, coin: args.item2, ), settings: RouteSettings( diff --git a/lib/services/coins/bitcoin/bitcoin_wallet.dart b/lib/services/coins/bitcoin/bitcoin_wallet.dart index 0750e9b93..342fb7b91 100644 --- a/lib/services/coins/bitcoin/bitcoin_wallet.dart +++ b/lib/services/coins/bitcoin/bitcoin_wallet.dart @@ -3825,4 +3825,34 @@ class BitcoinWallet extends CoinServiceAPI { return available - estimatedFee; } + + @override + Future generateNewAddress() async { + try { + await _incrementAddressIndexForChain( + 0, DerivePathType.bip84); // First increment the receiving index + final newReceivingIndex = DB.instance.get( + boxName: walletId, + key: 'receivingIndexP2WPKH') as int; // Check the new receiving index + final newReceivingAddress = await _generateAddressForChain( + 0, + newReceivingIndex, + DerivePathType + .bip84); // Use new index to derive a new receiving address + await _addToAddressesArrayForChain( + newReceivingAddress, + 0, + DerivePathType + .bip84); // Add that new receiving address to the array of receiving addresses + _currentReceivingAddress = Future(() => + newReceivingAddress); // Set the new receiving address that the service + + return true; + } catch (e, s) { + Logging.instance.log( + "Exception rethrown from generateNewAddress(): $e\n$s", + level: LogLevel.Error); + return false; + } + } } diff --git a/lib/services/coins/coin_service.dart b/lib/services/coins/coin_service.dart index 3d8b1bd10..bc0e4be28 100644 --- a/lib/services/coins/coin_service.dart +++ b/lib/services/coins/coin_service.dart @@ -212,4 +212,6 @@ abstract class CoinServiceAPI { bool get isConnected; Future estimateFeeFor(int satoshiAmount, int feeRate); + + Future generateNewAddress(); } diff --git a/lib/services/coins/dogecoin/dogecoin_wallet.dart b/lib/services/coins/dogecoin/dogecoin_wallet.dart index 7a3a70a96..a7b2132ad 100644 --- a/lib/services/coins/dogecoin/dogecoin_wallet.dart +++ b/lib/services/coins/dogecoin/dogecoin_wallet.dart @@ -3011,6 +3011,35 @@ class DogecoinWallet extends CoinServiceAPI { return available - estimatedFee; } + + Future generateNewAddress() async { + try { + await _incrementAddressIndexForChain( + 0, DerivePathType.bip44); // First increment the receiving index + final newReceivingIndex = DB.instance.get( + boxName: walletId, + key: 'receivingIndexP2PKH') as int; // Check the new receiving index + final newReceivingAddress = await _generateAddressForChain( + 0, + newReceivingIndex, + DerivePathType + .bip44); // Use new index to derive a new receiving address + await _addToAddressesArrayForChain( + newReceivingAddress, + 0, + DerivePathType + .bip44); // Add that new receiving address to the array of receiving addresses + _currentReceivingAddressP2PKH = Future(() => + newReceivingAddress); // Set the new receiving address that the service + + return true; + } catch (e, s) { + Logging.instance.log( + "Exception rethrown from generateNewAddress(): $e\n$s", + level: LogLevel.Error); + return false; + } + } } // Dogecoin Network diff --git a/lib/services/coins/epiccash/epiccash_wallet.dart b/lib/services/coins/epiccash/epiccash_wallet.dart index a99718dd3..514ddb79f 100644 --- a/lib/services/coins/epiccash/epiccash_wallet.dart +++ b/lib/services/coins/epiccash/epiccash_wallet.dart @@ -2306,4 +2306,29 @@ class EpicCashWallet extends CoinServiceAPI { // TODO: implement this return currentFee; } + + // not used in epic currently + @override + Future generateNewAddress() async { + try { + // await incrementAddressIndexForChain( + // 0); // First increment the receiving index + // final newReceivingIndex = + // DB.instance.get(boxName: walletId, key: 'receivingIndex') + // as int; // Check the new receiving index + // final newReceivingAddress = await _generateAddressForChain(0, + // newReceivingIndex); // Use new index to derive a new receiving address + // await addToAddressesArrayForChain(newReceivingAddress, + // 0); // Add that new receiving address to the array of receiving addresses + // _currentReceivingAddress = Future(() => + // newReceivingAddress); // Set the new receiving address that the service + + return true; + } catch (e, s) { + Logging.instance.log( + "Exception rethrown from generateNewAddress(): $e\n$s", + level: LogLevel.Error); + return false; + } + } } diff --git a/lib/services/coins/firo/firo_wallet.dart b/lib/services/coins/firo/firo_wallet.dart index c8bd9be24..4108cce39 100644 --- a/lib/services/coins/firo/firo_wallet.dart +++ b/lib/services/coins/firo/firo_wallet.dart @@ -3857,4 +3857,28 @@ class FiroWallet extends CoinServiceAPI { rethrow; } } + + @override + Future generateNewAddress() async { + try { + await incrementAddressIndexForChain( + 0); // First increment the receiving index + final newReceivingIndex = + DB.instance.get(boxName: walletId, key: 'receivingIndex') + as int; // Check the new receiving index + final newReceivingAddress = await _generateAddressForChain(0, + newReceivingIndex); // Use new index to derive a new receiving address + await addToAddressesArrayForChain(newReceivingAddress, + 0); // Add that new receiving address to the array of receiving addresses + _currentReceivingAddress = Future(() => + newReceivingAddress); // Set the new receiving address that the service + + return true; + } catch (e, s) { + Logging.instance.log( + "Exception rethrown from generateNewAddress(): $e\n$s", + level: LogLevel.Error); + return false; + } + } } diff --git a/lib/services/coins/manager.dart b/lib/services/coins/manager.dart index 659db73bd..c8329ec28 100644 --- a/lib/services/coins/manager.dart +++ b/lib/services/coins/manager.dart @@ -266,4 +266,12 @@ class Manager with ChangeNotifier { Future estimateFeeFor(int satoshiAmount, int feeRate) async { return _currentWallet.estimateFeeFor(satoshiAmount, feeRate); } + + Future generateNewAddress() async { + final success = await _currentWallet.generateNewAddress(); + if (success) { + notifyListeners(); + } + return success; + } } diff --git a/lib/services/coins/monero/monero_wallet.dart b/lib/services/coins/monero/monero_wallet.dart index 95794a356..b63e711a4 100644 --- a/lib/services/coins/monero/monero_wallet.dart +++ b/lib/services/coins/monero/monero_wallet.dart @@ -1521,4 +1521,33 @@ class MoneroWallet extends CoinServiceAPI { 10000; return fee; } + + @override + Future generateNewAddress() async { + try { + const String indexKey = "receivingIndex"; + // First increment the receiving index + await _incrementAddressIndexForChain(0); + final newReceivingIndex = + DB.instance.get(boxName: walletId, key: indexKey) as int; + + // Use new index to derive a new receiving address + final newReceivingAddress = + await _generateAddressForChain(0, newReceivingIndex); + + // Add that new receiving address to the array of receiving addresses + await _addToAddressesArrayForChain(newReceivingAddress, 0); + + // Set the new receiving address that the service + + _currentReceivingAddress = Future(() => newReceivingAddress); + + return true; + } catch (e, s) { + Logging.instance.log( + "Exception rethrown from generateNewAddress(): $e\n$s", + level: LogLevel.Error); + return false; + } + } } diff --git a/lib/utilities/assets.dart b/lib/utilities/assets.dart index 9adc50e59..906dd509c 100644 --- a/lib/utilities/assets.dart +++ b/lib/utilities/assets.dart @@ -84,6 +84,7 @@ class _SVG { String get solidSliders => "assets/svg/sliders-solid.svg"; String get questionMessage => "assets/svg/message-question.svg"; String get envelope => "assets/svg/envelope.svg"; + String get share => "assets/svg/share-2.svg"; String get receive => "assets/svg/tx-icon-receive.svg"; String get receivePending => "assets/svg/tx-icon-receive-pending.svg"; diff --git a/pubspec.yaml b/pubspec.yaml index f2faf1497..7f8bd4507 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -255,6 +255,7 @@ flutter: - assets/svg/sliders-solid.svg - assets/svg/message-question.svg - assets/svg/envelope.svg + - assets/svg/share-2.svg # coin icons - assets/svg/coin_icons/Bitcoin.svg - assets/svg/coin_icons/Dogecoin.svg