diff --git a/docs/building.md b/docs/building.md index 41df95be0..569b4244f 100644 --- a/docs/building.md +++ b/docs/building.md @@ -77,16 +77,7 @@ pip3 install --upgrade meson==0.64.1 markdown==3.4.1 markupsafe==2.1.1 jinja2==3 ``` ### Flutter -Install Flutter 3.35.7 by [following their guide](https://docs.flutter.dev/get-started/install/linux/desktop?tab=download#install-the-flutter-sdk). You can also clone https://github.com/flutter/flutter, check out the `3.35.7` tag, and add its `flutter/bin` folder to your PATH as in -```sh -FLUTTER_DIR="$HOME/development/flutter" -git clone https://github.com/flutter/flutter.git "$FLUTTER_DIR" -cd "$FLUTTER_DIR" -git checkout 3.35.7 -echo 'export PATH="$PATH:'"$FLUTTER_DIR"'/bin"' >> "$HOME/.profile" -source "$HOME/.profile" -flutter precache -``` +Install Flutter 3.38.5 by [following their guide](https://docs.flutter.dev/install/manual). Run `flutter doctor` in a terminal to confirm its installation. @@ -227,7 +218,7 @@ rustup target add aarch64-apple-ios aarch64-apple-darwin Optionally download [Android Studio](https://developer.android.com/studio) as an IDE and activate its Dart and Flutter plugins. VS Code may work as an alternative, but this is not recommended. ### Flutter -Install [Flutter](https://docs.flutter.dev/get-started/install) 3.29.2 on your Mac host by following [these instructions](https://docs.flutter.dev/get-started/install/macos). Run `flutter doctor` in a terminal to confirm its installation. +Install 3.38.5 on your Mac host by [following their guide](https://docs.flutter.dev/install/manual). Run `flutter doctor` in a terminal to confirm its installation. ### Build plugins and configure #### Building plugins for iOS @@ -293,16 +284,7 @@ If the DLLs were built on the WSL filesystem instead of on Windows, copy the res Frostdart will be built by the Windows host later. ### Install Flutter on Windows host -Install Flutter 3.35.7 on your Windows host (not in WSL2) by [following their guide](https://docs.flutter.dev/get-started/install/windows/desktop?tab=download#install-the-flutter-sdk) or by cloning https://github.com/flutter/flutter, checking out the `3.35.7` tag, and adding its `flutter/bin` folder to your PATH as in -```bat -@echo off -set "FLUTTER_DIR=%USERPROFILE%\development\flutter" -git clone https://github.com/flutter/flutter.git "%FLUTTER_DIR%" -cd /d "%FLUTTER_DIR%" -git checkout 3.35.7 -setx PATH "%PATH%;%FLUTTER_DIR%\bin" -echo Flutter setup completed. Please restart your command prompt. -``` +Install Flutter 3.38.5 on your Windows host (not in WSL2) by [following their guide](https://docs.flutter.dev/install/manual). Run `flutter doctor` in PowerShell to confirm its installation. diff --git a/lib/pages/send_view/send_view.dart b/lib/pages/send_view/send_view.dart index 884cb5dc2..07595bee7 100644 --- a/lib/pages/send_view/send_view.dart +++ b/lib/pages/send_view/send_view.dart @@ -520,10 +520,6 @@ class _SendViewState extends ConsumerState { Map cachedFiroPublicFees = {}; Future calculateFees(Amount amount) async { - if (amount <= Amount.zero) { - return "0"; - } - if (isFiro) { switch (ref.read(publicPrivateBalanceStateProvider.state).state) { case BalanceType.public: diff --git a/lib/pages/settings_views/wallet_settings_view/wallet_network_settings_view/wallet_network_settings_view.dart b/lib/pages/settings_views/wallet_settings_view/wallet_network_settings_view/wallet_network_settings_view.dart index b34743125..3eccb7928 100644 --- a/lib/pages/settings_views/wallet_settings_view/wallet_network_settings_view/wallet_network_settings_view.dart +++ b/lib/pages/settings_views/wallet_settings_view/wallet_network_settings_view/wallet_network_settings_view.dart @@ -154,6 +154,13 @@ class _WalletNetworkSettingsViewState // pop rescanning dialog Navigator.of(context, rootNavigator: isDesktop).pop(); + final String message; + if (wallet is CryptonoteWallet || wallet is EpiccashWallet) { + message = "Rescan started"; + } else { + message = "Rescan completed"; + } + // show success await showDialog( context: context, @@ -164,7 +171,7 @@ class _WalletNetworkSettingsViewState builder: (child) => DesktopDialog(maxHeight: 150, maxWidth: 500, child: child), child: StackDialog( - title: "Rescan completed", + title: message, rightButton: TextButton( style: Theme.of(context) .extension()! @@ -380,11 +387,8 @@ class _WalletNetworkSettingsViewState ), title: Text("Network", style: STextStyles.navBarTitle(context)), actions: [ - if (ref.watch(pWalletCoin(widget.walletId)) is! Epiccash && - ref.watch(pWalletCoin(widget.walletId)) - is! Mimblewimblecoin || - ref.watch(pWalletCoin(widget.walletId)) - is! Mimblewimblecoin) + if (ref.watch(pWalletCoin(widget.walletId)) + is! Mimblewimblecoin) Padding( padding: const EdgeInsets.only( top: 10, @@ -984,7 +988,6 @@ class _WalletNetworkSettingsViewState ), ), if (isDesktop && - ref.watch(pWalletCoin(widget.walletId)) is! Epiccash && ref.watch(pWalletCoin(widget.walletId)) is! Mimblewimblecoin) RoundedWhiteContainer( borderColor: isDesktop diff --git a/lib/pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/edit_refresh_height_view.dart b/lib/pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/edit_refresh_height_view.dart index 70ca0a421..b83be7e09 100644 --- a/lib/pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/edit_refresh_height_view.dart +++ b/lib/pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/edit_refresh_height_view.dart @@ -11,7 +11,9 @@ import '../../../../utilities/constants.dart'; import '../../../../utilities/text_styles.dart'; import '../../../../utilities/util.dart'; import '../../../../wallets/isar/providers/wallet_info_provider.dart'; +import '../../../../wallets/wallet/impl/epiccash_wallet.dart'; import '../../../../wallets/wallet/intermediate/cryptonote_wallet.dart'; +import '../../../../wallets/wallet/supporting/epiccash_wallet_info_extension.dart'; import '../../../../widgets/background.dart'; import '../../../../widgets/conditional_parent.dart'; import '../../../../widgets/custom_buttons/app_bar_icon_button.dart'; @@ -48,13 +50,19 @@ class _EditRefreshHeightViewState extends ConsumerState { try { final newHeight = int.tryParse(_controller.text); if (newHeight != null && newHeight >= 0) { - await ref - .read(pWalletInfo(widget.walletId)) - .updateRestoreHeight( - newRestoreHeight: newHeight, - isar: ref.read(mainDBProvider).isar, - ); final wallet = ref.read(pWallets).getWallet(widget.walletId); + + if (wallet is EpiccashWallet) { + await wallet.updateRestoreHeight(newHeight); + } else { + await ref + .read(pWalletInfo(widget.walletId)) + .updateRestoreHeight( + newRestoreHeight: newHeight, + isar: ref.read(mainDBProvider).isar, + ); + } + if (wallet is CryptonoteWallet && wallet.wallet != null) { wallet.setRefreshFromBlockHeight(newHeight); } @@ -95,7 +103,13 @@ class _EditRefreshHeightViewState extends ConsumerState { super.initState(); _controller = TextEditingController(); final wallet = ref.read(pWallets).getWallet(widget.walletId); - if (wallet is CryptonoteWallet && wallet.wallet != null) { + if (wallet is EpiccashWallet) { + _controller.text = ref + .read(pWalletInfo(widget.walletId)) + .epicData! + .restoreHeight + .toString(); + } else if (wallet is CryptonoteWallet && wallet.wallet != null) { _controller.text = wallet.getRefreshFromBlockHeight().toString(); } else { _controller.text = ref diff --git a/lib/pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/wallet_settings_wallet_settings_view.dart b/lib/pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/wallet_settings_wallet_settings_view.dart index a5464cdb5..6a266b247 100644 --- a/lib/pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/wallet_settings_wallet_settings_view.dart +++ b/lib/pages/settings_views/wallet_settings_view/wallet_settings_wallet_settings/wallet_settings_wallet_settings_view.dart @@ -23,6 +23,7 @@ import '../../../../utilities/text_styles.dart'; import '../../../../wallets/isar/models/wallet_info.dart'; import '../../../../wallets/isar/providers/wallet_info_provider.dart'; import '../../../../wallets/wallet/impl/bitcoin_wallet.dart'; +import '../../../../wallets/wallet/impl/epiccash_wallet.dart'; import '../../../../wallets/wallet/intermediate/cryptonote_wallet.dart'; import '../../../../wallets/wallet/wallet_mixin_interfaces/multi_address_interface.dart'; import '../../../../wallets/wallet/wallet_mixin_interfaces/mweb_interface.dart'; @@ -591,8 +592,9 @@ class _WalletSettingsWalletSettingsViewState ), ), ), - if (wallet is CryptonoteWallet) const SizedBox(height: 8), - if (wallet is CryptonoteWallet) + if (wallet is CryptonoteWallet || wallet is EpiccashWallet) + const SizedBox(height: 8), + if (wallet is CryptonoteWallet || wallet is EpiccashWallet) RoundedWhiteContainer( padding: const EdgeInsets.all(0), child: RawMaterialButton( diff --git a/lib/pages/wallet_view/transaction_views/tx_v2/all_transactions_v2_view.dart b/lib/pages/wallet_view/transaction_views/tx_v2/all_transactions_v2_view.dart index 356d106f8..49d51f249 100644 --- a/lib/pages/wallet_view/transaction_views/tx_v2/all_transactions_v2_view.dart +++ b/lib/pages/wallet_view/transaction_views/tx_v2/all_transactions_v2_view.dart @@ -52,8 +52,11 @@ import '../transaction_search_filter_view.dart'; import 'transaction_v2_card.dart'; import 'transaction_v2_details_view.dart' as tvd; -typedef _GroupedTransactions = - ({String label, DateTime startDate, List transactions}); +typedef _GroupedTransactions = ({ + String label, + DateTime startDate, + List transactions, +}); class AllTransactionsV2View extends ConsumerStatefulWidget { const AllTransactionsV2View({ @@ -106,14 +109,13 @@ class _AllTransactionsV2ViewState extends ConsumerState { // debugPrint("FILTER: $filter"); final contacts = ref.read(addressBookServiceProvider).contacts; - final notes = - ref - .read(mainDBProvider) - .isar - .transactionNotes - .where() - .walletIdEqualTo(walletId) - .findAllSync(); + final notes = ref + .read(mainDBProvider) + .isar + .transactionNotes + .where() + .walletIdEqualTo(walletId) + .findAllSync(); return transactions.where((tx) { if (!filter.sent && !filter.received) { @@ -159,25 +161,23 @@ class _AllTransactionsV2ViewState extends ConsumerState { bool contains = false; // check if address book name contains - contains |= - contacts - .where( - (e) => - e.addresses - .map((e) => e.address) - .toSet() - .intersection(tx.associatedAddresses()) - .isNotEmpty && - e.name.toLowerCase().contains(keyword), - ) - .isNotEmpty; + contains |= contacts + .where( + (e) => + e.addresses + .map((e) => e.address) + .toSet() + .intersection(tx.associatedAddresses()) + .isNotEmpty && + e.name.toLowerCase().contains(keyword), + ) + .isNotEmpty; // check if address contains - contains |= - tx - .associatedAddresses() - .where((e) => e.toLowerCase().contains(keyword)) - .isNotEmpty; + contains |= tx + .associatedAddresses() + .where((e) => e.toLowerCase().contains(keyword)) + .isNotEmpty; TransactionNote? note; final matchingNotes = notes.where((e) => e.txid == tx.txid); @@ -214,14 +214,13 @@ class _AllTransactionsV2ViewState extends ConsumerState { } text = text.toLowerCase(); final contacts = ref.read(addressBookServiceProvider).contacts; - final notes = - ref - .read(mainDBProvider) - .isar - .transactionNotes - .where() - .walletIdEqualTo(walletId) - .findAllSync(); + final notes = ref + .read(mainDBProvider) + .isar + .transactionNotes + .where() + .walletIdEqualTo(walletId) + .findAllSync(); return transactions .where((tx) => _isKeywordMatch(tx, text, contacts, notes)) @@ -258,94 +257,90 @@ class _AllTransactionsV2ViewState extends ConsumerState { return MasterScaffold( background: Theme.of(context).extension()!.background, isDesktop: isDesktop, - appBar: - isDesktop - ? DesktopAppBar( - isCompactHeight: true, - background: Theme.of(context).extension()!.popupBG, - leading: Row( - children: [ - const SizedBox(width: 32), - AppBarIconButton( - size: 32, - color: - Theme.of( - context, - ).extension()!.textFieldDefaultBG, + appBar: isDesktop + ? DesktopAppBar( + isCompactHeight: true, + background: Theme.of(context).extension()!.popupBG, + leading: Row( + children: [ + const SizedBox(width: 32), + AppBarIconButton( + size: 32, + color: Theme.of( + context, + ).extension()!.textFieldDefaultBG, + shadows: const [], + icon: SvgPicture.asset( + Assets.svg.arrowLeft, + width: 18, + height: 18, + color: Theme.of( + context, + ).extension()!.topNavIconPrimary, + ), + onPressed: Navigator.of(context).pop, + ), + const SizedBox(width: 12), + Text("Transactions", style: STextStyles.desktopH3(context)), + ], + ), + ) + : AppBar( + backgroundColor: Theme.of( + context, + ).extension()!.background, + leading: AppBarBackButton( + onPressed: () async { + if (FocusScope.of(context).hasFocus) { + FocusScope.of(context).unfocus(); + await Future.delayed( + const Duration(milliseconds: 75), + ); + } + if (context.mounted) { + Navigator.of(context).pop(); + } + }, + ), + title: Text( + "Transactions", + style: STextStyles.navBarTitle(context), + ), + actions: [ + Padding( + padding: const EdgeInsets.only( + top: 10, + bottom: 10, + right: 20, + ), + child: AspectRatio( + aspectRatio: 1, + child: AppBarIconButton( + key: const Key("transactionSearchFilterViewButton"), + size: 36, shadows: const [], + color: Theme.of( + context, + ).extension()!.background, icon: SvgPicture.asset( - Assets.svg.arrowLeft, - width: 18, - height: 18, - color: - Theme.of( - context, - ).extension()!.topNavIconPrimary, - ), - onPressed: Navigator.of(context).pop, - ), - const SizedBox(width: 12), - Text("Transactions", style: STextStyles.desktopH3(context)), - ], - ), - ) - : AppBar( - backgroundColor: - Theme.of(context).extension()!.background, - leading: AppBarBackButton( - onPressed: () async { - if (FocusScope.of(context).hasFocus) { - FocusScope.of(context).unfocus(); - await Future.delayed( - const Duration(milliseconds: 75), - ); - } - if (context.mounted) { - Navigator.of(context).pop(); - } - }, - ), - title: Text( - "Transactions", - style: STextStyles.navBarTitle(context), - ), - actions: [ - Padding( - padding: const EdgeInsets.only( - top: 10, - bottom: 10, - right: 20, - ), - child: AspectRatio( - aspectRatio: 1, - child: AppBarIconButton( - key: const Key("transactionSearchFilterViewButton"), - size: 36, - shadows: const [], - color: - Theme.of( - context, - ).extension()!.background, - icon: SvgPicture.asset( - Assets.svg.filter, - color: - Theme.of( - context, - ).extension()!.accentColorDark, - width: 20, - height: 20, - ), - onPressed: () { - Navigator.of(context).pushNamed( - TransactionSearchFilterView.routeName, - arguments: ref.read(pWalletCoin(walletId)), - ); - }, + Assets.svg.filter, + color: Theme.of( + context, + ).extension()!.accentColorDark, + width: 20, + height: 20, ), + onPressed: () { + Navigator.of(context).pushNamed( + TransactionSearchFilterView.routeName, + arguments: ref.read(pWalletCoin(walletId)), + ); + }, ), ), - ], - ), + ), + ], + ), body: Padding( padding: EdgeInsets.only( left: isDesktop ? 20 : 12, @@ -378,57 +373,57 @@ class _AllTransactionsV2ViewState extends ConsumerState { _searchString = value; }); }, - style: - isDesktop - ? STextStyles.desktopTextExtraSmall( - context, - ).copyWith( - color: - Theme.of(context) - .extension()! - .textFieldActiveText, - height: 1.8, - ) - : STextStyles.field(context), - decoration: standardInputDecoration( - "Search...", - searchFieldFocusNode, - context, - desktopMed: isDesktop, - ).copyWith( - prefixIcon: Padding( - padding: EdgeInsets.symmetric( - horizontal: isDesktop ? 12 : 10, - vertical: isDesktop ? 18 : 16, - ), - child: SvgPicture.asset( - Assets.svg.search, - width: isDesktop ? 20 : 16, - height: isDesktop ? 20 : 16, - ), - ), - suffixIcon: - _searchController.text.isNotEmpty + style: isDesktop + ? STextStyles.desktopTextExtraSmall( + context, + ).copyWith( + color: Theme.of(context) + .extension()! + .textFieldActiveText, + height: 1.8, + ) + : STextStyles.field(context), + decoration: + standardInputDecoration( + "Search...", + searchFieldFocusNode, + context, + desktopMed: isDesktop, + ).copyWith( + prefixIcon: Padding( + padding: EdgeInsets.symmetric( + horizontal: isDesktop ? 12 : 10, + vertical: isDesktop ? 18 : 16, + ), + child: SvgPicture.asset( + Assets.svg.search, + width: isDesktop ? 20 : 16, + height: isDesktop ? 20 : 16, + ), + ), + suffixIcon: _searchController.text.isNotEmpty ? Padding( - padding: const EdgeInsets.only(right: 0), - child: UnconstrainedBox( - child: Row( - children: [ - TextFieldIconButton( - child: const XIcon(), - onTap: () async { - setState(() { - _searchController.text = ""; - _searchString = ""; - }); - }, - ), - ], + padding: const EdgeInsets.only( + right: 0, ), - ), - ) + child: UnconstrainedBox( + child: Row( + children: [ + TextFieldIconButton( + child: const XIcon(), + onTap: () async { + setState(() { + _searchController.text = ""; + _searchString = ""; + }); + }, + ), + ], + ), + ), + ) : null, - ), + ), ), ), ), @@ -441,10 +436,9 @@ class _AllTransactionsV2ViewState extends ConsumerState { label: "Filter", icon: SvgPicture.asset( Assets.svg.filter, - color: - Theme.of( - context, - ).extension()!.accentColorDark, + color: Theme.of( + context, + ).extension()!.accentColorDark, width: 20, height: 20, ), @@ -480,39 +474,38 @@ class _AllTransactionsV2ViewState extends ConsumerState { Expanded( child: Consumer( builder: (_, ref, __) { - final criteria = - ref.watch(transactionFilterProvider.state).state; + final criteria = ref + .watch(transactionFilterProvider.state) + .state; return FutureBuilder( - future: - ref - .watch(mainDBProvider) - .isar - .transactionV2s - .buildQuery( - whereClauses: [ - IndexWhereClause.equalTo( - indexName: 'walletId', - value: [widget.walletId], - ), - ], - filter: - widget.contractAddress == null - ? ref - .watch(pWallets) - .getWallet(widget.walletId) - .transactionFilterOperation - : ref - .read(pCurrentTokenWallet)! - .transactionFilterOperation, - sortBy: [ - const SortProperty( - property: "timestamp", - sort: Sort.desc, - ), - ], - ) - .findAll(), + future: ref + .watch(mainDBProvider) + .isar + .transactionV2s + .buildQuery( + whereClauses: [ + IndexWhereClause.equalTo( + indexName: 'walletId', + value: [widget.walletId], + ), + ], + filter: widget.contractAddress == null + ? ref + .watch(pWallets) + .getWallet(widget.walletId) + .transactionFilterOperation + : ref + .read(pCurrentTokenWallet)! + .transactionFilterOperation, + sortBy: [ + const SortProperty( + property: "timestamp", + sort: Sort.desc, + ), + ], + ) + .findAll(), builder: (_, AsyncSnapshot> snapshot) { if (snapshot.connectionState == ConnectionState.done && snapshot.hasData) { @@ -553,27 +546,26 @@ class _AllTransactionsV2ViewState extends ConsumerState { child: ListView.separated( shrinkWrap: true, primary: false, - separatorBuilder: - (context, _) => Container( + separatorBuilder: (context, _) => + Container( height: 1, - color: - Theme.of(context) - .extension()! - .background, + color: Theme.of(context) + .extension()! + .background, ), itemCount: month.transactions.length, - itemBuilder: - (context, index) => Padding( - padding: const EdgeInsets.all(4), - child: DesktopTransactionCardRow( - key: Key( - "transactionCard_key_${month.transactions[index].txid}", - ), - transaction: - month.transactions[index], - walletId: walletId, - ), + itemBuilder: (context, index) => Padding( + padding: const EdgeInsets.all(4), + child: DesktopTransactionCardRow( + key: Key( + "transactionCard_key_" + "${month.transactions[index].txid}", ), + transaction: + month.transactions[index], + walletId: walletId, + ), + ), ), ), if (!isDesktop) @@ -785,8 +777,9 @@ class TransactionFilterOptionBarItem extends StatelessWidget { child: Container( height: 32, decoration: BoxDecoration( - color: - Theme.of(context).extension()!.buttonBackSecondary, + color: Theme.of( + context, + ).extension()!.buttonBackSecondary, borderRadius: BorderRadius.circular(1000), ), child: Padding( @@ -802,8 +795,9 @@ class TransactionFilterOptionBarItem extends StatelessWidget { label, textAlign: TextAlign.center, style: STextStyles.labelExtraExtraSmall(context).copyWith( - color: - Theme.of(context).extension()!.textDark, + color: Theme.of( + context, + ).extension()!.textDark, ), ), ), @@ -849,23 +843,21 @@ class _DesktopTransactionCardRowState String whatIsIt(TransactionV2 tx, int height) => tx.statusLabel( currentChainHeight: height, minConfirms: minConfirms, - minCoinbaseConfirms: - ref - .read(pWallets) - .getWallet(widget.walletId) - .cryptoCurrency - .minCoinbaseConfirms, + minCoinbaseConfirms: ref + .read(pWallets) + .getWallet(widget.walletId) + .cryptoCurrency + .minCoinbaseConfirms, ); @override void initState() { walletId = widget.walletId; - minConfirms = - ref - .read(pWallets) - .getWallet(widget.walletId) - .cryptoCurrency - .minConfirms; + minConfirms = ref + .read(pWallets) + .getWallet(widget.walletId) + .cryptoCurrency + .minConfirms; _transaction = widget.transaction; if (_transaction.subType == TransactionSubType.ethToken) { @@ -897,10 +889,9 @@ class _DesktopTransactionCardRowState )) { price = ref.watch( priceAnd24hChangeNotifierProvider.select( - (value) => - isTokenTx - ? value.getTokenPrice(_transaction.contractAddress!)?.value - : value.getPrice(coin)?.value, + (value) => isTokenTx + ? value.getTokenPrice(_transaction.contractAddress!)?.value + : value.getPrice(coin)?.value, ), ); } @@ -987,16 +978,15 @@ class _DesktopTransactionCardRowState if (Util.isDesktop) { await showDialog( context: context, - builder: - (context) => DesktopDialog( - maxHeight: MediaQuery.of(context).size.height - 64, - maxWidth: 580, - child: tvd.TransactionV2DetailsView( - transaction: _transaction, - coin: coin, - walletId: walletId, - ), - ), + builder: (context) => DesktopDialog( + maxHeight: MediaQuery.of(context).size.height - 64, + maxWidth: 640, + child: tvd.TransactionV2DetailsView( + transaction: _transaction, + coin: coin, + walletId: walletId, + ), + ), ); } else { unawaited( @@ -1021,11 +1011,12 @@ class _DesktopTransactionCardRowState flex: 3, child: Text( whatIsIt(_transaction, currentHeight), - style: STextStyles.desktopTextExtraExtraSmall( - context, - ).copyWith( - color: Theme.of(context).extension()!.textDark, - ), + style: STextStyles.desktopTextExtraExtraSmall(context) + .copyWith( + color: Theme.of( + context, + ).extension()!.textDark, + ), ), ), if (kDebugMode) @@ -1047,11 +1038,12 @@ class _DesktopTransactionCardRowState flex: 6, child: Text( "$prefix${ref.watch(pAmountFormatter(coin)).format(amount, ethContract: ethContract)}", - style: STextStyles.desktopTextExtraExtraSmall( - context, - ).copyWith( - color: Theme.of(context).extension()!.textDark, - ), + style: STextStyles.desktopTextExtraExtraSmall(context) + .copyWith( + color: Theme.of( + context, + ).extension()!.textDark, + ), ), ), if (price != null) @@ -1066,8 +1058,9 @@ class _DesktopTransactionCardRowState Assets.svg.circleInfo, width: 20, height: 20, - color: - Theme.of(context).extension()!.textSubtitle2, + color: Theme.of( + context, + ).extension()!.textSubtitle2, ), ], ), diff --git a/lib/pages/wallet_view/transaction_views/tx_v2/transaction_v2_card.dart b/lib/pages/wallet_view/transaction_views/tx_v2/transaction_v2_card.dart index a8b30fd5a..5f5f952b5 100644 --- a/lib/pages/wallet_view/transaction_views/tx_v2/transaction_v2_card.dart +++ b/lib/pages/wallet_view/transaction_views/tx_v2/transaction_v2_card.dart @@ -49,22 +49,20 @@ class _TransactionCardStateV2 extends ConsumerState { String whatIsIt(CryptoCurrency coin, int currentHeight) => _transaction.isCancelled && coin is Ethereum - ? "Failed" - : _transaction.statusLabel( - currentChainHeight: currentHeight, - minConfirms: - ref - .read(pWallets) - .getWallet(walletId) - .cryptoCurrency - .minConfirms, - minCoinbaseConfirms: - ref - .read(pWallets) - .getWallet(walletId) - .cryptoCurrency - .minCoinbaseConfirms, - ); + ? "Failed" + : _transaction.statusLabel( + currentChainHeight: currentHeight, + minConfirms: ref + .read(pWallets) + .getWallet(walletId) + .cryptoCurrency + .minConfirms, + minCoinbaseConfirms: ref + .read(pWallets) + .getWallet(walletId) + .cryptoCurrency + .minCoinbaseConfirms, + ); @override void initState() { @@ -115,10 +113,9 @@ class _TransactionCardStateV2 extends ConsumerState { )) { price = ref.watch( priceAnd24hChangeNotifierProvider.select( - (value) => - isTokenTx - ? value.getTokenPrice(tokenContract!.address)?.value - : value.getPrice(coin)?.value, + (value) => isTokenTx + ? value.getTokenPrice(tokenContract!.address)?.value + : value.getPrice(coin)?.value, ), ); } @@ -196,16 +193,15 @@ class _TransactionCardStateV2 extends ConsumerState { if (Util.isDesktop) { await showDialog( context: context, - builder: - (context) => DesktopDialog( - maxHeight: MediaQuery.of(context).size.height - 64, - maxWidth: 580, - child: tvd.TransactionV2DetailsView( - transaction: _transaction, - coin: coin, - walletId: walletId, - ), - ), + builder: (context) => DesktopDialog( + maxHeight: MediaQuery.of(context).size.height - 64, + maxWidth: 640, + child: tvd.TransactionV2DetailsView( + transaction: _transaction, + coin: coin, + walletId: walletId, + ), + ), ); } else { unawaited( @@ -246,15 +242,14 @@ class _TransactionCardStateV2 extends ConsumerState { coin.minConfirms, coin.minCoinbaseConfirms, ), - builder: - (child) => Row( - children: [ - child, + builder: (child) => Row( + children: [ + child, - const SizedBox(width: 10), - const CoinTickerTag(ticker: "INSTANT"), - ], - ), + const SizedBox(width: 10), + const CoinTickerTag(ticker: "INSTANT"), + ], + ), child: Text( whatIsIt(coin, currentHeight), style: STextStyles.itemSubtitle12(context), diff --git a/lib/pages/wallet_view/transaction_views/tx_v2/transaction_v2_details_view.dart b/lib/pages/wallet_view/transaction_views/tx_v2/transaction_v2_details_view.dart index 748912e9d..2b310bbde 100644 --- a/lib/pages/wallet_view/transaction_views/tx_v2/transaction_v2_details_view.dart +++ b/lib/pages/wallet_view/transaction_views/tx_v2/transaction_v2_details_view.dart @@ -448,147 +448,6 @@ class _TransactionV2DetailsViewState } } - Future showExplorerWarning(String explorer) async { - final bool? shouldContinue = await showDialog( - context: context, - barrierDismissible: false, - builder: (_) { - if (!isDesktop) { - return StackDialog( - title: "Attention", - message: - "You are about to view this transaction in a block explorer. The explorer may log your IP address and link it to the transaction. Only proceed if you trust $explorer.", - icon: Row( - children: [ - Consumer( - builder: (_, ref, __) { - return Checkbox( - value: ref.watch( - prefsChangeNotifierProvider.select( - (value) => value.hideBlockExplorerWarning, - ), - ), - onChanged: (value) { - if (value is bool) { - ref - .read(prefsChangeNotifierProvider) - .hideBlockExplorerWarning = - value; - setState(() {}); - } - }, - ); - }, - ), - Text( - "Never show again", - style: STextStyles.smallMed14(context), - ), - ], - ), - leftButton: TextButton( - onPressed: () { - Navigator.of(context).pop(false); - }, - child: Text( - "Cancel", - style: STextStyles.button(context).copyWith( - color: Theme.of( - context, - ).extension()!.accentColorDark, - ), - ), - ), - rightButton: TextButton( - style: Theme.of( - context, - ).extension()!.getPrimaryEnabledButtonStyle(context), - onPressed: () { - Navigator.of(context).pop(true); - }, - child: Text("Continue", style: STextStyles.button(context)), - ), - ); - } else { - return DesktopDialog( - maxWidth: 550, - maxHeight: 300, - child: Padding( - padding: const EdgeInsets.symmetric(horizontal: 32, vertical: 20), - child: Column( - children: [ - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text("Attention", style: STextStyles.desktopH2(context)), - Row( - children: [ - Consumer( - builder: (_, ref, __) { - return Checkbox( - value: ref.watch( - prefsChangeNotifierProvider.select( - (value) => value.hideBlockExplorerWarning, - ), - ), - onChanged: (value) { - if (value is bool) { - ref - .read(prefsChangeNotifierProvider) - .hideBlockExplorerWarning = - value; - setState(() {}); - } - }, - ); - }, - ), - Text( - "Never show again", - style: STextStyles.smallMed14(context), - ), - ], - ), - ], - ), - const SizedBox(height: 16), - Text( - "You are about to view this transaction in a block explorer. The explorer may log your IP address and link it to the transaction. Only proceed if you trust $explorer.", - style: STextStyles.desktopTextSmall(context), - ), - const SizedBox(height: 35), - Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - SecondaryButton( - width: 200, - buttonHeight: ButtonHeight.l, - label: "Cancel", - onPressed: () { - Navigator.of(context, rootNavigator: true).pop(false); - }, - ), - const SizedBox(width: 20), - PrimaryButton( - width: 200, - buttonHeight: ButtonHeight.l, - label: "Continue", - onPressed: () { - Navigator.of(context, rootNavigator: true).pop(true); - }, - ), - ], - ), - ], - ), - ), - ); - } - }, - ); - return shouldContinue ?? false; - } - @override Widget build(BuildContext context) { final currentHeight = ref.watch(pWalletChainHeight(walletId)); @@ -626,6 +485,16 @@ class _TransactionV2DetailsViewState ); } + final labelStyle = Util.isDesktop + ? STextStyles.desktopTextExtraExtraSmall(context) + : STextStyles.itemSubtitle(context); + + final detailStyle = Util.isDesktop + ? STextStyles.desktopTextExtraExtraSmall(context).copyWith( + color: Theme.of(context).extension()!.textDark, + ) + : STextStyles.itemSubtitle12(context); + return ConditionalParent( condition: !isDesktop, builder: (child) => Background(child: child), @@ -757,19 +626,7 @@ class _TransactionV2DetailsViewState children: [ SelectableText( "$amountPrefix${ref.watch(pAmountFormatter(coin)).format(amount, ethContract: ethContract)}", - style: isDesktop - ? STextStyles.desktopTextExtraExtraSmall( - context, - ).copyWith( - color: Theme.of(context) - .extension< - StackColors - >()! - .textDark, - ) - : STextStyles.titleBold12( - context, - ), + style: detailStyle, ), const SizedBox(height: 2), if (price != null) @@ -799,13 +656,7 @@ class _TransactionV2DetailsViewState ); return SelectableText( "$amountPrefix$formatted $ticker", - style: isDesktop - ? STextStyles.desktopTextExtraExtraSmall( - context, - ) - : STextStyles.itemSubtitle( - context, - ), + style: labelStyle, ); }, ), @@ -834,14 +685,7 @@ class _TransactionV2DetailsViewState mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Text( - "Status", - style: isDesktop - ? STextStyles.desktopTextExtraExtraSmall( - context, - ) - : STextStyles.itemSubtitle(context), - ), + Text("Status", style: labelStyle), // Flexible( // child: FittedBox( // fit: BoxFit.scaleDown, @@ -962,13 +806,7 @@ class _TransactionV2DetailsViewState }, child: Text( outputLabel, - style: isDesktop - ? STextStyles.desktopTextExtraExtraSmall( - context, - ) - : STextStyles.itemSubtitle( - context, - ), + style: labelStyle, ), ), const SizedBox(height: 8), @@ -1132,30 +970,12 @@ class _TransactionV2DetailsViewState children: [ Text( "On chain note", - style: isDesktop - ? STextStyles.desktopTextExtraExtraSmall( - context, - ) - : STextStyles.itemSubtitle( - context, - ), + style: labelStyle, ), const SizedBox(height: 8), SelectableText( _transaction.onChainNote ?? "", - style: isDesktop - ? STextStyles.desktopTextExtraExtraSmall( - context, - ).copyWith( - color: Theme.of(context) - .extension< - StackColors - >()! - .textDark, - ) - : STextStyles.itemSubtitle12( - context, - ), + style: detailStyle, ), ], ), @@ -1186,13 +1006,7 @@ class _TransactionV2DetailsViewState coin is Mimblewimblecoin) ? "Local Note" : "Note ", - style: isDesktop - ? STextStyles.desktopTextExtraExtraSmall( - context, - ) - : STextStyles.itemSubtitle( - context, - ), + style: labelStyle, ), isDesktop ? IconPencilButton( @@ -1205,6 +1019,8 @@ class _TransactionV2DetailsViewState maxHeight: 360, child: EditNoteView( txid: + _transaction + .slateId ?? _transaction.txid, walletId: walletId, ), @@ -1220,7 +1036,8 @@ class _TransactionV2DetailsViewState ).pushNamed( EditNoteView.routeName, arguments: Tuple2( - _transaction.txid, + _transaction.slateId ?? + _transaction.txid, walletId, ), ); @@ -1255,26 +1072,14 @@ class _TransactionV2DetailsViewState .watch( pTransactionNote(( txid: - (coin is Epiccash || - coin - is Mimblewimblecoin) - ? _transaction.slateId - .toString() - : _transaction.txid, + _transaction.slateId ?? + _transaction.txid, walletId: walletId, )), ) ?.value ?? "", - style: isDesktop - ? STextStyles.desktopTextExtraExtraSmall( - context, - ).copyWith( - color: Theme.of(context) - .extension()! - .textDark, - ) - : STextStyles.itemSubtitle12(context), + style: detailStyle, ), ], ), @@ -1284,45 +1089,10 @@ class _TransactionV2DetailsViewState ? const _Divider() : const SizedBox(height: 12), if (_sparkMemo != null) - RoundedWhiteContainer( - padding: isDesktop - ? const EdgeInsets.all(16) - : const EdgeInsets.all(12), - child: Column( - crossAxisAlignment: - CrossAxisAlignment.start, - children: [ - Row( - children: [ - Text( - "Memo", - style: isDesktop - ? STextStyles.desktopTextExtraExtraSmall( - context, - ) - : STextStyles.itemSubtitle( - context, - ), - ), - ], - ), - const SizedBox(height: 8), - SelectableText( - _sparkMemo!, - style: isDesktop - ? STextStyles.desktopTextExtraExtraSmall( - context, - ).copyWith( - color: Theme.of(context) - .extension()! - .textDark, - ) - : STextStyles.itemSubtitle12( - context, - ), - ), - ], - ), + _DetailItem( + label: "Memo", + detail: _sparkMemo!, + vertical: true, ), isDesktop ? const _Divider() @@ -1340,16 +1110,7 @@ class _TransactionV2DetailsViewState crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text( - "Date", - style: isDesktop - ? STextStyles.desktopTextExtraExtraSmall( - context, - ) - : STextStyles.itemSubtitle( - context, - ), - ), + Text("Date", style: labelStyle), if (isDesktop) const SizedBox(height: 2), if (isDesktop) @@ -1357,19 +1118,7 @@ class _TransactionV2DetailsViewState Format.extractDateFrom( _transaction.timestamp, ), - style: isDesktop - ? STextStyles.desktopTextExtraExtraSmall( - context, - ).copyWith( - color: Theme.of(context) - .extension< - StackColors - >()! - .textDark, - ) - : STextStyles.itemSubtitle12( - context, - ), + style: detailStyle, ), ], ), @@ -1378,17 +1127,7 @@ class _TransactionV2DetailsViewState Format.extractDateFrom( _transaction.timestamp, ), - style: isDesktop - ? STextStyles.desktopTextExtraExtraSmall( - context, - ).copyWith( - color: Theme.of(context) - .extension()! - .textDark, - ) - : STextStyles.itemSubtitle12( - context, - ), + style: detailStyle, ), if (isDesktop) IconCopyButton( @@ -1444,33 +1183,14 @@ class _TransactionV2DetailsViewState children: [ Text( "Transaction fee", - style: isDesktop - ? STextStyles.desktopTextExtraExtraSmall( - context, - ) - : STextStyles.itemSubtitle( - context, - ), + style: labelStyle, ), if (isDesktop) const SizedBox(height: 2), if (isDesktop) SelectableText( feeString, - style: isDesktop - ? STextStyles.desktopTextExtraExtraSmall( - context, - ).copyWith( - color: - Theme.of(context) - .extension< - StackColors - >()! - .textDark, - ) - : STextStyles.itemSubtitle12( - context, - ), + style: detailStyle, ), if (supportsRbf && !confirmedTxn) const SizedBox(height: 8), @@ -1484,19 +1204,7 @@ class _TransactionV2DetailsViewState if (!isDesktop) SelectableText( feeString, - style: isDesktop - ? STextStyles.desktopTextExtraExtraSmall( - context, - ).copyWith( - color: Theme.of(context) - .extension< - StackColors - >()! - .textDark, - ) - : STextStyles.itemSubtitle12( - context, - ), + style: detailStyle, ), if (isDesktop) IconCopyButton(data: feeString), @@ -1535,7 +1243,8 @@ class _TransactionV2DetailsViewState ); if (widget.coin is! Epiccash && confirmed) { height = - "${_transaction.height == 0 ? "Unknown" : _transaction.height}"; + "${_transaction.height == 0 ? "Unknow" + "n" : _transaction.height}"; } else { height = confirms > 0 ? "${_transaction.height}" @@ -1563,54 +1272,21 @@ class _TransactionV2DetailsViewState children: [ Text( "Block height", - style: isDesktop - ? STextStyles.desktopTextExtraExtraSmall( - context, - ) - : STextStyles.itemSubtitle( - context, - ), + style: labelStyle, ), if (isDesktop) const SizedBox(height: 2), if (isDesktop) SelectableText( height, - style: isDesktop - ? STextStyles.desktopTextExtraExtraSmall( - context, - ).copyWith( - color: - Theme.of( - context, - ) - .extension< - StackColors - >()! - .textDark, - ) - : STextStyles.itemSubtitle12( - context, - ), + style: detailStyle, ), ], ), if (!isDesktop) SelectableText( height, - style: isDesktop - ? STextStyles.desktopTextExtraExtraSmall( - context, - ).copyWith( - color: Theme.of(context) - .extension< - StackColors - >()! - .textDark, - ) - : STextStyles.itemSubtitle12( - context, - ), + style: detailStyle, ), if (isDesktop) IconCopyButton(data: height), @@ -1636,54 +1312,21 @@ class _TransactionV2DetailsViewState children: [ Text( "Confirmations", - style: isDesktop - ? STextStyles.desktopTextExtraExtraSmall( - context, - ) - : STextStyles.itemSubtitle( - context, - ), + style: labelStyle, ), if (isDesktop) const SizedBox(height: 2), if (isDesktop) SelectableText( confirmations, - style: isDesktop - ? STextStyles.desktopTextExtraExtraSmall( - context, - ).copyWith( - color: - Theme.of( - context, - ) - .extension< - StackColors - >()! - .textDark, - ) - : STextStyles.itemSubtitle12( - context, - ), + style: detailStyle, ), ], ), if (!isDesktop) SelectableText( confirmations, - style: isDesktop - ? STextStyles.desktopTextExtraExtraSmall( - context, - ).copyWith( - color: Theme.of(context) - .extension< - StackColors - >()! - .textDark, - ) - : STextStyles.itemSubtitle12( - context, - ), + style: detailStyle, ), if (isDesktop) IconCopyButton(data: height), @@ -1701,40 +1344,9 @@ class _TransactionV2DetailsViewState : const SizedBox(height: 12), if (coin is Ethereum && _transaction.type != TransactionType.incoming) - RoundedWhiteContainer( - padding: isDesktop - ? const EdgeInsets.all(16) - : const EdgeInsets.all(12), - child: Row( - crossAxisAlignment: - CrossAxisAlignment.start, - mainAxisAlignment: - MainAxisAlignment.spaceBetween, - children: [ - Text( - "Nonce", - style: isDesktop - ? STextStyles.desktopTextExtraExtraSmall( - context, - ) - : STextStyles.itemSubtitle(context), - ), - SelectableText( - _transaction.nonce.toString(), - style: isDesktop - ? STextStyles.desktopTextExtraExtraSmall( - context, - ).copyWith( - color: Theme.of(context) - .extension()! - .textDark, - ) - : STextStyles.itemSubtitle12( - context, - ), - ), - ], - ), + _DetailItem( + label: "Nonce", + detail: _transaction.nonce.toString(), ), if (coin is Salvium && _transaction.salviumTypeString != null) @@ -1743,86 +1355,21 @@ class _TransactionV2DetailsViewState : const SizedBox(height: 12), if (coin is Salvium && _transaction.salviumTypeString != null) - RoundedWhiteContainer( - padding: isDesktop - ? const EdgeInsets.all(16) - : const EdgeInsets.all(12), - child: Column( - crossAxisAlignment: - CrossAxisAlignment.start, - children: [ - Row( - children: [ - Text( - "Type", - style: isDesktop - ? STextStyles.desktopTextExtraExtraSmall( - context, - ) - : STextStyles.itemSubtitle( - context, - ), - ), - ], - ), - const SizedBox(height: 8), - SelectableText( - _transaction.salviumTypeString!, - style: isDesktop - ? STextStyles.desktopTextExtraExtraSmall( - context, - ).copyWith( - color: Theme.of(context) - .extension()! - .textDark, - ) - : STextStyles.itemSubtitle12( - context, - ), - ), - ], - ), + _DetailItem( + label: "Type", + detail: _transaction.salviumTypeString!, + vertical: true, ), if (kDebugMode) isDesktop ? const _Divider() : const SizedBox(height: 12), if (kDebugMode) - RoundedWhiteContainer( - padding: isDesktop - ? const EdgeInsets.all(16) - : const EdgeInsets.all(12), - child: Row( - crossAxisAlignment: - CrossAxisAlignment.start, - mainAxisAlignment: - MainAxisAlignment.spaceBetween, - children: [ - Text( - "Tx sub type", - style: isDesktop - ? STextStyles.desktopTextExtraExtraSmall( - context, - ) - : STextStyles.itemSubtitle(context), - ), - SelectableText( - _transaction.subType.toString(), - style: isDesktop - ? STextStyles.desktopTextExtraExtraSmall( - context, - ).copyWith( - color: Theme.of(context) - .extension()! - .textDark, - ) - : STextStyles.itemSubtitle12( - context, - ), - ), - ], - ), + _DetailItem( + label: "Tx sub type", + detail: _transaction.subType.toString(), ), + if (hasTxKeyProbably) isDesktop ? const _Divider() @@ -1835,299 +1382,11 @@ class _TransactionV2DetailsViewState isDesktop ? const _Divider() : const SizedBox(height: 12), - - _transaction.txid.startsWith("mweb_outputId_") && - _transaction.subType == - TransactionSubType.mweb - ? RoundedWhiteContainer( - padding: isDesktop - ? const EdgeInsets.all(16) - : const EdgeInsets.all(12), - child: Row( - crossAxisAlignment: - CrossAxisAlignment.start, - mainAxisAlignment: - MainAxisAlignment.spaceBetween, - children: [ - Expanded( - child: Column( - crossAxisAlignment: - CrossAxisAlignment.start, - children: [ - ConditionalParent( - condition: !isDesktop, - builder: (child) => Row( - children: [ - Expanded(child: child), - SimpleCopyButton( - data: _transaction.txid - .replaceFirst( - "mweb_outputId_", - "", - ), - ), - ], - ), - child: Text( - "MWEB Output ID", - style: isDesktop - ? STextStyles.desktopTextExtraExtraSmall( - context, - ) - : STextStyles.itemSubtitle( - context, - ), - ), - ), - const SizedBox(height: 8), - SelectableText( - _transaction.txid - .replaceFirst( - "mweb_outputId_", - "", - ), - style: isDesktop - ? STextStyles.desktopTextExtraExtraSmall( - context, - ).copyWith( - color: - Theme.of(context) - .extension< - StackColors - >()! - .textDark, - ) - : STextStyles.itemSubtitle12( - context, - ), - ), - // if (coin is Litecoin && - // coin.network == - // CryptoCurrencyNetwork - // .main) - // const SizedBox(height: 8), - // if (coin is Litecoin && - // coin.network == - // CryptoCurrencyNetwork - // .main) - // CustomTextButton( - // text: - // "Open in block explorer", - // onTap: () async { - // final uri = - // getBlockExplorerTransactionUrlFor( - // coin: coin, - // txid: _transaction - // .txid - // .replaceFirst( - // "mweb_outputId_", - // "", - // ), - // ); - // - // if (ref - // .read( - // prefsChangeNotifierProvider, - // ) - // .hideBlockExplorerWarning == - // false) { - // final shouldContinue = - // await showExplorerWarning( - // "${uri.scheme}://${uri.host}", - // ); - // - // if (!shouldContinue) { - // return; - // } - // } - // try { - // await launchUrl( - // uri, - // mode: - // LaunchMode - // .externalApplication, - // ); - // } catch (_) { - // if (context.mounted) { - // unawaited( - // showDialog( - // context: context, - // builder: - // ( - // _, - // ) => StackOkDialog( - // title: - // "Could not open in block explorer", - // message: - // "Failed to open \"${uri.toString()}\"", - // ), - // ), - // ); - // } - // } - // }, - // ), - ], - ), - ), - if (isDesktop) - const SizedBox(width: 12), - if (isDesktop) - IconCopyButton( - data: _transaction.txid - .replaceFirst( - "mweb_outputId_", - "", - ), - ), - ], - ), - ) - : RoundedWhiteContainer( - padding: isDesktop - ? const EdgeInsets.all(16) - : const EdgeInsets.all(12), - child: Row( - crossAxisAlignment: - CrossAxisAlignment.start, - mainAxisAlignment: - MainAxisAlignment.spaceBetween, - children: [ - Expanded( - child: Column( - crossAxisAlignment: - CrossAxisAlignment.start, - children: [ - ConditionalParent( - condition: !isDesktop, - builder: (child) => Row( - children: [ - Expanded(child: child), - SimpleCopyButton( - data: _transaction.txid, - ), - ], - ), - child: Text( - "Transaction ID", - style: isDesktop - ? STextStyles.desktopTextExtraExtraSmall( - context, - ) - : STextStyles.itemSubtitle( - context, - ), - ), - ), - const SizedBox(height: 8), - // Flexible( - // child: FittedBox( - // fit: BoxFit.scaleDown, - // child: - SelectableText( - _transaction.txid, - style: isDesktop - ? STextStyles.desktopTextExtraExtraSmall( - context, - ).copyWith( - color: - Theme.of(context) - .extension< - StackColors - >()! - .textDark, - ) - : STextStyles.itemSubtitle12( - context, - ), - ), - if (coin is! Epiccash && - coin is! Mimblewimblecoin) - const SizedBox(height: 8), - if (coin is! Epiccash && - coin is! Mimblewimblecoin) - CustomTextButton( - text: - "Open in block explorer", - onTap: () async { - final uri = - getBlockExplorerTransactionUrlFor( - coin: coin, - txid: _transaction - .txid, - ); - - if (ref - .read( - prefsChangeNotifierProvider, - ) - .hideBlockExplorerWarning == - false) { - final shouldContinue = - await showExplorerWarning( - "${uri.scheme}://${uri.host}", - ); - - if (!shouldContinue) { - return; - } - } - - // ref - // .read( - // shouldShowLockscreenOnResumeStateProvider - // .state) - // .state = false; - try { - await launchUrl( - uri, - mode: LaunchMode - .externalApplication, - ); - } catch (_) { - if (context.mounted) { - unawaited( - showDialog( - context: context, - builder: (_) => StackOkDialog( - title: - "Could not open in block explorer", - message: - "Failed to open \"${uri.toString()}\"", - maxWidth: - Util.isDesktop - ? 400 - : null, - ), - ), - ); - } - } finally { - // Future.delayed( - // const Duration(seconds: 1), - // () => ref - // .read( - // shouldShowLockscreenOnResumeStateProvider - // .state) - // .state = true, - // ); - } - }, - ), - // ), - // ), - ], - ), - ), - if (isDesktop) - const SizedBox(width: 12), - if (isDesktop) - IconCopyButton( - data: _transaction.txid, - ), - ], - ), - ), + _TxidDetailItem( + coin: coin, + txid: _transaction.txid, + subType: _transaction.subType, + ), // if ((coin is FiroTestNet || coin is Firo) && // _transaction.subType == "mint") // const SizedBox( @@ -2224,35 +1483,14 @@ class _TransactionV2DetailsViewState crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text( - "Slate ID", - style: isDesktop - ? STextStyles.desktopTextExtraExtraSmall( - context, - ) - : STextStyles.itemSubtitle( - context, - ), - ), + Text("Slate ID", style: labelStyle), // Flexible( // child: FittedBox( // fit: BoxFit.scaleDown, // child: SelectableText( _transaction.slateId ?? "Unknown", - style: isDesktop - ? STextStyles.desktopTextExtraExtraSmall( - context, - ).copyWith( - color: Theme.of(context) - .extension< - StackColors - >()! - .textDark, - ) - : STextStyles.itemSubtitle12( - context, - ), + style: detailStyle, ), // ), // ), @@ -2429,7 +1667,8 @@ class _TransactionV2DetailsViewState showFloatingFlushBar( type: FlushBarType.warning, message: - "ERROR: Wallet type is not Epic Cash or MimbleWimbleCoin", + "ERROR: Wallet type is not " + "Epic Cash or MimbleWimbleCoin", context: context, ), ); @@ -2508,7 +1747,9 @@ class OutputCard extends ConsumerWidget { } class _Divider extends StatelessWidget { - const _Divider({super.key}); + const _Divider( + // {super.key} + ); @override Widget build(BuildContext context) { @@ -2591,3 +1832,387 @@ class IconPencilButton extends StatelessWidget { ); } } + +class _DetailItemBase extends StatelessWidget { + const _DetailItemBase({ + // super.key, + required this.child, + }); + + final Widget child; + + @override + Widget build(BuildContext context) { + return RoundedWhiteContainer( + padding: Util.isDesktop + ? const EdgeInsets.all(16) + : const EdgeInsets.all(12), + + child: child, + ); + } +} + +class _DetailItem extends StatelessWidget { + const _DetailItem({ + // super.key, + required this.label, + required this.detail, + this.vertical = false, + }); + + final String label, detail; + final bool vertical; + + @override + Widget build(BuildContext context) { + final labelStyle = Util.isDesktop + ? STextStyles.desktopTextExtraExtraSmall(context) + : STextStyles.itemSubtitle(context); + + final detailStyle = Util.isDesktop + ? STextStyles.desktopTextExtraExtraSmall(context).copyWith( + color: Theme.of(context).extension()!.textDark, + ) + : STextStyles.itemSubtitle12(context); + + return _DetailItemBase( + child: vertical + ? Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row(children: [Text(label, style: labelStyle)]), + const SizedBox(height: 8), + SelectableText(detail, style: detailStyle), + ], + ) + : Row( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text(label, style: labelStyle), + SelectableText(detail, style: detailStyle), + ], + ), + ); + } +} + +class _TxidDetailItem extends ConsumerStatefulWidget { + const _TxidDetailItem({ + // super.key, + required this.coin, + required this.txid, + required this.subType, + }); + + final CryptoCurrency coin; + final String txid; + final TransactionSubType subType; + + @override + ConsumerState<_TxidDetailItem> createState() => _TxidDetailItemState(); +} + +class _TxidDetailItemState extends ConsumerState<_TxidDetailItem> { + Future showExplorerWarning(String explorer) async { + final warningMessage = + "You are about to view this transaction in a block explorer. " + "The explorer may log your IP address and link it to the " + "transaction. Only proceed if you trust $explorer."; + + final bool? shouldContinue = await showDialog( + context: context, + barrierDismissible: false, + builder: (_) { + if (!Util.isDesktop) { + return StackDialog( + title: "Attention", + message: warningMessage, + icon: Row( + children: [ + Consumer( + builder: (_, ref, __) { + return Checkbox( + value: ref.watch( + prefsChangeNotifierProvider.select( + (value) => value.hideBlockExplorerWarning, + ), + ), + onChanged: (value) { + if (value is bool) { + ref + .read(prefsChangeNotifierProvider) + .hideBlockExplorerWarning = + value; + setState(() {}); + } + }, + ); + }, + ), + Text( + "Never show again", + style: STextStyles.smallMed14(context), + ), + ], + ), + leftButton: TextButton( + onPressed: () { + Navigator.of(context).pop(false); + }, + child: Text( + "Cancel", + style: STextStyles.button(context).copyWith( + color: Theme.of( + context, + ).extension()!.accentColorDark, + ), + ), + ), + rightButton: TextButton( + style: Theme.of( + context, + ).extension()!.getPrimaryEnabledButtonStyle(context), + onPressed: () { + Navigator.of(context).pop(true); + }, + child: Text("Continue", style: STextStyles.button(context)), + ), + ); + } else { + return DesktopDialog( + maxWidth: 550, + maxHeight: 300, + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 32, vertical: 20), + child: Column( + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text("Attention", style: STextStyles.desktopH2(context)), + Row( + children: [ + Consumer( + builder: (_, ref, __) { + return Checkbox( + value: ref.watch( + prefsChangeNotifierProvider.select( + (value) => value.hideBlockExplorerWarning, + ), + ), + onChanged: (value) { + if (value is bool) { + ref + .read(prefsChangeNotifierProvider) + .hideBlockExplorerWarning = + value; + setState(() {}); + } + }, + ); + }, + ), + Text( + "Never show again", + style: STextStyles.smallMed14(context), + ), + ], + ), + ], + ), + const SizedBox(height: 16), + Text( + warningMessage, + style: STextStyles.desktopTextSmall(context), + ), + const SizedBox(height: 35), + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + SecondaryButton( + width: 200, + buttonHeight: ButtonHeight.l, + label: "Cancel", + onPressed: () { + Navigator.of(context, rootNavigator: true).pop(false); + }, + ), + const SizedBox(width: 20), + PrimaryButton( + width: 200, + buttonHeight: ButtonHeight.l, + label: "Continue", + onPressed: () { + Navigator.of(context, rootNavigator: true).pop(true); + }, + ), + ], + ), + ], + ), + ), + ); + } + }, + ); + return shouldContinue ?? false; + } + + @override + Widget build(BuildContext context) { + final isDesktop = Util.isDesktop; + + return _DetailItemBase( + child: + widget.txid.startsWith("mweb_outputId_") && + widget.subType == TransactionSubType.mweb + ? Row( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + + children: [ + ConditionalParent( + condition: !isDesktop, + builder: (child) => Row( + children: [ + Expanded(child: child), + SimpleCopyButton( + data: widget.txid.replaceFirst( + "mweb_outputId_", + "", + ), + ), + ], + ), + child: Text( + "MWEB Output ID", + style: isDesktop + ? STextStyles.desktopTextExtraExtraSmall(context) + : STextStyles.itemSubtitle(context), + ), + ), + const SizedBox(height: 8), + SelectableText( + widget.txid.replaceFirst("mweb_outputId_", ""), + style: isDesktop + ? STextStyles.desktopTextExtraExtraSmall( + context, + ).copyWith( + color: Theme.of( + context, + ).extension()!.textDark, + ) + : STextStyles.itemSubtitle12(context), + ), + ], + ), + ), + if (isDesktop) const SizedBox(width: 12), + if (isDesktop) + IconCopyButton( + data: widget.txid.replaceFirst("mweb_outputId_", ""), + ), + ], + ) + : Row( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + ConditionalParent( + condition: !isDesktop, + builder: (child) => Row( + children: [ + Expanded(child: child), + SimpleCopyButton(data: widget.txid), + ], + ), + child: Text( + "Transaction ID", + style: isDesktop + ? STextStyles.desktopTextExtraExtraSmall(context) + : STextStyles.itemSubtitle(context), + ), + ), + const SizedBox(height: 8), + + SelectableText( + widget.txid, + style: isDesktop + ? STextStyles.desktopTextExtraExtraSmall( + context, + ).copyWith( + color: Theme.of( + context, + ).extension()!.textDark, + ) + : STextStyles.itemSubtitle12(context), + ), + if (widget.coin is! Epiccash && + widget.coin is! Mimblewimblecoin) + const SizedBox(height: 8), + if (widget.coin is! Epiccash && + widget.coin is! Mimblewimblecoin) + CustomTextButton( + text: "Open in block explorer", + onTap: () async { + final uri = getBlockExplorerTransactionUrlFor( + coin: widget.coin, + txid: widget.txid, + ); + + if (ref + .read(prefsChangeNotifierProvider) + .hideBlockExplorerWarning == + false) { + final shouldContinue = await showExplorerWarning( + "${uri.scheme}://${uri.host}", + ); + + if (!shouldContinue) { + return; + } + } + + try { + await launchUrl( + uri, + mode: LaunchMode.externalApplication, + ); + } catch (_) { + if (context.mounted) { + unawaited( + showDialog( + context: context, + builder: (_) => StackOkDialog( + title: "Could not open in block explorer", + message: + "Failed to open " + "\"${uri.toString()}\"", + maxWidth: Util.isDesktop ? 400 : null, + ), + ), + ); + } + } + }, + ), + ], + ), + ), + if (isDesktop) const SizedBox(width: 12), + if (isDesktop) IconCopyButton(data: widget.txid), + ], + ), + ); + } +} diff --git a/lib/pages/wallet_view/transaction_views/tx_v2/transaction_v2_list_item.dart b/lib/pages/wallet_view/transaction_views/tx_v2/transaction_v2_list_item.dart index 0ae641401..a9d0dee50 100644 --- a/lib/pages/wallet_view/transaction_views/tx_v2/transaction_v2_list_item.dart +++ b/lib/pages/wallet_view/transaction_views/tx_v2/transaction_v2_list_item.dart @@ -4,6 +4,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:tuple/tuple.dart'; +import '../../../../models/exchange/response_objects/trade.dart'; import '../../../../models/isar/models/blockchain_data/v2/transaction_v2.dart'; import '../../../../models/isar/models/isar_models.dart'; import '../../../../providers/global/trades_service_provider.dart'; @@ -38,12 +39,17 @@ class TxListItem extends ConsumerWidget { if (tx is TransactionV2) { final _tx = tx as TransactionV2; - final matchingTrades = ref - .read(tradesServiceProvider) - .trades - .where((e) => e.payInTxid == _tx.txid || e.payOutTxid == _tx.txid); + final Iterable matchingTrades = + _tx.type == TransactionType.outgoing && _tx.txid.isNotEmpty + ? ref + .read(tradesServiceProvider) + .trades + .where( + (e) => e.payInTxid == _tx.txid || e.payOutTxid == _tx.txid, + ) + : []; - if (_tx.type == TransactionType.outgoing && matchingTrades.isNotEmpty) { + if (matchingTrades.isNotEmpty) { final trade = matchingTrades.first; return Container( decoration: BoxDecoration( @@ -54,10 +60,7 @@ class TxListItem extends ConsumerWidget { child: Column( mainAxisSize: MainAxisSize.min, children: [ - TransactionCardV2( - key: UniqueKey(), - transaction: _tx, - ), + TransactionCardV2(key: UniqueKey(), transaction: _tx), TradeCard( key: Key( _tx.txid + @@ -94,7 +97,8 @@ class TxListItem extends ConsumerWidget { Text( "Trade details", style: STextStyles.desktopH3( - context), + context, + ), ), DesktopDialogCloseButton( onPressedOverride: Navigator.of( @@ -111,8 +115,9 @@ class TxListItem extends ConsumerWidget { // TODO: [prio:med] // transactionIfSentFromStack: tx, transactionIfSentFromStack: null, - walletName: ref - .watch(pWalletName(_tx.walletId)), + walletName: ref.watch( + pWalletName(_tx.walletId), + ), walletId: _tx.walletId, ), ), @@ -171,10 +176,7 @@ class TxListItem extends ConsumerWidget { borderRadius: radius, ), child: Breathing( - child: FusionTxGroupCard( - key: UniqueKey(), - group: group, - ), + child: FusionTxGroupCard(key: UniqueKey(), group: group), ), ); } diff --git a/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/wallet_options_button.dart b/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/wallet_options_button.dart index fc348003a..4beb82514 100644 --- a/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/wallet_options_button.dart +++ b/lib/pages_desktop_specific/my_stack_view/wallet_view/sub_widgets/wallet_options_button.dart @@ -31,6 +31,7 @@ import '../../../../utilities/util.dart'; import '../../../../wallets/crypto_currency/intermediate/frost_currency.dart'; import '../../../../wallets/crypto_currency/intermediate/nano_currency.dart'; import '../../../../wallets/isar/providers/wallet_info_provider.dart'; +import '../../../../wallets/wallet/impl/epiccash_wallet.dart'; import '../../../../wallets/wallet/intermediate/cryptonote_wallet.dart'; import '../../../../wallets/wallet/wallet_mixin_interfaces/extended_keys_interface.dart'; import '../../../../wallets/wallet/wallet_mixin_interfaces/spark_interface.dart'; @@ -478,8 +479,9 @@ class WalletOptionsPopupMenu extends ConsumerWidget { ), ), ), - if (isCN) const SizedBox(height: 8), - if (isCN) + if (isCN || wallet is EpiccashWallet) + const SizedBox(height: 8), + if (isCN || wallet is EpiccashWallet) TransparentButton( onPressed: onRefreshHeightPressed, child: Padding( diff --git a/lib/wallets/wallet/impl/epiccash_wallet.dart b/lib/wallets/wallet/impl/epiccash_wallet.dart index a734626f4..3fc6b56e9 100644 --- a/lib/wallets/wallet/impl/epiccash_wallet.dart +++ b/lib/wallets/wallet/impl/epiccash_wallet.dart @@ -16,6 +16,7 @@ import '../../../models/isar/models/blockchain_data/transaction.dart'; import '../../../models/isar/models/blockchain_data/v2/input_v2.dart'; import '../../../models/isar/models/blockchain_data/v2/output_v2.dart'; import '../../../models/isar/models/blockchain_data/v2/transaction_v2.dart'; +import '../../../models/isar/models/transaction_note.dart'; import '../../../models/node_model.dart'; import '../../../models/paymint/fee_object_model.dart'; import '../../../pages/settings_views/global_settings_view/manage_nodes_views/add_edit_node_view.dart'; @@ -131,6 +132,15 @@ class EpiccashWallet extends Bip39Wallet { return _epicBoxConfig; } + Future updateRestoreHeight(int height) async { + final epicData = info.epicData!.copyWith(restoreHeight: height); + + await info.updateExtraEpiccashWalletInfo( + epicData: epicData, + isar: mainDB.isar, + ); + } + // ================= Private ================================================= Future _getConfig() async { @@ -444,6 +454,48 @@ class EpiccashWallet extends Bip39Wallet { return height; } + static const _mid = "_:'", _end = "':"; + + /// eeehhhhhhhhhhhhhhh + bool _fuzzyEquals(TransactionV2 a, TransactionV2 b) { + final isAmountReceivedMatches = + a.getAmountReceivedInThisWallet( + fractionDigits: cryptoCurrency.fractionDigits, + ) == + b.getAmountReceivedInThisWallet( + fractionDigits: cryptoCurrency.fractionDigits, + ); + + final isFeeMatches = + a.getFee(fractionDigits: cryptoCurrency.fractionDigits) == + b.getFee(fractionDigits: cryptoCurrency.fractionDigits); + + final isAmountSentMatches = + a.getAmountSentFromThisWallet( + fractionDigits: cryptoCurrency.fractionDigits, + subtractFee: false, + ) == + b.getAmountSentFromThisWallet( + fractionDigits: cryptoCurrency.fractionDigits, + subtractFee: false, + ); + + final isHeightMatches = a.height == b.height; + final isTxTypeMatches = a.type == b.type && a.subType == b.subType; + final isSlateIdMatches = a.slateId == b.slateId; + + if (isHeightMatches && + isTxTypeMatches && + isFeeMatches && + isSlateIdMatches && + isAmountSentMatches && + isAmountReceivedMatches) { + return true; + } + + return false; + } + // ============== Overrides ================================================== @override @@ -663,8 +715,67 @@ class EpiccashWallet extends Bip39Wallet { _hackedCheckTorNodePrefs(); await refreshMutex.protect(() async { if (isRescan) { - // clear blockchain info - await mainDB.deleteWalletBlockchainData(walletId); + // keep old transactions but id them somehow + // with the current db, there is no other way besides editing the + // unique key (txid+walletId). Since we cannot change the wallet id we + // must therefore hack some stupid stuff into the txid... + final currentTxns1 = await mainDB.isar.transactionV2s + .where() + .walletIdEqualTo(walletId) + .findAll(); + + final List currentTxns = []; + + for (final current in currentTxns1) { + if (currentTxns.where((e) => _fuzzyEquals(e, current)).isNotEmpty) { + Logging.instance.f("DELETING: $current"); + await mainDB.isar.writeTxn(() async { + await mainDB.isar.transactionV2s.delete(current.id); + }); + } else { + currentTxns.add(current); + } + } + + for (final current in currentTxns) { + // check notes first + final note = await mainDB.isar.transactionNotes + .where() + .txidWalletIdEqualTo(current.slateId ?? current.txid, walletId) + .findFirst(); + + // now handle transaction + final firstTime = + !(current.txid.contains(_mid) && current.txid.endsWith(_end)); + + final String txid; + if (firstTime) { + txid = "${current.txid}${_mid}0$_end"; + } else { + // this should always be 2 parts if we've gotten this far + final parts = current.txid.split(_mid); + final rescanCount = + int.parse(parts.last.replaceFirst(_end, "")) + 1; + txid = "${parts.first}$_mid$rescanCount$_end"; + } + + // finally update in db + await mainDB.isar.writeTxn(() async { + final updated = current.copyWith(txid: txid); + if (note != null) { + final updatedNote = TransactionNote( + walletId: walletId, + txid: current.slateId ?? txid, + value: note.value, + ); + await mainDB.isar.transactionNotes.delete(note.id); + await mainDB.isar.transactionNotes.put(updatedNote); + } + + await mainDB.isar.transactionV2s.delete(current.id); + await mainDB.isar.transactionV2s.put(updated); + }); + } await info.updateExtraEpiccashWalletInfo( epicData: info.epicData!.copyWith( @@ -673,7 +784,35 @@ class EpiccashWallet extends Bip39Wallet { isar: mainDB.isar, ); - unawaited(refresh(doScan: true)); + final stringConfig = await _getRealConfig(); + final password = await secureStorageInterface.read( + key: '${walletId}_password', + ); + + // maybe there is some way to tel epic-wallet rust to fully rescan... + final result = await deleteEpicWallet( + walletId: walletId, + secureStore: secureStorageInterface, + ); + Logging.instance.w("Epic rescan temporary delete result: $result"); + await libEpic.recoverWallet( + config: stringConfig, + password: password!, + mnemonic: await getMnemonic(), + name: info.walletId, + ); + + //Open Wallet + final walletOpen = await libEpic.openWallet( + config: stringConfig, + password: password, + ); + await secureStorageInterface.write( + key: '${walletId}_wallet', + value: walletOpen, + ); + + highestPercent = 0; } else { await updateNode(); final String password = generatePassword(); @@ -731,8 +870,9 @@ class EpiccashWallet extends Bip39Wallet { epicData.receivingIndex, ); } - unawaited(refresh(doScan: false)); }); + + unawaited(refresh(doScan: isRescan)); } catch (e, s) { Logging.instance.e( "Exception rethrown from electrumx_mixin recover(): ", @@ -1018,15 +1158,70 @@ class EpiccashWallet extends Bip39Wallet { otherData: jsonEncode(otherData), ); - txns.add(txn); + if (txns.where((e) => _fuzzyEquals(e, txn)).isEmpty) { + txns.add(txn); + } } + final existingTxns = await mainDB.isar.transactionV2s + .where() + .walletIdEqualTo(walletId) + .findAll(); + await mainDB.isar.writeTxn(() async { - await mainDB.isar.transactionV2s - .where() - .walletIdEqualTo(walletId) - .deleteAll(); - await mainDB.isar.transactionV2s.putAll(txns); + for (final tx in txns) { + final existingMatches = existingTxns.where( + (e) => _fuzzyEquals(e, tx), + ); + TransactionNote? note; + if (existingMatches.isNotEmpty) { + // there should only ever be one. If more then something is\ + // wrong somewhere, probably + if (existingMatches.length > 1) { + Logging.instance.w( + "existingMatches length: ${existingMatches.length}", + ); + } + for (final match in existingMatches) { + if (await mainDB.isar.transactionV2s + .where() + .txidWalletIdEqualTo(match.txid, walletId) + .idProperty() + .findFirst() != + null) { + note = await mainDB.isar.transactionNotes + .where() + .txidWalletIdEqualTo(match.slateId ?? match.txid, walletId) + .findFirst(); + + await mainDB.isar.transactionV2s.delete(match.id); + } + } + } + + final id = await mainDB.isar.transactionV2s + .where() + .txidWalletIdEqualTo(tx.txid, walletId) + .idProperty() + .findFirst(); + + if (id != null) { + await mainDB.isar.transactionV2s.delete(id); + } + + if (note != null) { + await mainDB.isar.transactionNotes.delete(note.id); + await mainDB.isar.transactionNotes.put( + TransactionNote( + walletId: walletId, + txid: tx.slateId ?? tx.txid, + value: note.value, + ), + ); + } + + await mainDB.isar.transactionV2s.put(tx); + } }); } catch (e, s) { Logging.instance.e( diff --git a/lib/wallets/wallet/wallet_mixin_interfaces/spark_interface.dart b/lib/wallets/wallet/wallet_mixin_interfaces/spark_interface.dart index 2fa46378d..5a2a025e1 100644 --- a/lib/wallets/wallet/wallet_mixin_interfaces/spark_interface.dart +++ b/lib/wallets/wallet/wallet_mixin_interfaces/spark_interface.dart @@ -1741,8 +1741,12 @@ mixin SparkInterface throw Exception("Transaction too large"); } + const nBytesBuffer = 10; final nFeeNeeded = BigInt.from( - estimateTxFee(vSize: nBytes, feeRatePerKB: feesObject.medium), + estimateTxFee( + vSize: nBytes + nBytesBuffer, + feeRatePerKB: feesObject.medium, + ), ); // One day we'll do this properly if (nFeeRet >= nFeeNeeded) { @@ -1993,6 +1997,7 @@ mixin SparkInterface ), ); + Logging.instance.i("nFeeRet=$nFeeRet, vSize=${data.vSize}"); if (nFeeRet.toInt() < data.vSize!) { Logging.instance.w( "Spark mint transaction failed: $nFeeRet is less than ${data.vSize}", diff --git a/lib/wl_gen/interfaces/libepiccash_interface.dart b/lib/wl_gen/interfaces/libepiccash_interface.dart index af5e65375..287993f6d 100644 --- a/lib/wl_gen/interfaces/libepiccash_interface.dart +++ b/lib/wl_gen/interfaces/libepiccash_interface.dart @@ -1,3 +1,5 @@ +import 'dart:math'; + export '../generated/libepiccash_interface_impl.dart'; abstract class LibEpicCashInterface { @@ -141,6 +143,24 @@ class EpicTransaction { this.kernelLookupMinHeight, this.paymentProof, }); + + @override + String toString() { + return 'EpicTransaction(' + 'id: $id, ' + 'txSlateId: $txSlateId, ' + 'type: $txType, ' + 'confirmed: $confirmed, ' + 'inputs: $numInputs, ' + 'outputs: $numOutputs, ' + 'credited: $amountCredited, ' + 'debited: $amountDebited, ' + 'fee: $fee, ' + 'created: $creationTs, ' + 'confirmed: $confirmationTs, ' + 'messages: ${messages?.length ?? 0}' + ')'; + } } class EpicMessage { @@ -155,6 +175,15 @@ class EpicMessage { this.message, this.messageSig, }); + + @override + String toString() { + return 'EpicMessage(' + 'id: $id, ' + 'publicKey: ${publicKey.substring(0, 8)}..., ' + 'message: ${message != null ? '"${message!.substring(0, min(20, message!.length))}..."' : 'null'}' + ')'; + } } class BadHttpAddressException implements Exception {}