From 650ae9fe09bdd5c4a6d85a246fd8827edc8155ab Mon Sep 17 00:00:00 2001 From: julian Date: Sun, 15 Jan 2023 18:09:11 -0600 Subject: [PATCH] buy quote preview view and sample quote class --- assets/svg/buy/Simplex-Nuvei-Logo.svg | 9 + lib/models/buy/response_objects/quote.dart | 25 +++ lib/pages/buy_view/buy_form.dart | 21 +- lib/pages/buy_view/buy_quote_preview.dart | 197 ++++++++++++++++++ .../confirm_change_now_send.dart | 6 +- lib/route_generator.dart | 16 ++ lib/utilities/assets.dart | 5 + pubspec.yaml | 3 + 8 files changed, 277 insertions(+), 5 deletions(-) create mode 100644 assets/svg/buy/Simplex-Nuvei-Logo.svg create mode 100644 lib/models/buy/response_objects/quote.dart create mode 100644 lib/pages/buy_view/buy_quote_preview.dart diff --git a/assets/svg/buy/Simplex-Nuvei-Logo.svg b/assets/svg/buy/Simplex-Nuvei-Logo.svg new file mode 100644 index 000000000..fab7e35e3 --- /dev/null +++ b/assets/svg/buy/Simplex-Nuvei-Logo.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/lib/models/buy/response_objects/quote.dart b/lib/models/buy/response_objects/quote.dart new file mode 100644 index 000000000..b422ff1af --- /dev/null +++ b/lib/models/buy/response_objects/quote.dart @@ -0,0 +1,25 @@ +import 'package:decimal/decimal.dart'; +import 'package:stackwallet/models/buy/response_objects/crypto.dart'; +import 'package:stackwallet/models/buy/response_objects/fiat.dart'; + +class SimplexQuote { + // todo: this class + + final Crypto crypto; + final Fiat fiat; + + final Decimal youPayFiatPrice; + final Decimal youReceiveCryptoAmount; + + final String purchaseId; + final String receivingAddress; + + SimplexQuote({ + required this.crypto, + required this.fiat, + required this.youPayFiatPrice, + required this.youReceiveCryptoAmount, + required this.purchaseId, + required this.receivingAddress, + }); +} diff --git a/lib/pages/buy_view/buy_form.dart b/lib/pages/buy_view/buy_form.dart index c45bb3b6c..eb41cfa1e 100644 --- a/lib/pages/buy_view/buy_form.dart +++ b/lib/pages/buy_view/buy_form.dart @@ -1,12 +1,15 @@ import 'dart:async'; +import 'package:decimal/decimal.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/svg.dart'; import 'package:stackwallet/models/buy/response_objects/crypto.dart'; import 'package:stackwallet/models/buy/response_objects/fiat.dart'; +import 'package:stackwallet/models/buy/response_objects/quote.dart'; import 'package:stackwallet/pages/address_book_views/address_book_view.dart'; +import 'package:stackwallet/pages/buy_view/buy_quote_preview.dart'; import 'package:stackwallet/pages/buy_view/sub_widgets/crypto_selection_view.dart'; import 'package:stackwallet/pages/buy_view/sub_widgets/fiat_crypto_toggle.dart'; import 'package:stackwallet/pages/buy_view/sub_widgets/fiat_selection_view.dart'; @@ -814,10 +817,24 @@ class _BuyFormState extends ConsumerState { ), PrimaryButton( buttonHeight: isDesktop ? ButtonHeight.l : null, - enabled: ref.watch(exchangeFormStateProvider - .select((value) => value.canExchange)), + enabled: _receiveAddressController.text.isNotEmpty && + _buyAmountController.text.isNotEmpty, onPressed: () { // preview buy quote + // TODO: show loading while fetching quote + final quote = SimplexQuote( + crypto: selectedCrypto!, + fiat: selectedFiat!, + youPayFiatPrice: Decimal.parse("100"), + youReceiveCryptoAmount: Decimal.parse("1.0238917"), + purchaseId: "someID", + receivingAddress: _receiveAddressController.text, + ); + + Navigator.of(context).pushNamed( + BuyQuotePreviewView.routeName, + arguments: quote, + ); }, label: "Preview quote", ) diff --git a/lib/pages/buy_view/buy_quote_preview.dart b/lib/pages/buy_view/buy_quote_preview.dart new file mode 100644 index 000000000..df21ad91a --- /dev/null +++ b/lib/pages/buy_view/buy_quote_preview.dart @@ -0,0 +1,197 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_svg/svg.dart'; +import 'package:stackwallet/models/buy/response_objects/quote.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/utilities/util.dart'; +import 'package:stackwallet/widgets/background.dart'; +import 'package:stackwallet/widgets/conditional_parent.dart'; +import 'package:stackwallet/widgets/custom_buttons/app_bar_icon_button.dart'; +import 'package:stackwallet/widgets/desktop/primary_button.dart'; +import 'package:stackwallet/widgets/rounded_white_container.dart'; + +class BuyQuotePreviewView extends StatefulWidget { + const BuyQuotePreviewView({ + Key? key, + required this.quote, + }) : super(key: key); + + final SimplexQuote quote; + + static const String routeName = "/buyQuotePreview"; + + @override + State createState() => _BuyQuotePreviewViewState(); +} + +class _BuyQuotePreviewViewState extends State { + final isDesktop = Util.isDesktop; + + Future _buy() async { + // do buy/redirect to simplex page + } + + @override + Widget build(BuildContext context) { + return ConditionalParent( + condition: !isDesktop, + builder: (child) { + return Background( + child: Scaffold( + backgroundColor: + Theme.of(context).extension()!.background, + appBar: AppBar( + backgroundColor: + Theme.of(context).extension()!.backgroundAppBar, + leading: const AppBarBackButton(), + title: Text( + "Preview quote", + 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: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Text( + "Buy ${widget.quote.crypto.ticker.toUpperCase()}", + style: STextStyles.pageTitleH1(context), + ), + const SizedBox( + height: 16, + ), + RoundedWhiteContainer( + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + "You pay", + style: STextStyles.label(context), + ), + Text( + "${widget.quote.youPayFiatPrice.toStringAsFixed(2)} ${widget.quote.fiat.ticker.toUpperCase()}", + style: STextStyles.label(context).copyWith( + color: Theme.of(context).extension()!.textDark, + ), + ), + ], + ), + ), + const SizedBox( + height: 8, + ), + RoundedWhiteContainer( + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + "You receive", + style: STextStyles.label(context), + ), + Text( + "${widget.quote.youReceiveCryptoAmount} ${widget.quote.crypto.ticker.toUpperCase()}", + style: STextStyles.label(context).copyWith( + color: Theme.of(context).extension()!.textDark, + ), + ), + ], + ), + ), + const SizedBox( + height: 8, + ), + RoundedWhiteContainer( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + "Receiving ${widget.quote.crypto.ticker.toUpperCase()} address", + style: STextStyles.label(context), + ), + Text( + "${widget.quote.receivingAddress} ", + style: STextStyles.label(context).copyWith( + color: Theme.of(context).extension()!.textDark, + ), + ), + ], + ), + ), + const SizedBox( + height: 8, + ), + RoundedWhiteContainer( + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + "Purchase ID", + style: STextStyles.label(context), + ), + Text( + widget.quote.purchaseId, + style: STextStyles.label(context).copyWith( + color: Theme.of(context).extension()!.textDark, + ), + ), + ], + ), + ), + const SizedBox( + height: 8, + ), + RoundedWhiteContainer( + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + "Provider", + style: STextStyles.label(context), + ), + SizedBox( + width: 64, + child: SvgPicture.asset(Assets.buy.simplexLogo), + ), + ], + ), + ), + const SizedBox( + height: 8, + ), + const Spacer(), + PrimaryButton( + label: "Buy", + onPressed: _buy, + ) + ], + ), + ); + } +} diff --git a/lib/pages/exchange_view/confirm_change_now_send.dart b/lib/pages/exchange_view/confirm_change_now_send.dart index 86599f2e5..b9205116b 100644 --- a/lib/pages/exchange_view/confirm_change_now_send.dart +++ b/lib/pages/exchange_view/confirm_change_now_send.dart @@ -61,6 +61,8 @@ class _ConfirmChangeNowSendViewState late final String routeOnSuccessName; late final Trade trade; + final isDesktop = Util.isDesktop; + Future _attemptSend(BuildContext context) async { unawaited( showDialog( @@ -227,8 +229,6 @@ class _ConfirmChangeNowSendViewState final managerProvider = ref.watch(walletsChangeNotifierProvider .select((value) => value.getManagerProvider(walletId))); - final isDesktop = Util.isDesktop; - return ConditionalParent( condition: !isDesktop, builder: (child) { @@ -238,7 +238,7 @@ class _ConfirmChangeNowSendViewState Theme.of(context).extension()!.background, appBar: AppBar( backgroundColor: - Theme.of(context).extension()!.background, + Theme.of(context).extension()!.backgroundAppBar, leading: AppBarBackButton( onPressed: () async { // if (FocusScope.of(context).hasFocus) { diff --git a/lib/route_generator.dart b/lib/route_generator.dart index 7af01cacf..f4c6f731e 100644 --- a/lib/route_generator.dart +++ b/lib/route_generator.dart @@ -2,6 +2,7 @@ import 'package:decimal/decimal.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:stackwallet/models/buy/response_objects/quote.dart'; import 'package:stackwallet/models/contact_address_entry.dart'; import 'package:stackwallet/models/exchange/incomplete_exchange.dart'; import 'package:stackwallet/models/exchange/response_objects/trade.dart'; @@ -22,6 +23,7 @@ import 'package:stackwallet/pages/address_book_views/subviews/address_book_filte import 'package:stackwallet/pages/address_book_views/subviews/contact_details_view.dart'; import 'package:stackwallet/pages/address_book_views/subviews/edit_contact_address_view.dart'; import 'package:stackwallet/pages/address_book_views/subviews/edit_contact_name_emoji_view.dart'; +import 'package:stackwallet/pages/buy_view/buy_quote_preview.dart'; import 'package:stackwallet/pages/exchange_view/choose_from_stack_view.dart'; import 'package:stackwallet/pages/exchange_view/edit_trade_note_view.dart'; import 'package:stackwallet/pages/exchange_view/exchange_loading_overlay.dart'; @@ -1001,6 +1003,20 @@ class RouteGenerator { } return _routeError("${settings.name} invalid args: ${args.toString()}"); + case BuyQuotePreviewView.routeName: + if (args is SimplexQuote) { + return getRoute( + shouldUseMaterialRoute: useMaterialPageRoute, + builder: (_) => BuyQuotePreviewView( + quote: args, + ), + settings: RouteSettings( + name: settings.name, + ), + ); + } + return _routeError("${settings.name} invalid args: ${args.toString()}"); + // == Desktop specific routes ============================================ case CreatePasswordView.routeName: if (args is bool) { diff --git a/lib/utilities/assets.dart b/lib/utilities/assets.dart index f6d43b26a..c8963a870 100644 --- a/lib/utilities/assets.dart +++ b/lib/utilities/assets.dart @@ -31,7 +31,12 @@ class _EXCHANGE { class _BUY { const _BUY(); + // TODO: switch this to something like + // String buy(BuildContext context) => + // "assets/svg/${Theme.of(context).extension()!.themeType.name}/buy.svg"; String get buy => "assets/svg/light/buy-coins-icon.svg"; + + String get simplexLogo => "assets/svg/buy/Simplex-Nuvei-Logo.svg"; } class _SVG { diff --git a/pubspec.yaml b/pubspec.yaml index 27f63c7c6..f9b92de3b 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -384,6 +384,9 @@ flutter: - assets/svg/oceanBreeze/buy-coins-icon.svg - assets/svg/oceanBreeze/bg.svg + # buy + - assets/svg/buy/Simplex-Nuvei-Logo.svg + # An image asset can refer to one or more resolution-specific "variants", see # https://flutter.dev/assets-and-images/#resolution-aware. # For details regarding adding assets from package dependencies, see