From 13888b9b961f6b3443e56715adfb96edcc68b94d Mon Sep 17 00:00:00 2001 From: Nguyen Phuoc Sang <47568867+ppupha@users.noreply.github.com> Date: Thu, 14 Dec 2023 14:22:30 +0700 Subject: [PATCH] USD Convert (#1419) * USD Convert * Fix lint --- lib/model/tzkt_operation.dart | 174 +++--- .../bloc/scan_wallet/scan_wallet_state.dart | 37 +- .../crypto/send/send_crypto_page.dart | 22 +- .../send_artwork/send_artwork_page.dart | 585 +++++++++--------- .../send_artwork_review_page.dart | 123 ++-- .../settings/crypto/send_review_page.dart | 85 ++- .../linked_wallet_detail_page.dart | 6 +- .../wallet_detail/wallet_detail_bloc.dart | 7 +- .../wallet_detail/wallet_detail_page.dart | 5 +- .../tb_send_transaction_page.dart | 275 ++++---- .../send/wc_send_transaction_page.dart | 189 +++--- .../wallet_connect/wc_connect_page.dart | 283 +++++---- lib/util/eth_amount_formatter.dart | 13 +- lib/util/usdc_amount_formatter.dart | 23 +- lib/util/xtz_utils.dart | 11 +- lib/view/account_view.dart | 39 +- 16 files changed, 1005 insertions(+), 872 deletions(-) diff --git a/lib/model/tzkt_operation.dart b/lib/model/tzkt_operation.dart index d586cd294..f25aae7d8 100644 --- a/lib/model/tzkt_operation.dart +++ b/lib/model/tzkt_operation.dart @@ -39,7 +39,7 @@ abstract class TZKTTransactionInterface { int getID(); - txUSDAmountSign(String? currentAddress) {} + String txUSDAmountSign(String? currentAddress); } @JsonSerializable() @@ -75,20 +75,20 @@ class TZKTOperation implements TZKTTransactionInterface { required this.block, required this.hash, required this.counter, + required this.gasLimit, + required this.gasUsed, + required this.bakerFee, + required this.quote, this.initiator, this.sender, this.target, - required this.gasLimit, - required this.gasUsed, this.storageLimit, this.storageUsed, - required this.bakerFee, this.storageFee, this.allocationFee, this.amount, this.status, this.hasInternals, - required this.quote, this.parameter, }); @@ -101,39 +101,51 @@ class TZKTOperation implements TZKTTransactionInterface { TZKTTokenTransfer? tokenTransfer; @override - DateTime getTimeStamp() { - return timestamp.toLocal(); - } + DateTime getTimeStamp() => timestamp.toLocal(); @override bool isReceiveNFT(String? currentAddress) { currentAddress = currentAddress ?? sender?.address; - if (currentAddress == null) return false; - if (tokenTransfer?.to?.address == currentAddress) return true; + if (currentAddress == null) { + return false; + } + if (tokenTransfer?.to?.address == currentAddress) { + return true; + } return false; } @override bool isSendNFT(String? currentAddress) { currentAddress = currentAddress ?? sender?.address; - if (currentAddress == null) return false; - if (tokenTransfer?.from?.address == currentAddress) return true; + if (currentAddress == null) { + return false; + } + if (tokenTransfer?.from?.address == currentAddress) { + return true; + } return false; } @override String transactionTitle(String? currentAddress) { - if (isBoughtNFT(currentAddress)) return "bought_nft".tr(); - if (isSendNFT(currentAddress)) return "sent_nft".tr(); - if (isReceiveNFT(currentAddress)) return "received_nft".tr(); - if (type != "transaction") { + if (isBoughtNFT(currentAddress)) { + return 'bought_nft'.tr(); + } + if (isSendNFT(currentAddress)) { + return 'sent_nft'.tr(); + } + if (isReceiveNFT(currentAddress)) { + return 'received_nft'.tr(); + } + if (type != 'transaction') { return type.capitalize(); } else if (parameter != null) { return parameter!.entrypoint.snakeToCapital(); } else { return sender?.address == currentAddress - ? "sent_xtz".tr() - : "received_xtz".tr(); + ? 'sent_xtz'.tr() + : 'received_xtz'.tr(); } } @@ -142,36 +154,33 @@ class TZKTOperation implements TZKTTransactionInterface { if (isReceiveNFT(currentAddress)) { return tokenTransfer!.totalAmount(currentAddress); } - return "${XtzAmountFormatter(getTotalAmount(currentAddress)).format()} XTZ"; + return '${XtzAmountFormatter().format(getTotalAmount(currentAddress))} XTZ'; } - String totalXTZAmount(String? currentAddress) { - return "${XtzAmountFormatter(getTotalAmount(currentAddress)).format()} XTZ"; - } + String totalXTZAmount(String? currentAddress) => + '${XtzAmountFormatter().format(getTotalAmount(currentAddress))} XTZ'; int getTotalAmount(String? currentAddress) { if (sender?.address == currentAddress) { - return ((amount ?? 0) + + return (amount ?? 0) + bakerFee + (storageFee ?? 0) + - (allocationFee ?? 0)); + (allocationFee ?? 0); } else { return amount ?? 0; } } @override - Widget transactionImage(String? currentAddress) { - return SvgPicture.asset( - "assets/images/tez.svg", - width: 40, - ); - } + Widget transactionImage(String? currentAddress) => SvgPicture.asset( + 'assets/images/tez.svg', + width: 40, + ); @override String transactionStatus() { if (status == null) { - return "pending".tr(); + return 'pending'.tr(); } else { return status!.capitalize(); } @@ -180,36 +189,46 @@ class TZKTOperation implements TZKTTransactionInterface { @override String txAmountSign(String? currentAddress) { String? a = transactionTitle(currentAddress); - if ((a == "received_nft".tr() || a == "received_xtz".tr())) return "+"; - return "-"; + if (a == 'received_nft'.tr() || a == 'received_xtz'.tr()) { + return '+'; + } + return '-'; } @override String transactionTitleDetail(String? currentAddress) { - if (isBoughtNFT(currentAddress)) return "bought_nft".tr(); - if (isSendNFT(currentAddress)) return "sent_nft".tr(); - if (isReceiveNFT(currentAddress)) return "received_nft".tr(); + if (isBoughtNFT(currentAddress)) { + return 'bought_nft'.tr(); + } + if (isSendNFT(currentAddress)) { + return 'sent_nft'.tr(); + } + if (isReceiveNFT(currentAddress)) { + return 'received_nft'.tr(); + } if (parameter != null) { - return "sc_interaction".tr(); - } else if (type != "transaction") { + return 'sc_interaction'.tr(); + } else if (type != 'transaction') { return type.capitalize(); } else { return sender?.address == currentAddress - ? "sent_xtz".tr() - : "received_xtz".tr(); + ? 'sent_xtz'.tr() + : 'received_xtz'.tr(); } } @override - int getID() { - return id; - } + int getID() => id; @override String txUSDAmountSign(String? currentAddress) { - if (transactionTitle(currentAddress) == "received_xtz".tr()) return "+"; - if (sender?.address == currentAddress) return "-"; - return ""; + if (transactionTitle(currentAddress) == 'received_xtz'.tr()) { + return '+'; + } + if (sender?.address == currentAddress) { + return '-'; + } + return ''; } @override @@ -260,80 +279,77 @@ class TZKTTokenTransfer implements TZKTTransactionInterface { TZKTTokenTransfer? tokenTransfer; @override - DateTime getTimeStamp() { - return timestamp.toLocal(); - } + DateTime getTimeStamp() => timestamp.toLocal(); @override bool isReceiveNFT(String? currentAddress) { - if (to?.address == currentAddress) return true; + if (to?.address == currentAddress) { + return true; + } return false; } @override bool isSendNFT(String? currentAddress) { - if (from?.address == currentAddress) return true; + if (from?.address == currentAddress) { + return true; + } return false; } @override String totalAmount(String? currentAddress) { - if (amount == null) return "0 Token"; - if (amount == "1" || amount == "0") return "$amount Token"; + if (amount == null) { + return '0 Token'; + } + if (amount == '1' || amount == '0') { + return '$amount Token'; + } try { final amountBigInt = BigInt.parse(amount!); final amountStr = amountBigInt < BigInt.from(1000000000) ? amount : amountBigInt.isValidInt ? NumberFormat.compact().format(int.parse(amount!)) - : "${amount!.substring(0, amount!.length - 12)}T"; - return "$amountStr Tokens"; + : '${amount!.substring(0, amount!.length - 12)}T'; + return '$amountStr Tokens'; } catch (_) { - return "N/A Token"; + return 'N/A Token'; } } @override - Widget transactionImage(String? currentAddress) { - return SvgPicture.asset("assets/images/tez.svg", width: 40); - } + Widget transactionImage(String? currentAddress) => + SvgPicture.asset('assets/images/tez.svg', width: 40); @override - String transactionStatus() { - return status?.tr() ?? "applied".tr(); - } + String transactionStatus() => status?.tr() ?? 'applied'.tr(); @override - String transactionTitle(String? currentAddress) { - return isSendNFT(currentAddress) ? "sent_nft".tr() : "received_nft".tr(); - } + String transactionTitle(String? currentAddress) => + isSendNFT(currentAddress) ? 'sent_nft'.tr() : 'received_nft'.tr(); @override String txAmountSign(String? currentAddress) { String? a = transactionTitle(currentAddress); - if ((a == "received_nft".tr() || a == "received_xtz".tr())) return "+"; - return "-"; + if (a == 'received_nft'.tr() || a == 'received_xtz'.tr()) { + return '+'; + } + return '-'; } @override - String transactionTitleDetail(String? currentAddress) { - return transactionTitle(currentAddress); - } + String transactionTitleDetail(String? currentAddress) => + transactionTitle(currentAddress); @override - int getID() { - return id; - } + int getID() => id; @override - txUSDAmountSign(String? currentAddress) { - return ""; - } + String txUSDAmountSign(String? currentAddress) => ''; @override - bool isBoughtNFT(String? currentAddress) { - return false; - } + bool isBoughtNFT(String? currentAddress) => false; } @JsonSerializable() diff --git a/lib/screen/bloc/scan_wallet/scan_wallet_state.dart b/lib/screen/bloc/scan_wallet/scan_wallet_state.dart index 074e8a03f..0d4c84307 100644 --- a/lib/screen/bloc/scan_wallet/scan_wallet_state.dart +++ b/lib/screen/bloc/scan_wallet/scan_wallet_state.dart @@ -17,12 +17,11 @@ class ScanWalletState { //add new addresses ScanWalletState addNewAddresses(List addresses, - {bool? hitStopGap, bool? isScanning}) { - return ScanWalletState( - addresses: [...this.addresses, ...addresses], - hitStopGap: hitStopGap ?? this.hitStopGap, - isScanning: isScanning ?? this.isScanning); - } + {bool? hitStopGap, bool? isScanning}) => + ScanWalletState( + addresses: [...this.addresses, ...addresses], + hitStopGap: hitStopGap ?? this.hitStopGap, + isScanning: isScanning ?? this.isScanning); } abstract class ScanWalletEvent {} @@ -87,19 +86,14 @@ class EthereumAddressInfo implements AddressInfo { // override toString @override - String toString() { - return 'EthereumAddressInfo{index: $index, address: $address, balance: ${balance.getInWei}}'; - } + String toString() => 'EthereumAddressInfo{index: $index, ' + 'address: $address, balance: ${balance.getInWei}}'; @override - String getBalance() { - return "${EthAmountFormatter(balance.getInWei).format()} ETH"; - } + String getBalance() => '${EthAmountFormatter().format(balance.getInWei)} ETH'; @override - getCryptoType() { - return CryptoType.ETH; - } + CryptoType getCryptoType() => CryptoType.ETH; @override bool hasBalance() => balance.getInWei > BigInt.zero; @@ -117,19 +111,14 @@ class TezosAddressInfo implements AddressInfo { // override toString @override - String toString() { - return 'TezosAddressInfo{index: $index, address: $address, balance: $balance}'; - } + String toString() => + 'TezosAddressInfo{index: $index, address: $address, balance: $balance}'; @override - String getBalance() { - return "${XtzAmountFormatter(balance).format()} XTZ"; - } + String getBalance() => '${XtzAmountFormatter().format(balance)} XTZ'; @override - CryptoType getCryptoType() { - return CryptoType.XTZ; - } + CryptoType getCryptoType() => CryptoType.XTZ; @override bool hasBalance() => balance > 0; diff --git a/lib/screen/settings/crypto/send/send_crypto_page.dart b/lib/screen/settings/crypto/send/send_crypto_page.dart index 93c9ab4c8..fc9c5877a 100644 --- a/lib/screen/settings/crypto/send/send_crypto_page.dart +++ b/lib/screen/settings/crypto/send/send_crypto_page.dart @@ -50,6 +50,9 @@ class _SendCryptoPageState extends State { final TextEditingController _amountController = TextEditingController(); bool _initialChangeAddress = false; late FeeOption _selectedPriority; + final xtzFormatter = XtzAmountFormatter(); + final ethFormatter = EthAmountFormatter(); + final usdcFormatter = USDCAmountFormatter(); @override void initState() { @@ -507,16 +510,16 @@ class _SendCryptoPageState extends State { switch (widget.data.type) { case CryptoType.ETH: text = state.isCrypto - ? '${EthAmountFormatter(max).format()} ETH' + ? '${ethFormatter.format(max)} ETH' : '${state.exchangeRate.ethToUsd(max)} USD'; break; case CryptoType.XTZ: text = state.isCrypto - ? '${XtzAmountFormatter(max.toInt()).format()} XTZ' + ? '${xtzFormatter.format(max.toInt())} XTZ' : '${state.exchangeRate.xtzToUsd(max.toInt())} USD'; break; case CryptoType.USDC: - text = '${USDCAmountFormatter(max).format()} USDC'; + text = '${usdcFormatter.format(max)} USDC'; break; default: break; @@ -533,14 +536,14 @@ class _SendCryptoPageState extends State { switch (widget.data.type) { case CryptoType.ETH: return state.isCrypto - ? EthAmountFormatter(max).format() + ? ethFormatter.format(max) : state.exchangeRate.ethToUsd(max); case CryptoType.XTZ: return state.isCrypto - ? XtzAmountFormatter(max.toInt()).format() + ? xtzFormatter.format(max.toInt()) : state.exchangeRate.xtzToUsd(max.toInt()); case CryptoType.USDC: - return USDCAmountFormatter(max).format(); + return usdcFormatter.format(max); default: return ''; } @@ -550,18 +553,19 @@ class _SendCryptoPageState extends State { if (state.feeOptionValue == null) { return widget.data.type.code; } + final ethFormatter = EthAmountFormatter(digit: 7); final fee = state.feeOptionValue!.getFee(feeOption ?? state.feeOption); switch (widget.data.type) { case CryptoType.ETH: return state.isCrypto - ? '''${EthAmountFormatter(fee, digit: 7).format()} ETH (${state.exchangeRate.ethToUsd(fee)} USD)''' + ? '''${ethFormatter.format(fee)} ETH (${state.exchangeRate.ethToUsd(fee)} USD)''' : '${state.exchangeRate.ethToUsd(fee)} USD'; case CryptoType.XTZ: return state.isCrypto - ? '''${XtzAmountFormatter(fee.toInt()).format()} XTZ (${state.exchangeRate.xtzToUsd(fee.toInt())} USD)''' + ? '''${xtzFormatter.format(fee.toInt())} XTZ (${state.exchangeRate.xtzToUsd(fee.toInt())} USD)''' : '${state.exchangeRate.xtzToUsd(fee.toInt())} USD'; case CryptoType.USDC: - return '''${EthAmountFormatter(fee, digit: 7).format()} ETH (${state.exchangeRate.ethToUsd(fee)} USD)'''; + return '''${ethFormatter.format(fee)} ETH (${state.exchangeRate.ethToUsd(fee)} USD)'''; default: return ''; } diff --git a/lib/screen/settings/crypto/send_artwork/send_artwork_page.dart b/lib/screen/settings/crypto/send_artwork/send_artwork_page.dart index 2d7479f8a..a2cd2da95 100644 --- a/lib/screen/settings/crypto/send_artwork/send_artwork_page.dart +++ b/lib/screen/settings/crypto/send_artwork/send_artwork_page.dart @@ -5,6 +5,8 @@ // that can be found in the LICENSE file. // +import 'dart:async'; + import 'package:autonomy_flutter/screen/app_router.dart'; import 'package:autonomy_flutter/screen/bloc/identity/identity_bloc.dart'; import 'package:autonomy_flutter/screen/scan_qr/scan_qr_page.dart'; @@ -40,7 +42,7 @@ class SendArtworkPage extends StatefulWidget { final SendArtworkPayload payload; - const SendArtworkPage({Key? key, required this.payload}) : super(key: key); + const SendArtworkPage({required this.payload, super.key}); @override State createState() => _SendArtworkPageState(); @@ -50,6 +52,7 @@ class _SendArtworkPageState extends State { final TextEditingController _addressController = TextEditingController(); final TextEditingController _quantityController = TextEditingController(); final feeWidgetKey = GlobalKey(); + final xtzFormatter = XtzAmountFormatter(); late int index; bool _initialChangeAddress = false; @@ -68,7 +71,7 @@ class _SendArtworkPageState extends State { context.read().add(QuantityUpdateEvent( quantity: 1, maxQuantity: widget.payload.ownedQuantity, index: index)); if (widget.payload.ownedQuantity == 1) { - _quantityController.text = "1"; + _quantityController.text = '1'; } if (widget.payload.asset.artistName != null) { context @@ -83,9 +86,7 @@ class _SendArtworkPageState extends State { }); } - int get _quantity { - return int.tryParse(_quantityController.text) ?? 0; - } + int get _quantity => int.tryParse(_quantityController.text) ?? 0; void _onQuantityUpdated() { context.read().add(QuantityUpdateEvent( @@ -102,24 +103,23 @@ class _SendArtworkPageState extends State { } Widget _quantityInputField( - {required int maxQuantity, required bool hasError}) { - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - AuTextField( - labelSemantics: "quantity_send_artwork", - title: "", - placeholder: - "enter_a_number_".tr(args: ["1", maxQuantity.toString()]), - controller: _quantityController, - isError: hasError, - keyboardType: TextInputType.number, - onChanged: (value) => _onQuantityUpdated(), - onSubmit: (value) => _onQuantityUpdated(), - ), - ], - ); - } + {required int maxQuantity, required bool hasError}) => + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + AuTextField( + labelSemantics: 'quantity_send_artwork', + title: '', + placeholder: + 'enter_a_number_'.tr(args: ['1', maxQuantity.toString()]), + controller: _quantityController, + isError: hasError, + keyboardType: TextInputType.number, + onChanged: (value) => _onQuantityUpdated(), + onSubmit: (value) => _onQuantityUpdated(), + ), + ], + ); @override void dispose() { @@ -138,7 +138,7 @@ class _SendArtworkPageState extends State { return Scaffold( appBar: getBackAppBar( context, - title: "send_artwork".tr(), + title: 'send_artwork'.tr(), onBack: () { Navigator.of(context).pop(); }, @@ -147,225 +147,234 @@ class _SendArtworkPageState extends State { children: [ BlocConsumer( listener: (context, state) { - if (state.fee != null && feeWidgetKey.currentContext != null) { - Scrollable.ensureVisible(feeWidgetKey.currentContext!); - } - if (state.fee != null && - state.balance != null && - state.fee! > state.balance! + BigInt.from(10)) { - UIHelper.showMessageAction( - context, - 'transaction_failed'.tr(), - 'dont_enough_money'.tr(), - ); - } - }, builder: (context, state) { - return GestureDetector( - behavior: HitTestBehavior.deferToChild, - onTap: () { - _unfocus(); + if (state.fee != null && feeWidgetKey.currentContext != null) { + unawaited( + Scrollable.ensureVisible(feeWidgetKey.currentContext!)); + } + if (state.fee != null && + state.balance != null && + state.fee! > state.balance! + BigInt.from(10)) { + unawaited(UIHelper.showMessageAction( + context, + 'transaction_failed'.tr(), + 'dont_enough_money'.tr(), + )); + } }, - child: Container( - margin: const EdgeInsets.only( - top: 16.0, - left: 16.0, - right: 16.0, - bottom: 40.0, - ), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Expanded( - child: SingleChildScrollView( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const SizedBox(height: 16), - _artworkView(context), - const SizedBox(height: 20), - _item( - context: context, - title: "token".tr(), - content: polishSource(asset.source ?? ""), - tapLink: asset.assetURL), - divider, - _item( - context: context, - title: "contract".tr(), - content: asset.blockchain.capitalize(), - tapLink: asset.getBlockchainUrl(), - ), - divider, - _item( - context: context, - title: "minted".tr(), - content: asset.mintedAt != null - ? localTimeString(asset.mintedAt!) - : ''), - divider, - if (widget.payload.asset.fungible == true) ...[ - _item( - context: context, - title: "owned_tokens".tr(), - content: "$maxQuantity"), - divider, - const SizedBox(height: 16), - Text( - "quantity_to_send".tr(), - style: theme.textTheme.ppMori400Black14, - ), - const SizedBox(height: 8), - _quantityInputField( - maxQuantity: maxQuantity, - hasError: state.isQuantityError), - if (state.isQuantityError) ...[ - const SizedBox(height: 8), - if (_quantity < 1) - Text( - "minimum_1".tr(), - style: theme.textTheme.ppMori400Black12 - .copyWith(color: AppColor.red), + builder: (context, state) => GestureDetector( + behavior: HitTestBehavior.deferToChild, + onTap: () { + _unfocus(); + }, + child: Container( + margin: const EdgeInsets.only( + top: 16, + left: 16, + right: 16, + bottom: 40, + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Expanded( + child: SingleChildScrollView( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const SizedBox(height: 16), + _artworkView(context), + const SizedBox(height: 20), + _item( + context: context, + title: 'token'.tr(), + content: polishSource(asset.source ?? ''), + tapLink: asset.assetURL), + divider, + _item( + context: context, + title: 'contract'.tr(), + content: asset.blockchain.capitalize(), + tapLink: asset.getBlockchainUrl(), ), - if (_quantity > maxQuantity) + divider, + _item( + context: context, + title: 'minted'.tr(), + content: asset.mintedAt != null + ? localTimeString(asset.mintedAt!) + : ''), + divider, + if (widget.payload.asset.fungible) ...[ + _item( + context: context, + title: 'owned_tokens'.tr(), + content: '$maxQuantity'), + divider, + const SizedBox(height: 16), + Text( + 'quantity_to_send'.tr(), + style: theme.textTheme.ppMori400Black14, + ), + const SizedBox(height: 8), + _quantityInputField( + maxQuantity: maxQuantity, + hasError: state.isQuantityError), + if (state.isQuantityError) ...[ + const SizedBox(height: 8), + if (_quantity < 1) + Text( + 'minimum_1'.tr(), + style: theme + .textTheme.ppMori400Black12 + .copyWith(color: AppColor.red), + ), + if (_quantity > maxQuantity) + Text( + 'maximum_'.tr( + args: [maxQuantity.toString()]), + style: theme + .textTheme.ppMori400Black12 + .copyWith(color: AppColor.red), + ), + ] + ], + const SizedBox(height: 16), Text( - "maximum_" - .tr(args: [maxQuantity.toString()]), - style: theme.textTheme.ppMori400Black12 - .copyWith(color: AppColor.red), + 'to'.tr(), + style: theme.textTheme.ppMori400Black14, ), - ] - ], - const SizedBox(height: 16.0), - Text( - "to".tr(), - style: theme.textTheme.ppMori400Black14, - ), - const SizedBox(height: 8), - AuTextField( - labelSemantics: "to_address_send_artwork", - title: "", - placeholder: "paste_or_scan_address".tr(), - controller: _addressController, - isError: state.isAddressError, - suffix: IconButton( - icon: Icon(state.isScanQR - ? AuIcon.scan - : AuIcon.close), - onPressed: () async { - if (_addressController.text.isNotEmpty) { - _addressController.text = ""; - context - .read() - .add(AddressChangedEvent("", index)); - _initialChangeAddress = true; - } else { - dynamic address = - await Navigator.of(context).pushNamed( - ScanQRPage.tag, - arguments: - asset.blockchain == "ethereum" - ? ScannerItem.ETH_ADDRESS - : ScannerItem.XTZ_ADDRESS); - if (address != null && address is String) { - address = address.replacePrefix( - "ethereum:", ""); - _addressController.text = address; - if (!mounted) return; - context.read().add( - AddressChangedEvent(address, index)); + const SizedBox(height: 8), + AuTextField( + labelSemantics: 'to_address_send_artwork', + title: '', + placeholder: 'paste_or_scan_address'.tr(), + controller: _addressController, + isError: state.isAddressError, + suffix: IconButton( + icon: Icon(state.isScanQR + ? AuIcon.scan + : AuIcon.close), + onPressed: () async { + if (_addressController + .text.isNotEmpty) { + _addressController.text = ''; + context.read().add( + AddressChangedEvent('', index)); + _initialChangeAddress = true; + } else { + dynamic address = await Navigator.of( + context) + .pushNamed(ScanQRPage.tag, + arguments: asset.blockchain == + 'ethereum' + ? ScannerItem.ETH_ADDRESS + : ScannerItem + .XTZ_ADDRESS); + if (address != null && + address is String) { + address = address.replacePrefix( + 'ethereum:', ''); + _addressController.text = address; + if (!mounted) { + return; + } + context.read().add( + AddressChangedEvent( + address, index)); + _initialChangeAddress = true; + } + } + }, + ), + onSubmit: (value) { + if (value != state.address) { + context.read().add( + AddressChangedEvent( + _addressController.text, + index), + ); + _initialChangeAddress = true; + } + }, + onChanged: (value) { _initialChangeAddress = true; - } - } - }, - ), - onSubmit: (value) { - if (value != state.address) { - context.read().add( - AddressChangedEvent( - _addressController.text, index), - ); - _initialChangeAddress = true; - } - }, - onChanged: (value) { - _initialChangeAddress = true; - context.read().add( - AddressChangedEvent( - _addressController.text, index), - ); - }, - ), - const SizedBox(height: 8), - Visibility( - visible: state.isAddressError, - child: Text( - 'error_invalid_address'.tr(), - style: theme.textTheme.ppMori400Black12 - .copyWith(color: AppColor.red), - )), - Visibility( - visible: !state.isAddressError, - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - gasFeeStatus(state, theme), - const SizedBox(height: 8.0), - if (state.feeOptionValue != null) - feeTable(state, context), - const SizedBox(height: 24.0), + context.read().add( + AddressChangedEvent( + _addressController.text, index), + ); + }, + ), + const SizedBox(height: 8), + Visibility( + visible: state.isAddressError, + child: Text( + 'error_invalid_address'.tr(), + style: theme.textTheme.ppMori400Black12 + .copyWith(color: AppColor.red), + )), + Visibility( + visible: !state.isAddressError, + child: Column( + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + gasFeeStatus(state, theme), + const SizedBox(height: 8), + if (state.feeOptionValue != null) + feeTable(state, context), + const SizedBox(height: 24), + ], + ), + ), ], ), ), - ], - ), + ), + _reviewButton(asset, state) + ], ), ), - _reviewButton(asset, state) - ], - ), - ), - ); - }), + )), ], ), ); } - Widget _reviewButton(AssetToken asset, SendArtworkState state) { - return Row( - children: [ - Expanded( - child: PrimaryButton( - text: "review".tr(), - onTap: state.isValid - ? () async { - _unfocus(); - final payload = await Navigator.of(context).pushNamed( - AppRouter.sendArtworkReviewPage, - arguments: SendArtworkReviewPayload( - asset, - widget.payload.wallet, - widget.payload.index, - state.address!, - state.fee!, - state.exchangeRate, - widget.payload.ownedQuantity, - state.quantity, - state.feeOption, - state.feeOptionValue!)) as Map?; - if (payload != null && - payload["hash"] != null && - payload["hash"] is String) { - if (!mounted) return; - Navigator.of(context).pop(payload); + Widget _reviewButton(AssetToken asset, SendArtworkState state) => Row( + children: [ + Expanded( + child: PrimaryButton( + text: 'review'.tr(), + onTap: state.isValid + ? () async { + _unfocus(); + final payload = await Navigator.of(context).pushNamed( + AppRouter.sendArtworkReviewPage, + arguments: SendArtworkReviewPayload( + asset, + widget.payload.wallet, + widget.payload.index, + state.address!, + state.fee!, + state.exchangeRate, + widget.payload.ownedQuantity, + state.quantity, + state.feeOption, + state.feeOptionValue!)) as Map?; + if (payload != null && + payload['hash'] != null && + payload['hash'] is String) { + if (!mounted) { + return; + } + Navigator.of(context).pop(payload); + } } - } - : null, + : null, + ), ), - ), - ], - ); - } + ], + ); Widget _item({ required BuildContext context, @@ -380,10 +389,10 @@ class _SendArtworkPageState extends State { if (tapLink != null) { final uri = Uri.parse(tapLink); - onValueTap = () => launchUrl(uri, - mode: forceSafariVC == true + onValueTap = () => unawaited(launchUrl(uri, + mode: forceSafariVC ? LaunchMode.externalApplication - : LaunchMode.platformDefault); + : LaunchMode.platformDefault)); } return Row( children: [ @@ -410,7 +419,7 @@ class _SendArtworkPageState extends State { Widget gasFeeStatus(SendArtworkState state, ThemeData theme) { if (_initialChangeAddress && state.feeOptionValue == null) { - return Text("gas_fee_calculating".tr(), + return Text('gas_fee_calculating'.tr(), style: theme.textTheme.ppMori400Black12); } if (state.feeOptionValue != null && state.balance != null) { @@ -418,7 +427,7 @@ class _SendArtworkPageState extends State { state.feeOptionValue!.getFee(state.feeOption) + BigInt.from(10); if (!isValid) { return Text( - "gas_fee_insufficient".tr(), + 'gas_fee_insufficient'.tr(), textAlign: TextAlign.start, style: theme.textTheme.ppMori400Black12.copyWith(color: AppColor.red), ); @@ -432,7 +441,7 @@ class _SendArtworkPageState extends State { final feeOption = state.feeOption; return Row( children: [ - Text("gas_fee".tr(), style: theme.textTheme.ppMori400Black12), + Text('gas_fee'.tr(), style: theme.textTheme.ppMori400Black12), const SizedBox(width: 8), Text(feeOption.name, style: theme.textTheme.ppMori400Black12), const Spacer(), @@ -442,9 +451,9 @@ class _SendArtworkPageState extends State { ), GestureDetector( onTap: () { - UIHelper.showDialog( + unawaited(UIHelper.showDialog( context, - "edit_priority".tr().capitalize(), + 'edit_priority'.tr().capitalize(), _editPriorityView(state, context, onSave: () { context .read() @@ -453,9 +462,9 @@ class _SendArtworkPageState extends State { backgroundColor: AppColor.auGreyBackground, padding: const EdgeInsets.symmetric(vertical: 32), paddingTitle: ResponsiveLayout.pageHorizontalEdgeInsets, - ); + )); }, - child: Text("edit_priority".tr(), + child: Text('edit_priority'.tr(), style: theme.textTheme.linkStyle .copyWith(fontWeight: FontWeight.w400, fontSize: 12)), ), @@ -467,49 +476,48 @@ class _SendArtworkPageState extends State { {required Function() onSave}) { final theme = Theme.of(context); final padding = ResponsiveLayout.pageEdgeInsets.copyWith(top: 0, bottom: 0); - return StatefulBuilder(builder: (context, setState) { - return Column( - children: [ - Padding( - padding: padding, - child: getFeeRow(FeeOption.LOW, state, theme, setState), - ), - addDivider(color: AppColor.white), - Padding( - padding: padding, - child: getFeeRow(FeeOption.MEDIUM, state, theme, setState), - ), - addDivider(color: AppColor.white), - Padding( - padding: padding, - child: getFeeRow(FeeOption.HIGH, state, theme, setState), - ), - addDivider(color: AppColor.white), - const SizedBox(height: 12), - Padding( - padding: padding, - child: PrimaryButton( - text: "save_priority".tr(), - onTap: () { - onSave(); - Navigator.of(context).pop(); - }, - ), - ), - const SizedBox(height: 8), - Padding( - padding: padding, - child: OutlineButton( - text: "cancel".tr(), - onTap: () { - _selectedPriority = state.feeOption; - Navigator.of(context).pop(); - }, - ), - ) - ], - ); - }); + return StatefulBuilder( + builder: (context, setState) => Column( + children: [ + Padding( + padding: padding, + child: getFeeRow(FeeOption.LOW, state, theme, setState), + ), + addDivider(color: AppColor.white), + Padding( + padding: padding, + child: getFeeRow(FeeOption.MEDIUM, state, theme, setState), + ), + addDivider(color: AppColor.white), + Padding( + padding: padding, + child: getFeeRow(FeeOption.HIGH, state, theme, setState), + ), + addDivider(color: AppColor.white), + const SizedBox(height: 12), + Padding( + padding: padding, + child: PrimaryButton( + text: 'save_priority'.tr(), + onTap: () { + onSave(); + Navigator.of(context).pop(); + }, + ), + ), + const SizedBox(height: 8), + Padding( + padding: padding, + child: OutlineButton( + text: 'cancel'.tr(), + onTap: () { + _selectedPriority = state.feeOption; + Navigator.of(context).pop(); + }, + ), + ) + ], + )); } Widget getFeeRow(FeeOption feeOption, SendArtworkState state, ThemeData theme, @@ -546,18 +554,23 @@ class _SendArtworkPageState extends State { } String _gasFee(SendArtworkState state, {FeeOption? feeOption}) { - final type = widget.payload.asset.blockchain == "ethereum" + final type = widget.payload.asset.blockchain == 'ethereum' ? CryptoType.ETH : CryptoType.XTZ; - if (state.feeOptionValue == null) return type.code; + if (state.feeOptionValue == null) { + return type.code; + } + final ethFormatter = EthAmountFormatter(digit: 7); final fee = state.feeOptionValue!.getFee(feeOption ?? state.feeOption); switch (type) { case CryptoType.ETH: - return "${EthAmountFormatter(fee, digit: 7).format()} ETH (${state.exchangeRate.ethToUsd(fee)} USD)"; + return '${ethFormatter.format(fee)} ETH ' + '(${state.exchangeRate.ethToUsd(fee)} USD)'; case CryptoType.XTZ: - return "${XtzAmountFormatter(fee.toInt()).format()} XTZ (${state.exchangeRate.xtzToUsd(fee.toInt())} USD)"; + return '${xtzFormatter.format(fee.toInt())} XTZ ' + '(${state.exchangeRate.xtzToUsd(fee.toInt())} USD)'; default: - return ""; + return ''; } } @@ -598,14 +611,14 @@ class _SendArtworkPageState extends State { text: TextSpan( style: theme.textTheme.ppMori400White14, children: [ - TextSpan(text: "by".tr(args: [artistName ?? ""])), + TextSpan(text: 'by'.tr(args: [artistName ?? ''])), ], ), ), if (asset.maxEdition == -1) ...[ const SizedBox(height: 8), Text( - asset.editionName ?? "", + asset.editionName ?? '', style: theme.textTheme.ppMori400Grey14, ), ] diff --git a/lib/screen/settings/crypto/send_artwork/send_artwork_review_page.dart b/lib/screen/settings/crypto/send_artwork/send_artwork_review_page.dart index 4fefda11f..4c3f3507c 100644 --- a/lib/screen/settings/crypto/send_artwork/send_artwork_review_page.dart +++ b/lib/screen/settings/crypto/send_artwork/send_artwork_review_page.dart @@ -5,6 +5,7 @@ // that can be found in the LICENSE file. // +import 'dart:async'; import 'dart:convert'; import 'dart:typed_data'; @@ -40,8 +41,7 @@ import 'package:web3dart/web3dart.dart'; class SendArtworkReviewPage extends StatefulWidget { final SendArtworkReviewPayload payload; - const SendArtworkReviewPage({Key? key, required this.payload}) - : super(key: key); + const SendArtworkReviewPage({required this.payload, super.key}); @override State createState() => _SendArtworkReviewPageState(); @@ -49,8 +49,11 @@ class SendArtworkReviewPage extends StatefulWidget { class _SendArtworkReviewPageState extends State { bool _isSending = false; + final tokensService = injector(); + final ethFormatter = EthAmountFormatter(); + final xtzFormatter = XtzAmountFormatter(); - void _sendArtwork() async { + Future _sendArtwork() async { setState(() { _isSending = true; }); @@ -64,7 +67,7 @@ class _SendArtworkReviewPageState extends State { try { final assetToken = widget.payload.assetToken; - if (widget.payload.assetToken.blockchain == "ethereum") { + if (widget.payload.assetToken.blockchain == 'ethereum') { final ethereumService = injector(); final contractAddress = @@ -74,7 +77,7 @@ class _SendArtworkReviewPageState extends State { .getETHEip55Address(index: widget.payload.index)); final tokenId = assetToken.tokenId!; - final data = widget.payload.assetToken.contractType == "erc1155" + final data = widget.payload.assetToken.contractType == 'erc1155' ? await ethereumService.getERC1155TransferTransactionData( contractAddress, from, to, tokenId, widget.payload.quantity, feeOption: widget.payload.feeOption) @@ -99,22 +102,24 @@ class _SendArtworkReviewPageState extends State { Uint8List.fromList(utf8.encode(timestamp))); final pendingTxParams = PendingTxParams( blockchain: assetToken.blockchain, - id: assetToken.tokenId ?? "", - contractAddress: assetToken.contractAddress ?? "", + id: assetToken.tokenId ?? '', + contractAddress: assetToken.contractAddress ?? '', ownerAccount: assetToken.owner, pendingTx: txHash, timestamp: timestamp, signature: signature, ); - injector().postPendingToken(pendingTxParams); + unawaited(tokensService.postPendingToken(pendingTxParams)); } - if (!mounted) return; + if (!mounted) { + return; + } final payload = { - "isTezos": false, - "hash": txHash, - "isSentAll": widget.payload.quantity >= widget.payload.ownedTokens, - "sentQuantity": widget.payload.quantity, + 'isTezos': false, + 'hash': txHash, + 'isSentAll': widget.payload.quantity >= widget.payload.ownedTokens, + 'sentQuantity': widget.payload.quantity, }; Navigator.of(context).pop(payload); } else { @@ -144,32 +149,36 @@ class _SendArtworkReviewPageState extends State { wallet, index, Uint8List.fromList(utf8.encode(timestamp))); final pendingTxParams = PendingTxParams( blockchain: assetToken.blockchain, - id: assetToken.tokenId ?? "", - contractAddress: assetToken.contractAddress ?? "", + id: assetToken.tokenId ?? '', + contractAddress: assetToken.contractAddress ?? '', ownerAccount: assetToken.owner, pendingTx: opHash, timestamp: timestamp, signature: signature, publicKey: publicKey, ); - injector().postPendingToken(pendingTxParams); + unawaited(tokensService.postPendingToken(pendingTxParams)); + } + if (!mounted) { + return; } - if (!mounted) return; final payload = { - "isTezos": true, - "hash": opHash, - "isSentAll": widget.payload.quantity >= widget.payload.ownedTokens, - "sentQuantity": widget.payload.quantity, + 'isTezos': true, + 'hash': opHash, + 'isSentAll': widget.payload.quantity >= widget.payload.ownedTokens, + 'sentQuantity': widget.payload.quantity, }; Navigator.of(context).pop(payload); } } catch (e) { - if (!mounted) return; - UIHelper.showMessageAction( + if (!mounted) { + return; + } + unawaited(UIHelper.showMessageAction( context, 'transaction_failed'.tr(), 'try_later'.tr(), - ); + )); } setState(() { _isSending = false; @@ -192,7 +201,7 @@ class _SendArtworkReviewPageState extends State { child: Scaffold( appBar: getBackAppBar( context, - title: "confirmation".tr(), + title: 'confirmation'.tr(), onBack: () { Navigator.of(context).pop(); }, @@ -214,11 +223,11 @@ class _SendArtworkReviewPageState extends State { Padding( padding: padding, child: Text( - "send_artwork".tr(), + 'send_artwork'.tr(), style: theme.textTheme.ppMori400Black16, ), ), - const SizedBox(height: 40.0), + const SizedBox(height: 40), divider, Padding( padding: padding, @@ -226,56 +235,54 @@ class _SendArtworkReviewPageState extends State { children: [ _item( context: context, - title: "title".tr(), + title: 'title'.tr(), content: assetToken.title ?? '', ), divider, _item( context: context, - title: "artist".tr(), - content: artistName ?? "", + title: 'artist'.tr(), + content: artistName ?? '', tapLink: assetToken.artistURL), divider, - if (!(widget.payload.assetToken.fungible == - true)) ...[ + if (!widget.payload.assetToken.fungible) ...[ _item( context: context, - title: "edition".tr(), + title: 'edition'.tr(), content: assetToken.editionSlashMax), divider, ], _item( context: context, - title: "token".tr(), + title: 'token'.tr(), content: - polishSource(assetToken.source ?? ""), + polishSource(assetToken.source ?? ''), tapLink: assetToken.assetURL), divider, _item( context: context, - title: "contract".tr(), + title: 'contract'.tr(), content: assetToken.blockchain.capitalize(), tapLink: assetToken.getBlockchainUrl(), ), divider, _item( context: context, - title: "minted".tr(), + title: 'minted'.tr(), content: assetToken.mintedAt != null ? localTimeString(assetToken.mintedAt!) : ''), divider, - if (widget.payload.assetToken.fungible == - true) ...[ + if (widget.payload.assetToken.fungible) ...[ _item( context: context, - title: "owned_tokens".tr(), - content: "${widget.payload.ownedTokens}"), + title: 'owned_tokens'.tr(), + content: '${widget.payload.ownedTokens}'), divider, _item( context: context, - title: "quantity_sent".tr(), - content: "${widget.payload.quantity}"), + title: 'quantity_sent'.tr(), + content: '${widget.payload.quantity}'), divider, ], const SizedBox(height: 16), @@ -289,26 +296,26 @@ class _SendArtworkReviewPageState extends State { CrossAxisAlignment.start, children: [ Text( - "to".tr(), + 'to'.tr(), style: theme.textTheme.ppMori400Grey14, ), - const SizedBox(height: 8.0), + const SizedBox(height: 8), Text( widget.payload.address, style: theme.textTheme.ppMori400White14, ), addDivider(color: AppColor.white), Text( - "gas_fee2".tr(), + 'gas_fee2'.tr(), style: theme.textTheme.ppMori400Grey14, ), - const SizedBox(height: 8.0), + const SizedBox(height: 8), Text( _amountFormat( fee, isETH: widget.payload.assetToken .blockchain == - "ethereum", + 'ethereum', ), style: theme.textTheme.ppMori400White14, ), @@ -328,14 +335,14 @@ class _SendArtworkReviewPageState extends State { children: [ Expanded( child: PrimaryButton( - text: _isSending ? "sending".tr() : "sendH".tr(), + text: _isSending ? 'sending'.tr() : 'sendH'.tr(), isProcessing: _isSending, onTap: _isSending ? null : _sendArtwork), ), ], ), ), - const SizedBox(height: 16.0), + const SizedBox(height: 16), ], ), ), @@ -358,10 +365,10 @@ class _SendArtworkReviewPageState extends State { if (tapLink != null) { final uri = Uri.parse(tapLink); - onValueTap = () => launchUrl(uri, - mode: forceSafariVC == true + onValueTap = () => unawaited(launchUrl(uri, + mode: forceSafariVC ? LaunchMode.externalApplication - : LaunchMode.platformDefault); + : LaunchMode.platformDefault)); } return Row( children: [ @@ -386,11 +393,11 @@ class _SendArtworkReviewPageState extends State { ); } - String _amountFormat(BigInt fee, {required bool isETH}) { - return isETH - ? "${EthAmountFormatter(fee).format()} ETH (${widget.payload.exchangeRate.ethToUsd(fee)} USD)" - : "${XtzAmountFormatter(fee.toInt()).format()} XTZ (${widget.payload.exchangeRate.xtzToUsd(fee.toInt())} USD)"; - } + String _amountFormat(BigInt fee, {required bool isETH}) => isETH + ? '${ethFormatter.format(fee)} ETH ' + '(${widget.payload.exchangeRate.ethToUsd(fee)} USD)' + : '${xtzFormatter.format(fee.toInt())} XTZ ' + '(${widget.payload.exchangeRate.xtzToUsd(fee.toInt())} USD)'; } class SendArtworkReviewPayload { diff --git a/lib/screen/settings/crypto/send_review_page.dart b/lib/screen/settings/crypto/send_review_page.dart index 83c0330aa..3d70c220f 100644 --- a/lib/screen/settings/crypto/send_review_page.dart +++ b/lib/screen/settings/crypto/send_review_page.dart @@ -5,6 +5,8 @@ // that can be found in the LICENSE file. // +import 'dart:async'; + import 'package:autonomy_flutter/common/injector.dart'; import 'package:autonomy_flutter/screen/settings/crypto/send/send_crypto_page.dart'; import 'package:autonomy_flutter/service/ethereum_service.dart'; @@ -33,7 +35,7 @@ class SendReviewPage extends StatefulWidget { final SendCryptoPayload payload; - const SendReviewPage({Key? key, required this.payload}) : super(key: key); + const SendReviewPage({required this.payload, super.key}); @override State createState() => _SendReviewPageState(); @@ -41,8 +43,11 @@ class SendReviewPage extends StatefulWidget { class _SendReviewPageState extends State { bool _isSending = false; + final ethFormatter = EthAmountFormatter(); + final xtzFormatter = XtzAmountFormatter(); + final usdcFormatter = USDCAmountFormatter(); - void _send() async { + Future _send() async { setState(() { _isSending = true; }); @@ -67,10 +72,12 @@ class _SendReviewPageState extends State { null, feeOption: widget.payload.feeOption); - if (!mounted) return; + if (!mounted) { + return; + } final payload = { - "isTezos": false, - "hash": txHash, + 'isTezos': false, + 'hash': txHash, }; Navigator.of(context).pop(payload); break; @@ -82,10 +89,12 @@ class _SendReviewPageState extends State { widget.payload.amount.toInt(), baseOperationCustomFee: widget.payload.feeOption.tezosBaseOperationCustomFee); - if (!mounted) return; + if (!mounted) { + return; + } final payload = { - "isTezos": true, - "hash": opHash, + 'isTezos': true, + 'hash': opHash, }; Navigator.of(context).pop(payload); break; @@ -109,10 +118,12 @@ class _SendReviewPageState extends State { data, feeOption: widget.payload.feeOption); - if (!mounted) return; + if (!mounted) { + return; + } final payload = { - "isTezos": false, - "hash": txHash, + 'isTezos': false, + 'hash': txHash, }; Navigator.of(context).pop(payload); break; @@ -120,12 +131,14 @@ class _SendReviewPageState extends State { break; } } catch (e) { - if (!mounted) return; - UIHelper.showMessageAction( + if (!mounted) { + return; + } + unawaited(UIHelper.showMessageAction( context, 'transaction_failed'.tr(), 'try_later'.tr(), - ); + )); } setState(() { @@ -136,13 +149,13 @@ class _SendReviewPageState extends State { String _titleText() { switch (widget.payload.type) { case CryptoType.ETH: - return "send_eth".tr(); + return 'send_eth'.tr(); case CryptoType.XTZ: - return "send_xtz".tr(); + return 'send_xtz'.tr(); case CryptoType.USDC: - return "send_usdc".tr(); + return 'send_usdc'.tr(); default: - return ""; + return ''; } } @@ -156,7 +169,7 @@ class _SendReviewPageState extends State { return Scaffold( appBar: getBackAppBar( context, - title: "confirmation".tr(), + title: 'confirmation'.tr(), onBack: () { Navigator.of(context).pop(); }, @@ -188,7 +201,7 @@ class _SendReviewPageState extends State { SizedBox( width: 120, child: Text( - "amount".tr(), + 'amount'.tr(), style: theme.textTheme.ppMori400Grey14, ), ), @@ -204,7 +217,7 @@ class _SendReviewPageState extends State { SizedBox( width: 120, child: Text( - "total_amount".tr(), + 'total_amount'.tr(), style: theme.textTheme.ppMori400Grey14, ), ), @@ -225,20 +238,20 @@ class _SendReviewPageState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - "to".tr(), + 'to'.tr(), style: theme.textTheme.ppMori400Grey14, ), - const SizedBox(height: 8.0), + const SizedBox(height: 8), Text( widget.payload.address, style: theme.textTheme.ppMori400White14, ), addDivider(color: AppColor.white), Text( - "gas_fee2".tr(), + 'gas_fee2'.tr(), style: theme.textTheme.ppMori400Grey14, ), - const SizedBox(height: 8.0), + const SizedBox(height: 8), Text( _amountFormat(widget.payload.fee, isETH: true), style: theme.textTheme.ppMori400White14, @@ -256,7 +269,7 @@ class _SendReviewPageState extends State { children: [ Expanded( child: PrimaryButton( - text: _isSending ? "sending".tr() : "send".tr(), + text: _isSending ? 'sending'.tr() : 'send'.tr(), isProcessing: _isSending, onTap: _isSending ? null : _send, ), @@ -267,9 +280,10 @@ class _SendReviewPageState extends State { ], ), ), - _isSending - ? const Center(child: CupertinoActivityIndicator()) - : const SizedBox(), + if (_isSending) + const Center(child: CupertinoActivityIndicator()) + else + const SizedBox(), ], ), ); @@ -278,17 +292,20 @@ class _SendReviewPageState extends State { String _amountFormat(BigInt amount, {bool isETH = false}) { switch (widget.payload.type) { case CryptoType.ETH: - return "${EthAmountFormatter(amount).format()} ETH (${widget.payload.exchangeRate.ethToUsd(amount)} USD)"; + return '${ethFormatter.format(amount)} ETH ' + '(${widget.payload.exchangeRate.ethToUsd(amount)} USD)'; case CryptoType.XTZ: - return "${XtzAmountFormatter(amount.toInt()).format()} XTZ (${widget.payload.exchangeRate.xtzToUsd(amount.toInt())} USD)"; + return '${xtzFormatter.format(amount.toInt())} XTZ ' + '(${widget.payload.exchangeRate.xtzToUsd(amount.toInt())} USD)'; case CryptoType.USDC: if (isETH) { - return "${EthAmountFormatter(amount).format()} ETH (${widget.payload.exchangeRate.ethToUsd(amount)} USD)"; + return '${ethFormatter.format(amount)} ETH ' + '(${widget.payload.exchangeRate.ethToUsd(amount)} USD)'; } else { - return "${USDCAmountFormatter(amount).format()} USDC"; + return '${usdcFormatter.format(amount)} USDC'; } default: - return ""; + return ''; } } } diff --git a/lib/screen/settings/crypto/wallet_detail/linked_wallet_detail_page.dart b/lib/screen/settings/crypto/wallet_detail/linked_wallet_detail_page.dart index 933f3815c..616e9fabe 100644 --- a/lib/screen/settings/crypto/wallet_detail/linked_wallet_detail_page.dart +++ b/lib/screen/settings/crypto/wallet_detail/linked_wallet_detail_page.dart @@ -58,6 +58,8 @@ class _LinkedWalletDetailPageState extends State late Connection _connection; late String _address; + final usdcFormatter = USDCAmountFormatter(); + @override void initState() { super.initState(); @@ -246,7 +248,7 @@ class _LinkedWalletDetailPageState extends State state.usdcBalances[_address]; final balance = usdcBalance == null ? '-- USDC' - : '''${USDCAmountFormatter(usdcBalance).format()} USDC'''; + : '''${usdcFormatter.format(usdcBalance)} USDC'''; return Padding( padding: padding, child: _usdcBalance( @@ -380,7 +382,7 @@ class _LinkedWalletDetailPageState extends State final usdcBalance = state.usdcBalances[_address]; final balance = usdcBalance == null ? '-- USDC' - : '${USDCAmountFormatter(usdcBalance).format()} USDC'; + : '${usdcFormatter.format(usdcBalance)} USDC'; return SizedBox( child: Column( mainAxisAlignment: MainAxisAlignment.center, diff --git a/lib/screen/settings/crypto/wallet_detail/wallet_detail_bloc.dart b/lib/screen/settings/crypto/wallet_detail/wallet_detail_bloc.dart index a1cce0e9f..5f182868b 100644 --- a/lib/screen/settings/crypto/wallet_detail/wallet_detail_bloc.dart +++ b/lib/screen/settings/crypto/wallet_detail/wallet_detail_bloc.dart @@ -21,6 +21,8 @@ class WalletDetailBloc extends AuBloc { final EthereumService _ethereumService; final TezosService _tezosService; final CurrencyService _currencyService; + final ethFormatter = EthAmountFormatter(); + final xtzFormatter = XtzAmountFormatter(); WalletDetailBloc( this._ethereumService, this._tezosService, this._currencyService) @@ -32,8 +34,7 @@ class WalletDetailBloc extends AuBloc { switch (event.type) { case CryptoType.ETH: final balance = await _ethereumService.getBalance(event.address); - newState.balance = - '${EthAmountFormatter(balance.getInWei).format()} ETH'; + newState.balance = '${ethFormatter.format(balance.getInWei)} ETH'; final usdBalance = balance.getInWei.toDouble() / pow(10, 18) * double.parse(exchangeRate.eth); @@ -42,7 +43,7 @@ class WalletDetailBloc extends AuBloc { break; case CryptoType.XTZ: final balance = await _tezosService.getBalance(event.address); - newState.balance = '${XtzAmountFormatter(balance).format()} XTZ'; + newState.balance = '${xtzFormatter.format(balance)} XTZ'; final usdBalance = balance / pow(10, 6) / double.parse(exchangeRate.xtz); final balanceInUSD = '${FiatFormatter(usdBalance).format()} USD'; diff --git a/lib/screen/settings/crypto/wallet_detail/wallet_detail_page.dart b/lib/screen/settings/crypto/wallet_detail/wallet_detail_page.dart index 49201b248..0bca1b31d 100644 --- a/lib/screen/settings/crypto/wallet_detail/wallet_detail_page.dart +++ b/lib/screen/settings/crypto/wallet_detail/wallet_detail_page.dart @@ -66,6 +66,7 @@ class _WalletDetailPageState extends State with RouteAware { bool _isRename = false; final TextEditingController _renameController = TextEditingController(); final FocusNode _renameFocusNode = FocusNode(); + final usdcFormatter = USDCAmountFormatter(); @override void initState() { @@ -275,7 +276,7 @@ class _WalletDetailPageState extends State with RouteAware { state.usdcBalances[address]; final balance = usdcBalance == null ? '-- USDC' - : '''${USDCAmountFormatter(usdcBalance).format()} USDC'''; + : '''${usdcFormatter.format(usdcBalance)} USDC'''; return Padding( padding: padding, child: _usdcBalance(balance), @@ -436,7 +437,7 @@ class _WalletDetailPageState extends State with RouteAware { final usdcBalance = state.usdcBalances[address]; final balance = usdcBalance == null ? '-- USDC' - : '${USDCAmountFormatter(usdcBalance).format()} USDC'; + : '${usdcFormatter.format(usdcBalance)} USDC'; return SizedBox( child: Column( mainAxisAlignment: MainAxisAlignment.center, diff --git a/lib/screen/tezos_beacon/tb_send_transaction_page.dart b/lib/screen/tezos_beacon/tb_send_transaction_page.dart index 89d4794d1..2b0592ace 100644 --- a/lib/screen/tezos_beacon/tb_send_transaction_page.dart +++ b/lib/screen/tezos_beacon/tb_send_transaction_page.dart @@ -5,6 +5,8 @@ // that can be found in the LICENSE file. // +import 'dart:async'; + import 'package:autonomy_flutter/common/injector.dart'; import 'package:autonomy_flutter/database/cloud_database.dart'; import 'package:autonomy_flutter/model/connection_request_args.dart'; @@ -18,6 +20,7 @@ import 'package:autonomy_flutter/service/tezos_service.dart'; import 'package:autonomy_flutter/service/wc2_service.dart'; import 'package:autonomy_flutter/util/constants.dart'; import 'package:autonomy_flutter/util/error_handler.dart'; +import 'package:autonomy_flutter/util/eth_amount_formatter.dart'; import 'package:autonomy_flutter/util/fee_util.dart'; import 'package:autonomy_flutter/util/log.dart'; import 'package:autonomy_flutter/util/string_ext.dart'; @@ -43,8 +46,7 @@ class TBSendTransactionPage extends StatefulWidget { final BeaconRequest request; - const TBSendTransactionPage({Key? key, required this.request}) - : super(key: key); + const TBSendTransactionPage({required this.request, super.key}); @override State createState() => _TBSendTransactionPageState(); @@ -62,20 +64,23 @@ class _TBSendTransactionPageState extends State { final metricClient = injector.get(); late CurrencyExchangeRate exchangeRate; late FeeOption _selectedPriority; + final xtzFormatter = XtzAmountFormatter(); + final ethFormatter = EthAmountFormatter(); @override void dispose() { super.dispose(); Future.delayed(const Duration(seconds: 2), () { - injector().handleNextRequest(isRemoved: true); + unawaited( + injector().handleNextRequest(isRemoved: true)); }); } - void _send() async { + Future _send() async { setState(() { _isSending = true; }); - metricClient.addEvent(MixpanelEvent.confirmTransaction); + unawaited(metricClient.addEvent(MixpanelEvent.confirmTransaction)); final didAuthenticate = await LocalAuthenticationService.checkLocalAuth(); if (!didAuthenticate) { @@ -94,39 +99,43 @@ class _TBSendTransactionPageState extends State { widget.request.operations!, baseOperationCustomFee: feeOption.tezosBaseOperationCustomFee); if (wc2Topic != null) { - _wc2Service.respondOnApprove( + unawaited(_wc2Service.respondOnApprove( wc2Topic, - txHash ?? "", - ); + txHash ?? '', + )); } else { - injector() - .operationResponse(widget.request.id, txHash); + unawaited(injector() + .operationResponse(widget.request.id, txHash)); } final address = widget.request.sourceAddress; if (address != null) { - injector() + unawaited(injector() .checkPendingTezosTokens(address) .then((tokens) { if (tokens.isNotEmpty) { NftCollectionBloc.eventController .add(UpdateTokensEvent(tokens: tokens)); } - }); + })); + } + if (!mounted) { + return; } - if (!mounted) return; Navigator.of(context).pop(txHash); } on TezartNodeError catch (err) { log.info(err); - if (!mounted) return; - UIHelper.showInfoDialog( + if (!mounted) { + return; + } + unawaited(UIHelper.showInfoDialog( context, - "operation_failed".tr(), + 'operation_failed'.tr(), getTezosErrorMessage(err), isDismissible: true, - ); + )); } catch (err) { - showErrorDialogFromException(err); + unawaited(showErrorDialogFromException(err)); log.warning(err); } @@ -146,7 +155,7 @@ class _TBSendTransactionPageState extends State { (previousValue, element) => (previousValue ?? 0) + (element.amount ?? 0)) ?? 0; - fetchPersona(); + unawaited(fetchPersona()); feeOption = DEFAULT_FEE_OPTION; _selectedPriority = feeOption; } @@ -168,17 +177,20 @@ class _TBSendTransactionPageState extends State { if (wc2Topic != null) { await _wc2Service.respondOnReject( wc2Topic, - reason: "Address ${widget.request.sourceAddress} not found", + reason: 'Address ${widget.request.sourceAddress} not found', ); } else { - injector().signResponse(widget.request.id, null); + unawaited(injector() + .signResponse(widget.request.id, null)); + } + if (!mounted) { + return; } - if (!mounted) return; Navigator.of(context).pop(); return; } - _estimateFee(currentWallet.wallet, currentWallet.index); + unawaited(_estimateFee(currentWallet.wallet, currentWallet.index)); setState(() { _currentWallet = currentWallet; @@ -214,7 +226,9 @@ class _TBSendTransactionPageState extends State { final message = getTezosErrorMessage(err); final tezosError = getTezosError(err); log.info(err); - if (!mounted) return; + if (!mounted) { + return; + } setState(() { _estimateMessage = 'estimation_failed'.tr(); }); @@ -223,19 +237,23 @@ class _TBSendTransactionPageState extends State { _estimateMessage = 'estimation_failed'.tr(); }); } - UIHelper.showInfoDialog( + unawaited(UIHelper.showInfoDialog( context, - "estimation_failed".tr(), + 'estimation_failed'.tr(), message, isDismissible: true, - ); + )); } on TezartHttpError catch (err) { log.info(err); - if (!mounted) return; + if (!mounted) { + return; + } _handleShowErrorEstimationFailed(wallet, index); } catch (err) { final handleDialog = await showErrorDialogFromException(err); - if (!mounted) return; + if (!mounted) { + return; + } if (!handleDialog) { _handleShowErrorEstimationFailed(wallet, index); } @@ -247,9 +265,9 @@ class _TBSendTransactionPageState extends State { setState(() { _estimateMessage = 'estimation_failed'.tr(); }); - UIHelper.showInfoDialog( + unawaited(UIHelper.showInfoDialog( context, - "estimation_failed".tr(), + 'estimation_failed'.tr(), 'cannot_connect_to_rpc'.tr(), isDismissible: true, closeButton: 'try_again'.tr(), @@ -257,7 +275,7 @@ class _TBSendTransactionPageState extends State { _estimateFee(wallet, index); Navigator.of(context).pop(); }, - ); + )); } @override @@ -267,35 +285,43 @@ class _TBSendTransactionPageState extends State { final wc2Topic = widget.request.wc2Topic; final padding = ResponsiveLayout.pageEdgeInsets.copyWith(top: 0, bottom: 0); final divider = addDivider(height: 20); - + final tezosAmount = widget.request.operations!.first.amount ?? 0; + final tezosAmountInUsd = exchangeRate.xtzToUsd(tezosAmount); + final amountText = '${xtzFormatter.format(tezosAmount)} XTZ ' + '($tezosAmountInUsd USD)'; + final totalAmountText = total == null + ? '"-" XTZ ("-" USD)' + : '${xtzFormatter.format(total)} XTZ ' + '(${exchangeRate.xtzToUsd(total)} USD)'; return WillPopScope( onWillPop: () async { - metricClient.addEvent(MixpanelEvent.backConfirmTransaction); + unawaited(metricClient.addEvent(MixpanelEvent.backConfirmTransaction)); if (wc2Topic != null) { - _wc2Service.respondOnReject( + unawaited(_wc2Service.respondOnReject( wc2Topic, - reason: "User reject", - ); + reason: 'User reject', + )); } else { - injector() - .operationResponse(widget.request.id, null); + unawaited(injector() + .operationResponse(widget.request.id, null)); } return true; }, child: Scaffold( appBar: getBackAppBar( context, - title: "confirmation".tr(), + title: 'confirmation'.tr(), onBack: () { - metricClient.addEvent(MixpanelEvent.backConfirmTransaction); + unawaited( + metricClient.addEvent(MixpanelEvent.backConfirmTransaction)); if (wc2Topic != null) { - _wc2Service.respondOnReject( + unawaited(_wc2Service.respondOnReject( wc2Topic, - reason: "User reject", - ); + reason: 'User reject', + )); } else { - injector() - .operationResponse(widget.request.id, null); + unawaited(injector() + .operationResponse(widget.request.id, null)); } Navigator.of(context).pop(); }, @@ -317,11 +343,11 @@ class _TBSendTransactionPageState extends State { Padding( padding: padding, child: Text( - "confirm_transaction".tr(), + 'confirm_transaction'.tr(), style: theme.textTheme.ppMori400Black16, ), ), - const SizedBox(height: 64.0), + const SizedBox(height: 64), divider, Padding( padding: padding, @@ -329,26 +355,24 @@ class _TBSendTransactionPageState extends State { children: [ _item( context: context, - title: "asset".tr(), - content: "tezos_xtz".tr()), + title: 'asset'.tr(), + content: 'tezos_xtz'.tr()), divider, _item( context: context, - title: "connection".tr(), - content: widget.request.appName ?? "", + title: 'connection'.tr(), + content: widget.request.appName ?? '', ), divider, _item( context: context, - title: "amount".tr(), - content: - "${XtzAmountFormatter(widget.request.operations!.first.amount ?? 0).format()} XTZ"), + title: 'amount'.tr(), + content: amountText), divider, _item( context: context, - title: "total_amount".tr().capitalize(), - content: - "${total != null ? XtzAmountFormatter(total).format() : "-"} XTZ"), + title: 'total_amount'.tr().capitalize(), + content: totalAmountText), divider, const SizedBox(height: 16), Container( @@ -361,20 +385,20 @@ class _TBSendTransactionPageState extends State { CrossAxisAlignment.start, children: [ Text( - "from".tr(), + 'from'.tr(), style: theme.textTheme.ppMori400Grey14, ), - const SizedBox(height: 8.0), + const SizedBox(height: 8), Text( - widget.request.sourceAddress ?? "", + widget.request.sourceAddress ?? '', style: theme.textTheme.ppMori400White14, ), addDivider(color: AppColor.white), Text( - "gas_fee2".tr(), + 'gas_fee2'.tr(), style: theme.textTheme.ppMori400Grey14, ), - const SizedBox(height: 8.0), + const SizedBox(height: 8), if (feeOptionValue != null) ...[ feeTable(context) ], @@ -398,15 +422,15 @@ class _TBSendTransactionPageState extends State { const Spacer(), GestureDetector( onTap: () { - _estimateFee( + unawaited(_estimateFee( _currentWallet!.wallet, _currentWallet!.index, - ); + )); }, child: Row( children: [ Text( - "try_again" + 'try_again' .tr() .toLowerCase(), style: theme.textTheme @@ -438,7 +462,7 @@ class _TBSendTransactionPageState extends State { children: [ Expanded( child: PrimaryButton( - text: "sendH".tr(), + text: 'sendH'.tr(), onTap: (_currentWallet != null && _fee != null && !_isSending) @@ -452,9 +476,10 @@ class _TBSendTransactionPageState extends State { ], ), ), - _isSending - ? const Center(child: CupertinoActivityIndicator()) - : const SizedBox(), + if (_isSending) + const Center(child: CupertinoActivityIndicator()) + else + const SizedBox(), ], ), ), @@ -474,10 +499,10 @@ class _TBSendTransactionPageState extends State { if (tapLink != null) { final uri = Uri.parse(tapLink); - onValueTap = () => launchUrl(uri, - mode: forceSafariVC == true + onValueTap = () => unawaited(launchUrl(uri, + mode: forceSafariVC ? LaunchMode.externalApplication - : LaunchMode.platformDefault); + : LaunchMode.platformDefault)); } return Row( children: [ @@ -504,12 +529,12 @@ class _TBSendTransactionPageState extends State { Widget gasFeeStatus(ThemeData theme) { if (feeOptionValue == null || balance == null) { - return Text("gas_fee_calculating".tr(), + return Text('gas_fee_calculating'.tr(), style: theme.textTheme.ppMori400White12); } bool isValid = balance! > feeOptionValue!.getFee(feeOption).toInt() + 10; if (!isValid) { - return Text("gas_fee_insufficient".tr(), + return Text('gas_fee_insufficient'.tr(), style: theme.textTheme.ppMori400Black12.copyWith( color: AppColor.red, )); @@ -525,9 +550,9 @@ class _TBSendTransactionPageState extends State { const Spacer(), GestureDetector( onTap: () { - UIHelper.showDialog( + unawaited(UIHelper.showDialog( context, - "edit_priority".tr().capitalize(), + 'edit_priority'.tr().capitalize(), _editPriorityView(context, onSave: () { setState(() { feeOption = _selectedPriority; @@ -536,9 +561,9 @@ class _TBSendTransactionPageState extends State { backgroundColor: AppColor.auGreyBackground, padding: const EdgeInsets.symmetric(vertical: 32), paddingTitle: ResponsiveLayout.pageHorizontalEdgeInsets, - ); + )); }, - child: Text("edit_priority".tr(), + child: Text('edit_priority'.tr(), style: theme.textTheme.ppMori400White14.copyWith( decoration: TextDecoration.underline, )), @@ -550,49 +575,48 @@ class _TBSendTransactionPageState extends State { Widget _editPriorityView(BuildContext context, {required Function() onSave}) { final theme = Theme.of(context); final padding = ResponsiveLayout.pageEdgeInsets.copyWith(top: 0, bottom: 0); - return StatefulBuilder(builder: (context, setState) { - return Column( - children: [ - Padding( - padding: padding, - child: getFeeRow(FeeOption.LOW, theme, setState), - ), - addDivider(color: AppColor.white), - Padding( - padding: padding, - child: getFeeRow(FeeOption.MEDIUM, theme, setState), - ), - addDivider(color: AppColor.white), - Padding( - padding: padding, - child: getFeeRow(FeeOption.HIGH, theme, setState), - ), - addDivider(color: AppColor.white), - const SizedBox(height: 12), - Padding( - padding: padding, - child: PrimaryButton( - text: "save_priority".tr(), - onTap: () { - onSave(); - Navigator.of(context).pop(); - }, - ), - ), - const SizedBox(height: 8), - Padding( - padding: padding, - child: OutlineButton( - text: "cancel".tr(), - onTap: () { - _selectedPriority = feeOption; - Navigator.of(context).pop(); - }, - ), - ) - ], - ); - }); + return StatefulBuilder( + builder: (context, setState) => Column( + children: [ + Padding( + padding: padding, + child: getFeeRow(FeeOption.LOW, theme, setState), + ), + addDivider(color: AppColor.white), + Padding( + padding: padding, + child: getFeeRow(FeeOption.MEDIUM, theme, setState), + ), + addDivider(color: AppColor.white), + Padding( + padding: padding, + child: getFeeRow(FeeOption.HIGH, theme, setState), + ), + addDivider(color: AppColor.white), + const SizedBox(height: 12), + Padding( + padding: padding, + child: PrimaryButton( + text: 'save_priority'.tr(), + onTap: () { + onSave(); + Navigator.of(context).pop(); + }, + ), + ), + const SizedBox(height: 8), + Padding( + padding: padding, + child: OutlineButton( + text: 'cancel'.tr(), + onTap: () { + _selectedPriority = feeOption; + Navigator.of(context).pop(); + }, + ), + ) + ], + )); } Widget getFeeRow(FeeOption feeOption, ThemeData theme, StateSetter setState) { @@ -624,8 +648,11 @@ class _TBSendTransactionPageState extends State { } String _gasFee(FeeOption feeOption) { - if (feeOptionValue == null) return ""; + if (feeOptionValue == null) { + return ''; + } final fee = feeOptionValue!.getFee(feeOption).toInt(); - return "${XtzAmountFormatter(fee).format()} XTZ (${exchangeRate.xtzToUsd(fee.toInt())} USD)"; + return '${xtzFormatter.format(fee)} XTZ ' + '(${exchangeRate.xtzToUsd(fee)} USD)'; } } diff --git a/lib/screen/wallet_connect/send/wc_send_transaction_page.dart b/lib/screen/wallet_connect/send/wc_send_transaction_page.dart index a20c49eeb..04000c9e4 100644 --- a/lib/screen/wallet_connect/send/wc_send_transaction_page.dart +++ b/lib/screen/wallet_connect/send/wc_send_transaction_page.dart @@ -5,6 +5,8 @@ // that can be found in the LICENSE file. // +import 'dart:async'; + import 'package:autonomy_flutter/common/injector.dart'; import 'package:autonomy_flutter/model/connection_request_args.dart'; import 'package:autonomy_flutter/model/wc_ethereum_transaction.dart'; @@ -34,7 +36,7 @@ class WCSendTransactionPage extends StatefulWidget { final WCSendTransactionPageArgs args; - const WCSendTransactionPage({Key? key, required this.args}) : super(key: key); + const WCSendTransactionPage({required this.args, super.key}); @override State createState() => _WCSendTransactionPageState(); @@ -43,6 +45,7 @@ class WCSendTransactionPage extends StatefulWidget { class _WCSendTransactionPageState extends State { final metricClient = injector.get(); late FeeOption _selectedPriority; + final ethFormatter = EthAmountFormatter(); @override void initState() { @@ -55,7 +58,7 @@ class _WCSendTransactionPageState extends State { context.read().add(WCSendTransactionEstimateEvent( to, amount, - widget.args.transaction.data ?? "", + widget.args.transaction.data ?? '', widget.args.uuid, widget.args.index)); _selectedPriority = context.read().state.feeOption; @@ -68,7 +71,7 @@ class _WCSendTransactionPageState extends State { return WillPopScope( onWillPop: () async { - metricClient.addEvent(MixpanelEvent.backConfirmTransaction); + unawaited(metricClient.addEvent(MixpanelEvent.backConfirmTransaction)); context.read().add( WCSendTransactionRejectEvent( @@ -84,9 +87,10 @@ class _WCSendTransactionPageState extends State { child: Scaffold( appBar: getBackAppBar( context, - title: "confirmation".tr(), + title: 'confirmation'.tr(), onBack: () { - metricClient.addEvent(MixpanelEvent.backConfirmTransaction); + unawaited( + metricClient.addEvent(MixpanelEvent.backConfirmTransaction)); context.read().add( WCSendTransactionRejectEvent( widget.args.peerMeta, @@ -107,19 +111,19 @@ class _WCSendTransactionPageState extends State { if (total != null && state.balance != null && total > state.balance!) { - UIHelper.showMessageAction( + unawaited(UIHelper.showMessageAction( context, 'transaction_failed'.tr(), 'dont_enough_money'.tr(), - ); + )); return; } if (state.isError) { - UIHelper.showMessageAction( + unawaited(UIHelper.showMessageAction( context, 'transaction_failed'.tr(), 'try_later'.tr(), - ); + )); } }, builder: (context, state) { @@ -128,6 +132,13 @@ class _WCSendTransactionPageState extends State { final total = state.fee != null ? state.fee! + amount.getInWei : null; final theme = Theme.of(context); + final ethAmountText = '${ethFormatter.format(amount.getInWei)} ETH ' + '(${state.exchangeRate.ethToUsd(amount.getInWei)} USD)'; + final ethTotalAmountText = total == null + ? '"-" ETH' + ' ("-" USD)' + : '${ethFormatter.format(total)} ETH' + ' (${state.exchangeRate.ethToUsd(total)} USD)'; return Stack( children: [ Container( @@ -145,11 +156,11 @@ class _WCSendTransactionPageState extends State { Padding( padding: padding, child: Text( - "confirm_transaction".tr(), + 'confirm_transaction'.tr(), style: theme.textTheme.ppMori400Black16, ), ), - const SizedBox(height: 64.0), + const SizedBox(height: 64), divider, Padding( padding: padding, @@ -158,26 +169,24 @@ class _WCSendTransactionPageState extends State { children: [ _item( context: context, - title: "asset".tr(), - content: "ethereum_eth".tr(), + title: 'asset'.tr(), + content: 'ethereum_eth'.tr(), ), divider, _item( context: context, - title: "connection".tr(), + title: 'connection'.tr(), content: widget.args.peerMeta.name), divider, _item( context: context, - title: "amount".tr(), - content: - "${EthAmountFormatter(amount.getInWei).format()} ETH"), + title: 'amount'.tr(), + content: ethAmountText), divider, _item( context: context, - title: "total_amount".tr(), - content: - "${total != null ? EthAmountFormatter(total).format() : "-"} ETH"), + title: 'total_amount'.tr(), + content: ethTotalAmountText), divider, const SizedBox(height: 16), Container( @@ -191,11 +200,11 @@ class _WCSendTransactionPageState extends State { CrossAxisAlignment.start, children: [ Text( - "from".tr(), + 'from'.tr(), style: theme.textTheme.ppMori400Grey14, ), - const SizedBox(height: 8.0), + const SizedBox(height: 8), Text( widget.args.transaction.from, style: theme @@ -203,11 +212,11 @@ class _WCSendTransactionPageState extends State { ), addDivider(color: AppColor.white), Text( - "gas_fee2".tr(), + 'gas_fee2'.tr(), style: theme.textTheme.ppMori400Grey14, ), - const SizedBox(height: 8.0), + const SizedBox(height: 8), if (state.feeOptionValue != null) ...[ feeTable(state, context) ], @@ -228,14 +237,14 @@ class _WCSendTransactionPageState extends State { children: [ Expanded( child: PrimaryButton( - text: "send".tr(), + text: 'send'.tr(), enabled: widget.args.transaction.to != null, onTap: (state.fee != null && !state.isSending && widget.args.transaction.to != null) ? () async { - metricClient.addEvent( - MixpanelEvent.confirmTransaction); + unawaited(metricClient.addEvent( + MixpanelEvent.confirmTransaction)); final to = EthereumAddress.fromHex( widget.args.transaction.to!); @@ -248,14 +257,13 @@ class _WCSendTransactionPageState extends State { widget.args.id, to, amount.getInWei, - state.fee!, + state.fee, widget.args.transaction.data, widget.args.uuid, widget.args.index, isWalletConnect2: widget .args.isWalletConnect2, topic: widget.args.topic, - // Used for wallet Connect 2.0 only isIRL: widget.args.isIRL, ), ); @@ -269,9 +277,10 @@ class _WCSendTransactionPageState extends State { ], ), ), - state.isSending - ? const Center(child: CupertinoActivityIndicator()) - : const SizedBox(), + if (state.isSending) + const Center(child: CupertinoActivityIndicator()) + else + const SizedBox(), ], ); }, @@ -293,10 +302,10 @@ class _WCSendTransactionPageState extends State { if (tapLink != null) { final uri = Uri.parse(tapLink); - onValueTap = () => launchUrl(uri, - mode: forceSafariVC == true + onValueTap = () => unawaited(launchUrl(uri, + mode: forceSafariVC ? LaunchMode.externalApplication - : LaunchMode.platformDefault); + : LaunchMode.platformDefault)); } return Row( children: [ @@ -325,17 +334,19 @@ class _WCSendTransactionPageState extends State { Widget gasFeeStatus(WCSendTransactionState state, ThemeData theme) { if (state.feeOptionValue == null) { - return Text("gas_fee_calculating".tr(), + return Text('gas_fee_calculating'.tr(), style: theme.textTheme.ppMori400Black12); } if (state.feeOptionValue != null) { - if (state.balance == null) return const SizedBox(); + if (state.balance == null) { + return const SizedBox(); + } bool isValid = state.balance! > - ((BigInt.parse(widget.args.transaction.value ?? "0")) + + ((BigInt.parse(widget.args.transaction.value ?? '0')) + (state.fee ?? BigInt.zero) + BigInt.from(10)); if (!isValid) { - return Text("gas_fee_insufficient".tr(), + return Text('gas_fee_insufficient'.tr(), style: theme.textTheme.ppMori400Black12.copyWith( color: AppColor.red, )); @@ -352,9 +363,9 @@ class _WCSendTransactionPageState extends State { const Spacer(), GestureDetector( onTap: () { - UIHelper.showDialog( + unawaited(UIHelper.showDialog( context, - "edit_priority".tr().capitalize(), + 'edit_priority'.tr().capitalize(), _editPriorityView(context, state, onSave: () { context .read() @@ -363,9 +374,9 @@ class _WCSendTransactionPageState extends State { backgroundColor: AppColor.auGreyBackground, padding: const EdgeInsets.symmetric(vertical: 32), paddingTitle: ResponsiveLayout.pageHorizontalEdgeInsets, - ); + )); }, - child: Text("edit_priority".tr(), + child: Text('edit_priority'.tr(), style: theme.textTheme.ppMori400White14.copyWith( decoration: TextDecoration.underline, )), @@ -378,49 +389,48 @@ class _WCSendTransactionPageState extends State { {required Function() onSave}) { final theme = Theme.of(context); final padding = ResponsiveLayout.pageEdgeInsets.copyWith(top: 0, bottom: 0); - return StatefulBuilder(builder: (context, setState) { - return Column( - children: [ - Padding( - padding: padding, - child: getFeeRow(FeeOption.LOW, state, theme, setState), - ), - addDivider(color: AppColor.white), - Padding( - padding: padding, - child: getFeeRow(FeeOption.MEDIUM, state, theme, setState), - ), - addDivider(color: AppColor.white), - Padding( - padding: padding, - child: getFeeRow(FeeOption.HIGH, state, theme, setState), - ), - addDivider(color: AppColor.white), - const SizedBox(height: 12), - Padding( - padding: padding, - child: PrimaryButton( - text: "save_priority".tr(), - onTap: () { - onSave(); - Navigator.of(context).pop(); - }, - ), - ), - const SizedBox(height: 8), - Padding( - padding: padding, - child: OutlineButton( - text: "cancel".tr(), - onTap: () { - _selectedPriority = state.feeOption; - Navigator.of(context).pop(); - }, - ), - ) - ], - ); - }); + return StatefulBuilder( + builder: (context, setState) => Column( + children: [ + Padding( + padding: padding, + child: getFeeRow(FeeOption.LOW, state, theme, setState), + ), + addDivider(color: AppColor.white), + Padding( + padding: padding, + child: getFeeRow(FeeOption.MEDIUM, state, theme, setState), + ), + addDivider(color: AppColor.white), + Padding( + padding: padding, + child: getFeeRow(FeeOption.HIGH, state, theme, setState), + ), + addDivider(color: AppColor.white), + const SizedBox(height: 12), + Padding( + padding: padding, + child: PrimaryButton( + text: 'save_priority'.tr(), + onTap: () { + onSave(); + Navigator.of(context).pop(); + }, + ), + ), + const SizedBox(height: 8), + Padding( + padding: padding, + child: OutlineButton( + text: 'cancel'.tr(), + onTap: () { + _selectedPriority = state.feeOption; + Navigator.of(context).pop(); + }, + ), + ) + ], + )); } Widget getFeeRow(FeeOption feeOption, WCSendTransactionState state, @@ -457,9 +467,12 @@ class _WCSendTransactionPageState extends State { } String _gasFee(WCSendTransactionState state, {FeeOption? feeOption}) { - if (state.feeOptionValue == null) return ""; + if (state.feeOptionValue == null) { + return ''; + } final fee = state.feeOptionValue!.getFee(feeOption ?? state.feeOption); - return "${EthAmountFormatter(fee).format()} ETH (${state.exchangeRate.ethToUsd(fee)} USD)"; + return '${ethFormatter.format(fee)} ETH ' + '(${state.exchangeRate.ethToUsd(fee)} USD)'; } } diff --git a/lib/screen/wallet_connect/wc_connect_page.dart b/lib/screen/wallet_connect/wc_connect_page.dart index d041cfa7f..cfac69639 100644 --- a/lib/screen/wallet_connect/wc_connect_page.dart +++ b/lib/screen/wallet_connect/wc_connect_page.dart @@ -5,6 +5,8 @@ // that can be found in the LICENSE file. // +import 'dart:async'; + import 'package:after_layout/after_layout.dart'; import 'package:autonomy_flutter/common/injector.dart'; import 'package:autonomy_flutter/database/cloud_database.dart'; @@ -13,6 +15,7 @@ import 'package:autonomy_flutter/model/connection_request_args.dart'; import 'package:autonomy_flutter/screen/app_router.dart'; import 'package:autonomy_flutter/screen/bloc/accounts/accounts_bloc.dart'; import 'package:autonomy_flutter/screen/connection/persona_connections_page.dart'; +import 'package:autonomy_flutter/service/account_service.dart'; import 'package:autonomy_flutter/service/configuration_service.dart'; import 'package:autonomy_flutter/service/ethereum_service.dart'; import 'package:autonomy_flutter/service/metric_client_service.dart'; @@ -38,8 +41,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_svg/flutter_svg.dart'; -import '../../service/account_service.dart'; - /* Because WalletConnect & TezosBeacon are using same logic: - select persona @@ -52,9 +53,9 @@ class WCConnectPage extends StatefulWidget { final ConnectionRequest connectionRequest; const WCConnectPage({ - Key? key, required this.connectionRequest, - }) : super(key: key); + super.key, + }); @override State createState() => _WCConnectPageState(); @@ -71,6 +72,8 @@ class _WCConnectPageState extends State late ConnectionRequest connectionRequest; String? ethSelectedAddress; String? tezSelectedAddress; + final tezosBeaconService = injector(); + final configurationService = injector(); bool get _confirmEnable => (categorizedAccounts != null && @@ -118,7 +121,7 @@ class _WCConnectPageState extends State routeObserver.unsubscribe(this); injector().setIsWCConnectInShow(false); Future.delayed(const Duration(seconds: 2), () { - injector().handleNextRequest(isRemoved: true); + unawaited(tezosBeaconService.handleNextRequest(isRemoved: true)); }); } @@ -131,10 +134,10 @@ class _WCConnectPageState extends State try { await injector().rejectSession( connectionRequest.id, - reason: "User reject", + reason: 'User reject', ); } catch (e) { - log.info("[WCConnectPage] Reject AutonomyConnect Proposal $e"); + log.info('[WCConnectPage] Reject AutonomyConnect Proposal $e'); } return; } @@ -143,22 +146,24 @@ class _WCConnectPageState extends State try { await injector().rejectSession( connectionRequest.id, - reason: "User reject", + reason: 'User reject', ); } catch (e) { - log.info("[WCConnectPage] Reject WalletConnect2 Proposal $e"); + log.info('[WCConnectPage] Reject WalletConnect2 Proposal $e'); } return; } if (connectionRequest.isBeaconConnect) { - injector() - .permissionResponse(null, null, connectionRequest.id, null, null); + unawaited(injector() + .permissionResponse(null, null, connectionRequest.id, null, null)); } } Future _approve({bool onBoarding = false}) async { - if (selectedPersona == null && !connectionRequest.isAutonomyConnect) return; + if (selectedPersona == null && !connectionRequest.isAutonomyConnect) { + return; + } UIHelper.showLoadingScreen(context, text: 'connecting_wallet'.tr()); late String payloadAddress; @@ -172,10 +177,10 @@ class _WCConnectPageState extends State .addressDao .findByWalletID(account.uuid); final accountNumber = - walletAddresses.map((e) => e.address).join("||"); + walletAddresses.map((e) => e.address).join('||'); await injector().approveSession( connectionRequest as Wc2Proposal, - account: accountDid.substring("did:key:".length), + account: accountDid.substring('did:key:'.length), connectionKey: account.uuid, accountNumber: accountNumber, isAuConnect: true, @@ -183,14 +188,14 @@ class _WCConnectPageState extends State payloadType = CryptoType.ETH; payloadAddress = await account.getETHEip55Address(index: selectedPersona!.index); - metricClient.addEvent( + unawaited(metricClient.addEvent( MixpanelEvent.connectExternal, data: { - "method": "autonomy_connect", - "name": connectionRequest.name, - "url": connectionRequest.url, + 'method': 'autonomy_connect', + 'name': connectionRequest.name, + 'url': connectionRequest.url, }, - ); + )); } else { final address = await injector() .getETHAddress(selectedPersona!.wallet, selectedPersona!.index); @@ -200,14 +205,14 @@ class _WCConnectPageState extends State connectionKey: address, accountNumber: address, ); - metricClient.addEvent( + unawaited(metricClient.addEvent( MixpanelEvent.connectExternal, data: { - "method": "wallet_connect", - "name": connectionRequest.name, - "url": connectionRequest.url, + 'method': 'wallet_connect', + 'name': connectionRequest.name, + 'url': connectionRequest.url, }, - ); + )); payloadType = CryptoType.ETH; payloadAddress = address; } @@ -227,15 +232,15 @@ class _WCConnectPageState extends State ); payloadAddress = address; payloadType = CryptoType.XTZ; - metricClient.addEvent( + unawaited(metricClient.addEvent( MixpanelEvent.connectExternal, data: { - "method": "tezos_beacon", - "name": (connectionRequest as BeaconRequest).appName ?? "unknown", - "url": - (connectionRequest as BeaconRequest).sourceAddress ?? "unknown", + 'method': 'tezos_beacon', + 'name': (connectionRequest as BeaconRequest).appName ?? 'unknown', + 'url': + (connectionRequest as BeaconRequest).sourceAddress ?? 'unknown', }, - ); + )); break; default: } @@ -243,7 +248,9 @@ class _WCConnectPageState extends State metricClient.incrementPropertyLabel( MixpanelProp.connectedToMarket(connectionRequest.name ?? 'unknown'), 1); - if (!mounted) return; + if (!mounted) { + return; + } UIHelper.hideInfoDialog(context); if (memoryValues.scopedPersona != null) { @@ -259,22 +266,24 @@ class _WCConnectPageState extends State isBackHome: onBoarding, ); - Navigator.of(context).pushReplacementNamed( + unawaited(Navigator.of(context).pushReplacementNamed( AppRouter.personaConnectionsPage, arguments: payload, - ); + )); } Future _approveThenNotify({bool onBoarding = false}) async { await _approve(onBoarding: onBoarding); - metricClient.addEvent(MixpanelEvent.connectMarketSuccess); - if (!mounted) return; + unawaited(metricClient.addEvent(MixpanelEvent.connectMarketSuccess)); + if (!mounted) { + return; + } showInfoNotification( - const Key("connected"), - "connected_to".tr(), + const Key('connected'), + 'connected_to'.tr(), frontWidget: SvgPicture.asset( - "assets/images/checkbox_icon.svg", + 'assets/images/checkbox_icon.svg', width: 24, ), addOnTextSpan: [ @@ -292,8 +301,8 @@ class _WCConnectPageState extends State final padding = ResponsiveLayout.pageEdgeInsets.copyWith(top: 0, bottom: 0); return WillPopScope( onWillPop: () async { - metricClient.addEvent(MixpanelEvent.backConnectMarket); - _reject(); + unawaited(metricClient.addEvent(MixpanelEvent.backConnectMarket)); + unawaited(_reject()); return true; }, child: Scaffold( @@ -301,9 +310,11 @@ class _WCConnectPageState extends State context, title: 'connect'.tr(), onBack: () async { - metricClient.addEvent(MixpanelEvent.backConnectMarket); + unawaited(metricClient.addEvent(MixpanelEvent.backConnectMarket)); await _reject(); - if (!mounted) return; + if (!mounted) { + return; + } Navigator.pop(context); }, ), @@ -330,7 +341,7 @@ class _WCConnectPageState extends State crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - "you_about_to_grant".tr(), + 'you_about_to_grant'.tr(), style: theme.textTheme.ppMori400Black16, ), const SizedBox( @@ -352,7 +363,7 @@ class _WCConnectPageState extends State const SizedBox( width: 6, ), - Text("•", + Text('•', style: theme .textTheme.ppMori400Black14), const SizedBox( @@ -372,26 +383,31 @@ class _WCConnectPageState extends State const SizedBox(height: 40), BlocConsumer( listener: (context, state) async { - var stateCategorizedAccounts = state.accounts; - - if (connectionRequest.isAutonomyConnect) { - final persona = await injector() - .getOrCreateDefaultPersona(); - selectedPersona = WalletIndex(persona.wallet(), 0); - } - if (stateCategorizedAccounts == null || - stateCategorizedAccounts.isEmpty) { - setState(() { - createPersona = true; - }); - return; - } - categorizedAccounts = stateCategorizedAccounts; - await _autoSelectDefault(categorizedAccounts); - setState(() {}); - }, builder: (context, state) { - return _selectAccount(context); - }), + var stateCategorizedAccounts = state.accounts; + + if (connectionRequest.isAutonomyConnect) { + final persona = await injector() + .getOrCreateDefaultPersona(); + selectedPersona = + WalletIndex(persona.wallet(), 0); + } + if (!mounted) { + return; + } + if (stateCategorizedAccounts == null || + stateCategorizedAccounts.isEmpty) { + setState(() { + createPersona = true; + }); + return; + } + categorizedAccounts = stateCategorizedAccounts; + await _autoSelectDefault(categorizedAccounts); + if (mounted) { + setState(() {}); + } + }, + builder: (context, state) => _selectAccount(context)), ], ), ), @@ -405,10 +421,16 @@ class _WCConnectPageState extends State } Future _autoSelectDefault(List? categorizedAccounts) async { - if (categorizedAccounts == null) return; - if (categorizedAccounts.length != 1) return; + if (categorizedAccounts == null) { + return; + } + if (categorizedAccounts.length != 1) { + return; + } final persona = categorizedAccounts.first.persona; - if (persona == null) return; + if (persona == null) { + return; + } final ethAccounts = categorizedAccounts.where((element) => element.isEth); final xtzAccounts = categorizedAccounts.where((element) => element.isTez); @@ -448,7 +470,9 @@ class _WCConnectPageState extends State Widget _selectAccount(BuildContext context) { final stateCategorizedAccounts = categorizedAccounts; - if (stateCategorizedAccounts == null) return const SizedBox(); + if (stateCategorizedAccounts == null) { + return const SizedBox(); + } if (stateCategorizedAccounts.isEmpty) { return const SizedBox(); // Expanded(child: _createAccountAndConnect()); @@ -471,8 +495,9 @@ class _WCConnectPageState extends State padding: padding, child: PrimaryButton( enabled: _confirmEnable, - text: "h_confirm".tr(), - onTap: () => withDebounce(() => _approveThenNotify()), + text: 'h_confirm'.tr(), + onTap: () => + withDebounce(() => unawaited(_approveThenNotify())), ), ), ) @@ -480,7 +505,7 @@ class _WCConnectPageState extends State ) ], ); - } else if (createPersona == true) { + } else if (createPersona) { return _createAccountAndConnect(context); } else { return Row( @@ -490,11 +515,12 @@ class _WCConnectPageState extends State padding: padding, child: PrimaryButton( enabled: _confirmEnable, - text: "h_confirm".tr(), + text: 'h_confirm'.tr(), onTap: selectedPersona != null ? () { - metricClient.addEvent(MixpanelEvent.connectMarket); - withDebounce(() => _approveThenNotify()); + unawaited( + metricClient.addEvent(MixpanelEvent.connectMarket)); + withDebounce(() => unawaited(_approveThenNotify())); } : null, ), @@ -513,22 +539,22 @@ class _WCConnectPageState extends State if (peerMeta.icons.isNotEmpty) ...[ CachedNetworkImage( imageUrl: peerMeta.icons.first, - width: 64.0, - height: 64.0, + width: 64, + height: 64, errorWidget: (context, url, error) => SizedBox( width: 64, height: 64, child: - Image.asset("assets/images/walletconnect-alternative.png")), + Image.asset('assets/images/walletconnect-alternative.png')), ), ] else ...[ SizedBox( width: 64, height: 64, child: - Image.asset("assets/images/walletconnect-alternative.png")), + Image.asset('assets/images/walletconnect-alternative.png')), ], - const SizedBox(width: 16.0), + const SizedBox(width: 16), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, @@ -546,28 +572,29 @@ class _WCConnectPageState extends State return Row( children: [ - request.icon != null - ? CachedNetworkImage( - imageUrl: request.icon!, - width: 64.0, - height: 64.0, - errorWidget: (context, url, error) => SvgPicture.asset( - "assets/images/tezos_social_icon.svg", - width: 64.0, - height: 64.0, - ), - ) - : SvgPicture.asset( - "assets/images/tezos_social_icon.svg", - width: 64.0, - height: 64.0, - ), - const SizedBox(width: 24.0), + if (request.icon != null) + CachedNetworkImage( + imageUrl: request.icon!, + width: 64, + height: 64, + errorWidget: (context, url, error) => SvgPicture.asset( + 'assets/images/tezos_social_icon.svg', + width: 64, + height: 64, + ), + ) + else + SvgPicture.asset( + 'assets/images/tezos_social_icon.svg', + width: 64, + height: 64, + ), + const SizedBox(width: 24), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text(request.appName ?? "", + Text(request.appName ?? '', style: theme.textTheme.ppMori700Black24), ], ), @@ -578,11 +605,11 @@ class _WCConnectPageState extends State Widget _selectPersonaWidget(BuildContext context, List accounts) { final theme = Theme.of(context); - String select = ""; + String select = ''; if (widget.connectionRequest.isBeaconConnect) { - select = "select_tezos".tr(args: ["1"]); + select = 'select_tezos'.tr(args: ['1']); } else if (widget.connectionRequest.isWalletConnect2) { - select = "select_ethereum".tr(args: ["1"]); + select = 'select_ethereum'.tr(args: ['1']); } return Column( @@ -595,7 +622,7 @@ class _WCConnectPageState extends State style: theme.textTheme.ppMori400Black16, ), ), - const SizedBox(height: 16.0), + const SizedBox(height: 16), ListAccountConnect( accounts: accounts, onSelectEth: (value) { @@ -618,41 +645,43 @@ class _WCConnectPageState extends State ); } - Widget _createAccountAndConnect(BuildContext context) { - return Padding( - padding: ResponsiveLayout.pageHorizontalEdgeInsets, - child: Column( - children: [ - Row( - children: [ - Expanded( - child: PrimaryButton( - text: "h_confirm".tr(), - onTap: () { - metricClient.addEvent(MixpanelEvent.connectMarket); - withDebounce(() => _createAccount(context)); - }, - ), - ) - ], - ), - ], - ), - ); - } + Widget _createAccountAndConnect(BuildContext context) => Padding( + padding: ResponsiveLayout.pageHorizontalEdgeInsets, + child: Column( + children: [ + Row( + children: [ + Expanded( + child: PrimaryButton( + text: 'h_confirm'.tr(), + onTap: () { + unawaited( + metricClient.addEvent(MixpanelEvent.connectMarket)); + withDebounce(() async => _createAccount(context)); + }, + ), + ) + ], + ), + ], + ), + ); Future _createAccount(BuildContext context) async { - UIHelper.showLoadingScreen(context, text: "connecting".tr()); + UIHelper.showLoadingScreen(context, text: 'connecting'.tr()); final persona = await injector().getOrCreateDefaultPersona(); await persona.insertNextAddress(connectionRequest.isBeaconConnect ? WalletType.Tezos : WalletType.Ethereum); - injector().setDoneOnboarding(true); - injector().mixPanelClient.initIfDefaultAccount(); + unawaited(configurationService.setDoneOnboarding(true)); + unawaited(metricClient.mixPanelClient.initIfDefaultAccount()); + if (!mounted) { + return; + } setState(() { selectedPersona = WalletIndex(persona.wallet(), 0); }); - _approveThenNotify(onBoarding: true); + unawaited(_approveThenNotify(onBoarding: true)); } } diff --git a/lib/util/eth_amount_formatter.dart b/lib/util/eth_amount_formatter.dart index 883fa92e5..3a7bb5cea 100644 --- a/lib/util/eth_amount_formatter.dart +++ b/lib/util/eth_amount_formatter.dart @@ -9,16 +9,17 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:web3dart/web3dart.dart'; class EthAmountFormatter { - EthAmountFormatter(this.amount, {this.digit = 6}); + EthAmountFormatter({this.digit = 6}); - final BigInt amount; final int digit; - String format({ - fromUnit = EtherUnit.wei, - toUnit = EtherUnit.ether, + + String format( + BigInt amount, { + EtherUnit fromUnit = EtherUnit.wei, + EtherUnit toUnit = EtherUnit.ether, }) { final formater = - NumberFormat("${'#' * 10}0.0${'#' * (digit - 1)}", "en_US"); + NumberFormat("${'#' * 10}0.0${'#' * (digit - 1)}", 'en_US'); return formater.format( EtherAmount.fromBigInt(fromUnit, amount).getValueInUnit(toUnit)); diff --git a/lib/util/usdc_amount_formatter.dart b/lib/util/usdc_amount_formatter.dart index c3731fae3..be50ae704 100644 --- a/lib/util/usdc_amount_formatter.dart +++ b/lib/util/usdc_amount_formatter.dart @@ -8,14 +8,25 @@ import 'package:easy_localization/easy_localization.dart'; class USDCAmountFormatter { - USDCAmountFormatter(this.amount, {this.digit = 6}); + USDCAmountFormatter({this.digit = 6}); - final BigInt amount; final int digit; - String format() { - final formater = - NumberFormat("${'#' * 10}0.0${'#' * (digit - 1)}", "en_US"); - return formater.format(amount.toDouble() / 1000000); + String format(BigInt amount) { + final formatter = + NumberFormat("${'#' * 10}0.0${'#' * (digit - 1)}", 'en_US'); + return formatter.format(amount.toDouble() / 1000000); } } + +class USDAmountFormatter { + late NumberFormat formatter; + + USDAmountFormatter({this.digit = 2}) { + formatter = NumberFormat("${'#' * 10}0.0${'#' * (digit - 1)}", 'en_US'); + } + + final int digit; + + String format(double amount) => formatter.format(amount / 100); +} diff --git a/lib/util/xtz_utils.dart b/lib/util/xtz_utils.dart index cf8a007f7..a7205fd60 100644 --- a/lib/util/xtz_utils.dart +++ b/lib/util/xtz_utils.dart @@ -18,14 +18,13 @@ import 'package:tezart/src/crypto/crypto.dart' as crypto; import 'package:web3dart/crypto.dart'; class XtzAmountFormatter { - final int amount; final int digit; - XtzAmountFormatter(this.amount, {this.digit = 6}); + XtzAmountFormatter({this.digit = 6}); - String format() { + String format(int amount) { final formater = - NumberFormat("${'#' * 10}0.0${'#' * (digit - 1)}", "en_US"); + NumberFormat("${'#' * 10}0.0${'#' * (digit - 1)}", 'en_US'); return formater.format(amount / 1000000); } } @@ -89,7 +88,7 @@ class TezosPack { const addressFixedLength = 22; if (bytes.length < 5) { throw const FormatException( - "Invalid Base58Check encoded string: must be at least size 5"); + 'Invalid Base58Check encoded string: must be at least size 5'); } List subBytes = bytes.sublist(0, bytes.length - 4); @@ -97,7 +96,7 @@ class TezosPack { List providedChecksum = bytes.sublist(bytes.length - 4, bytes.length); if (!const ListEquality() .equals(providedChecksum, checksum.sublist(0, 4))) { - throw const FormatException("Invalid checksum in Base58Check encoding."); + throw const FormatException('Invalid checksum in Base58Check encoding.'); } subBytes = hexToBytes('01') + bytes.sublist(3, bytes.length - 4); diff --git a/lib/view/account_view.dart b/lib/view/account_view.dart index 9f0a9a70c..c3494416f 100644 --- a/lib/view/account_view.dart +++ b/lib/view/account_view.dart @@ -5,6 +5,8 @@ // that can be found in the LICENSE file. // +import 'dart:async'; + import 'package:autonomy_flutter/common/injector.dart'; import 'package:autonomy_flutter/model/pair.dart'; import 'package:autonomy_flutter/screen/bloc/accounts/accounts_bloc.dart'; @@ -40,12 +42,12 @@ Widget accountWithConnectionItem( address: a.accountNumber, blockchain: a.blockchain!, account: a), - onTap: () => Navigator.of(context).pushNamed( + onTap: () => unawaited(Navigator.of(context).pushNamed( GlobalReceiveDetailPage.tag, arguments: GlobalReceivePayload( address: a.accountNumber, blockchain: a.blockchain!, - account: a)), + account: a))), ), addOnlyDivider(color: AppColor.auLightGrey), ], @@ -63,6 +65,7 @@ Widget accountItem(BuildContext context, Account account, return const SizedBox(); } final theme = Theme.of(context); + // ignore: discarded_futures final balance = getAddressBalance(account.key, account.cryptoType); final isViewAccount = account.persona == null || account.walletAddress == null; @@ -95,7 +98,7 @@ Widget accountItem(BuildContext context, Account account, ], if (isViewAccount) ...[ const SizedBox(width: 10), - Container( + DecoratedBox( decoration: BoxDecoration( color: Colors.transparent, border: Border.all(color: AppColor.auGrey), @@ -103,7 +106,7 @@ Widget accountItem(BuildContext context, Account account, child: Padding( padding: const EdgeInsets.symmetric(vertical: 4, horizontal: 10), - child: Text("view_only".tr(), + child: Text('view_only'.tr(), style: theme.textTheme.ppMori400Grey14), ), ) @@ -116,7 +119,7 @@ Widget accountItem(BuildContext context, Account account, FutureBuilder>( future: balance, builder: (context, snapshot) { - final balances = snapshot.data ?? Pair("--", "--"); + final balances = snapshot.data ?? Pair('--', '--'); final style = theme.textTheme.ppMori400Grey14; return Row( children: [ @@ -134,7 +137,7 @@ Widget accountItem(BuildContext context, Account account, child: Text( account.accountNumber, style: theme.textTheme.ppMori400Black14, - key: const Key("fullAccount_address"), + key: const Key('fullAccount_address'), ), ), ], @@ -155,15 +158,15 @@ Future> getAddressBalance( case CryptoType.ETH: final etherAmount = await injector().getBalance(address); final cryptoBalance = - "${EthAmountFormatter(etherAmount.getInWei).format()} ETH"; + '${EthAmountFormatter().format(etherAmount.getInWei)} ETH'; return Pair(cryptoBalance, nftBalance); case CryptoType.XTZ: final tezosAmount = await injector().getBalance(address); - final cryptoBalance = "${XtzAmountFormatter(tezosAmount).format()} XTZ"; + final cryptoBalance = '${XtzAmountFormatter().format(tezosAmount)} XTZ'; return Pair(cryptoBalance, nftBalance); case CryptoType.USDC: case CryptoType.UNKNOWN: - return Pair("", ""); + return Pair('', ''); } } @@ -198,16 +201,16 @@ Widget _blockchainAddressView( Widget _blockchainLogo(String? blockchain) { switch (blockchain) { - case "Bitmark": + case 'Bitmark': return SvgPicture.asset('assets/images/iconBitmark.svg'); - case "Ethereum": - case "walletConnect": - case "walletBrowserConnect": + case 'Ethereum': + case 'walletConnect': + case 'walletBrowserConnect': return SvgPicture.asset( 'assets/images/ether.svg', ); - case "Tezos": - case "walletBeacon": + case 'Tezos': + case 'walletBeacon': return SvgPicture.asset('assets/images/tez.svg'); default: return const SizedBox(); @@ -224,7 +227,7 @@ Widget linkedBox(BuildContext context, {double fontSize = 12.0}) { color: theme.colorScheme.surface, )), child: Text( - "view_only".tr(), + 'view_only'.tr(), style: theme.textTheme.ppMori400Grey12.copyWith(fontSize: fontSize), ), ); @@ -232,14 +235,14 @@ Widget linkedBox(BuildContext context, {double fontSize = 12.0}) { Widget viewOnlyLabel(BuildContext context) { final theme = Theme.of(context); - return Container( + return DecoratedBox( decoration: BoxDecoration( color: Colors.transparent, border: Border.all(color: AppColor.auGrey), borderRadius: BorderRadius.circular(20)), child: Padding( padding: const EdgeInsets.symmetric(vertical: 4, horizontal: 10), - child: Text("view_only".tr(), style: theme.textTheme.ppMori400Grey14), + child: Text('view_only'.tr(), style: theme.textTheme.ppMori400Grey14), ), ); }