diff --git a/lib/models/exchange/incomplete_exchange.dart b/lib/models/exchange/incomplete_exchange.dart index 58219dc5d..46e7ffd68 100644 --- a/lib/models/exchange/incomplete_exchange.dart +++ b/lib/models/exchange/incomplete_exchange.dart @@ -1,8 +1,9 @@ import 'package:decimal/decimal.dart'; +import 'package:flutter/foundation.dart'; import 'package:stackwallet/models/exchange/response_objects/trade.dart'; import 'package:stackwallet/pages/exchange_view/sub_widgets/exchange_rate_sheet.dart'; -class IncompleteExchangeModel { +class IncompleteExchangeModel extends ChangeNotifier { final String sendTicker; final String receiveTicker; @@ -15,12 +16,49 @@ class IncompleteExchangeModel { final bool reversed; - String? recipientAddress; - String? refundAddress; + String? _recipientAddress; - String? rateId; + String? get recipientAddress => _recipientAddress; - Trade? trade; + set recipientAddress(String? recipientAddress) { + if (_recipientAddress != recipientAddress) { + _recipientAddress = recipientAddress; + notifyListeners(); + } + } + + String? _refundAddress; + + String? get refundAddress => _refundAddress; + + set refundAddress(String? refundAddress) { + if (_refundAddress != refundAddress) { + _refundAddress = refundAddress; + notifyListeners(); + } + } + + String? _rateId; + + String? get rateId => _rateId; + + set rateId(String? rateId) { + if (_rateId != rateId) { + _rateId = rateId; + notifyListeners(); + } + } + + Trade? _trade; + + Trade? get trade => _trade; + + set trade(Trade? trade) { + if (_trade != trade) { + _trade = trade; + notifyListeners(); + } + } IncompleteExchangeModel({ required this.sendTicker, @@ -30,6 +68,6 @@ class IncompleteExchangeModel { required this.receiveAmount, required this.rateType, required this.reversed, - this.rateId, - }); + String? rateId, + }) : _rateId = rateId; } diff --git a/lib/pages/exchange_view/exchange_form.dart b/lib/pages/exchange_view/exchange_form.dart index 5a8745474..b25a6fa3f 100644 --- a/lib/pages/exchange_view/exchange_form.dart +++ b/lib/pages/exchange_view/exchange_form.dart @@ -18,8 +18,6 @@ import 'package:stackwallet/pages/exchange_view/sub_widgets/exchange_provider_op import 'package:stackwallet/pages/exchange_view/sub_widgets/exchange_rate_sheet.dart'; import 'package:stackwallet/pages/exchange_view/sub_widgets/rate_type_toggle.dart'; import 'package:stackwallet/pages_desktop_specific/desktop_exchange/exchange_steps/step_scaffold.dart'; -import 'package:stackwallet/pages_desktop_specific/desktop_exchange/exchange_steps/subwidgets/desktop_step_1.dart'; -import 'package:stackwallet/pages_desktop_specific/desktop_exchange/exchange_steps/subwidgets/desktop_step_2.dart'; import 'package:stackwallet/providers/providers.dart'; import 'package:stackwallet/services/exchange/change_now/change_now_exchange.dart'; import 'package:stackwallet/services/exchange/simpleswap/simpleswap_exchange.dart'; @@ -1022,19 +1020,16 @@ class _ExchangeFormState extends ConsumerState { ref.read(exchangeSendFromWalletIdStateProvider.state).state = Tuple2(walletId!, coin!); if (isDesktop) { + ref.read(ssss.state).state = model; await showDialog( context: context, barrierDismissible: false, builder: (context) { - return DesktopDialog( + return const DesktopDialog( maxWidth: 720, maxHeight: double.infinity, child: StepScaffold( - step: 2, - model: model, - body: DesktopStep2( - model: model, - ), + initialStep: 2, ), ); }, @@ -1051,19 +1046,16 @@ class _ExchangeFormState extends ConsumerState { ref.read(exchangeSendFromWalletIdStateProvider.state).state = null; if (isDesktop) { + ref.read(ssss.state).state = model; await showDialog( context: context, barrierDismissible: false, builder: (context) { - return DesktopDialog( + return const DesktopDialog( maxWidth: 720, maxHeight: double.infinity, child: StepScaffold( - step: 1, - model: model, - body: DesktopStep1( - model: model, - ), + initialStep: 1, ), ); }, diff --git a/lib/pages_desktop_specific/desktop_exchange/exchange_steps/step_scaffold.dart b/lib/pages_desktop_specific/desktop_exchange/exchange_steps/step_scaffold.dart index 1ac1df764..9d71b9cb7 100644 --- a/lib/pages_desktop_specific/desktop_exchange/exchange_steps/step_scaffold.dart +++ b/lib/pages_desktop_specific/desktop_exchange/exchange_steps/step_scaffold.dart @@ -1,39 +1,190 @@ -import 'package:flutter/material.dart'; -import 'package:stackwallet/models/exchange/incomplete_exchange.dart'; -import 'package:stackwallet/pages_desktop_specific/desktop_exchange/subwidgets/desktop_exchange_steps_indicator.dart'; -import 'package:stackwallet/utilities/text_styles.dart'; -import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart'; -import 'package:stackwallet/widgets/desktop/desktop_dialog_close_button.dart'; +import 'dart:async'; -class StepScaffold extends StatefulWidget { +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/models/exchange/response_objects/trade.dart'; +import 'package:stackwallet/pages/exchange_view/sub_widgets/exchange_rate_sheet.dart'; +import 'package:stackwallet/pages_desktop_specific/desktop_exchange/exchange_steps/subwidgets/desktop_step_1.dart'; +import 'package:stackwallet/pages_desktop_specific/desktop_exchange/exchange_steps/subwidgets/desktop_step_2.dart'; +import 'package:stackwallet/pages_desktop_specific/desktop_exchange/exchange_steps/subwidgets/desktop_step_3.dart'; +import 'package:stackwallet/pages_desktop_specific/desktop_exchange/exchange_steps/subwidgets/desktop_step_4.dart'; +import 'package:stackwallet/pages_desktop_specific/desktop_exchange/subwidgets/desktop_exchange_steps_indicator.dart'; +import 'package:stackwallet/providers/exchange/exchange_provider.dart'; +import 'package:stackwallet/providers/global/trades_service_provider.dart'; +import 'package:stackwallet/services/exchange/exchange_response.dart'; +import 'package:stackwallet/services/notifications_api.dart'; +import 'package:stackwallet/utilities/assets.dart'; +import 'package:stackwallet/utilities/text_styles.dart'; +import 'package:stackwallet/utilities/theme/stack_colors.dart'; +import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart'; +import 'package:stackwallet/widgets/custom_loading_overlay.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/desktop/simple_desktop_dialog.dart'; +import 'package:stackwallet/widgets/fade_stack.dart'; + +final ssss = StateProvider((_) => null); + +final desktopExchangeModelProvider = + ChangeNotifierProvider( + (ref) => ref.watch(ssss.state).state); + +class StepScaffold extends ConsumerStatefulWidget { const StepScaffold({ Key? key, - required this.body, - required this.step, - required this.model, + required this.initialStep, }) : super(key: key); - final Widget body; - final int step; - final IncompleteExchangeModel model; + final int initialStep; @override - State createState() => _StepScaffoldState(); + ConsumerState createState() => _StepScaffoldState(); } -class _StepScaffoldState extends State { - int currentStep = 0; - late final IncompleteExchangeModel model; +class _StepScaffoldState extends ConsumerState { + int currentStep = 1; + bool enableNext = false; + + late final Duration duration; + + void updateEnableNext(bool enableNext) { + if (enableNext != this.enableNext) { + setState(() => this.enableNext = enableNext); + } + } + + Future createTrade() async { + unawaited( + showDialog( + context: context, + barrierDismissible: false, + builder: (_) => WillPopScope( + onWillPop: () async => false, + child: Container( + color: Theme.of(context) + .extension()! + .overlay + .withOpacity(0.6), + child: const CustomLoadingOverlay( + message: "Creating a trade", + eventBus: null, + ), + ), + ), + ), + ); + + final ExchangeResponse response = await ref + .read(exchangeProvider) + .createTrade( + from: ref.read(desktopExchangeModelProvider)!.sendTicker, + to: ref.read(desktopExchangeModelProvider)!.receiveTicker, + fixedRate: ref.read(desktopExchangeModelProvider)!.rateType != + ExchangeRateType.estimated, + amount: ref.read(desktopExchangeModelProvider)!.reversed + ? ref.read(desktopExchangeModelProvider)!.receiveAmount + : ref.read(desktopExchangeModelProvider)!.sendAmount, + addressTo: ref.read(desktopExchangeModelProvider)!.recipientAddress!, + extraId: null, + addressRefund: ref.read(desktopExchangeModelProvider)!.refundAddress!, + refundExtraId: "", + rateId: ref.read(desktopExchangeModelProvider)!.rateId, + reversed: ref.read(desktopExchangeModelProvider)!.reversed, + ); + + if (response.value == null) { + if (mounted) { + Navigator.of(context).pop(); + } + + unawaited( + showDialog( + context: context, + barrierDismissible: true, + builder: (_) => SimpleDesktopDialog( + title: "Failed to create trade", + message: response.exception?.toString() ?? ""), + ), + ); + return false; + } + + // save trade to hive + await ref.read(tradesServiceProvider).add( + trade: response.value!, + shouldNotifyListeners: true, + ); + + String status = response.value!.status; + + ref.read(desktopExchangeModelProvider)!.trade = response.value!; + + // extra info if status is waiting + if (status == "Waiting") { + status += " for deposit"; + } + + if (mounted) { + Navigator.of(context).pop(); + } + + unawaited( + NotificationApi.showNotification( + changeNowId: ref.read(desktopExchangeModelProvider)!.trade!.tradeId, + title: status, + body: + "Trade ID ${ref.read(desktopExchangeModelProvider)!.trade!.tradeId}", + walletId: "", + iconAssetName: Assets.svg.arrowRotate, + date: ref.read(desktopExchangeModelProvider)!.trade!.timestamp, + shouldWatchForUpdates: true, + coinName: "coinName", + ), + ); + + return true; + // if (mounted) { + // unawaited( + // showDialog( + // context: context, + // barrierColor: Colors.transparent, + // barrierDismissible: false, + // builder: (context) { + // return DesktopDialog( + // maxWidth: 720, + // maxHeight: double.infinity, + // child: StepScaffold( + // initialStep: 4, + // ), + // ); + // }, + // ), + // ); + // } + } + + void onBack() { + if (currentStep > 1 && currentStep < 4) { + setState(() => currentStep = currentStep - 1); + } else if (currentStep == 1) { + Navigator.of(context, rootNavigator: true).pop(); + } + } @override void initState() { - currentStep = widget.step; - model = widget.model; + duration = const Duration(milliseconds: 250); + currentStep = widget.initialStep; super.initState(); } @override Widget build(BuildContext context) { + final model = ref.watch(desktopExchangeModelProvider); return Column( mainAxisAlignment: MainAxisAlignment.start, children: [ @@ -43,15 +194,16 @@ class _StepScaffoldState extends State { Row( children: [ currentStep != 4 - ? const AppBarBackButton( + ? AppBarBackButton( isCompact: true, iconSize: 23, + onPressed: onBack, ) : const SizedBox( width: 32, ), Text( - "Exchange ${model.sendTicker.toUpperCase()} to ${model.receiveTicker.toUpperCase()}", + "Exchange ${model?.sendTicker.toUpperCase()} to ${model?.receiveTicker.toUpperCase()}", style: STextStyles.desktopH3(context), ), ], @@ -60,9 +212,6 @@ class _StepScaffoldState extends State { DesktopDialogCloseButton( onPressedOverride: () { Navigator.of(context, rootNavigator: true).pop(); - Navigator.of(context, rootNavigator: true).pop(); - Navigator.of(context, rootNavigator: true).pop(); - Navigator.of(context, rootNavigator: true).pop(); }, ), ], @@ -85,7 +234,139 @@ class _StepScaffoldState extends State { padding: const EdgeInsets.symmetric( horizontal: 32, ), - child: widget.body, + child: FadeStack( + index: currentStep - 1, + children: [ + const DesktopStep1(), + DesktopStep2( + enableNextChanged: updateEnableNext, + ), + const DesktopStep3(), + const DesktopStep4(), + ], + ), + ), + Padding( + padding: const EdgeInsets.only( + top: 20, + left: 32, + right: 32, + bottom: 32, + ), + child: Row( + children: [ + Expanded( + child: AnimatedCrossFade( + duration: const Duration(milliseconds: 250), + crossFadeState: currentStep == 4 + ? CrossFadeState.showSecond + : CrossFadeState.showFirst, + firstChild: SecondaryButton( + label: "Back", + buttonHeight: ButtonHeight.l, + onPressed: onBack, + ), + secondChild: SecondaryButton( + label: "Send from Stack Wallet", + buttonHeight: ButtonHeight.l, + onPressed: onBack, + ), + ), + ), + const SizedBox( + width: 16, + ), + Expanded( + child: AnimatedCrossFade( + duration: const Duration(milliseconds: 250), + crossFadeState: currentStep == 4 + ? CrossFadeState.showSecond + : CrossFadeState.showFirst, + firstChild: AnimatedCrossFade( + duration: const Duration(milliseconds: 250), + crossFadeState: currentStep == 3 + ? CrossFadeState.showSecond + : CrossFadeState.showFirst, + firstChild: PrimaryButton( + label: "Next", + enabled: currentStep != 2 ? true : enableNext, + buttonHeight: ButtonHeight.l, + onPressed: () async { + setState(() => currentStep = currentStep + 1); + }, + ), + secondChild: PrimaryButton( + label: "Confirm", + enabled: currentStep != 2 ? true : enableNext, + buttonHeight: ButtonHeight.l, + onPressed: () async { + if (currentStep == 3) { + final success = await createTrade(); + if (!success) { + return; + } + } + setState(() => currentStep = currentStep + 1); + }, + ), + ), + secondChild: PrimaryButton( + label: "Show QR code", + enabled: currentStep != 2 ? true : enableNext, + buttonHeight: ButtonHeight.l, + onPressed: () { + showDialog( + context: context, + barrierColor: Colors.transparent, + barrierDismissible: true, + builder: (_) { + return DesktopDialog( + maxHeight: 720, + maxWidth: 720, + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Text( + "Send ${ref.watch(desktopExchangeModelProvider.select((value) => value!.sendAmount.toStringAsFixed(8)))} ${ref.watch(desktopExchangeModelProvider.select((value) => value!.sendTicker))} to this address", + style: STextStyles.desktopH3(context), + ), + const SizedBox( + height: 48, + ), + Center( + child: QrImage( + // TODO: grab coin uri scheme from somewhere + // data: "${coin.uriScheme}:$receivingAddress", + data: ref.watch(desktopExchangeModelProvider + .select((value) => + value!.trade!.payInAddress)), + size: 290, + foregroundColor: Theme.of(context) + .extension()! + .accentColorDark, + ), + ), + const SizedBox( + height: 48, + ), + SecondaryButton( + label: "Cancel", + width: 310, + buttonHeight: ButtonHeight.l, + onPressed: Navigator.of(context).pop, + ), + ], + ), + ); + }, + ); + }, + ), + ), + ), + ], + ), ), ], ); diff --git a/lib/pages_desktop_specific/desktop_exchange/exchange_steps/subwidgets/desktop_step_1.dart b/lib/pages_desktop_specific/desktop_exchange/exchange_steps/subwidgets/desktop_step_1.dart index 031dc0649..4ec96f95d 100644 --- a/lib/pages_desktop_specific/desktop_exchange/exchange_steps/subwidgets/desktop_step_1.dart +++ b/lib/pages_desktop_specific/desktop_exchange/exchange_steps/subwidgets/desktop_step_1.dart @@ -1,26 +1,18 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:stackwallet/models/exchange/incomplete_exchange.dart'; import 'package:stackwallet/pages/exchange_view/sub_widgets/exchange_rate_sheet.dart'; import 'package:stackwallet/pages_desktop_specific/desktop_exchange/exchange_steps/step_scaffold.dart'; -import 'package:stackwallet/pages_desktop_specific/desktop_exchange/exchange_steps/subwidgets/desktop_step_2.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/utilities/text_styles.dart'; import 'package:stackwallet/utilities/theme/stack_colors.dart'; -import 'package:stackwallet/widgets/desktop/desktop_dialog.dart'; -import 'package:stackwallet/widgets/desktop/primary_button.dart'; -import 'package:stackwallet/widgets/desktop/secondary_button.dart'; import 'package:stackwallet/widgets/rounded_white_container.dart'; class DesktopStep1 extends ConsumerWidget { const DesktopStep1({ Key? key, - required this.model, }) : super(key: key); - final IncompleteExchangeModel model; - @override Widget build(BuildContext context, WidgetRef ref) { return Column( @@ -55,7 +47,7 @@ class DesktopStep1 extends ConsumerWidget { DesktopStepItem( label: "You send", value: - "${model.sendAmount.toStringAsFixed(8)} ${model.sendTicker.toUpperCase()}", + "${ref.watch(desktopExchangeModelProvider.select((value) => value!.sendAmount.toStringAsFixed(8)))} ${ref.watch(desktopExchangeModelProvider.select((value) => value!.sendTicker.toUpperCase()))}", ), Container( height: 1, @@ -64,63 +56,20 @@ class DesktopStep1 extends ConsumerWidget { DesktopStepItem( label: "You receive", value: - "~${model.receiveAmount.toStringAsFixed(8)} ${model.receiveTicker.toUpperCase()}", + "~${ref.watch(desktopExchangeModelProvider.select((value) => value!.receiveAmount.toStringAsFixed(8)))} ${ref.watch(desktopExchangeModelProvider.select((value) => value!.receiveTicker.toUpperCase()))}", ), Container( height: 1, color: Theme.of(context).extension()!.background, ), DesktopStepItem( - label: model.rateType == ExchangeRateType.estimated + label: ref.watch(desktopExchangeModelProvider + .select((value) => value!.rateType)) == + ExchangeRateType.estimated ? "Estimated rate" : "Fixed rate", - value: model.rateInfo, - ), - ], - ), - ), - Padding( - padding: const EdgeInsets.only( - top: 20, - bottom: 32, - ), - child: Row( - children: [ - Expanded( - child: SecondaryButton( - label: "Back", - buttonHeight: ButtonHeight.l, - onPressed: Navigator.of(context).pop, - ), - ), - const SizedBox( - width: 16, - ), - Expanded( - child: PrimaryButton( - label: "Next", - buttonHeight: ButtonHeight.l, - onPressed: () async { - await showDialog( - context: context, - barrierColor: Colors.transparent, - barrierDismissible: false, - builder: (context) { - return DesktopDialog( - maxWidth: 720, - maxHeight: double.infinity, - child: StepScaffold( - step: 2, - model: model, - body: DesktopStep2( - model: model, - ), - ), - ); - }, - ); - }, - ), + value: ref.watch(desktopExchangeModelProvider + .select((value) => value!.rateInfo)), ), ], ), 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 7b793210c..66a487013 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 @@ -2,9 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:stackwallet/models/contact_address_entry.dart'; -import 'package:stackwallet/models/exchange/incomplete_exchange.dart'; import 'package:stackwallet/pages_desktop_specific/desktop_exchange/exchange_steps/step_scaffold.dart'; -import 'package:stackwallet/pages_desktop_specific/desktop_exchange/exchange_steps/subwidgets/desktop_step_3.dart'; import 'package:stackwallet/pages_desktop_specific/desktop_exchange/subwidgets/desktop_choose_from_stack.dart'; import 'package:stackwallet/pages_desktop_specific/home/my_stack_view/wallet_view/sub_widgets/address_book_address_chooser/address_book_address_chooser.dart'; import 'package:stackwallet/providers/exchange/exchange_send_from_wallet_id_provider.dart'; @@ -18,31 +16,29 @@ import 'package:stackwallet/utilities/theme/stack_colors.dart'; import 'package:stackwallet/widgets/custom_buttons/blue_text_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/icon_widgets/addressbook_icon.dart'; import 'package:stackwallet/widgets/icon_widgets/clipboard_icon.dart'; import 'package:stackwallet/widgets/icon_widgets/x_icon.dart'; import 'package:stackwallet/widgets/rounded_white_container.dart'; import 'package:stackwallet/widgets/stack_text_field.dart'; import 'package:stackwallet/widgets/textfield_icon_button.dart'; +import 'package:tuple/tuple.dart'; class DesktopStep2 extends ConsumerStatefulWidget { const DesktopStep2({ Key? key, - required this.model, + required this.enableNextChanged, this.clipboard = const ClipboardWrapper(), }) : super(key: key); - final IncompleteExchangeModel model; final ClipboardInterface clipboard; + final void Function(bool) enableNextChanged; @override ConsumerState createState() => _DesktopStep2State(); } class _DesktopStep2State extends ConsumerState { - late final IncompleteExchangeModel model; late final ClipboardInterface clipboard; late final TextEditingController _toController; @@ -51,8 +47,6 @@ class _DesktopStep2State extends ConsumerState { late final FocusNode _toFocusNode; late final FocusNode _refundFocusNode; - bool enableNext = false; - bool isStackCoin(String ticker) { try { coinFromTickerCaseInsensitive(ticker); @@ -65,10 +59,10 @@ class _DesktopStep2State extends ConsumerState { void selectRecipientAddressFromStack() async { try { final coin = coinFromTickerCaseInsensitive( - model.receiveTicker, + ref.read(desktopExchangeModelProvider)!.receiveTicker, ); - final address = await showDialog( + final info = await showDialog?>( context: context, barrierColor: Colors.transparent, builder: (context) => DesktopDialog( @@ -83,29 +77,25 @@ class _DesktopStep2State extends ConsumerState { ), ); - if (address is String) { - final manager = - ref.read(walletsChangeNotifierProvider).getManager(address); - - _toController.text = manager.walletName; - model.recipientAddress = await manager.currentReceivingAddress; + if (info is Tuple2) { + _toController.text = info.item1; + ref.read(desktopExchangeModelProvider)!.recipientAddress = info.item2; } } catch (e, s) { Logging.instance.log("$e\n$s", level: LogLevel.Info); } - setState(() { - enableNext = - _toController.text.isNotEmpty && _refundController.text.isNotEmpty; - }); + + widget.enableNextChanged.call( + _toController.text.isNotEmpty && _refundController.text.isNotEmpty); } void selectRefundAddressFromStack() async { try { final coin = coinFromTickerCaseInsensitive( - model.sendTicker, + ref.read(desktopExchangeModelProvider)!.sendTicker, ); - final address = await showDialog( + final info = await showDialog?>( context: context, barrierColor: Colors.transparent, builder: (context) => DesktopDialog( @@ -119,25 +109,20 @@ class _DesktopStep2State extends ConsumerState { ), ), ); - if (address is String) { - final manager = - ref.read(walletsChangeNotifierProvider).getManager(address); - - _refundController.text = manager.walletName; - model.refundAddress = await manager.currentReceivingAddress; + if (info is Tuple2) { + _refundController.text = info.item1; + ref.read(desktopExchangeModelProvider)!.refundAddress = info.item2; } } catch (e, s) { Logging.instance.log("$e\n$s", level: LogLevel.Info); } - setState(() { - enableNext = - _toController.text.isNotEmpty && _refundController.text.isNotEmpty; - }); + widget.enableNextChanged.call( + _toController.text.isNotEmpty && _refundController.text.isNotEmpty); } void selectRecipientFromAddressBook() async { final coin = coinFromTickerCaseInsensitive( - model.receiveTicker, + ref.read(desktopExchangeModelProvider)!.receiveTicker, ); final entry = await showDialog( @@ -176,17 +161,15 @@ class _DesktopStep2State extends ConsumerState { if (entry != null) { _toController.text = entry.address; - model.recipientAddress = entry.address; - setState(() { - enableNext = - _toController.text.isNotEmpty && _refundController.text.isNotEmpty; - }); + ref.read(desktopExchangeModelProvider)!.recipientAddress = entry.address; + widget.enableNextChanged.call( + _toController.text.isNotEmpty && _refundController.text.isNotEmpty); } } void selectRefundFromAddressBook() async { final coin = coinFromTickerCaseInsensitive( - model.sendTicker, + ref.read(desktopExchangeModelProvider)!.sendTicker, ); final entry = await showDialog( @@ -225,17 +208,14 @@ class _DesktopStep2State extends ConsumerState { if (entry != null) { _refundController.text = entry.address; - model.refundAddress = entry.address; - setState(() { - enableNext = - _toController.text.isNotEmpty && _refundController.text.isNotEmpty; - }); + ref.read(desktopExchangeModelProvider)!.refundAddress = entry.address; + widget.enableNextChanged.call( + _toController.text.isNotEmpty && _refundController.text.isNotEmpty); } } @override void initState() { - model = widget.model; clipboard = widget.clipboard; _toController = TextEditingController(); @@ -246,7 +226,7 @@ class _DesktopStep2State extends ConsumerState { final tuple = ref.read(exchangeSendFromWalletIdStateProvider.state).state; if (tuple != null) { - if (model.receiveTicker.toLowerCase() == + if (ref.read(desktopExchangeModelProvider)!.receiveTicker.toLowerCase() == tuple.item2.ticker.toLowerCase()) { ref .read(walletsChangeNotifierProvider) @@ -254,10 +234,11 @@ class _DesktopStep2State extends ConsumerState { .currentReceivingAddress .then((value) { _toController.text = value; - model.recipientAddress = _toController.text; + ref.read(desktopExchangeModelProvider)!.recipientAddress = + _toController.text; }); } else { - if (model.sendTicker.toUpperCase() == + if (ref.read(desktopExchangeModelProvider)!.sendTicker.toUpperCase() == tuple.item2.ticker.toUpperCase()) { ref .read(walletsChangeNotifierProvider) @@ -265,7 +246,8 @@ class _DesktopStep2State extends ConsumerState { .currentReceivingAddress .then((value) { _refundController.text = value; - model.refundAddress = _refundController.text; + ref.read(desktopExchangeModelProvider)!.refundAddress = + _refundController.text; }); } } @@ -316,7 +298,8 @@ class _DesktopStep2State extends ConsumerState { .extension()! .textFieldActiveSearchIconRight), ), - if (isStackCoin(model.receiveTicker)) + if (isStackCoin(ref.watch(desktopExchangeModelProvider + .select((value) => value!.receiveTicker)))) BlueTextButton( text: "Choose from stack", onTap: selectRecipientAddressFromStack, @@ -349,13 +332,11 @@ class _DesktopStep2State extends ConsumerState { focusNode: _toFocusNode, style: STextStyles.field(context), onChanged: (value) { - setState(() { - enableNext = _toController.text.isNotEmpty && - _refundController.text.isNotEmpty; - }); + widget.enableNextChanged.call(_toController.text.isNotEmpty && + _refundController.text.isNotEmpty); }, decoration: standardInputDecoration( - "Enter the ${model.receiveTicker.toUpperCase()} payout address", + "Enter the ${ref.watch(desktopExchangeModelProvider.select((value) => value!.receiveTicker.toUpperCase()))} payout address", _toFocusNode, context, desktopMed: true, @@ -380,11 +361,12 @@ class _DesktopStep2State extends ConsumerState { "sendViewClearAddressFieldButtonKey"), onTap: () { _toController.text = ""; - model.recipientAddress = _toController.text; - setState(() { - enableNext = _toController.text.isNotEmpty && - _refundController.text.isNotEmpty; - }); + ref + .read(desktopExchangeModelProvider)! + .recipientAddress = _toController.text; + widget.enableNextChanged.call( + _toController.text.isNotEmpty && + _refundController.text.isNotEmpty); }, child: const XIcon(), ) @@ -398,12 +380,12 @@ class _DesktopStep2State extends ConsumerState { data!.text!.isNotEmpty) { final content = data.text!.trim(); _toController.text = content; - model.recipientAddress = _toController.text; - setState(() { - enableNext = - _toController.text.isNotEmpty && - _refundController.text.isNotEmpty; - }); + ref + .read(desktopExchangeModelProvider)! + .recipientAddress = _toController.text; + widget.enableNextChanged.call( + _toController.text.isNotEmpty && + _refundController.text.isNotEmpty); } }, child: _toController.text.isEmpty @@ -411,7 +393,8 @@ class _DesktopStep2State extends ConsumerState { : const XIcon(), ), if (_toController.text.isEmpty && - isStackCoin(model.receiveTicker)) + isStackCoin(ref.watch(desktopExchangeModelProvider + .select((value) => value!.receiveTicker)))) TextFieldIconButton( key: const Key("sendViewAddressBookButtonKey"), onTap: selectRecipientFromAddressBook, @@ -430,7 +413,7 @@ class _DesktopStep2State extends ConsumerState { RoundedWhiteContainer( borderColor: Theme.of(context).extension()!.background, child: Text( - "This is the wallet where your ${model.receiveTicker.toUpperCase()} will be sent to.", + "This is the wallet where your ${ref.watch(desktopExchangeModelProvider.select((value) => value!.receiveTicker.toUpperCase()))} will be sent to.", style: STextStyles.desktopTextExtraExtraSmall(context), ), ), @@ -447,7 +430,8 @@ class _DesktopStep2State extends ConsumerState { .extension()! .textFieldActiveSearchIconRight), ), - if (isStackCoin(model.sendTicker)) + if (isStackCoin(ref.watch(desktopExchangeModelProvider + .select((value) => value!.sendTicker)))) BlueTextButton( text: "Choose from stack", onTap: selectRefundAddressFromStack, @@ -479,13 +463,11 @@ class _DesktopStep2State extends ConsumerState { focusNode: _refundFocusNode, style: STextStyles.field(context), onChanged: (value) { - setState(() { - enableNext = _toController.text.isNotEmpty && - _refundController.text.isNotEmpty; - }); + widget.enableNextChanged.call(_toController.text.isNotEmpty && + _refundController.text.isNotEmpty); }, decoration: standardInputDecoration( - "Enter ${model.sendTicker.toUpperCase()} refund address", + "Enter ${ref.watch(desktopExchangeModelProvider.select((value) => value!.sendTicker.toUpperCase()))} refund address", _refundFocusNode, context, desktopMed: true, @@ -510,12 +492,13 @@ class _DesktopStep2State extends ConsumerState { "sendViewClearAddressFieldButtonKey"), onTap: () { _refundController.text = ""; - model.refundAddress = _refundController.text; + ref + .read(desktopExchangeModelProvider)! + .refundAddress = _refundController.text; - setState(() { - enableNext = _toController.text.isNotEmpty && - _refundController.text.isNotEmpty; - }); + widget.enableNextChanged.call( + _toController.text.isNotEmpty && + _refundController.text.isNotEmpty); }, child: const XIcon(), ) @@ -530,13 +513,13 @@ class _DesktopStep2State extends ConsumerState { final content = data.text!.trim(); _refundController.text = content; - model.refundAddress = _refundController.text; + ref + .read(desktopExchangeModelProvider)! + .refundAddress = _refundController.text; - setState(() { - enableNext = - _toController.text.isNotEmpty && - _refundController.text.isNotEmpty; - }); + widget.enableNextChanged.call( + _toController.text.isNotEmpty && + _refundController.text.isNotEmpty); } }, child: _refundController.text.isEmpty @@ -544,7 +527,8 @@ class _DesktopStep2State extends ConsumerState { : const XIcon(), ), if (_refundController.text.isEmpty && - isStackCoin(model.sendTicker)) + isStackCoin(ref.watch(desktopExchangeModelProvider + .select((value) => value!.sendTicker)))) TextFieldIconButton( key: const Key("sendViewAddressBookButtonKey"), onTap: selectRefundFromAddressBook, @@ -567,53 +551,6 @@ class _DesktopStep2State extends ConsumerState { style: STextStyles.desktopTextExtraExtraSmall(context), ), ), - Padding( - padding: const EdgeInsets.only( - top: 20, - bottom: 32, - ), - child: Row( - children: [ - Expanded( - child: SecondaryButton( - label: "Back", - buttonHeight: ButtonHeight.l, - onPressed: Navigator.of(context).pop, - ), - ), - const SizedBox( - width: 16, - ), - Expanded( - child: PrimaryButton( - label: "Next", - enabled: enableNext, - buttonHeight: ButtonHeight.l, - onPressed: () async { - await showDialog( - context: context, - barrierColor: Colors.transparent, - barrierDismissible: false, - builder: (context) { - return DesktopDialog( - maxWidth: 720, - maxHeight: double.infinity, - child: StepScaffold( - step: 3, - model: model, - body: DesktopStep3( - model: model, - ), - ), - ); - }, - ); - }, - ), - ), - ], - ), - ), ], ); } diff --git a/lib/pages_desktop_specific/desktop_exchange/exchange_steps/subwidgets/desktop_step_3.dart b/lib/pages_desktop_specific/desktop_exchange/exchange_steps/subwidgets/desktop_step_3.dart index 284416545..714cee971 100644 --- a/lib/pages_desktop_specific/desktop_exchange/exchange_steps/subwidgets/desktop_step_3.dart +++ b/lib/pages_desktop_specific/desktop_exchange/exchange_steps/subwidgets/desktop_step_3.dart @@ -1,157 +1,23 @@ -import 'dart:async'; - import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:stackwallet/models/exchange/incomplete_exchange.dart'; -import 'package:stackwallet/models/exchange/response_objects/trade.dart'; import 'package:stackwallet/pages/exchange_view/sub_widgets/exchange_rate_sheet.dart'; import 'package:stackwallet/pages_desktop_specific/desktop_exchange/exchange_steps/step_scaffold.dart'; -import 'package:stackwallet/pages_desktop_specific/desktop_exchange/exchange_steps/subwidgets/desktop_step_4.dart'; import 'package:stackwallet/pages_desktop_specific/desktop_exchange/exchange_steps/subwidgets/desktop_step_item.dart'; import 'package:stackwallet/providers/exchange/current_exchange_name_state_provider.dart'; -import 'package:stackwallet/providers/exchange/exchange_provider.dart'; -import 'package:stackwallet/providers/global/trades_service_provider.dart'; -import 'package:stackwallet/services/exchange/exchange_response.dart'; -import 'package:stackwallet/services/notifications_api.dart'; -import 'package:stackwallet/utilities/assets.dart'; import 'package:stackwallet/utilities/text_styles.dart'; import 'package:stackwallet/utilities/theme/stack_colors.dart'; -import 'package:stackwallet/widgets/custom_loading_overlay.dart'; -import 'package:stackwallet/widgets/desktop/desktop_dialog.dart'; -import 'package:stackwallet/widgets/desktop/primary_button.dart'; -import 'package:stackwallet/widgets/desktop/secondary_button.dart'; -import 'package:stackwallet/widgets/desktop/simple_desktop_dialog.dart'; import 'package:stackwallet/widgets/rounded_white_container.dart'; class DesktopStep3 extends ConsumerStatefulWidget { const DesktopStep3({ Key? key, - required this.model, }) : super(key: key); - final IncompleteExchangeModel model; - @override ConsumerState createState() => _DesktopStep3State(); } class _DesktopStep3State extends ConsumerState { - late final IncompleteExchangeModel model; - - Future createTrade() async { - unawaited( - showDialog( - context: context, - barrierDismissible: false, - builder: (_) => WillPopScope( - onWillPop: () async => false, - child: Container( - color: Theme.of(context) - .extension()! - .overlay - .withOpacity(0.6), - child: const CustomLoadingOverlay( - message: "Creating a trade", - eventBus: null, - ), - ), - ), - ), - ); - - final ExchangeResponse response = - await ref.read(exchangeProvider).createTrade( - from: model.sendTicker, - to: model.receiveTicker, - fixedRate: model.rateType != ExchangeRateType.estimated, - amount: model.reversed ? model.receiveAmount : model.sendAmount, - addressTo: model.recipientAddress!, - extraId: null, - addressRefund: model.refundAddress!, - refundExtraId: "", - rateId: model.rateId, - reversed: model.reversed, - ); - - if (response.value == null) { - if (mounted) { - Navigator.of(context).pop(); - } - - unawaited( - showDialog( - context: context, - barrierDismissible: true, - builder: (_) => SimpleDesktopDialog( - title: "Failed to create trade", - message: response.exception?.toString() ?? ""), - ), - ); - return; - } - - // save trade to hive - await ref.read(tradesServiceProvider).add( - trade: response.value!, - shouldNotifyListeners: true, - ); - - String status = response.value!.status; - - model.trade = response.value!; - - // extra info if status is waiting - if (status == "Waiting") { - status += " for deposit"; - } - - if (mounted) { - Navigator.of(context).pop(); - } - - unawaited( - NotificationApi.showNotification( - changeNowId: model.trade!.tradeId, - title: status, - body: "Trade ID ${model.trade!.tradeId}", - walletId: "", - iconAssetName: Assets.svg.arrowRotate, - date: model.trade!.timestamp, - shouldWatchForUpdates: true, - coinName: "coinName", - ), - ); - - if (mounted) { - unawaited( - showDialog( - context: context, - barrierColor: Colors.transparent, - barrierDismissible: false, - builder: (context) { - return DesktopDialog( - maxWidth: 720, - maxHeight: double.infinity, - child: StepScaffold( - step: 4, - model: model, - body: DesktopStep4( - model: model, - ), - ), - ); - }, - ), - ); - } - } - - @override - void initState() { - model = widget.model; - super.initState(); - } - @override Widget build(BuildContext context) { return Column( @@ -179,7 +45,7 @@ class _DesktopStep3State extends ConsumerState { DesktopStepItem( label: "You send", value: - "${model.sendAmount.toStringAsFixed(8)} ${model.sendTicker.toUpperCase()}", + "${ref.watch(desktopExchangeModelProvider.select((value) => value!.sendAmount.toStringAsFixed(8)))} ${ref.watch(desktopExchangeModelProvider.select((value) => value!.sendTicker.toUpperCase()))}", ), Container( height: 1, @@ -188,17 +54,20 @@ class _DesktopStep3State extends ConsumerState { DesktopStepItem( label: "You receive", value: - "~${model.receiveAmount.toStringAsFixed(8)} ${model.receiveTicker.toUpperCase()}", + "~${ref.watch(desktopExchangeModelProvider.select((value) => value!.receiveAmount.toStringAsFixed(8)))} ${ref.watch(desktopExchangeModelProvider.select((value) => value!.receiveTicker.toUpperCase()))}", ), Container( height: 1, color: Theme.of(context).extension()!.background, ), DesktopStepItem( - label: model.rateType == ExchangeRateType.estimated + label: ref.watch(desktopExchangeModelProvider + .select((value) => value!.rateType)) == + ExchangeRateType.estimated ? "Estimated rate" : "Fixed rate", - value: model.rateInfo, + value: ref.watch(desktopExchangeModelProvider + .select((value) => value!.rateInfo)), ), Container( height: 1, @@ -206,8 +75,11 @@ class _DesktopStep3State extends ConsumerState { ), DesktopStepItem( vertical: true, - label: "Recipient ${model.receiveTicker.toUpperCase()} address", - value: model.recipientAddress!, + label: + "Recipient ${ref.watch(desktopExchangeModelProvider.select((value) => value!.receiveTicker.toUpperCase()))} address", + value: ref.watch(desktopExchangeModelProvider + .select((value) => value!.recipientAddress)) ?? + "Error", ), Container( height: 1, @@ -215,35 +87,11 @@ class _DesktopStep3State extends ConsumerState { ), DesktopStepItem( vertical: true, - label: "Refund ${model.sendTicker.toUpperCase()} address", - value: model.refundAddress!, - ), - ], - ), - ), - Padding( - padding: const EdgeInsets.only( - top: 20, - bottom: 32, - ), - child: Row( - children: [ - Expanded( - child: SecondaryButton( - label: "Back", - buttonHeight: ButtonHeight.l, - onPressed: Navigator.of(context).pop, - ), - ), - const SizedBox( - width: 16, - ), - Expanded( - child: PrimaryButton( - label: "Confirm", - buttonHeight: ButtonHeight.l, - onPressed: createTrade, - ), + label: + "Refund ${ref.watch(desktopExchangeModelProvider.select((value) => value!.sendTicker.toUpperCase()))} address", + value: ref.watch(desktopExchangeModelProvider + .select((value) => value!.refundAddress)) ?? + "Error", ), ], ), 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 5b69f064d..4943ed442 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,38 +1,26 @@ 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/step_scaffold.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'; -import 'package:stackwallet/widgets/desktop/desktop_dialog.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'; class DesktopStep4 extends ConsumerStatefulWidget { const DesktopStep4({ Key? key, - required this.model, }) : super(key: key); - final IncompleteExchangeModel model; - @override ConsumerState createState() => _DesktopStep4State(); } class _DesktopStep4State extends ConsumerState { - late final IncompleteExchangeModel model; - String _statusString = "New"; Timer? _statusTimer; @@ -51,8 +39,13 @@ class _DesktopStep4State extends ConsumerState { } Future _updateStatus() async { - final statusResponse = - await ref.read(exchangeProvider).updateTrade(model.trade!); + final trade = ref.read(desktopExchangeModelProvider)?.trade; + + if (trade == null) { + return; + } + + final statusResponse = await ref.read(exchangeProvider).updateTrade(trade); String status = "Waiting"; if (statusResponse.value != null) { status = statusResponse.value!.status; @@ -72,8 +65,6 @@ class _DesktopStep4State extends ConsumerState { @override void initState() { - model = widget.model; - _statusTimer = Timer.periodic(const Duration(seconds: 60), (_) { _updateStatus(); }); @@ -93,14 +84,14 @@ class _DesktopStep4State extends ConsumerState { return Column( children: [ Text( - "Send ${model.sendTicker.toUpperCase()} to the address below", + "Send ${ref.watch(desktopExchangeModelProvider.select((value) => value!.sendTicker.toUpperCase()))} to the address below", style: STextStyles.desktopTextMedium(context), ), const SizedBox( height: 8, ), Text( - "Send ${model.sendTicker.toUpperCase()} to the address below. Once it is received, ${model.trade!.exchangeName} will send the ${model.receiveTicker.toUpperCase()} to the recipient address you provided. You can find this trade details and check its status in the list of trades.", + "Send ${ref.watch(desktopExchangeModelProvider.select((value) => value!.sendTicker.toUpperCase()))} to the address below. Once it is received, ${ref.watch(desktopExchangeModelProvider.select((value) => value!.trade?.exchangeName))} will send the ${ref.watch(desktopExchangeModelProvider.select((value) => value!.receiveTicker.toUpperCase()))} to the recipient address you provided. You can find this trade details and check its status in the list of trades.", style: STextStyles.desktopTextExtraExtraSmall(context), ), const SizedBox( @@ -111,7 +102,7 @@ class _DesktopStep4State extends ConsumerState { child: RichText( text: TextSpan( text: - "You must send at least ${model.sendAmount.toString()} ${model.sendTicker}. ", + "You must send at least ${ref.watch(desktopExchangeModelProvider.select((value) => value!.sendAmount.toString()))} ${ref.watch(desktopExchangeModelProvider.select((value) => value!.sendTicker))}. ", style: STextStyles.label700(context).copyWith( color: Theme.of(context) .extension()! @@ -121,7 +112,7 @@ class _DesktopStep4State extends ConsumerState { children: [ TextSpan( text: - "If you send less than ${model.sendAmount.toString()} ${model.sendTicker}, your transaction may not be converted and it may not be refunded.", + "If you send less than ${ref.watch(desktopExchangeModelProvider.select((value) => value!.sendAmount.toString()))} ${ref.watch(desktopExchangeModelProvider.select((value) => value!.sendTicker))}, your transaction may not be converted and it may not be refunded.", style: STextStyles.label(context).copyWith( color: Theme.of(context) .extension()! @@ -143,8 +134,11 @@ class _DesktopStep4State extends ConsumerState { children: [ DesktopStepItem( vertical: true, - label: "Send ${model.sendTicker.toUpperCase()} to this address", - value: model.trade!.payInAddress, + label: + "Send ${ref.watch(desktopExchangeModelProvider.select((value) => value!.sendTicker.toUpperCase()))} to this address", + value: ref.watch(desktopExchangeModelProvider + .select((value) => value!.trade?.payInAddress)) ?? + "Error", ), Container( height: 1, @@ -153,7 +147,7 @@ class _DesktopStep4State extends ConsumerState { DesktopStepItem( label: "Amount", value: - "${model.sendAmount.toStringAsFixed(8)} ${model.sendTicker.toUpperCase()}", + "${ref.watch(desktopExchangeModelProvider.select((value) => value!.sendAmount.toStringAsFixed(8)))} ${ref.watch(desktopExchangeModelProvider.select((value) => value!.sendTicker.toUpperCase()))}", ), Container( height: 1, @@ -161,7 +155,9 @@ class _DesktopStep4State extends ConsumerState { ), DesktopStepItem( label: "Trade ID", - value: model.trade!.tradeId, + value: ref.watch(desktopExchangeModelProvider + .select((value) => value!.trade?.tradeId)) ?? + "Error", ), Container( height: 1, @@ -191,110 +187,6 @@ class _DesktopStep4State extends ConsumerState { ], ), ), - Padding( - padding: const EdgeInsets.only( - top: 20, - bottom: 32, - ), - child: Row( - children: [ - Expanded( - child: SecondaryButton( - label: "Send from Stack Wallet", - buttonHeight: ButtonHeight.l, - 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, - fromDesktopStep4: true, - ), - const RouteSettings( - name: SendFromView.routeName, - ), - ), - ]; - }, - ), - ); - }, - ), - ), - const SizedBox( - width: 16, - ), - Expanded( - child: PrimaryButton( - label: "Show QR code", - buttonHeight: ButtonHeight.l, - onPressed: () { - showDialog( - context: context, - barrierColor: Colors.transparent, - barrierDismissible: true, - builder: (_) { - return DesktopDialog( - maxHeight: 720, - maxWidth: 720, - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Text( - "Send ${model.sendAmount.toStringAsFixed(8)} ${model.sendTicker} to this address", - style: STextStyles.desktopH3(context), - ), - const SizedBox( - height: 48, - ), - Center( - child: QrImage( - // TODO: grab coin uri scheme from somewhere - // data: "${coin.uriScheme}:$receivingAddress", - data: model.trade!.payInAddress, - size: 290, - foregroundColor: Theme.of(context) - .extension()! - .accentColorDark, - ), - ), - const SizedBox( - height: 48, - ), - SecondaryButton( - label: "Cancel", - width: 310, - buttonHeight: ButtonHeight.l, - onPressed: Navigator.of(context).pop, - ), - ], - ), - ); - }, - ); - }, - ), - ), - ], - ), - ), ], ); } diff --git a/lib/pages_desktop_specific/desktop_exchange/subwidgets/desktop_choose_from_stack.dart b/lib/pages_desktop_specific/desktop_exchange/subwidgets/desktop_choose_from_stack.dart index a3fb91f61..efa939871 100644 --- a/lib/pages_desktop_specific/desktop_exchange/subwidgets/desktop_choose_from_stack.dart +++ b/lib/pages_desktop_specific/desktop_exchange/subwidgets/desktop_choose_from_stack.dart @@ -17,6 +17,7 @@ import 'package:stackwallet/widgets/rounded_white_container.dart'; import 'package:stackwallet/widgets/stack_text_field.dart'; import 'package:stackwallet/widgets/textfield_icon_button.dart'; import 'package:stackwallet/widgets/wallet_info_row/sub_widgets/wallet_info_row_coin_icon.dart'; +import 'package:tuple/tuple.dart'; class DesktopChooseFromStack extends ConsumerStatefulWidget { const DesktopChooseFromStack({ @@ -222,8 +223,18 @@ class _DesktopChooseFromStackState ), BlueTextButton( text: "Select wallet", - onTap: () { - Navigator.of(context).pop(manager.walletId); + onTap: () async { + final address = + await manager.currentReceivingAddress; + + if (mounted) { + Navigator.of(context).pop( + Tuple2( + manager.walletName, + address, + ), + ); + } }, ), ], diff --git a/lib/widgets/desktop/simple_desktop_dialog.dart b/lib/widgets/desktop/simple_desktop_dialog.dart index cd066c221..1260deb3b 100644 --- a/lib/widgets/desktop/simple_desktop_dialog.dart +++ b/lib/widgets/desktop/simple_desktop_dialog.dart @@ -22,41 +22,54 @@ class SimpleDesktopDialog extends StatelessWidget { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text( - title, - style: STextStyles.desktopH3(context), - ), - const DesktopDialogCloseButton(), - ], + Padding( + padding: const EdgeInsets.only(left: 32), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + title, + style: STextStyles.desktopH3(context), + ), + const DesktopDialogCloseButton(), + ], + ), ), const Spacer(), - Text( - message, - style: STextStyles.desktopTextSmall(context), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 32), + child: Text( + message, + style: STextStyles.desktopTextSmall(context), + ), ), const Spacer( flex: 2, ), - Row( - children: [ - const Spacer(), - const SizedBox( - width: 16, - ), - Expanded( - child: PrimaryButton( - label: "Ok", - buttonHeight: ButtonHeight.l, - onPressed: Navigator.of( - context, - rootNavigator: true, - ).pop, + Padding( + padding: const EdgeInsets.only( + left: 32, + right: 32, + bottom: 32, + ), + child: Row( + children: [ + const Spacer(), + const SizedBox( + width: 16, ), - ), - ], + Expanded( + child: PrimaryButton( + label: "Ok", + buttonHeight: ButtonHeight.l, + onPressed: Navigator.of( + context, + rootNavigator: true, + ).pop, + ), + ), + ], + ), ) ], ), diff --git a/lib/widgets/fade_stack.dart b/lib/widgets/fade_stack.dart new file mode 100644 index 000000000..6cac16030 --- /dev/null +++ b/lib/widgets/fade_stack.dart @@ -0,0 +1,57 @@ +import 'package:flutter/material.dart'; + +class FadeStack extends StatefulWidget { + final int index; + final List children; + + const FadeStack({ + super.key, + required this.index, + required this.children, + }); + + @override + FadeStackState createState() => FadeStackState(); +} + +class FadeStackState extends State + with SingleTickerProviderStateMixin { + late final AnimationController animationController; + + @override + void didUpdateWidget(FadeStack oldWidget) { + if (widget.index != oldWidget.index) { + animationController.forward(from: 0.0); + } + super.didUpdateWidget(oldWidget); + } + + @override + void initState() { + animationController = AnimationController( + vsync: this, + duration: const Duration( + milliseconds: 250, + ), + ); + animationController.forward(); + super.initState(); + } + + @override + void dispose() { + animationController.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return FadeTransition( + opacity: animationController, + child: IndexedStack( + index: widget.index, + children: widget.children, + ), + ); + } +}