From ba41a617c1ed957e82e0401d76dbbe82f2ac20d1 Mon Sep 17 00:00:00 2001 From: Willy <11148913+willyfromtheblock@users.noreply.github.com> Date: Fri, 24 May 2024 22:32:02 +0200 Subject: [PATCH] 1.2.7 (#253) * version bump * version bump * Bump version to 1.2.7+140 in pubspec.yaml * version bump * chore: Update color scheme in various screens * chore: Trim whitespace in address input field * chore: Update switch theme colors in app_themes.dart * Changelog * skeleton to push sign transaction results into separate confirmation screen * better argument and prop handling * todos * Added translation using Weblate (English (Middle)) * chore: Update wallet sign transaction confirmation screen to include network information and recipient details * chore: Refactor address_from_asm to generic_address and add tests * Refactor signing inputs in wallet_sign_transaction.dart to use index instead of input object * feat: Add handling for no signed inputs in WalletSignTransactionConfirmationScreen * Refactor P2TR address handling in generic_address.dart * feat: Add transaction input signing status to confirmation screen * feat: Improve error handling for signing inputs in WalletSignTransactionScreenState * Refactor generic_address_test.dart to improve error handling for unknown program types in fromAsm method * Refactor key_new.dart to improve transaction signing flow * chore: Update CHANGELOG.md with latest release date --------- Co-authored-by: Weblate Co-authored-by: Josh Edward Fairbank --- CHANGELOG.md | 5 + assets/translations/en.json | 6 +- assets/translations/enm.json | 1 + .../settings/app_settings_default_wallet.dart | 6 +- lib/screens/setup/setup_auth.dart | 3 - lib/screens/setup/setup_create_wallet.dart | 2 +- lib/screens/setup/setup_import_seed.dart | 4 +- lib/screens/setup/setup_landing.dart | 4 +- lib/screens/wallet/address_selector.dart | 6 +- lib/screens/wallet/transaction_details.dart | 2 +- lib/screens/wallet/wallet_home.dart | 8 +- lib/screens/wallet/wallet_list.dart | 8 +- .../wallet/wallet_sign_transaction.dart | 255 +++++---------- .../wallet_sign_transaction_confirmation.dart | 214 +++++++++++++ lib/tools/app_routes.dart | 8 + lib/tools/app_themes.dart | 64 +++- lib/tools/generic_address.dart | 32 ++ lib/widgets/buttons.dart | 8 +- lib/widgets/loading_indicator.dart | 2 +- lib/widgets/service_container.dart | 4 +- .../addresses_tab_expandable.dart | 27 -- .../addresses_tab_watch_only.dart | 10 +- lib/widgets/wallet/addresses_tab.dart | 300 +++++++++--------- lib/widgets/wallet/transactions_list.dart | 8 +- .../wallet/wallet_home_connection.dart | 4 +- pubspec.lock | 48 +-- pubspec.yaml | 10 +- test/generic_address_test.dart | 34 ++ test_driver/key_new.dart | 11 +- 29 files changed, 649 insertions(+), 445 deletions(-) create mode 100644 assets/translations/enm.json create mode 100644 lib/screens/wallet/wallet_sign_transaction_confirmation.dart create mode 100644 lib/tools/generic_address.dart create mode 100644 test/generic_address_test.dart diff --git a/CHANGELOG.md b/CHANGELOG.md index a15d87be..80b31f78 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +### **1.2.7** (2023-05-24) + +- Various bug fixes and improvements related to flutter upgrade +- Improved flow for transaction signing, now with confirmation and more feedback + ### **1.2.6** (2023-04-20) - Improved transaction signing handling and error messages diff --git a/assets/translations/en.json b/assets/translations/en.json index 4df6e355..d2f08871 100644 --- a/assets/translations/en.json +++ b/assets/translations/en.json @@ -510,5 +510,9 @@ "sign_transaction_step_3_button_alt": "Copy hex to clipboard", "sign_transaction_step_3_description": "This is your signed transaction hex. You may double tap to copy it to the clipboard.", "sign_transaction_input_selector_title": "Select which inputs to sign", - "sign_transaction_signing_failed": "Transaction signing failed." + "sign_transaction_signing_failed": "Transaction signing failed.", + "sign_transaction_inputs": "Inputs", + "sign_transaction_confirmation_title": "Please review transaction", + "sign_transaction_confirmation_input_signed": "Signed", + "sign_transaction_confirmation_input_unsigned": "Not signed" } diff --git a/assets/translations/enm.json b/assets/translations/enm.json new file mode 100644 index 00000000..0967ef42 --- /dev/null +++ b/assets/translations/enm.json @@ -0,0 +1 @@ +{} diff --git a/lib/screens/settings/app_settings_default_wallet.dart b/lib/screens/settings/app_settings_default_wallet.dart index 2d6f3d0a..fe0c4576 100644 --- a/lib/screens/settings/app_settings_default_wallet.dart +++ b/lib/screens/settings/app_settings_default_wallet.dart @@ -50,13 +50,13 @@ class _AppSettingsDefaultWalletScreenState List generateDefaultWallets() { final inkwells = _availableWallets.map((wallet) { return InkWell( - onTap: () => saveDefaultWallet(wallet.letterCode), + onTap: () => saveDefaultWallet(wallet.name), child: ListTile( title: Text(wallet.title), leading: Radio( - value: wallet.letterCode, + value: wallet.name, groupValue: _defaultWallet, - onChanged: (dynamic _) => saveDefaultWallet(wallet.letterCode), + onChanged: (dynamic _) => saveDefaultWallet(wallet.name), ), ), ); diff --git a/lib/screens/setup/setup_auth.dart b/lib/screens/setup/setup_auth.dart index 31448b72..01ebe089 100644 --- a/lib/screens/setup/setup_auth.dart +++ b/lib/screens/setup/setup_auth.dart @@ -115,9 +115,6 @@ class _SetupAuthScreenState extends State { ), ), value: _biometricsAllowed, - activeColor: - Theme.of(context).colorScheme.background, - inactiveThumbColor: Colors.grey, onChanged: (newState) { if (_biometricsAvailable == false) { ScaffoldMessenger.of(context).showSnackBar( diff --git a/lib/screens/setup/setup_create_wallet.dart b/lib/screens/setup/setup_create_wallet.dart index 43f64c91..1f6aca73 100644 --- a/lib/screens/setup/setup_create_wallet.dart +++ b/lib/screens/setup/setup_create_wallet.dart @@ -306,7 +306,7 @@ class _SetupCreateWalletScreenState extends State { ), color: Theme.of(context) .colorScheme - .background, + .surface, border: Border.all( width: 2, color: _sharedYet diff --git a/lib/screens/setup/setup_import_seed.dart b/lib/screens/setup/setup_import_seed.dart index 839a79b9..f3b63aa9 100644 --- a/lib/screens/setup/setup_import_seed.dart +++ b/lib/screens/setup/setup_import_seed.dart @@ -176,7 +176,7 @@ class _SetupImportSeedState extends State { borderRadius: const BorderRadius.all( Radius.circular(20), ), - color: Theme.of(context).colorScheme.background, + color: Theme.of(context).colorScheme.surface, border: Border.all( width: 2, color: Theme.of(context).primaryColor, @@ -223,7 +223,7 @@ class _SetupImportSeedState extends State { filled: true, fillColor: Theme.of(context) .colorScheme - .background, + .surface, suffixIcon: IconButton( onPressed: () async { final focusScope = diff --git a/lib/screens/setup/setup_landing.dart b/lib/screens/setup/setup_landing.dart index bd542618..9aa1f4cf 100644 --- a/lib/screens/setup/setup_landing.dart +++ b/lib/screens/setup/setup_landing.dart @@ -57,7 +57,7 @@ class _SetupLandingScreenState extends State { height: MediaQuery.of(context).size.height / 20, ), Image.asset( - 'assets/icon/ppc-icon-white.png', + 'assets/icon/ppc-icon-white-256.png', height: MediaQuery.of(context).size.height / 5, ), Column( @@ -135,7 +135,7 @@ class _SetupLandingScreenState extends State { }, icon: Icon( Icons.language_rounded, - color: Theme.of(context).colorScheme.background, + color: Theme.of(context).colorScheme.surface, size: 32, ), ), diff --git a/lib/screens/wallet/address_selector.dart b/lib/screens/wallet/address_selector.dart index 84dac5a3..605810ac 100644 --- a/lib/screens/wallet/address_selector.dart +++ b/lib/screens/wallet/address_selector.dart @@ -140,7 +140,7 @@ class _AddressSelectorScreenState extends State { child: TextFormField( autofocus: true, style: TextStyle( - color: Theme.of(context).colorScheme.background, + color: Theme.of(context).colorScheme.surface, ), key: const Key('selectorSearchKey'), textInputAction: TextInputAction.done, @@ -149,7 +149,7 @@ class _AddressSelectorScreenState extends State { hintText: AppLocalizations.instance .translate('addressbook_search'), hintStyle: TextStyle( - color: Theme.of(context).colorScheme.background, + color: Theme.of(context).colorScheme.surface, ), suffixIcon: IconButton( onPressed: () => setState(() { @@ -157,7 +157,7 @@ class _AddressSelectorScreenState extends State { }), icon: Icon( Icons.close, - color: Theme.of(context).colorScheme.background, + color: Theme.of(context).colorScheme.surface, ), ), ), diff --git a/lib/screens/wallet/transaction_details.dart b/lib/screens/wallet/transaction_details.dart index 9629f588..8e36e76e 100644 --- a/lib/screens/wallet/transaction_details.dart +++ b/lib/screens/wallet/transaction_details.dart @@ -165,7 +165,7 @@ class TransactionDetails extends StatelessWidget { children: [ DoubleTabToClipboard( clipBoardData: tx.broadcastHex, - child: Text( + child: SelectableText( tx.broadcastHex, ), ), diff --git a/lib/screens/wallet/wallet_home.dart b/lib/screens/wallet/wallet_home.dart index 003a86d2..d903a6d4 100644 --- a/lib/screens/wallet/wallet_home.dart +++ b/lib/screens/wallet/wallet_home.dart @@ -6,6 +6,7 @@ import 'package:flutter/material.dart'; import 'package:loader_overlay/loader_overlay.dart'; import 'package:peercoin/data_sources/electrum_backend.dart'; import 'package:peercoin/providers/server_provider.dart'; +import 'package:peercoin/screens/wallet/wallet_sign_transaction.dart'; import 'package:peercoin/widgets/wallet/address_book/addresses_tab_watch_only.dart'; import 'package:peercoin/widgets/wallet/wallet_reset_bottom_sheet.dart'; import 'package:provider/provider.dart'; @@ -465,7 +466,10 @@ class _WalletHomeState extends State case 'transaction_signing': Navigator.of(context).pushNamed( Routes.walletTransactionSigning, - arguments: _wallet.name, + arguments: WalletSignTransactionArguments( + walletName: _wallet.name, + coinLetterCode: _wallet.letterCode, + ), ); break; default: @@ -791,7 +795,7 @@ class _WalletHomeState extends State Widget build(BuildContext context) { return Scaffold( bottomNavigationBar: _calcBottomNavBar(context), - appBar: _selectedTab == WalletTab.addresses + appBar: _selectedTab == WalletTab.addresses && _wallet.watchOnly ? addressTabWatchOnlySearchAppBar() : AppBar( centerTitle: true, diff --git a/lib/screens/wallet/wallet_list.dart b/lib/screens/wallet/wallet_list.dart index e325a6d7..bfc03a9f 100644 --- a/lib/screens/wallet/wallet_list.dart +++ b/lib/screens/wallet/wallet_list.dart @@ -335,7 +335,7 @@ class _WalletListScreenState extends State borderRadius: const BorderRadius.all(Radius.circular(50.0)), border: Border.all( - color: Theme.of(context).colorScheme.background, + color: Theme.of(context).colorScheme.surface, width: 2, ), ), @@ -365,7 +365,7 @@ class _WalletListScreenState extends State style: TextStyle( letterSpacing: 1.4, fontSize: 24, - color: Theme.of(context).colorScheme.background, + color: Theme.of(context).colorScheme.surface, ), ), ), @@ -386,7 +386,7 @@ class _WalletListScreenState extends State fontSize: 16, fontStyle: FontStyle.italic, color: - Theme.of(context).colorScheme.background, + Theme.of(context).colorScheme.surface, ), ), if (_importedSeed) @@ -438,7 +438,7 @@ class _WalletListScreenState extends State horizontal: 16, ), color: - Theme.of(context).colorScheme.background, + Theme.of(context).colorScheme.surface, child: Column( children: [ InkWell( diff --git a/lib/screens/wallet/wallet_sign_transaction.dart b/lib/screens/wallet/wallet_sign_transaction.dart index 2e93ba96..e65521fc 100644 --- a/lib/screens/wallet/wallet_sign_transaction.dart +++ b/lib/screens/wallet/wallet_sign_transaction.dart @@ -3,7 +3,9 @@ import 'package:collection/collection.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; +import 'package:peercoin/models/available_coins.dart'; import 'package:peercoin/providers/wallet_provider.dart'; +import 'package:peercoin/screens/wallet/wallet_sign_transaction_confirmation.dart'; import 'package:peercoin/tools/app_localizations.dart'; import 'package:peercoin/tools/app_routes.dart'; import 'package:peercoin/tools/logger_wrapper.dart'; @@ -12,6 +14,15 @@ import 'package:peercoin/widgets/double_tab_to_clipboard.dart'; import 'package:peercoin/widgets/service_container.dart'; import 'package:provider/provider.dart'; +class WalletSignTransactionArguments { + final String walletName; + final String coinLetterCode; + WalletSignTransactionArguments({ + required this.walletName, + required this.coinLetterCode, + }); +} + class WalletSignTransactionScreen extends StatefulWidget { const WalletSignTransactionScreen({super.key}); @@ -23,20 +34,23 @@ class WalletSignTransactionScreen extends StatefulWidget { class _WalletSignTransactionScreenState extends State { late String _walletName; + late String _coinLetterCode; late WalletProvider _walletProvider; bool _initial = true; - bool _signingDone = false; String _signingError = ''; - String _signedTx = ''; String _signingAddress = ''; + final List _successfullySignedInputs = []; final TextEditingController _txInputController = TextEditingController(); - final Map _checkedInputs = {}; @override void didChangeDependencies() { if (_initial == true) { - _walletName = ModalRoute.of(context)!.settings.arguments as String; + final args = ModalRoute.of(context)!.settings.arguments + as WalletSignTransactionArguments; _walletProvider = Provider.of(context); + _walletName = args.walletName; + _coinLetterCode = args.coinLetterCode; + setState(() { _initial = false; }); @@ -76,86 +90,6 @@ class _WalletSignTransactionScreenState } } - Future _showInputSelector(int inputN) async { - return await showModalBottomSheet( - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(20.0), - ), - isDismissible: false, - context: context, - enableDrag: false, - builder: (BuildContext context) { - return StatefulBuilder( - builder: (BuildContext context, StateSetter setState) { - return ModalBottomSheetContainer( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - mainAxisSize: MainAxisSize.min, - children: [ - Text( - AppLocalizations.instance.translate( - 'sign_transaction_input_selector_title', - ), - style: TextStyle( - letterSpacing: 1.4, - fontSize: 24, - color: Theme.of(context).colorScheme.primary, - ), - ), - const SizedBox( - height: 30, - ), - Expanded( - child: ListView.builder( - itemBuilder: (ctx, n) => CheckboxListTile( - title: Text('Input $n'), - value: _checkedInputs[n] ?? false, - onChanged: (value) { - setState(() { - _checkedInputs[n] = value!; - }); - }, - ), - itemCount: inputN, - ), - ), - const SizedBox( - height: 20, - ), - Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: [ - TextButton.icon( - label: Text( - AppLocalizations.instance - .translate('server_settings_alert_cancel'), - ), - icon: const Icon(Icons.cancel), - onPressed: () { - Navigator.of(context).pop(false); - }, - ), - TextButton.icon( - label: Text( - AppLocalizations.instance - .translate('jail_dialog_button'), - ), - icon: const Icon(Icons.check), - onPressed: () { - Navigator.of(context).pop(true); - }, - ), - ], - ), - ], - ), - ); - }, - ); - }, - ); - } - Future _handleSign() async { LoggerWrapper.logInfo( 'WalletTransactionSigning', @@ -171,18 +105,10 @@ class _WalletSignTransactionScreenState final privKey = WIF.fromString(wif).privkey; Transaction tx = Transaction.fromHex(_txInputController.text); - final selectedInputResult = await _showInputSelector(tx.inputs.length); - if (selectedInputResult == false) return; - if (_checkedInputs.values.every((element) => element == false)) return; // conversion step for cointoolkit start tx = Transaction( inputs: tx.inputs.mapIndexed((i, input) { - if (!_checkedInputs.containsKey(i) || _checkedInputs[i]! == false) { - //don't convert this unselected input, return as is - return input; - } - // Determine program from cointoolkit input script data final program = Program.decompile(input.scriptSig); @@ -209,26 +135,62 @@ class _WalletSignTransactionScreenState // conversion step for cointoolkit end Transaction txToSign = tx; - _checkedInputs.forEach((key, value) { - if (value) { + //try to sign all inputs + String errorMessage = ''; + for (var i in tx.inputs) { + final index = tx.inputs.indexOf(i); + try { txToSign = txToSign.sign( - inputN: key, + inputN: index, key: privKey, ); + _successfullySignedInputs.add(index); + } catch (e) { + LoggerWrapper.logError( + 'WalletTransactionSigning', + 'handleSign', + 'failed to sign input $i: $e', + ); + errorMessage = e.toString(); } - }); + } final signedTx = txToSign.toHex(); - - setState(() { - _signedTx = signedTx; - _signingDone = true; - }); - LoggerWrapper.logInfo( 'WalletTransactionSigning', 'handleSign', 'tx produced $signedTx', ); + + //if no inputs were signed, show error + if (_successfullySignedInputs.isEmpty) { + setState(() { + _signingError = errorMessage; + }); + return; + } + + //show confirmation + if (!mounted) return; + await Navigator.of(context).pushNamed( + Routes.walletTransactionSigningConfirmation, + arguments: WalletSignTransactionConfirmationArguments( + tx: txToSign, + decimalProduct: AvailableCoins.getDecimalProduct( + identifier: _walletName, + ), + network: AvailableCoins.getSpecificCoin( + _walletName, + ).networkType, + coinLetterCode: _coinLetterCode, + selectedInputs: _successfullySignedInputs, + ), + ); + + //reset state + setState(() { + _successfullySignedInputs.clear(); + _signingError = ''; + }); } catch (e) { LoggerWrapper.logError( 'WalletTransactionSigning', @@ -303,7 +265,10 @@ class _WalletSignTransactionScreenState } return false; }, - arguments: _walletName, + arguments: WalletSignTransactionArguments( + walletName: _walletName, + coinLetterCode: _coinLetterCode, + ), ); }, ), @@ -358,17 +323,15 @@ class _WalletSignTransactionScreenState ), ), PeerButton( - action: () => - _signingDone ? null : _showAddressSelector(), + action: () => _showAddressSelector(), text: AppLocalizations.instance.translate( _signingAddress == '' ? 'sign_step_1_button' : 'sign_step_1_button_alt', ), small: true, - active: !_signingDone, ), - _signingAddress.isNotEmpty && !_signingDone + _signingAddress.isNotEmpty ? PeerButton( action: () => _copyPubKeyToClipboard( _signingAddress, @@ -377,7 +340,6 @@ class _WalletSignTransactionScreenState 'sign_transaction_step_1_copy_pubkey', ), small: true, - active: !_signingDone, ) : Container(), const SizedBox( @@ -401,7 +363,6 @@ class _WalletSignTransactionScreenState key: const Key('transactionHexInput'), controller: _txInputController, autocorrect: false, - readOnly: _signingDone, minLines: 5, maxLines: 5, onChanged: (_) => setState( @@ -410,7 +371,6 @@ class _WalletSignTransactionScreenState decoration: InputDecoration( suffixIcon: IconButton( onPressed: () async { - if (_signingDone) return; final data = await Clipboard.getData('text/plain'); setState(() { @@ -419,9 +379,7 @@ class _WalletSignTransactionScreenState }, icon: Icon( Icons.paste_rounded, - color: _signingDone - ? Theme.of(context).colorScheme.secondary - : Theme.of(context).primaryColor, + color: Theme.of(context).primaryColor, ), ), icon: Icon( @@ -446,57 +404,14 @@ class _WalletSignTransactionScreenState const SizedBox( height: 10, ), - _signedTx.isNotEmpty - ? Column( - children: [ - DoubleTabToClipboard( - clipBoardData: _signedTx, - child: SelectableText( - _signedTx, - key: const Key('signature'), - ), - ), - const SizedBox( - height: 10, - ), - Text( - AppLocalizations.instance.translate( - 'sign_transaction_step_3_description', - ), - textAlign: TextAlign.center, - style: TextStyle( - fontSize: 12, - color: - Theme.of(context).colorScheme.secondary, - ), - ), - ], - ) - : Container(), - const SizedBox( - height: 10, + PeerButton( + action: () => _handleSign(), + text: AppLocalizations.instance + .translate('sign_step_3_button'), + small: true, + active: _signingAddress.isNotEmpty && + _txInputController.text.isNotEmpty, ), - _signedTx.isNotEmpty - ? PeerButton( - action: () => DoubleTabToClipboard.tapEvent( - context, - _signedTx, - ), - text: AppLocalizations.instance.translate( - 'sign_transaction_step_3_button_alt', - ), - small: true, - active: _signingAddress.isNotEmpty && - _txInputController.text.isNotEmpty, - ) - : PeerButton( - action: () => _handleSign(), - text: AppLocalizations.instance - .translate('sign_step_3_button'), - small: true, - active: _signingAddress.isNotEmpty && - _txInputController.text.isNotEmpty, - ), _signingError.isNotEmpty ? Padding( padding: const EdgeInsets.symmetric(vertical: 10), @@ -516,14 +431,12 @@ class _WalletSignTransactionScreenState const SizedBox( height: 20, ), - _signingDone - ? PeerButton( - text: AppLocalizations.instance - .translate('sign_reset_button'), - small: true, - action: () async => await _performReset(context), - ) - : Container(), + PeerButton( + text: AppLocalizations.instance + .translate('sign_reset_button'), + small: true, + action: () async => await _performReset(context), + ), ], ), ), diff --git a/lib/screens/wallet/wallet_sign_transaction_confirmation.dart b/lib/screens/wallet/wallet_sign_transaction_confirmation.dart new file mode 100644 index 00000000..48dd70f7 --- /dev/null +++ b/lib/screens/wallet/wallet_sign_transaction_confirmation.dart @@ -0,0 +1,214 @@ +import 'dart:typed_data'; +import 'package:coinlib_flutter/coinlib_flutter.dart'; +import 'package:flutter/material.dart'; +import 'package:peercoin/screens/wallet/transaction_details.dart'; +import 'package:peercoin/tools/generic_address.dart'; +import 'package:peercoin/tools/app_localizations.dart'; +import 'package:peercoin/widgets/double_tab_to_clipboard.dart'; +import 'package:peercoin/widgets/service_container.dart'; + +class WalletSignTransactionConfirmationArguments { + Transaction tx; + List selectedInputs; + int decimalProduct; + String coinLetterCode; + Network network; + + WalletSignTransactionConfirmationArguments({ + required this.tx, + required this.selectedInputs, + required this.decimalProduct, + required this.coinLetterCode, + required this.network, + }); +} + +class WalletSignTransactionConfirmationScreen extends StatelessWidget { + const WalletSignTransactionConfirmationScreen({super.key}); + List renderRecipients({ + required Map recipients, + required String letterCode, + required int decimalProduct, + }) { + List list = []; + + recipients.forEach( + (addr, value) => list.add( + const TransactionDetails().renderRow( + addr, + value / decimalProduct, + letterCode, + ), + ), + ); + return list; + } + + List renderInputs({ + required List selectedInputs, + required String letterCode, + required int decimalProduct, + required Transaction tx, + required BuildContext context, + }) { + List list = []; + + for (var input in selectedInputs) { + final inputTxId = bytesToHex( + Uint8List.fromList(tx.inputs[input].prevOut.hash.reversed.toList()), + ); + list.add( + Row( + key: Key(input.toString()), + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Flexible( + flex: 2, + child: Text( + inputTxId, + style: const TextStyle( + fontSize: 13, + ), + overflow: TextOverflow.ellipsis, + ), + ), + Flexible( + child: Text( + selectedInputs.contains(input) ? 'Signed' : 'Not signed', + style: TextStyle( + fontWeight: FontWeight.bold, + color: selectedInputs.contains(input) + ? Theme.of(context).primaryColor + : Theme.of(context).colorScheme.error, + ), + ), + ), + ], + ), + ); + } + return list; + } + + @override + Widget build(BuildContext context) { + final WalletSignTransactionConfirmationArguments arguments = + ModalRoute.of(context)!.settings.arguments + as WalletSignTransactionConfirmationArguments; + final Transaction tx = arguments.tx; + final Map recipients = tx.outputs + .map( + (e) => MapEntry( + GenericAddress.fromAsm(e.program!.script.asm, arguments.network) + .toString(), + e.value, + ), + ) + .fold>({}, (prev, element) { + prev[element.key] = element.value.toInt(); + return prev; + }); + + final totalAmount = tx.outputs.map((e) => e.value).reduce((a, b) => a + b); + final decimalProduct = arguments.decimalProduct; + final selectedInputs = arguments.selectedInputs; + final coinLetterCode = arguments.coinLetterCode; + + return Scaffold( + appBar: AppBar( + centerTitle: true, + title: Text( + AppLocalizations.instance + .translate('sign_transaction_confirmation_title'), + ), + ), + body: Column( + children: [ + Expanded( + child: SingleChildScrollView( + child: Align( + child: PeerContainer( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + AppLocalizations.instance + .translate('send_total_amount'), + style: const TextStyle( + fontWeight: FontWeight.bold, + ), + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + SelectableText( + '${totalAmount.toInt() / decimalProduct} $coinLetterCode', + ), + ], + ), + ], + ), + const Divider(), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + AppLocalizations.instance + .translate('sign_transaction_inputs'), + style: const TextStyle(fontWeight: FontWeight.bold), + ), + ...renderInputs( + selectedInputs: selectedInputs, + letterCode: coinLetterCode, + decimalProduct: decimalProduct, + tx: tx, + context: context, + ), + ], + ), + const Divider(), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + AppLocalizations.instance + .translate('tx_recipients'), + style: const TextStyle(fontWeight: FontWeight.bold), + ), + ...renderRecipients( + recipients: recipients, + letterCode: coinLetterCode, + decimalProduct: decimalProduct, + ), + ], + ), + const SizedBox( + height: 20, + ), + const Divider(), + Text( + AppLocalizations.instance + .translate('sign_transaction_step_3_description'), + style: const TextStyle(fontWeight: FontWeight.bold), + ), + const SizedBox( + height: 20, + ), + DoubleTabToClipboard( + clipBoardData: tx.toHex(), + child: SelectableText(tx.toHex()), + ), + ], + ), + ), + ), + ), + ), + ], + ), + ); + } +} diff --git a/lib/tools/app_routes.dart b/lib/tools/app_routes.dart index e0d76912..88516a79 100644 --- a/lib/tools/app_routes.dart +++ b/lib/tools/app_routes.dart @@ -1,6 +1,7 @@ import 'package:flutter/widgets.dart'; import 'package:peercoin/screens/wallet/transaction_confirmation.dart'; import 'package:peercoin/screens/wallet/wallet_sign_transaction.dart'; +import 'package:peercoin/screens/wallet/wallet_sign_transaction_confirmation.dart'; import 'package:peercoin/screens/wallet/wallet_verify_message.dart'; import '../screens/settings/app_settings_app_theme.dart'; @@ -62,6 +63,8 @@ class Routes { static const String walletMessageVerification = '/wallet-message-verification'; static const String walletTransactionSigning = '/wallet-transaction-signing'; + static const String walletTransactionSigningConfirmation = + '/wallet-transaction-signing-confirmation'; static const String importPaperWallet = '/import-paperwallet'; static const String importWif = '/import-wif'; static const String authJail = '/auth-jail'; @@ -202,6 +205,11 @@ class Routes { widget: WalletSignTransactionScreen(), routeType: RouteTypes.requiresArguments, ), + Routes.walletTransactionSigningConfirmation: (context) => + const RouterMaster( + widget: WalletSignTransactionConfirmationScreen(), + routeType: RouteTypes.requiresArguments, + ), }; } } diff --git a/lib/tools/app_themes.dart b/lib/tools/app_themes.dart index f99142cd..c6559091 100644 --- a/lib/tools/app_themes.dart +++ b/lib/tools/app_themes.dart @@ -57,24 +57,46 @@ class MyTheme { foregroundColor: LightColors.green, ), ), + sliderTheme: SliderThemeData( + valueIndicatorTextStyle: TextStyle( + color: LightColors.green, + fontWeight: FontWeight.bold, + ), + ), + switchTheme: SwitchThemeData( + thumbColor: WidgetStateProperty.resolveWith((states) { + if (states.contains(WidgetState.selected)) { + return LightColors.white; + } + return LightColors.green; + }), + trackColor: WidgetStateProperty.resolveWith((states) { + if (states.contains(WidgetState.selected)) { + return LightColors.green; + } + return LightColors.white; + }), + trackOutlineColor: WidgetStateProperty.resolveWith((states) { + if (states.contains(WidgetState.selected)) { + return LightColors.white; + } + return LightColors.green; + }), + ), colorScheme: ColorScheme( primary: LightColors.green, primaryContainer: LightColors.darkGreen, secondary: LightColors.grey, secondaryContainer: LightColors.lightGrey, surface: LightColors.white, - background: LightColors.grey, error: LightColors.red, onPrimary: LightColors.white, onSecondary: LightColors.blackGreen, onSurface: LightColors.black, - onBackground: LightColors.green, onError: LightColors.red, brightness: Brightness.light, tertiary: LightColors.white, - ) - .copyWith(background: LightColors.white) - .copyWith(error: LightColors.red), + ).copyWith(surface: LightColors.white).copyWith(error: LightColors.red), bottomAppBarTheme: const BottomAppBarTheme(color: Color(0x003cb054)), ), ThemeMode.dark: ThemeData.dark().copyWith( @@ -95,7 +117,7 @@ class MyTheme { sliderTheme: SliderThemeData(valueIndicatorColor: DarkColors.darkBlue), unselectedWidgetColor: DarkColors.white, radioTheme: RadioThemeData( - fillColor: MaterialStateProperty.all(DarkColors.white), + fillColor: WidgetStateProperty.all(DarkColors.white), ), textTheme: TextTheme( titleLarge: TextStyle(color: DarkColors.white), @@ -150,11 +172,27 @@ class MyTheme { cursorColor: DarkColors.white, ), switchTheme: SwitchThemeData( - thumbColor: MaterialStateProperty.all(DarkColors.white), - trackColor: MaterialStateProperty.all(DarkColors.grey), + thumbColor: WidgetStateProperty.resolveWith((states) { + if (states.contains(WidgetState.selected)) { + return DarkColors.white; + } + return DarkColors.grey; + }), + trackColor: WidgetStateProperty.resolveWith((states) { + if (states.contains(WidgetState.selected)) { + return DarkColors.grey; + } + return DarkColors.white; + }), + trackOutlineColor: WidgetStateProperty.resolveWith((states) { + if (states.contains(WidgetState.selected)) { + return DarkColors.white; + } + return DarkColors.grey; + }), ), checkboxTheme: CheckboxThemeData( - fillColor: MaterialStateProperty.all(DarkColors.white), + fillColor: WidgetStateProperty.all(DarkColors.white), ), expansionTileTheme: ExpansionTileThemeData( iconColor: DarkColors.white, @@ -164,17 +202,15 @@ class MyTheme { primaryContainer: DarkColors.white, secondary: DarkColors.white, secondaryContainer: DarkColors.grey, - surface: DarkColors.black, - background: DarkColors.green, + surface: DarkColors.green, error: DarkColors.red, onPrimary: DarkColors.green, onSecondary: DarkColors.green, - onSurface: DarkColors.white, - onBackground: DarkColors.green, + onSurface: DarkColors.black, onError: DarkColors.red, brightness: Brightness.dark, tertiary: DarkColors.white, - ).copyWith(background: DarkColors.green).copyWith(error: DarkColors.red), + ).copyWith(surface: DarkColors.green).copyWith(error: DarkColors.red), bottomAppBarTheme: const BottomAppBarTheme(color: Color(0x000D1821)), ), }; diff --git a/lib/tools/generic_address.dart b/lib/tools/generic_address.dart new file mode 100644 index 00000000..bd686040 --- /dev/null +++ b/lib/tools/generic_address.dart @@ -0,0 +1,32 @@ +import 'package:coinlib_flutter/coinlib_flutter.dart'; + +class GenericAddress { + static Address fromAsm(String asm, Network network) { + final program = Program.fromAsm(asm); + if (program is P2PKH) { + return P2PKHAddress.fromHash( + program.pkHash, + version: network.p2pkhPrefix, + ); + } else if (program is P2SH) { + return P2SHAddress.fromHash( + program.scriptHash, + version: network.p2shPrefix, + ); + } else if (program is P2WPKH) { + return P2WPKHAddress.fromHash( + program.pkHash, + hrp: network.bech32Hrp, + ); + } else if (program is P2WSH) { + return P2WSHAddress.fromHash(program.scriptHash, hrp: network.bech32Hrp); + } else if (program is P2TR) { + return P2TRAddress.fromTweakedKey( + program.tweakedKey, + hrp: network.bech32Hrp, + ); + } else { + throw Exception('Unknown program type'); + } + } +} diff --git a/lib/widgets/buttons.dart b/lib/widgets/buttons.dart index 9156d8ae..73a2a748 100644 --- a/lib/widgets/buttons.dart +++ b/lib/widgets/buttons.dart @@ -55,8 +55,8 @@ class PeerButtonBorder extends StatelessWidget { Widget build(BuildContext context) { return ElevatedButton( style: ElevatedButton.styleFrom( - foregroundColor: Theme.of(context).colorScheme.background, - backgroundColor: Theme.of(context).colorScheme.background, + foregroundColor: Theme.of(context).colorScheme.surface, + backgroundColor: Theme.of(context).colorScheme.surface, fixedSize: Size( MediaQuery.of(context).size.width > 768 ? MediaQuery.of(context).size.width / 4 @@ -103,7 +103,7 @@ class PeerButtonSetup extends StatelessWidget { style: ElevatedButton.styleFrom( foregroundColor: Theme.of(context).primaryColor, backgroundColor: - active ? Theme.of(context).colorScheme.background : Colors.grey, + active ? Theme.of(context).colorScheme.surface : Colors.grey, fixedSize: Size( MediaQuery.of(context).size.width > 768 ? MediaQuery.of(context).size.width / 4 @@ -148,7 +148,7 @@ class PeerButtonSetupLoading extends StatelessWidget { return ElevatedButton( style: ElevatedButton.styleFrom( foregroundColor: Theme.of(context).primaryColor, - backgroundColor: Theme.of(context).colorScheme.background, + backgroundColor: Theme.of(context).colorScheme.surface, fixedSize: Size( MediaQuery.of(context).size.width > 768 ? MediaQuery.of(context).size.width / 4 diff --git a/lib/widgets/loading_indicator.dart b/lib/widgets/loading_indicator.dart index ebb0408f..598bf72b 100644 --- a/lib/widgets/loading_indicator.dart +++ b/lib/widgets/loading_indicator.dart @@ -7,7 +7,7 @@ class LoadingIndicator extends StatelessWidget { Widget build(BuildContext context) { return LinearProgressIndicator( color: Theme.of(context).primaryColor, - backgroundColor: Theme.of(context).colorScheme.background, + backgroundColor: Theme.of(context).colorScheme.surface, ); } //TODO try to move heavy computation away from Main thread so this animation actually continues diff --git a/lib/widgets/service_container.dart b/lib/widgets/service_container.dart index 1a3845f9..b5e534ae 100644 --- a/lib/widgets/service_container.dart +++ b/lib/widgets/service_container.dart @@ -62,7 +62,7 @@ class PeerContainer extends StatelessWidget { decoration: BoxDecoration( color: isTransparent ? Colors.transparent - : Theme.of(context).colorScheme.background, + : Theme.of(context).colorScheme.surface, borderRadius: BorderRadius.circular(20), ), child: child, @@ -85,7 +85,7 @@ class ModalBottomSheetContainer extends StatelessWidget { ? MediaQuery.of(context).size.width / 2 : MediaQuery.of(context).size.width, decoration: BoxDecoration( - color: Theme.of(context).colorScheme.background, + color: Theme.of(context).colorScheme.surface, borderRadius: const BorderRadius.vertical( top: Radius.circular(20), ), diff --git a/lib/widgets/wallet/address_book/addresses_tab_expandable.dart b/lib/widgets/wallet/address_book/addresses_tab_expandable.dart index 96a1309a..316f26b3 100644 --- a/lib/widgets/wallet/address_book/addresses_tab_expandable.dart +++ b/lib/widgets/wallet/address_book/addresses_tab_expandable.dart @@ -109,33 +109,6 @@ class AddressTabExpandable extends StatelessWidget { const SizedBox( height: 10, ), - // IconSlideAction( - // caption: AppLocalizations.instance - // .translate('addressbook_swipe_edit'), - // color: Theme.of(context).primaryColor, - // icon: Icons.edit, - // onTap: - // ), - // IconSlideAction( - // caption: AppLocalizations.instance - // .translate('addressbook_swipe_share'), - // color: Theme.of(context).colorScheme.background, - // iconWidget: Icon( - // Icons.share, - // color: Theme.of(context).colorScheme.secondary, - // ), - // onTap: - // ), - // IconSlideAction( - // caption: AppLocalizations.instance - // .translate('addressbook_swipe_delete'), - // color: Theme.of(context).colorScheme.error, - // iconWidget: const Icon( - // Icons.delete, - // color: Colors.white, - // ), - // onTap: () async {}, - // ), ], ), ), diff --git a/lib/widgets/wallet/address_book/addresses_tab_watch_only.dart b/lib/widgets/wallet/address_book/addresses_tab_watch_only.dart index 9724e714..21602539 100644 --- a/lib/widgets/wallet/address_book/addresses_tab_watch_only.dart +++ b/lib/widgets/wallet/address_book/addresses_tab_watch_only.dart @@ -152,7 +152,7 @@ class _AddressesTabWatchOnlyState extends State { formKey.currentState!.save(); context.read().createWatchOnlyAddres( identifier: widget.walletName, - address: addressController.text, + address: addressController.text.trim(), label: labelController.text == '' ? '' : labelController.text, @@ -189,10 +189,8 @@ class _AddressesTabWatchOnlyState extends State { padding: const EdgeInsets.all(20), child: ElevatedButton( style: ElevatedButton.styleFrom( - foregroundColor: - Theme.of(context).colorScheme.background, - backgroundColor: - Theme.of(context).colorScheme.background, + foregroundColor: Theme.of(context).colorScheme.surface, + backgroundColor: Theme.of(context).colorScheme.surface, fixedSize: Size( MediaQuery.of(context).size.width > 1200 ? MediaQuery.of(context).size.width / 5 @@ -239,7 +237,7 @@ class _AddressesTabWatchOnlyState extends State { style: TextStyle( fontSize: 16, fontStyle: FontStyle.italic, - color: Theme.of(context).colorScheme.background, + color: Theme.of(context).colorScheme.surface, ), ), ), diff --git a/lib/widgets/wallet/addresses_tab.dart b/lib/widgets/wallet/addresses_tab.dart index ee94acd0..6603c63a 100644 --- a/lib/widgets/wallet/addresses_tab.dart +++ b/lib/widgets/wallet/addresses_tab.dart @@ -457,107 +457,103 @@ class _AddressTabState extends State { child: ClipRect( child: Slidable( key: Key(addr.address), - actionPane: const SlidableScrollActionPane(), - secondaryActions: [ - IconSlideAction( - caption: AppLocalizations.instance - .translate('addressbook_swipe_edit'), - color: Theme.of(context).primaryColor, - icon: Icons.edit, - onTap: () => _addressEditDialog(context, addr), - ), - IconSlideAction( - caption: AppLocalizations.instance - .translate('addressbook_swipe_share'), - color: Theme.of(context).colorScheme.background, - iconWidget: Icon( - Icons.share, - color: Theme.of(context).colorScheme.secondary, - ), - onTap: () => WalletHomeQr.showQrDialog( - context, - addr.address, - ), - ), - IconSlideAction( - caption: AppLocalizations.instance - .translate('addressbook_swipe_send'), - color: Theme.of(context).colorScheme.secondary, - iconWidget: Icon( - Icons.send, - color: Theme.of(context).colorScheme.background, + endActionPane: ActionPane( + motion: const DrawerMotion(), + extentRatio: 1, + children: [ + SlidableAction( + label: AppLocalizations.instance + .translate('addressbook_swipe_edit'), + backgroundColor: Theme.of(context).primaryColor, + icon: Icons.edit, + onPressed: (ctx) => _addressEditDialog(ctx, addr), ), - onTap: () => widget.changeTab( - WalletTab.send, - addr.address, - addr.addressBookName, + SlidableAction( + label: AppLocalizations.instance + .translate('addressbook_swipe_share'), + backgroundColor: + Theme.of(context).colorScheme.surface, + icon: Icons.share, + onPressed: (ctx) => WalletHomeQr.showQrDialog( + ctx, + addr.address, + ), ), - ), - IconSlideAction( - caption: AppLocalizations.instance - .translate('addressbook_swipe_delete'), - color: Theme.of(context).colorScheme.error, - iconWidget: const Icon( - Icons.delete, - color: Colors.white, + SlidableAction( + label: AppLocalizations.instance + .translate('addressbook_swipe_send'), + backgroundColor: + Theme.of(context).colorScheme.secondary, + icon: Icons.send, + onPressed: (_) => widget.changeTab( + WalletTab.send, + addr.address, + addr.addressBookName, + ), ), - onTap: () async { - await showDialog( - context: context, - builder: (_) => AlertDialog( - title: Text( - AppLocalizations.instance.translate( - 'addressbook_dialog_remove_title', - ), - ), - content: Text(addr.address), - actions: [ - TextButton.icon( - label: Text( - AppLocalizations.instance.translate( - 'server_settings_alert_cancel', - ), + SlidableAction( + label: AppLocalizations.instance + .translate('addressbook_swipe_delete'), + backgroundColor: Theme.of(context).colorScheme.error, + icon: Icons.delete, + onPressed: (ctx) async { + await showDialog( + context: ctx, + builder: (_) => AlertDialog( + title: Text( + AppLocalizations.instance.translate( + 'addressbook_dialog_remove_title', ), - icon: const Icon(Icons.cancel), - onPressed: () { - Navigator.of(context).pop(); - }, ), - TextButton.icon( - label: Text( - AppLocalizations.instance - .translate('jail_dialog_button'), + content: Text(addr.address), + actions: [ + TextButton.icon( + label: Text( + AppLocalizations.instance.translate( + 'server_settings_alert_cancel', + ), + ), + icon: const Icon(Icons.cancel), + onPressed: () { + Navigator.of(context).pop(); + }, ), - icon: const Icon(Icons.check), - onPressed: () { - context - .read() - .removeAddress( - widget.walletName, - addr, - ); - applyFilter(); - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text( - AppLocalizations.instance.translate( - 'addressbook_dialog_remove_snack', + TextButton.icon( + label: Text( + AppLocalizations.instance + .translate('jail_dialog_button'), + ), + icon: const Icon(Icons.check), + onPressed: () { + context + .read() + .removeAddress( + widget.walletName, + addr, + ); + applyFilter(); + ScaffoldMessenger.of(context) + .showSnackBar( + SnackBar( + content: Text( + AppLocalizations.instance.translate( + 'addressbook_dialog_remove_snack', + ), + textAlign: TextAlign.center, ), - textAlign: TextAlign.center, + duration: const Duration(seconds: 5), ), - duration: const Duration(seconds: 5), - ), - ); - Navigator.of(context).pop(); - }, - ), - ], - ), - ); - }, - ), - ], - actionExtentRatio: 0.25, + ); + Navigator.of(context).pop(); + }, + ), + ], + ), + ); + }, + ), + ], + ), child: ListTile( leading: const Column( mainAxisAlignment: MainAxisAlignment.center, @@ -603,62 +599,57 @@ class _AddressTabState extends State { child: ClipRect( child: Slidable( key: Key(addr.address), - actionPane: const SlidableScrollActionPane(), - secondaryActions: [ - IconSlideAction( - caption: AppLocalizations.instance - .translate('addressbook_swipe_edit'), - color: Theme.of(context).primaryColor, - icon: Icons.edit, - onTap: () => _addressEditDialog(context, addr), - ), - IconSlideAction( - caption: AppLocalizations.instance - .translate('addressbook_swipe_share'), - color: Theme.of(context).colorScheme.background, - iconWidget: Icon( - Icons.share, - color: Theme.of(context).colorScheme.secondary, + endActionPane: ActionPane( + motion: const DrawerMotion(), + extentRatio: 1, + children: [ + SlidableAction( + label: AppLocalizations.instance + .translate('addressbook_swipe_edit'), + backgroundColor: Theme.of(context).primaryColor, + icon: Icons.edit, + onPressed: (ctx) => _addressEditDialog(ctx, addr), ), - onTap: () => WalletHomeQr.showQrDialog( - context, - addr.address, - ), - ), - IconSlideAction( - caption: AppLocalizations.instance.translate( - _isWatchedMap[addr.address] == true - ? 'addressbook_swipe_unwatch' - : 'addressbook_swipe_watch', + SlidableAction( + label: AppLocalizations.instance + .translate('addressbook_swipe_share'), + backgroundColor: + Theme.of(context).colorScheme.surface, + icon: Icons.share, + onPressed: (ctx) => WalletHomeQr.showQrDialog( + ctx, + addr.address, + ), ), - color: Theme.of(context).colorScheme.secondary, - iconWidget: Icon( - _isWatchedMap[addr.address] == true + SlidableAction( + label: AppLocalizations.instance.translate( + _isWatchedMap[addr.address] == true + ? 'addressbook_swipe_unwatch' + : 'addressbook_swipe_watch', + ), + backgroundColor: + Theme.of(context).colorScheme.secondary, + icon: _isWatchedMap[addr.address] == true ? Icons.visibility_off : Icons.visibility, - color: Theme.of(context).colorScheme.background, + onPressed: (_) => _toggleWatched(addr), ), - onTap: () => _toggleWatched(addr), - ), - IconSlideAction( - caption: AppLocalizations.instance - .translate('addressbook_swipe_export'), - color: Theme.of(context).colorScheme.error, - iconWidget: Icon( - Icons.vpn_key, - color: Theme.of(context).colorScheme.background, - ), - onTap: () => Auth.requireAuth( - context: context, - biometricsAllowed: context - .read() - .biometricsAllowed, - callback: () => - _showAddressExportDialog(context, addr), + SlidableAction( + label: AppLocalizations.instance + .translate('addressbook_swipe_export'), + backgroundColor: Theme.of(context).colorScheme.error, + icon: Icons.vpn_key, + onPressed: (ctx) => Auth.requireAuth( + context: ctx, + biometricsAllowed: context + .read() + .biometricsAllowed, + callback: () => + _showAddressExportDialog(context, addr), + ), ), - ), - ], - actionExtentRatio: 0.25, + ], + ), child: ListTile( leading: const Column( mainAxisAlignment: MainAxisAlignment.center, @@ -720,7 +711,7 @@ class _AddressTabState extends State { spacing: 10, children: [ ChoiceChip( - backgroundColor: Theme.of(context).colorScheme.background, + backgroundColor: Theme.of(context).colorScheme.surface, selectedColor: Theme.of(context).shadowColor, visualDensity: const VisualDensity( horizontal: 0.0, @@ -743,7 +734,7 @@ class _AddressTabState extends State { }, ), ChoiceChip( - backgroundColor: Theme.of(context).colorScheme.background, + backgroundColor: Theme.of(context).colorScheme.surface, selectedColor: Theme.of(context).shadowColor, visualDensity: const VisualDensity( horizontal: 0.0, @@ -768,7 +759,7 @@ class _AddressTabState extends State { Padding( padding: const EdgeInsets.all(kIsWeb ? 8.0 : 0), child: ChoiceChip( - backgroundColor: Theme.of(context).colorScheme.background, + backgroundColor: Theme.of(context).colorScheme.surface, selectedColor: Theme.of(context).shadowColor, visualDensity: const VisualDensity( horizontal: 0.0, @@ -798,7 +789,7 @@ class _AddressTabState extends State { ), ), ChoiceChip( - backgroundColor: Theme.of(context).colorScheme.background, + backgroundColor: Theme.of(context).colorScheme.surface, selectedColor: Theme.of(context).shadowColor, visualDensity: const VisualDensity( horizontal: 0.0, @@ -822,7 +813,7 @@ class _AddressTabState extends State { }, ), ChoiceChip( - backgroundColor: Theme.of(context).colorScheme.background, + backgroundColor: Theme.of(context).colorScheme.surface, selectedColor: Theme.of(context).shadowColor, visualDensity: const VisualDensity( horizontal: 0.0, @@ -862,7 +853,7 @@ class _AddressTabState extends State { automaticallyImplyLeading: false, floating: true, backgroundColor: _search - ? Theme.of(context).colorScheme.background + ? Theme.of(context).colorScheme.surface : Theme.of(context).primaryColor, title: Container( margin: const EdgeInsets.only(top: 8), @@ -878,10 +869,11 @@ class _AddressTabState extends State { autocorrect: false, decoration: InputDecoration( border: InputBorder.none, + focusColor: Colors.red, hintText: AppLocalizations.instance .translate('addressbook_search'), suffixIcon: IconButton( - icon: const Center(child: Icon(Icons.clear)), + icon: const Icon(Icons.clear), iconSize: 24, onPressed: () { _search = false; @@ -899,9 +891,9 @@ class _AddressTabState extends State { ElevatedButton( style: ElevatedButton.styleFrom( foregroundColor: - Theme.of(context).colorScheme.background, + Theme.of(context).colorScheme.secondary, backgroundColor: - Theme.of(context).colorScheme.background, + Theme.of(context).colorScheme.surface, fixedSize: Size( MediaQuery.of(context).size.width > 1200 ? MediaQuery.of(context).size.width / 5 @@ -938,9 +930,9 @@ class _AddressTabState extends State { ElevatedButton( style: ElevatedButton.styleFrom( foregroundColor: - Theme.of(context).colorScheme.background, + Theme.of(context).colorScheme.surface, backgroundColor: - Theme.of(context).colorScheme.background, + Theme.of(context).colorScheme.surface, fixedSize: Size( MediaQuery.of(context).size.width > 1200 ? MediaQuery.of(context).size.width / 5 diff --git a/lib/widgets/wallet/transactions_list.dart b/lib/widgets/wallet/transactions_list.dart index ba84c365..955ca6ba 100644 --- a/lib/widgets/wallet/transactions_list.dart +++ b/lib/widgets/wallet/transactions_list.dart @@ -132,7 +132,7 @@ class _TransactionListState extends State { style: TextStyle( fontSize: 16, fontStyle: FontStyle.italic, - color: Theme.of(context).colorScheme.background, + color: Theme.of(context).colorScheme.surface, ), ), ), @@ -289,7 +289,7 @@ class _TransactionListState extends State { ChoiceChip( backgroundColor: Theme.of(context) .colorScheme - .background, + .surface, selectedColor: Theme.of(context).shadowColor, visualDensity: const VisualDensity( @@ -311,7 +311,7 @@ class _TransactionListState extends State { ChoiceChip( backgroundColor: Theme.of(context) .colorScheme - .background, + .surface, selectedColor: Theme.of(context).shadowColor, visualDensity: const VisualDensity( @@ -333,7 +333,7 @@ class _TransactionListState extends State { ChoiceChip( backgroundColor: Theme.of(context) .colorScheme - .background, + .surface, selectedColor: Theme.of(context).shadowColor, visualDensity: const VisualDensity( diff --git a/lib/widgets/wallet/wallet_home_connection.dart b/lib/widgets/wallet/wallet_home_connection.dart index caf7e99b..2f71cff5 100644 --- a/lib/widgets/wallet/wallet_home_connection.dart +++ b/lib/widgets/wallet/wallet_home_connection.dart @@ -15,7 +15,7 @@ class WalletHomeConnection extends StatelessWidget { widget = Text( AppLocalizations.instance.translate('wallet_connected'), style: TextStyle( - color: Theme.of(context).colorScheme.background, + color: Theme.of(context).colorScheme.surface, letterSpacing: 1.4, fontSize: 16, ), @@ -24,7 +24,7 @@ class WalletHomeConnection extends StatelessWidget { widget = Text( AppLocalizations.instance.translate('wallet_offline'), style: TextStyle( - color: Theme.of(context).colorScheme.background, + color: Theme.of(context).colorScheme.surface, fontSize: 16, letterSpacing: 1.4, ), diff --git a/pubspec.lock b/pubspec.lock index 3053b6a8..65bb512a 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -69,10 +69,10 @@ packages: dependency: "direct main" description: name: background_fetch - sha256: "67d28aa747d344e8ab4cb56f0fba4f957c1d41504fc6be1ef275864000b30eb6" + sha256: "2fe367c9be0e256dadb75b8b637b0b58a2a2d2317b7c8420bb1ae8b41e23fde3" url: "https://pub.dev" source: hosted - version: "1.3.1" + version: "1.3.4" bip39: dependency: "direct main" description: @@ -532,10 +532,10 @@ packages: dependency: "direct main" description: name: flutter_screen_lock - sha256: f8e504aa0e5fe61ea13b66692ecdec3e8761118173b51c9b3b5518b8e2911e5b + sha256: "5c09314747a70d8e1259a1d787f49f4f29d6eae224cd9b2f045e513e8408cbcc" url: "https://pub.dev" source: hosted - version: "9.0.2" + version: "9.0.3" flutter_secure_storage: dependency: "direct main" description: @@ -588,10 +588,10 @@ packages: dependency: "direct main" description: name: flutter_slidable - sha256: c7607eb808cdef19c8468246e95a133308aeaeb3971cdd9edfb9d5e31cedfbe9 + sha256: "673403d2eeef1f9e8483bd6d8d92aae73b1d8bd71f382bc3930f699c731bc27c" url: "https://pub.dev" source: hosted - version: "0.6.0" + version: "3.1.0" flutter_test: dependency: "direct dev" description: flutter @@ -744,10 +744,10 @@ packages: dependency: "direct main" description: name: intl - sha256: "3bc132a9dbce73a7e4a21a17d06e1878839ffbf975568bc875c60537824b0c4d" + sha256: d6f56758b7d3014a48af9701c085700aac781a92a87a62b1333b46d8879661cf url: "https://pub.dev" source: hosted - version: "0.18.1" + version: "0.19.0" io: dependency: transitive description: @@ -776,26 +776,26 @@ packages: dependency: transitive description: name: leak_tracker - sha256: "78eb209deea09858f5269f5a5b02be4049535f568c07b275096836f01ea323fa" + sha256: "7f0df31977cb2c0b88585095d168e689669a2cc9b97c309665e3386f3e9d341a" url: "https://pub.dev" source: hosted - version: "10.0.0" + version: "10.0.4" leak_tracker_flutter_testing: dependency: transitive description: name: leak_tracker_flutter_testing - sha256: b46c5e37c19120a8a01918cfaf293547f47269f7cb4b0058f21531c2465d6ef0 + sha256: "06e98f569d004c1315b991ded39924b21af84cf14cc94791b8aea337d25b57f8" url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "3.0.3" leak_tracker_testing: dependency: transitive description: name: leak_tracker_testing - sha256: a597f72a664dbd293f3bfc51f9ba69816f84dcd403cdac7066cb3f6003f3ab47 + sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3" url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "3.0.1" lints: dependency: transitive description: @@ -896,10 +896,10 @@ packages: dependency: transitive description: name: meta - sha256: d584fa6707a52763a52446f02cc621b077888fb63b93bbcb1143a7be5a0c0c04 + sha256: "7687075e408b093f36e6bbf6c91878cc0d4cd10f409506f7bc996f68220b9136" url: "https://pub.dev" source: hosted - version: "1.11.0" + version: "1.12.0" mime: dependency: transitive description: @@ -1357,26 +1357,26 @@ packages: dependency: "direct dev" description: name: test - sha256: a1f7595805820fcc05e5c52e3a231aedd0b72972cb333e8c738a8b1239448b6f + sha256: "7ee446762c2c50b3bd4ea96fe13ffac69919352bd3b4b17bac3f3465edc58073" url: "https://pub.dev" source: hosted - version: "1.24.9" + version: "1.25.2" test_api: dependency: transitive description: name: test_api - sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b" + sha256: "9955ae474176f7ac8ee4e989dadfb411a58c30415bcfb648fa04b2b8a03afa7f" url: "https://pub.dev" source: hosted - version: "0.6.1" + version: "0.7.0" test_core: dependency: transitive description: name: test_core - sha256: a757b14fc47507060a162cc2530d9a4a2f92f5100a952c7443b5cad5ef5b106a + sha256: "2bc4b4ecddd75309300d8096f781c0e3280ca1ef85beda558d33fcbedc2eead4" url: "https://pub.dev" source: hosted - version: "0.5.9" + version: "0.6.0" theme_mode_handler: dependency: "direct main" description: @@ -1501,10 +1501,10 @@ packages: dependency: transitive description: name: vm_service - sha256: b3d56ff4341b8f182b96aceb2fa20e3dcb336b9f867bc0eafc0de10f1048e957 + sha256: "3923c89304b715fb1eb6423f017651664a03bf5f4b29983627c4da791f74a4ec" url: "https://pub.dev" source: hosted - version: "13.0.0" + version: "14.2.1" wasm_interop: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index b20f9ed3..fdde5156 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,7 +1,7 @@ name: peercoin description: A new Peercoin wallet. -version: 1.2.6+139 +version: 1.2.7+140 environment: sdk: '>=3.2.0 <4.0.0' @@ -24,24 +24,24 @@ dependencies: provider: ^6.0.3 share_plus: ^7.0.2 qr_flutter: ^4.0.0 - intl: ^0.18.0 + intl: ^0.19.0 step_progress_indicator: ^1.0.0 qr_code_scanner: ^1.0.0 url_launcher: ^6.1.4 flutter_local_notifications: ^17.0.0 - flutter_screen_lock: ^9.0.1 + flutter_screen_lock: ^9.0.3 local_auth: ^2.1.6 local_auth_android: any package_info_plus: ^4.1.0 mailto: ^2.0.0 - flutter_slidable: ^0.6.0 + flutter_slidable: ^3.1.0 flutter_typeahead: ^4.0.0 connectivity_plus: ^3.0.2 theme_mode_handler: ^3.0.0 http: ^0.13.3 collection: ^1.15.0-nullsafety.4 flutter_markdown: ^0.6.6 - background_fetch: ^1.1.2 + background_fetch: ^1.3.4 flutter_logs: ^2.1.11 auto_size_text: ^3.0.0 camera: ^0.10.0+4 diff --git a/test/generic_address_test.dart b/test/generic_address_test.dart new file mode 100644 index 00000000..8fc77dad --- /dev/null +++ b/test/generic_address_test.dart @@ -0,0 +1,34 @@ +import 'package:coinlib_flutter/coinlib_flutter.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:peercoin/tools/generic_address.dart'; + +void main() { + group('GenericAddress', () { + test('fromAsm - P2PKH', () { + const asm = + 'OP_DUP OP_HASH160 ff9296d92c5efc397d0e0b9ebe94d95a532270c4 OP_EQUALVERIFY OP_CHECKSIG'; + + final address = GenericAddress.fromAsm(asm, Network.testnet); + expect(address is P2PKHAddress, true); + expect(address.toString(), 'n4pJDAqsagWbouT7G7xRH8548s9pZpQwtG'); + }); + + test('fromAsm - P2SH', () { + const asm = + 'OP_HASH160 1123c89acd257e796c209f6f1914ed999f45076d OP_EQUAL'; + final address = GenericAddress.fromAsm(asm, Network.mainnet); + expect(address is P2SHAddress, true); + expect(address.toString(), 'p77CZFn9jvg9waCzKBzkQfSvBBzPH1nRre'); + }); + + test('fromAsm - Unknown program type', () { + const asm = 'OP_RETURN'; + expect( + () => GenericAddress.fromAsm(asm, Network.mainnet), + throwsException, + ); + }); + + //TODO - Implement P2WPKH, P2WSH and P2TR + }); +} diff --git a/test_driver/key_new.dart b/test_driver/key_new.dart index af47caee..a08811fd 100644 --- a/test_driver/key_new.dart +++ b/test_driver/key_new.dart @@ -141,8 +141,6 @@ void main() { '0300000001d8af09713b116ecce194add86bd6def0e2dc3abe99c53bfbcd34576061baca9f000000002221022ef8df0bfd68434e2db934e88a7e30b06b88507dac60fa7cc2b732a1b5147ef7ffffffff010a8f9800000000001976a914ff9296d92c5efc397d0e0b9ebe94d95a532270c488ac00000000', ); await driver.tap(find.text('Sign')); - await driver.tap(find.text('Input 0')); - await driver.tap(find.text('Okay')); await driver.waitFor( find.text( '0300000001d8af09713b116ecce194add86bd6def0e2dc3abe99c53bfbcd34576061baca9f000000006a47304402200455cf81bde046213814387da5bde30e657fe7977c4c35ffe78edd3fe5cada7b0220186524598ac87de9b944f61e819b29c8a9a331c8a52b1694b8559cd9ec3395800121022ef8df0bfd68434e2db934e88a7e30b06b88507dac60fa7cc2b732a1b5147ef7ffffffff010a8f9800000000001976a914ff9296d92c5efc397d0e0b9ebe94d95a532270c488ac00000000', @@ -152,13 +150,8 @@ void main() { test('Transaction signing, fail', () async { await driver.tap(find.pageBack()); - await driver.tap(find.byTooltip('Transactions')); - await driver.tap(find.byTooltip('Show menu')); - await driver.runUnsynchronized( - () async { - await driver.tap(find.text('Sign Transaction')); - }, - ); + await driver.tap(find.text('Reset')); + await driver.tap(find.text('Reset')); //yes, twice to reset await driver.tap(find.text('Select')); await driver.tap(find.text('mm5pM9sJzVjsafctQJJrJuhGsw1CTucZ2v')); await driver.tap(find.pageBack());