diff --git a/lib/pages/exchange_view/confirm_change_now_send.dart b/lib/pages/exchange_view/confirm_change_now_send.dart index e99cf2df4..9f62bd8ec 100644 --- a/lib/pages/exchange_view/confirm_change_now_send.dart +++ b/lib/pages/exchange_view/confirm_change_now_send.dart @@ -7,15 +7,23 @@ import 'package:stackwallet/models/trade_wallet_lookup.dart'; import 'package:stackwallet/pages/pinpad_views/lock_screen_view.dart'; import 'package:stackwallet/pages/send_view/sub_widgets/sending_transaction_dialog.dart'; import 'package:stackwallet/pages/wallet_view/wallet_view.dart'; +import 'package:stackwallet/pages_desktop_specific/home/my_stack_view/wallet_view/sub_widgets/desktop_auth_send.dart'; import 'package:stackwallet/providers/exchange/trade_sent_from_stack_lookup_provider.dart'; import 'package:stackwallet/providers/providers.dart'; import 'package:stackwallet/route_generator.dart'; import 'package:stackwallet/services/coins/firo/firo_wallet.dart'; +import 'package:stackwallet/utilities/constants.dart'; import 'package:stackwallet/utilities/enums/coin_enum.dart'; import 'package:stackwallet/utilities/format.dart'; import 'package:stackwallet/utilities/text_styles.dart'; import 'package:stackwallet/utilities/theme/stack_colors.dart'; +import 'package:stackwallet/utilities/util.dart'; +import 'package:stackwallet/widgets/conditional_parent.dart'; import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart'; +import 'package:stackwallet/widgets/desktop/desktop_dialog.dart'; +import 'package:stackwallet/widgets/desktop/desktop_dialog_close_button.dart'; +import 'package:stackwallet/widgets/desktop/primary_button.dart'; +import 'package:stackwallet/widgets/desktop/secondary_button.dart'; import 'package:stackwallet/widgets/rounded_container.dart'; import 'package:stackwallet/widgets/rounded_white_container.dart'; import 'package:stackwallet/widgets/stack_dialog.dart'; @@ -52,14 +60,16 @@ class _ConfirmChangeNowSendViewState late final Trade trade; Future _attemptSend(BuildContext context) async { - unawaited(showDialog( - context: context, - useSafeArea: false, - barrierDismissible: false, - builder: (context) { - return const SendingTransactionDialog(); - }, - )); + unawaited( + showDialog( + context: context, + useSafeArea: false, + barrierDismissible: false, + builder: (context) { + return const SendingTransactionDialog(); + }, + ), + ); final String note = transactionInfo["note"] as String? ?? ""; final manager = @@ -93,6 +103,9 @@ class _ConfirmChangeNowSendViewState // pop back to wallet if (mounted) { + if (Util.isDesktop) { + Navigator.of(context, rootNavigator: true).pop(); + } Navigator.of(context).popUntil(ModalRoute.withName(routeOnSuccessName)); } } catch (e) { @@ -129,6 +142,60 @@ class _ConfirmChangeNowSendViewState } } + Future _confirmSend() async { + final dynamic unlocked; + + if (Util.isDesktop) { + unlocked = await showDialog( + context: context, + builder: (context) => DesktopDialog( + maxWidth: 580, + maxHeight: double.infinity, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.end, + children: const [ + DesktopDialogCloseButton(), + ], + ), + const Padding( + padding: EdgeInsets.only( + left: 32, + right: 32, + bottom: 32, + ), + child: DesktopAuthSend(), + ), + ], + ), + ), + ); + } else { + unlocked = await Navigator.push( + context, + RouteGenerator.getRoute( + shouldUseMaterialRoute: RouteGenerator.useMaterialPageRoute, + builder: (_) => const LockscreenView( + showBackButton: true, + popOnSuccess: true, + routeOnSuccessArguments: true, + routeOnSuccess: "", + biometricsCancelButtonString: "CANCEL", + biometricsLocalizedReason: "Authenticate to send transaction", + biometricsAuthenticationTitle: "Confirm Transaction", + ), + settings: const RouteSettings(name: "/confirmsendlockscreen"), + ), + ); + } + + if (unlocked is bool && unlocked && mounted) { + await _attemptSend(context); + } + } + @override void initState() { transactionInfo = widget.transactionInfo; @@ -142,280 +209,503 @@ class _ConfirmChangeNowSendViewState Widget build(BuildContext context) { final managerProvider = ref.watch(walletsChangeNotifierProvider .select((value) => value.getManagerProvider(walletId))); - return Scaffold( - backgroundColor: Theme.of(context).extension()!.background, - appBar: AppBar( - backgroundColor: Theme.of(context).extension()!.background, - leading: AppBarBackButton( - onPressed: () async { - // if (FocusScope.of(context).hasFocus) { - // FocusScope.of(context).unfocus(); - // await Future.delayed(Duration(milliseconds: 50)); - // } - Navigator.of(context).pop(); - }, - ), - title: Text( - "Confirm transaction", - style: STextStyles.navBarTitle(context), - ), - ), - body: LayoutBuilder( - builder: (builderContext, constraints) { - return Padding( - padding: const EdgeInsets.only( - left: 12, - top: 12, - right: 12, - ), - child: SingleChildScrollView( - child: ConstrainedBox( - constraints: BoxConstraints( - minHeight: constraints.maxHeight - 24, - ), - child: IntrinsicHeight( - child: Padding( - padding: const EdgeInsets.all(4), - child: Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - Text( - "Send ${ref.watch(managerProvider.select((value) => value.coin)).ticker}", - style: STextStyles.pageTitleH1(context), - ), - const SizedBox( - height: 12, - ), - RoundedWhiteContainer( - child: Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - Text( - "Send from", - style: STextStyles.smallMed12(context), - ), - const SizedBox( - height: 4, - ), - Text( - ref - .watch(walletsChangeNotifierProvider) - .getManager(walletId) - .walletName, - style: STextStyles.itemSubtitle12(context), - ), - ], - ), - ), - const SizedBox( - height: 12, - ), - const SizedBox( - height: 12, - ), - RoundedWhiteContainer( - child: Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - Text( - "${trade.exchangeName} address", - style: STextStyles.smallMed12(context), - ), - const SizedBox( - height: 4, - ), - Text( - "${transactionInfo["address"] ?? "ERROR"}", - style: STextStyles.itemSubtitle12(context), - ), - ], - ), - ), - const SizedBox( - height: 12, - ), - RoundedWhiteContainer( - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text( - "Amount", - style: STextStyles.smallMed12(context), - ), - Text( - "${Format.satoshiAmountToPrettyString( - transactionInfo["recipientAmt"] as int, - ref.watch( - localeServiceChangeNotifierProvider - .select((value) => value.locale), - ), - )} ${ref.watch( - managerProvider - .select((value) => value.coin), - ).ticker}", - style: STextStyles.itemSubtitle12(context), - textAlign: TextAlign.right, - ), - ], - ), - ), - const SizedBox( - height: 12, - ), - RoundedWhiteContainer( - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text( - "Transaction fee", - style: STextStyles.smallMed12(context), - ), - Text( - "${Format.satoshiAmountToPrettyString( - transactionInfo["fee"] as int, - ref.watch( - localeServiceChangeNotifierProvider - .select((value) => value.locale), - ), - )} ${ref.watch( - managerProvider - .select((value) => value.coin), - ).ticker}", - style: STextStyles.itemSubtitle12(context), - textAlign: TextAlign.right, - ), - ], - ), - ), - const SizedBox( - height: 12, - ), - RoundedWhiteContainer( - child: Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - Text( - "Note", - style: STextStyles.smallMed12(context), - ), - const SizedBox( - height: 4, - ), - Text( - transactionInfo["note"] as String? ?? "", - style: STextStyles.itemSubtitle12(context), - ), - ], - ), - ), - const SizedBox( - height: 12, - ), - RoundedWhiteContainer( - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text( - "Trade ID", - style: STextStyles.smallMed12(context), - ), - Text( - trade.tradeId, - style: STextStyles.itemSubtitle12(context), - textAlign: TextAlign.right, - ), - ], - ), - ), - const SizedBox( - height: 12, - ), - RoundedContainer( - color: Theme.of(context) - .extension()! - .snackBarBackSuccess, - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text( - "Total amount", - style: - STextStyles.titleBold12(context).copyWith( - color: Theme.of(context) - .extension()! - .textConfirmTotalAmount, - ), - ), - Text( - "${Format.satoshiAmountToPrettyString( - (transactionInfo["fee"] as int) + - (transactionInfo["recipientAmt"] as int), - ref.watch( - localeServiceChangeNotifierProvider - .select((value) => value.locale), - ), - )} ${ref.watch( - managerProvider - .select((value) => value.coin), - ).ticker}", - style: STextStyles.itemSubtitle12(context) - .copyWith( - color: Theme.of(context) - .extension()! - .textConfirmTotalAmount, - ), - textAlign: TextAlign.right, - ), - ], - ), - ), - const SizedBox( - height: 16, - ), - const Spacer(), - TextButton( - style: Theme.of(context) - .extension()! - .getPrimaryEnabledButtonColor(context), - onPressed: () async { - final unlocked = await Navigator.push( - context, - RouteGenerator.getRoute( - shouldUseMaterialRoute: - RouteGenerator.useMaterialPageRoute, - builder: (_) => const LockscreenView( - showBackButton: true, - popOnSuccess: true, - routeOnSuccessArguments: true, - routeOnSuccess: "", - biometricsCancelButtonString: "CANCEL", - biometricsLocalizedReason: - "Authenticate to send transaction", - biometricsAuthenticationTitle: - "Confirm Transaction", - ), - settings: const RouteSettings( - name: "/confirmsendlockscreen"), - ), - ); - if (unlocked is bool && unlocked && mounted) { - await _attemptSend(context); - } - }, - child: Text( - "Send", - style: STextStyles.button(context), - ), - ), - ], + final isDesktop = Util.isDesktop; + + return ConditionalParent( + condition: !isDesktop, + builder: (child) { + return Scaffold( + backgroundColor: + Theme.of(context).extension()!.background, + appBar: AppBar( + backgroundColor: + Theme.of(context).extension()!.background, + leading: AppBarBackButton( + onPressed: () async { + // if (FocusScope.of(context).hasFocus) { + // FocusScope.of(context).unfocus(); + // await Future.delayed(Duration(milliseconds: 50)); + // } + Navigator.of(context).pop(); + }, + ), + title: Text( + "Confirm transaction", + style: STextStyles.navBarTitle(context), + ), + ), + body: LayoutBuilder( + builder: (builderContext, constraints) { + return Padding( + padding: const EdgeInsets.only( + left: 12, + top: 12, + right: 12, + ), + child: SingleChildScrollView( + child: ConstrainedBox( + constraints: BoxConstraints( + minHeight: constraints.maxHeight - 24, + ), + child: IntrinsicHeight( + child: Padding( + padding: const EdgeInsets.all(4), + child: child, + ), ), ), ), + ); + }, + ), + ); + }, + child: ConditionalParent( + condition: isDesktop, + builder: (child) => DesktopDialog( + maxHeight: double.infinity, + maxWidth: 580, + child: Column( + children: [ + Row( + children: [ + const SizedBox( + width: 6, + ), + const AppBarBackButton( + isCompact: true, + iconSize: 23, + ), + const SizedBox( + width: 12, + ), + Text( + "Confirm ${ref.watch(managerProvider.select((value) => value.coin)).ticker} transaction", + style: STextStyles.desktopH3(context), + ) + ], + ), + Padding( + padding: const EdgeInsets.only( + left: 32, + right: 32, + bottom: 32, + ), + child: Column( + children: [ + RoundedWhiteContainer( + padding: const EdgeInsets.all(0), + borderColor: Theme.of(context) + .extension()! + .background, + child: child, + ), + const SizedBox( + height: 16, + ), + Row( + children: [ + Text( + "Transaction fee", + style: + STextStyles.desktopTextExtraExtraSmall(context), + ), + ], + ), + const SizedBox( + height: 10, + ), + RoundedContainer( + color: Theme.of(context) + .extension()! + .textFieldDefaultBG, + child: Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + Text( + "${Format.satoshiAmountToPrettyString( + (transactionInfo["fee"] as int), + ref.watch( + localeServiceChangeNotifierProvider + .select((value) => value.locale), + ), + )} ${ref.watch( + managerProvider.select((value) => value.coin), + ).ticker}", + style: + STextStyles.desktopTextExtraExtraSmall(context) + .copyWith( + color: Theme.of(context) + .extension()! + .textDark, + ), + ), + ], + ), + ), + const SizedBox( + height: 16, + ), + RoundedContainer( + color: Theme.of(context) + .extension()! + .snackBarBackSuccess, + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + "Total amount", + style: STextStyles.titleBold12(context).copyWith( + color: Theme.of(context) + .extension()! + .textConfirmTotalAmount, + ), + ), + Text( + "${Format.satoshiAmountToPrettyString( + (transactionInfo["fee"] as int) + + (transactionInfo["recipientAmt"] as int), + ref.watch( + localeServiceChangeNotifierProvider + .select((value) => value.locale), + ), + )} ${ref.watch( + managerProvider.select((value) => value.coin), + ).ticker}", + style: STextStyles.itemSubtitle12(context).copyWith( + color: Theme.of(context) + .extension()! + .textConfirmTotalAmount, + ), + textAlign: TextAlign.right, + ), + ], + ), + ), + const SizedBox( + height: 16, + ), + Row( + children: [ + Expanded( + child: SecondaryButton( + label: "Cancel", + buttonHeight: ButtonHeight.l, + onPressed: Navigator.of(context).pop, + ), + ), + const SizedBox( + width: 16, + ), + Expanded( + child: PrimaryButton( + label: "Send", + buttonHeight: isDesktop ? ButtonHeight.l : null, + onPressed: _confirmSend, + ), + ), + ], + ) + ], + ), + ), + ], + ), + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + ConditionalParent( + condition: isDesktop, + builder: (child) => Container( + decoration: BoxDecoration( + color: Theme.of(context).extension()!.background, + borderRadius: BorderRadius.vertical( + top: Radius.circular( + Constants.size.circularBorderRadius, + ), + ), + ), + child: Padding( + padding: const EdgeInsets.all(12), + child: Row( + children: [ + child, + ], + ), + ), + ), + child: Text( + "Send ${ref.watch(managerProvider.select((value) => value.coin)).ticker}", + style: isDesktop + ? STextStyles.desktopTextMedium(context) + : STextStyles.pageTitleH1(context), ), ), - ); - }, + isDesktop + ? Container( + color: + Theme.of(context).extension()!.background, + height: 1, + ) + : const SizedBox( + height: 12, + ), + RoundedWhiteContainer( + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Text( + "Send from", + style: STextStyles.smallMed12(context), + ), + const SizedBox( + height: 4, + ), + Text( + ref + .watch(walletsChangeNotifierProvider) + .getManager(walletId) + .walletName, + style: STextStyles.itemSubtitle12(context), + ), + ], + ), + ), + isDesktop + ? Container( + color: + Theme.of(context).extension()!.background, + height: 1, + ) + : const SizedBox( + height: 12, + ), + RoundedWhiteContainer( + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Text( + "${trade.exchangeName} address", + style: STextStyles.smallMed12(context), + ), + const SizedBox( + height: 4, + ), + Text( + "${transactionInfo["address"] ?? "ERROR"}", + style: STextStyles.itemSubtitle12(context), + ), + ], + ), + ), + isDesktop + ? Container( + color: + Theme.of(context).extension()!.background, + height: 1, + ) + : const SizedBox( + height: 12, + ), + RoundedWhiteContainer( + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + "Amount", + style: STextStyles.smallMed12(context), + ), + ConditionalParent( + condition: isDesktop, + builder: (child) => Row( + children: [ + child, + Builder(builder: (context) { + final coin = ref.watch( + walletsChangeNotifierProvider.select( + (value) => value.getManager(walletId).coin)); + final price = ref.watch( + priceAnd24hChangeNotifierProvider + .select((value) => value.getPrice(coin))); + final amount = Format.satoshisToAmount( + transactionInfo["recipientAmt"] as int, + coin: coin, + ); + final value = price.item1 * amount; + final currency = ref.watch(prefsChangeNotifierProvider + .select((value) => value.currency)); + + return Text( + " | ${value.toStringAsFixed(Constants.decimalPlacesForCoin(coin))} $currency", + style: + STextStyles.desktopTextExtraExtraSmall(context) + .copyWith( + color: Theme.of(context) + .extension()! + .textSubtitle2, + ), + ); + }) + ], + ), + child: Text( + "${Format.satoshiAmountToPrettyString( + transactionInfo["recipientAmt"] as int, + ref.watch( + localeServiceChangeNotifierProvider + .select((value) => value.locale), + ), + )} ${ref.watch( + managerProvider.select((value) => value.coin), + ).ticker}", + style: STextStyles.itemSubtitle12(context), + textAlign: TextAlign.right, + ), + ), + ], + ), + ), + isDesktop + ? Container( + color: + Theme.of(context).extension()!.background, + height: 1, + ) + : const SizedBox( + height: 12, + ), + RoundedWhiteContainer( + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + "Transaction fee", + style: STextStyles.smallMed12(context), + ), + Text( + "${Format.satoshiAmountToPrettyString( + transactionInfo["fee"] as int, + ref.watch( + localeServiceChangeNotifierProvider + .select((value) => value.locale), + ), + )} ${ref.watch( + managerProvider.select((value) => value.coin), + ).ticker}", + style: STextStyles.itemSubtitle12(context), + textAlign: TextAlign.right, + ), + ], + ), + ), + isDesktop + ? Container( + color: + Theme.of(context).extension()!.background, + height: 1, + ) + : const SizedBox( + height: 12, + ), + RoundedWhiteContainer( + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Text( + "Note", + style: STextStyles.smallMed12(context), + ), + const SizedBox( + height: 4, + ), + Text( + transactionInfo["note"] as String? ?? "", + style: STextStyles.itemSubtitle12(context), + ), + ], + ), + ), + isDesktop + ? Container( + color: + Theme.of(context).extension()!.background, + height: 1, + ) + : const SizedBox( + height: 12, + ), + RoundedWhiteContainer( + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + "Trade ID", + style: STextStyles.smallMed12(context), + ), + Text( + trade.tradeId, + style: STextStyles.itemSubtitle12(context), + textAlign: TextAlign.right, + ), + ], + ), + ), + if (!isDesktop) + const SizedBox( + height: 12, + ), + if (!isDesktop) + RoundedContainer( + color: Theme.of(context) + .extension()! + .snackBarBackSuccess, + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + "Total amount", + style: STextStyles.titleBold12(context).copyWith( + color: Theme.of(context) + .extension()! + .textConfirmTotalAmount, + ), + ), + Text( + "${Format.satoshiAmountToPrettyString( + (transactionInfo["fee"] as int) + + (transactionInfo["recipientAmt"] as int), + ref.watch( + localeServiceChangeNotifierProvider + .select((value) => value.locale), + ), + )} ${ref.watch( + managerProvider.select((value) => value.coin), + ).ticker}", + style: STextStyles.itemSubtitle12(context).copyWith( + color: Theme.of(context) + .extension()! + .textConfirmTotalAmount, + ), + textAlign: TextAlign.right, + ), + ], + ), + ), + if (!isDesktop) + const SizedBox( + height: 16, + ), + if (!isDesktop) const Spacer(), + if (!isDesktop) + PrimaryButton( + label: "Send", + buttonHeight: isDesktop ? ButtonHeight.l : null, + onPressed: _confirmSend, + ), + ], + ), ), ); } diff --git a/lib/pages/exchange_view/send_from_view.dart b/lib/pages/exchange_view/send_from_view.dart index 59675e4e4..7cbf38384 100644 --- a/lib/pages/exchange_view/send_from_view.dart +++ b/lib/pages/exchange_view/send_from_view.dart @@ -7,8 +7,8 @@ import 'package:flutter_svg/svg.dart'; import 'package:stackwallet/models/exchange/response_objects/trade.dart'; import 'package:stackwallet/pages/exchange_view/confirm_change_now_send.dart'; import 'package:stackwallet/pages/home_view/home_view.dart'; -import 'package:stackwallet/pages/pinpad_views/lock_screen_view.dart'; import 'package:stackwallet/pages/send_view/sub_widgets/building_transaction_dialog.dart'; +import 'package:stackwallet/pages_desktop_specific/desktop_exchange/desktop_exchange_view.dart'; import 'package:stackwallet/providers/providers.dart'; import 'package:stackwallet/route_generator.dart'; import 'package:stackwallet/services/coins/firo/firo_wallet.dart'; @@ -30,8 +30,6 @@ import 'package:stackwallet/widgets/expandable.dart'; import 'package:stackwallet/widgets/rounded_white_container.dart'; import 'package:stackwallet/widgets/stack_dialog.dart'; -import '../../pages_desktop_specific/home/my_stack_view/wallet_view/sub_widgets/desktop_auth_send.dart'; - class SendFromView extends ConsumerStatefulWidget { const SendFromView({ Key? key, @@ -39,6 +37,7 @@ class SendFromView extends ConsumerStatefulWidget { required this.trade, required this.amount, required this.address, + this.shouldPopRoot = false, }) : super(key: key); static const String routeName = "/sendFrom"; @@ -47,6 +46,7 @@ class SendFromView extends ConsumerStatefulWidget { final Decimal amount; final String address; final Trade trade; + final bool shouldPopRoot; @override ConsumerState createState() => _SendFromViewState(); @@ -142,7 +142,7 @@ class _SendFromViewState extends ConsumerState { DesktopDialogCloseButton( onPressedOverride: Navigator.of( context, - rootNavigator: false, + rootNavigator: widget.shouldPopRoot, ).pop, ), ], @@ -239,12 +239,23 @@ class _SendFromCardState extends ConsumerState { useSafeArea: false, barrierDismissible: false, builder: (context) { - return BuildingTransactionDialog( - onCancel: () { - wasCancelled = true; + return ConditionalParent( + condition: Util.isDesktop, + builder: (child) => DesktopDialog( + maxWidth: 400, + maxHeight: double.infinity, + child: Padding( + padding: const EdgeInsets.all(32), + child: child, + ), + ), + child: BuildingTransactionDialog( + onCancel: () { + wasCancelled = true; - Navigator.of(context).pop(); - }, + Navigator.of(context).pop(); + }, + ), ); }, ), @@ -290,7 +301,10 @@ class _SendFromCardState extends ConsumerState { // pop building dialog if (mounted) { - Navigator.of(context).pop(); + Navigator.of( + context, + rootNavigator: Util.isDesktop, + ).pop(); } txData["note"] = @@ -304,7 +318,9 @@ class _SendFromCardState extends ConsumerState { builder: (_) => ConfirmChangeNowSendView( transactionInfo: txData, walletId: walletId, - routeOnSuccessName: HomeView.routeName, + routeOnSuccessName: Util.isDesktop + ? DesktopExchangeView.routeName + : HomeView.routeName, trade: trade, shouldSendPublicFiroFunds: shouldSendPublicFiroFunds, ), @@ -401,58 +417,7 @@ class _SendFromCardState extends ConsumerState { ), ), onPressed: () async { - final dynamic unlocked; - - if (Util.isDesktop) { - unlocked = await showDialog( - context: context, - builder: (context) => DesktopDialog( - maxWidth: 580, - maxHeight: double.infinity, - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - Row( - mainAxisAlignment: MainAxisAlignment.end, - children: const [ - DesktopDialogCloseButton(), - ], - ), - const Padding( - padding: EdgeInsets.only( - left: 32, - right: 32, - bottom: 32, - ), - child: DesktopAuthSend(), - ), - ], - ), - ), - ); - } else { - unlocked = await Navigator.push( - context, - RouteGenerator.getRoute( - shouldUseMaterialRoute: - RouteGenerator.useMaterialPageRoute, - builder: (_) => const LockscreenView( - showBackButton: true, - popOnSuccess: true, - routeOnSuccessArguments: true, - routeOnSuccess: "", - biometricsCancelButtonString: "CANCEL", - biometricsLocalizedReason: - "Authenticate to send transaction", - biometricsAuthenticationTitle: "Confirm Transaction", - ), - settings: - const RouteSettings(name: "/confirmsendlockscreen"), - ), - ); - } - - if (unlocked is bool && unlocked && mounted) { + if (mounted) { unawaited( _send( manager, @@ -537,58 +502,7 @@ class _SendFromCardState extends ConsumerState { ), ), onPressed: () async { - final dynamic unlocked; - - if (Util.isDesktop) { - unlocked = await showDialog( - context: context, - builder: (context) => DesktopDialog( - maxWidth: 580, - maxHeight: double.infinity, - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - Row( - mainAxisAlignment: MainAxisAlignment.end, - children: const [ - DesktopDialogCloseButton(), - ], - ), - const Padding( - padding: EdgeInsets.only( - left: 32, - right: 32, - bottom: 32, - ), - child: DesktopAuthSend(), - ), - ], - ), - ), - ); - } else { - unlocked = await Navigator.push( - context, - RouteGenerator.getRoute( - shouldUseMaterialRoute: - RouteGenerator.useMaterialPageRoute, - builder: (_) => const LockscreenView( - showBackButton: true, - popOnSuccess: true, - routeOnSuccessArguments: true, - routeOnSuccess: "", - biometricsCancelButtonString: "CANCEL", - biometricsLocalizedReason: - "Authenticate to send transaction", - biometricsAuthenticationTitle: "Confirm Transaction", - ), - settings: - const RouteSettings(name: "/confirmsendlockscreen"), - ), - ); - } - - if (unlocked is bool && unlocked && mounted) { + if (mounted) { unawaited( _send( manager, @@ -680,57 +594,7 @@ class _SendFromCardState extends ConsumerState { ), ), onPressed: () async { - final dynamic unlocked; - - if (Util.isDesktop) { - unlocked = await showDialog( - context: context, - builder: (context) => DesktopDialog( - maxWidth: 580, - maxHeight: double.infinity, - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - Row( - mainAxisAlignment: MainAxisAlignment.end, - children: const [ - DesktopDialogCloseButton(), - ], - ), - const Padding( - padding: EdgeInsets.only( - left: 32, - right: 32, - bottom: 32, - ), - child: DesktopAuthSend(), - ), - ], - ), - ), - ); - } else { - unlocked = await Navigator.push( - context, - RouteGenerator.getRoute( - shouldUseMaterialRoute: RouteGenerator.useMaterialPageRoute, - builder: (_) => const LockscreenView( - showBackButton: true, - popOnSuccess: true, - routeOnSuccessArguments: true, - routeOnSuccess: "", - biometricsCancelButtonString: "CANCEL", - biometricsLocalizedReason: - "Authenticate to send transaction", - biometricsAuthenticationTitle: "Confirm Transaction", - ), - settings: - const RouteSettings(name: "/confirmsendlockscreen"), - ), - ); - } - - if (unlocked is bool && unlocked && mounted) { + if (mounted) { unawaited( _send(manager), ); diff --git a/lib/pages/exchange_view/trade_details_view.dart b/lib/pages/exchange_view/trade_details_view.dart index f28d1d617..0b7f4b502 100644 --- a/lib/pages/exchange_view/trade_details_view.dart +++ b/lib/pages/exchange_view/trade_details_view.dart @@ -219,7 +219,8 @@ class _TradeDetailsViewState extends ConsumerState { children: children, ), ), - if (isStackCoin(trade.payInCurrency) && + if (!hasTx && + isStackCoin(trade.payInCurrency) && (trade.status == "New" || trade.status == "new" || trade.status == "waiting" || @@ -227,7 +228,8 @@ class _TradeDetailsViewState extends ConsumerState { const SizedBox( height: 32, ), - if (isStackCoin(trade.payInCurrency) && + if (!hasTx && + isStackCoin(trade.payInCurrency) && (trade.status == "New" || trade.status == "new" || trade.status == "waiting" || @@ -1142,6 +1144,7 @@ class _TradeDetailsViewState extends ConsumerState { height: 12, ), if (!isDesktop && + !hasTx && isStackCoin(trade.payInCurrency) && (trade.status == "New" || trade.status == "new" || diff --git a/lib/pages_desktop_specific/desktop_exchange/exchange_steps/subwidgets/desktop_step_2.dart b/lib/pages_desktop_specific/desktop_exchange/exchange_steps/subwidgets/desktop_step_2.dart index 525d561fc..7b793210c 100644 --- a/lib/pages_desktop_specific/desktop_exchange/exchange_steps/subwidgets/desktop_step_2.dart +++ b/lib/pages_desktop_specific/desktop_exchange/exchange_steps/subwidgets/desktop_step_2.dart @@ -51,6 +51,8 @@ class _DesktopStep2State extends ConsumerState { late final FocusNode _toFocusNode; late final FocusNode _refundFocusNode; + bool enableNext = false; + bool isStackCoin(String ticker) { try { coinFromTickerCaseInsensitive(ticker); @@ -60,13 +62,13 @@ class _DesktopStep2State extends ConsumerState { } } - void selectRecipientAddressFromStack() { + void selectRecipientAddressFromStack() async { try { final coin = coinFromTickerCaseInsensitive( model.receiveTicker, ); - showDialog( + final address = await showDialog( context: context, barrierColor: Colors.transparent, builder: (context) => DesktopDialog( @@ -79,27 +81,31 @@ class _DesktopStep2State extends ConsumerState { ), ), ), - ).then((value) async { - if (value is String) { - final manager = - ref.read(walletsChangeNotifierProvider).getManager(value); + ); - _toController.text = manager.walletName; - model.recipientAddress = await manager.currentReceivingAddress; - } - }); + if (address is String) { + final manager = + ref.read(walletsChangeNotifierProvider).getManager(address); + + _toController.text = manager.walletName; + model.recipientAddress = await manager.currentReceivingAddress; + } } catch (e, s) { Logging.instance.log("$e\n$s", level: LogLevel.Info); } + setState(() { + enableNext = + _toController.text.isNotEmpty && _refundController.text.isNotEmpty; + }); } - void selectRefundAddressFromStack() { + void selectRefundAddressFromStack() async { try { final coin = coinFromTickerCaseInsensitive( model.sendTicker, ); - showDialog( + final address = await showDialog( context: context, barrierColor: Colors.transparent, builder: (context) => DesktopDialog( @@ -112,18 +118,21 @@ class _DesktopStep2State extends ConsumerState { ), ), ), - ).then((value) async { - if (value is String) { - final manager = - ref.read(walletsChangeNotifierProvider).getManager(value); + ); + if (address is String) { + final manager = + ref.read(walletsChangeNotifierProvider).getManager(address); - _refundController.text = manager.walletName; - model.refundAddress = await manager.currentReceivingAddress; - } - }); + _refundController.text = manager.walletName; + model.refundAddress = await manager.currentReceivingAddress; + } } catch (e, s) { Logging.instance.log("$e\n$s", level: LogLevel.Info); } + setState(() { + enableNext = + _toController.text.isNotEmpty && _refundController.text.isNotEmpty; + }); } void selectRecipientFromAddressBook() async { @@ -168,7 +177,10 @@ class _DesktopStep2State extends ConsumerState { if (entry != null) { _toController.text = entry.address; model.recipientAddress = entry.address; - setState(() {}); + setState(() { + enableNext = + _toController.text.isNotEmpty && _refundController.text.isNotEmpty; + }); } } @@ -214,7 +226,10 @@ class _DesktopStep2State extends ConsumerState { if (entry != null) { _refundController.text = entry.address; model.refundAddress = entry.address; - setState(() {}); + setState(() { + enableNext = + _toController.text.isNotEmpty && _refundController.text.isNotEmpty; + }); } } @@ -334,7 +349,10 @@ class _DesktopStep2State extends ConsumerState { focusNode: _toFocusNode, style: STextStyles.field(context), onChanged: (value) { - setState(() {}); + setState(() { + enableNext = _toController.text.isNotEmpty && + _refundController.text.isNotEmpty; + }); }, decoration: standardInputDecoration( "Enter the ${model.receiveTicker.toUpperCase()} payout address", @@ -363,7 +381,10 @@ class _DesktopStep2State extends ConsumerState { onTap: () { _toController.text = ""; model.recipientAddress = _toController.text; - setState(() {}); + setState(() { + enableNext = _toController.text.isNotEmpty && + _refundController.text.isNotEmpty; + }); }, child: const XIcon(), ) @@ -378,7 +399,11 @@ class _DesktopStep2State extends ConsumerState { final content = data.text!.trim(); _toController.text = content; model.recipientAddress = _toController.text; - setState(() {}); + setState(() { + enableNext = + _toController.text.isNotEmpty && + _refundController.text.isNotEmpty; + }); } }, child: _toController.text.isEmpty @@ -454,7 +479,10 @@ class _DesktopStep2State extends ConsumerState { focusNode: _refundFocusNode, style: STextStyles.field(context), onChanged: (value) { - setState(() {}); + setState(() { + enableNext = _toController.text.isNotEmpty && + _refundController.text.isNotEmpty; + }); }, decoration: standardInputDecoration( "Enter ${model.sendTicker.toUpperCase()} refund address", @@ -484,7 +512,10 @@ class _DesktopStep2State extends ConsumerState { _refundController.text = ""; model.refundAddress = _refundController.text; - setState(() {}); + setState(() { + enableNext = _toController.text.isNotEmpty && + _refundController.text.isNotEmpty; + }); }, child: const XIcon(), ) @@ -501,7 +532,11 @@ class _DesktopStep2State extends ConsumerState { _refundController.text = content; model.refundAddress = _refundController.text; - setState(() {}); + setState(() { + enableNext = + _toController.text.isNotEmpty && + _refundController.text.isNotEmpty; + }); } }, child: _refundController.text.isEmpty @@ -552,6 +587,7 @@ class _DesktopStep2State extends ConsumerState { Expanded( child: PrimaryButton( label: "Next", + enabled: enableNext, buttonHeight: ButtonHeight.l, onPressed: () async { await showDialog( diff --git a/lib/pages_desktop_specific/desktop_exchange/exchange_steps/subwidgets/desktop_step_4.dart b/lib/pages_desktop_specific/desktop_exchange/exchange_steps/subwidgets/desktop_step_4.dart index 7747f570f..c86713a76 100644 --- a/lib/pages_desktop_specific/desktop_exchange/exchange_steps/subwidgets/desktop_step_4.dart +++ b/lib/pages_desktop_specific/desktop_exchange/exchange_steps/subwidgets/desktop_step_4.dart @@ -1,11 +1,14 @@ import 'dart:async'; +import 'package:decimal/decimal.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:qr_flutter/qr_flutter.dart'; import 'package:stackwallet/models/exchange/incomplete_exchange.dart'; +import 'package:stackwallet/pages/exchange_view/send_from_view.dart'; import 'package:stackwallet/pages_desktop_specific/desktop_exchange/exchange_steps/subwidgets/desktop_step_item.dart'; import 'package:stackwallet/providers/providers.dart'; +import 'package:stackwallet/route_generator.dart'; import 'package:stackwallet/utilities/enums/coin_enum.dart'; import 'package:stackwallet/utilities/text_styles.dart'; import 'package:stackwallet/utilities/theme/stack_colors.dart'; @@ -199,7 +202,38 @@ class _DesktopStep4State extends ConsumerState { child: SecondaryButton( label: "Send from Stack Wallet", buttonHeight: ButtonHeight.l, - onPressed: Navigator.of(context).pop, + onPressed: () { + final trade = model.trade!; + final amount = Decimal.parse(trade.payInAmount); + final address = trade.payInAddress; + + final coin = + coinFromTickerCaseInsensitive(trade.payInCurrency); + + showDialog( + context: context, + builder: (context) => Navigator( + initialRoute: SendFromView.routeName, + onGenerateRoute: RouteGenerator.generateRoute, + onGenerateInitialRoutes: (_, __) { + return [ + FadePageRoute( + SendFromView( + coin: coin, + trade: trade, + amount: amount, + address: address, + shouldPopRoot: true, + ), + const RouteSettings( + name: SendFromView.routeName, + ), + ), + ]; + }, + ), + ); + }, ), ), const SizedBox(