From 6418b28c08736aef5b4ef5092e046c64ae734d1f Mon Sep 17 00:00:00 2001 From: M Date: Mon, 16 Nov 2020 20:17:03 +0200 Subject: [PATCH] CAKE-177. CAKE-176. CAKE-115. --- lib/di.dart | 3 +- lib/monero/pending_monero_transaction.dart | 21 +- lib/src/screens/auth/auth_page.dart | 14 +- lib/src/screens/receive/receive_page.dart | 196 ++++++++++-------- .../screens/receive/widgets/qr_widget.dart | 8 +- 5 files changed, 142 insertions(+), 100 deletions(-) diff --git a/lib/di.dart b/lib/di.dart index 1dba56bc1..1571c925b 100644 --- a/lib/di.dart +++ b/lib/di.dart @@ -203,7 +203,8 @@ Future setup( authPageState.changeProcessText('Loading the wallet'); if (loginError != null) { - authPageState.changeProcessText('ERROR: ${loginError.toString()}'); + authPageState + .changeProcessText('ERROR: ${loginError.toString()}'); } ReactionDisposer _reaction; diff --git a/lib/monero/pending_monero_transaction.dart b/lib/monero/pending_monero_transaction.dart index ef18a8f12..c939110a0 100644 --- a/lib/monero/pending_monero_transaction.dart +++ b/lib/monero/pending_monero_transaction.dart @@ -5,6 +5,14 @@ import 'package:cake_wallet/entities/crypto_currency.dart'; import 'package:cake_wallet/core/amount_converter.dart'; import 'package:cake_wallet/core/pending_transaction.dart'; +class DoubleSpendException implements Exception { + DoubleSpendException(); + + @override + String toString() => + 'This transaction cannot be committed. This can be due to many reasons including the wallet not being synced, there is not enough XMR in your available balance, or previous transactions are not yet fully processed.'; +} + class PendingMoneroTransaction with PendingTransaction { PendingMoneroTransaction(this.pendingTransactionDescription); @@ -22,7 +30,18 @@ class PendingMoneroTransaction with PendingTransaction { CryptoCurrency.xmr, pendingTransactionDescription.fee); @override - Future commit() async => + Future commit() async { + try { monero_transaction_history.commitTransactionFromPointerAddress( address: pendingTransactionDescription.pointerAddress); + } catch (e) { + final message = e.toString(); + + if (message.contains('Reason: double spend')) { + throw DoubleSpendException(); + } + + rethrow; + } + } } diff --git a/lib/src/screens/auth/auth_page.dart b/lib/src/screens/auth/auth_page.dart index 82d765fd6..a1e82d18e 100644 --- a/lib/src/screens/auth/auth_page.dart +++ b/lib/src/screens/auth/auth_page.dart @@ -39,14 +39,12 @@ class AuthPageState extends State { _reaction ??= reaction((_) => widget.authViewModel.state, (ExecutionState state) { if (state is ExecutedSuccessfullyState) { - WidgetsBinding.instance.addPostFrameCallback((_) { - if (widget.onAuthenticationFinished != null) { - widget.onAuthenticationFinished(true, this); - } else { - _authBar?.dismiss(); - showBar(context, S.of(context).authenticated); - } - }); + if (widget.onAuthenticationFinished != null) { + widget.onAuthenticationFinished(true, this); + } else { + _authBar?.dismiss(); + showBar(context, S.of(context).authenticated); + } } if (state is IsExecutingState) { diff --git a/lib/src/screens/receive/receive_page.dart b/lib/src/screens/receive/receive_page.dart index 1ac4a073b..30d2b7cea 100644 --- a/lib/src/screens/receive/receive_page.dart +++ b/lib/src/screens/receive/receive_page.dart @@ -1,3 +1,4 @@ +import 'package:cake_wallet/src/widgets/keyboard_done_button.dart'; import 'package:cake_wallet/utils/show_pop_up.dart'; import 'package:flutter/material.dart'; import 'package:flutter/cupertino.dart'; @@ -15,9 +16,10 @@ import 'package:cake_wallet/view_model/wallet_address_list/wallet_address_list_h import 'package:cake_wallet/view_model/wallet_address_list/wallet_address_list_item.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'; +import 'package:keyboard_actions/keyboard_actions.dart'; class ReceivePage extends BasePage { - ReceivePage({this.addressListViewModel}); + ReceivePage({this.addressListViewModel}) : _cryptoAmountFocus = FocusNode(); final WalletAddressListViewModel addressListViewModel; @@ -33,6 +35,8 @@ class ReceivePage extends BasePage { @override Color get titleColor => Colors.white; + final FocusNode _cryptoAmountFocus; + @override Widget Function(BuildContext, Widget) get rootWrapper => (BuildContext context, Widget scaffold) => Container( @@ -67,93 +71,109 @@ class ReceivePage extends BasePage { @override Widget body(BuildContext context) { - return SingleChildScrollView( - child: Column( - children: [ - Padding( - padding: EdgeInsets.fromLTRB(24, 80, 24, 40), - child: QRWidget( - addressListViewModel: addressListViewModel, - isAmountFieldShow: true, - ), + return KeyboardActions( + config: KeyboardActionsConfig( + keyboardActionsPlatform: KeyboardActionsPlatform.IOS, + keyboardBarColor: isDarkTheme + ? Color.fromRGBO(48, 51, 60, 1.0) + : Color.fromRGBO(98, 98, 98, 1.0), + nextFocus: false, + actions: [ + KeyboardActionsItem( + focusNode: _cryptoAmountFocus, + toolbarButtons: [(_) => KeyboardDoneButton()], + ) + ]), + child: SingleChildScrollView( + child: Column( + children: [ + Padding( + padding: EdgeInsets.fromLTRB(24, 80, 24, 40), + child: QRWidget( + addressListViewModel: addressListViewModel, + isAmountFieldShow: true, + amountTextFieldFocusNode: _cryptoAmountFocus), + ), + Observer( + builder: (_) => ListView.separated( + padding: EdgeInsets.all(0), + separatorBuilder: (context, _) => Container( + height: 1, color: Theme.of(context).dividerColor), + shrinkWrap: true, + physics: NeverScrollableScrollPhysics(), + itemCount: addressListViewModel.items.length, + itemBuilder: (context, index) { + final item = addressListViewModel.items[index]; + Widget cell = Container(); + + if (item is WalletAccountListHeader) { + cell = HeaderTile( + onTap: () async => await showPopUp( + context: context, + builder: (_) => + getIt.get()), + title: S.of(context).accounts, + icon: Icon( + Icons.arrow_forward_ios, + size: 14, + color: + Theme.of(context).textTheme.display1.color, + )); + } + + if (item is WalletAddressListHeader) { + cell = HeaderTile( + onTap: () => Navigator.of(context) + .pushNamed(Routes.newSubaddress), + title: S.of(context).addresses, + icon: Icon( + Icons.add, + size: 20, + color: + Theme.of(context).textTheme.display1.color, + )); + } + + if (item is WalletAddressListItem) { + cell = Observer(builder: (_) { + final isCurrent = item.address == + addressListViewModel.address.address; + final backgroundColor = isCurrent + ? Theme.of(context) + .textTheme + .display3 + .decorationColor + : Theme.of(context) + .textTheme + .display2 + .decorationColor; + final textColor = isCurrent + ? Theme.of(context).textTheme.display3.color + : Theme.of(context).textTheme.display2.color; + + return AddressCell.fromItem(item, + isCurrent: isCurrent, + backgroundColor: backgroundColor, + textColor: textColor, + onTap: (_) => + addressListViewModel.setAddress(item), + onEdit: () => Navigator.of(context).pushNamed( + Routes.newSubaddress, + arguments: item)); + }); + } + + return index != 0 + ? cell + : ClipRRect( + borderRadius: BorderRadius.only( + topLeft: Radius.circular(30), + topRight: Radius.circular(30)), + child: cell, + ); + })), + ], ), - Observer( - builder: (_) => ListView.separated( - padding: EdgeInsets.all(0), - separatorBuilder: (context, _) => Container( - height: 1, color: Theme.of(context).dividerColor), - shrinkWrap: true, - physics: NeverScrollableScrollPhysics(), - itemCount: addressListViewModel.items.length, - itemBuilder: (context, index) { - final item = addressListViewModel.items[index]; - Widget cell = Container(); - - if (item is WalletAccountListHeader) { - cell = HeaderTile( - onTap: () async => await showPopUp( - context: context, - builder: (_) => - getIt.get()), - title: S.of(context).accounts, - icon: Icon( - Icons.arrow_forward_ios, - size: 14, - color: Theme.of(context).textTheme.display1.color, - )); - } - - if (item is WalletAddressListHeader) { - cell = HeaderTile( - onTap: () => Navigator.of(context) - .pushNamed(Routes.newSubaddress), - title: S.of(context).addresses, - icon: Icon( - Icons.add, - size: 20, - color: Theme.of(context).textTheme.display1.color, - )); - } - - if (item is WalletAddressListItem) { - cell = Observer(builder: (_) { - final isCurrent = item.address == - addressListViewModel.address.address; - final backgroundColor = isCurrent - ? Theme.of(context) - .textTheme - .display3 - .decorationColor - : Theme.of(context) - .textTheme - .display2 - .decorationColor; - final textColor = isCurrent - ? Theme.of(context).textTheme.display3.color - : Theme.of(context).textTheme.display2.color; - - return AddressCell.fromItem(item, - isCurrent: isCurrent, - backgroundColor: backgroundColor, - textColor: textColor, - onTap: (_) => addressListViewModel.setAddress(item), - onEdit: () => Navigator.of(context).pushNamed( - Routes.newSubaddress, - arguments: item)); - }); - } - - return index != 0 - ? cell - : ClipRRect( - borderRadius: BorderRadius.only( - topLeft: Radius.circular(30), - topRight: Radius.circular(30)), - child: cell, - ); - })), - ], - ), - ); + )); } } diff --git a/lib/src/screens/receive/widgets/qr_widget.dart b/lib/src/screens/receive/widgets/qr_widget.dart index 46daa7f9e..ba2101d93 100644 --- a/lib/src/screens/receive/widgets/qr_widget.dart +++ b/lib/src/screens/receive/widgets/qr_widget.dart @@ -11,7 +11,9 @@ import 'package:cake_wallet/view_model/wallet_address_list/wallet_address_list_v class QRWidget extends StatelessWidget { QRWidget( - {@required this.addressListViewModel, this.isAmountFieldShow = false}) + {@required this.addressListViewModel, + this.isAmountFieldShow = false, + this.amountTextFieldFocusNode}) : amountController = TextEditingController(), _formKey = GlobalKey() { amountController.addListener(() => addressListViewModel.amount = @@ -21,6 +23,7 @@ class QRWidget extends StatelessWidget { final WalletAddressListViewModel addressListViewModel; final bool isAmountFieldShow; final TextEditingController amountController; + final FocusNode amountTextFieldFocusNode; final GlobalKey _formKey; @override @@ -45,7 +48,7 @@ class QRWidget extends StatelessWidget { data: addressListViewModel.uri.toString(), backgroundColor: Colors.transparent, foregroundColor: Colors.white, - //Theme.of(context).textTheme.headline.color, + //Theme.of(context).textTheme.headline.color, ))))), Spacer(flex: 3) ]), @@ -68,6 +71,7 @@ class QRWidget extends StatelessWidget { child: Form( key: _formKey, child: BaseTextFormField( + focusNode: amountTextFieldFocusNode, controller: amountController, keyboardType: TextInputType.numberWithOptions( decimal: true),