From 346a034d0a3b8cd41a508c4cd3cd7ecd0aaac5d2 Mon Sep 17 00:00:00 2001 From: OleksandrSobol Date: Mon, 12 Apr 2021 21:22:22 +0300 Subject: [PATCH 01/17] CAKE-306 | added MoonPay option for BTC buying; applied PreOrderPage to the app; created Wyre and MoonPay buy providers --- lib/buy/buy_amount.dart | 8 + lib/buy/buy_exception.dart | 12 + lib/buy/buy_provider.dart | 26 +++ lib/buy/buy_provider_description.dart | 21 ++ lib/buy/moonpay/moonpay_buy_provider.dart | 109 +++++++++ lib/{entities => buy}/order.dart | 11 +- lib/buy/wyre/wyre_buy_provider.dart | 161 ++++++++++++++ lib/di.dart | 19 +- lib/entities/wyre_service.dart | 39 +++- lib/main.dart | 2 +- lib/palette.dart | 1 + lib/router.dart | 8 +- lib/routes.dart | 1 + lib/src/screens/buy/pre_order_page.dart | 210 ++++++++++++++++++ .../screens/buy/widgets/buy_list_item.dart | 131 +++++++++++ lib/src/screens/dashboard/dashboard_page.dart | 40 +--- lib/src/screens/send/send_page.dart | 2 +- lib/store/dashboard/orders_store.dart | 2 +- lib/view_model/buy/buy_amount_view_model.dart | 28 +++ lib/view_model/buy/buy_item.dart | 29 +++ lib/view_model/buy/buy_view_model.dart | 81 +++++++ .../dashboard/dashboard_view_model.dart | 7 +- lib/view_model/dashboard/order_list_item.dart | 2 +- lib/view_model/order_details_view_model.dart | 2 +- lib/view_model/wyre_view_model.dart | 2 +- 25 files changed, 903 insertions(+), 51 deletions(-) create mode 100644 lib/buy/buy_amount.dart create mode 100644 lib/buy/buy_exception.dart create mode 100644 lib/buy/buy_provider.dart create mode 100644 lib/buy/buy_provider_description.dart create mode 100644 lib/buy/moonpay/moonpay_buy_provider.dart rename lib/{entities => buy}/order.dart (76%) create mode 100644 lib/buy/wyre/wyre_buy_provider.dart create mode 100644 lib/src/screens/buy/pre_order_page.dart create mode 100644 lib/src/screens/buy/widgets/buy_list_item.dart create mode 100644 lib/view_model/buy/buy_amount_view_model.dart create mode 100644 lib/view_model/buy/buy_item.dart create mode 100644 lib/view_model/buy/buy_view_model.dart diff --git a/lib/buy/buy_amount.dart b/lib/buy/buy_amount.dart new file mode 100644 index 000000000..d049c1e03 --- /dev/null +++ b/lib/buy/buy_amount.dart @@ -0,0 +1,8 @@ +import 'package:flutter/foundation.dart'; + +class BuyAmount { + BuyAmount({@required this.sourceAmount, @required this.destAmount}); + + final double sourceAmount; + final double destAmount; +} \ No newline at end of file diff --git a/lib/buy/buy_exception.dart b/lib/buy/buy_exception.dart new file mode 100644 index 000000000..28064fdfc --- /dev/null +++ b/lib/buy/buy_exception.dart @@ -0,0 +1,12 @@ +import 'package:flutter/foundation.dart'; +import 'package:cake_wallet/buy/buy_provider_description.dart'; + +class BuyException implements Exception { + BuyException({@required this.description, @required this.text}); + + final BuyProviderDescription description; + final String text; + + @override + String toString() => '${description.title}: $text'; +} \ No newline at end of file diff --git a/lib/buy/buy_provider.dart b/lib/buy/buy_provider.dart new file mode 100644 index 000000000..78ad54451 --- /dev/null +++ b/lib/buy/buy_provider.dart @@ -0,0 +1,26 @@ +import 'package:cake_wallet/buy/buy_amount.dart'; +import 'package:cake_wallet/buy/buy_provider_description.dart'; +import 'package:cake_wallet/buy/order.dart'; +import 'package:cake_wallet/core/wallet_base.dart'; +import 'package:cake_wallet/entities/wallet_type.dart'; + +abstract class BuyProvider { + BuyProvider({this.wallet, this.isTestEnvironment}); + + final WalletBase wallet; + final bool isTestEnvironment; + + String get title; + BuyProviderDescription get description; + + WalletType get walletType => wallet.type; + String get walletAddress => wallet.address; + String get walletId => wallet.id; + + @override + String toString() => title; + + Future requestUrl(String amount, String sourceCurrency); + Future findOrderById(String id); + Future calculateAmount(String amount, String sourceCurrency); +} \ No newline at end of file diff --git a/lib/buy/buy_provider_description.dart b/lib/buy/buy_provider_description.dart new file mode 100644 index 000000000..46b3bcdc1 --- /dev/null +++ b/lib/buy/buy_provider_description.dart @@ -0,0 +1,21 @@ +import 'package:cake_wallet/entities/enumerable_item.dart'; + +class BuyProviderDescription extends EnumerableItem + with Serializable { + const BuyProviderDescription({String title, int raw}) + : super(title: title, raw: raw); + + static const wyre = BuyProviderDescription(title: 'Wyre', raw: 0); + static const moonPay = BuyProviderDescription(title: 'MoonPay', raw: 1); + + static BuyProviderDescription deserialize({int raw}) { + switch (raw) { + case 0: + return wyre; + case 1: + return moonPay; + default: + return null; + } + } +} \ No newline at end of file diff --git a/lib/buy/moonpay/moonpay_buy_provider.dart b/lib/buy/moonpay/moonpay_buy_provider.dart new file mode 100644 index 000000000..972b2d2a4 --- /dev/null +++ b/lib/buy/moonpay/moonpay_buy_provider.dart @@ -0,0 +1,109 @@ +import 'dart:convert'; +import 'package:cake_wallet/buy/buy_exception.dart'; +import 'package:http/http.dart'; +import 'package:cake_wallet/buy/buy_amount.dart'; +import 'package:cake_wallet/buy/buy_provider.dart'; +import 'package:cake_wallet/buy/buy_provider_description.dart'; +import 'package:cake_wallet/buy/order.dart'; +import 'package:cake_wallet/core/wallet_base.dart'; +import 'package:cake_wallet/entities/wallet_type.dart'; +import 'package:cake_wallet/exchange/trade_state.dart'; +import 'package:cake_wallet/.secrets.g.dart' as secrets; + +class MoonPayBuyProvider extends BuyProvider { + MoonPayBuyProvider({WalletBase wallet, bool isTestEnvironment = false}) + : super(wallet: wallet, isTestEnvironment: isTestEnvironment) { + baseApiUrl = isTestEnvironment + ? _baseTestApiUrl + : _baseProductApiUrl; + } + + static const _baseTestApiUrl = 'https://buy-staging.moonpay.com'; + static const _baseProductApiUrl = 'https://api.moonpay.com'; + static const _currenciesSuffix = '/v3/currencies'; + static const _quoteSuffix = '/buy_quote'; + static const _transactionsSuffix = '/v1/transactions'; + static const _apiKey = secrets.moonPayApiKey; + + @override + String get title => 'MoonPay'; + + @override + BuyProviderDescription get description => BuyProviderDescription.moonPay; + + String get currencyCode => + walletTypeToCryptoCurrency(walletType).title.toLowerCase(); + + String baseApiUrl; + + @override + Future requestUrl(String amount, String sourceCurrency) async { + final enabledPaymentMethods = + 'credit_debit_card%2Capple_pay%2Cgoogle_pay%2Csamsung_pay' + '%2Csepa_bank_transfer%2Cgbp_bank_transfer%2Cgbp_open_banking_payment'; + + final originalUrl = baseApiUrl + '?apiKey=' + _apiKey + '¤cyCode=' + + currencyCode + '&enabledPaymentMethods=' + enabledPaymentMethods + + '&walletAddress=' + walletAddress + + '&baseCurrencyCode=' + sourceCurrency.toLowerCase() + + '&baseCurrencyAmount=' + amount + '&lockAmount=true' + + '&showAllCurrencies=false' + '&showWalletAddressForm=false'; + + return originalUrl; + } + + @override + Future calculateAmount(String amount, String sourceCurrency) async { + final url = baseApiUrl + _currenciesSuffix + '/$currencyCode' + + _quoteSuffix + '/?apiKey=' + _apiKey + + '&baseCurrencyAmount=' + amount + + '&baseCurrencyCode' + sourceCurrency.toLowerCase(); + + final response = await get(url); + + if (response.statusCode != 200) { + throw BuyException( + description: description, + text: 'Quote is not found!'); + } + + final responseJSON = json.decode(response.body) as Map; + final sourceAmount = responseJSON['totalAmount'] as double; + final destAmount = responseJSON['quoteCurrencyAmount'] as double; + + return BuyAmount(sourceAmount: sourceAmount, destAmount: destAmount); + } + + @override + Future findOrderById(String id) async { + final url = baseApiUrl + _transactionsSuffix + '/$id' + + '?apiKey=' + _apiKey; + + final response = await get(url); + + if (response.statusCode != 200) { + throw BuyException( + description: description, + text: 'Transaction $id is not found!'); + } + + final responseJSON = json.decode(response.body) as Map; + final status = responseJSON['status'] as String; + final state = TradeState.deserialize(raw: status.toLowerCase()); + final createdAt = responseJSON['createdAt'] as DateTime; + final amount = responseJSON['quoteCurrencyAmount'] as double; + + return Order( + id: id, + provider: description, + transferId: id, + from: 'USD', //FIXME + to: 'BTC', //FIXME + state: state, + createdAt: createdAt, + amount: amount.toString(), + receiveAddress: walletAddress, + walletId: walletId + ); + } +} \ No newline at end of file diff --git a/lib/entities/order.dart b/lib/buy/order.dart similarity index 76% rename from lib/entities/order.dart rename to lib/buy/order.dart index 1676edd99..54c13bd37 100644 --- a/lib/entities/order.dart +++ b/lib/buy/order.dart @@ -1,3 +1,4 @@ +import 'package:cake_wallet/buy/buy_provider_description.dart'; import 'package:hive/hive.dart'; import 'package:cake_wallet/exchange/trade_state.dart'; import 'package:cake_wallet/entities/format_amount.dart'; @@ -8,6 +9,7 @@ part 'order.g.dart'; class Order extends HiveObject { Order( {this.id, + BuyProviderDescription provider, this.transferId, this.from, this.to, @@ -16,7 +18,8 @@ class Order extends HiveObject { this.amount, this.receiveAddress, this.walletId}) - : stateRaw = state?.raw; + : providerRaw = provider?.raw, + stateRaw = state?.raw; static const typeId = 8; static const boxName = 'Orders'; @@ -51,5 +54,11 @@ class Order extends HiveObject { @HiveField(8) String walletId; + @HiveField(9) + int providerRaw; + + BuyProviderDescription get provider => + BuyProviderDescription.deserialize(raw: providerRaw); + String amountFormatted() => formatAmount(amount); } \ No newline at end of file diff --git a/lib/buy/wyre/wyre_buy_provider.dart b/lib/buy/wyre/wyre_buy_provider.dart new file mode 100644 index 000000000..0b94816b2 --- /dev/null +++ b/lib/buy/wyre/wyre_buy_provider.dart @@ -0,0 +1,161 @@ +import 'dart:convert'; +import 'package:cake_wallet/buy/buy_exception.dart'; +import 'package:http/http.dart'; +import 'package:cake_wallet/buy/buy_amount.dart'; +import 'package:cake_wallet/buy/buy_provider.dart'; +import 'package:cake_wallet/buy/buy_provider_description.dart'; +import 'package:cake_wallet/buy/order.dart'; +import 'package:cake_wallet/core/wallet_base.dart'; +import 'package:cake_wallet/entities/wallet_type.dart'; +import 'package:cake_wallet/exchange/trade_state.dart'; +import 'package:cake_wallet/.secrets.g.dart' as secrets; + +class WyreBuyProvider extends BuyProvider { + WyreBuyProvider({WalletBase wallet, bool isTestEnvironment = false}) + : super(wallet: wallet, isTestEnvironment: isTestEnvironment) { + baseApiUrl = isTestEnvironment + ? _baseTestApiUrl + : _baseProductApiUrl; + trackUrl = isTestEnvironment + ? _trackTestUrl + : _trackProductUrl; + } + + static const _baseTestApiUrl = 'https://api.testwyre.com'; + static const _baseProductApiUrl = 'https://api.sendwyre.com'; + static const _trackTestUrl = 'https://dash.testwyre.com/track/'; + static const _trackProductUrl = 'https://dash.sendwyre.com/track/'; + static const _ordersSuffix = '/v3/orders'; + static const _reserveSuffix = '/reserve'; + static const _quoteSuffix = '/quote/partner'; + static const _timeStampSuffix = '?timestamp='; + static const _transferSuffix = '/v2/transfer/'; + static const _trackSuffix = '/track'; + static const _secretKey = secrets.wyreSecretKey; + static const _accountId = secrets.wyreAccountId; + + @override + String get title => 'Wyre'; + + @override + BuyProviderDescription get description => BuyProviderDescription.wyre; + + String baseApiUrl; + String trackUrl; + + @override + Future requestUrl(String amount, String sourceCurrency) async { + final timestamp = DateTime.now().millisecondsSinceEpoch.toString(); + final url = baseApiUrl + _ordersSuffix + _reserveSuffix + + _timeStampSuffix + timestamp; + final body = { + 'amount': amount, + 'sourceCurrency': sourceCurrency, + 'destCurrency': walletTypeToCryptoCurrency(walletType).title, + 'dest': walletTypeToString(walletType).toLowerCase() + ':' + walletAddress, + 'referrerAccountId': _accountId, + 'lockFields': ['amount', 'sourceCurrency', 'destCurrency', 'dest'] + }; + + final response = await post(url, + headers: { + 'Authorization': 'Bearer $_secretKey', + 'Content-Type': 'application/json', + 'cache-control': 'no-cache' + }, + body: json.encode(body)); + + if (response.statusCode != 200) { + throw BuyException( + description: description, + text: 'Url $url is not found!'); + } + + final responseJSON = json.decode(response.body) as Map; + final urlFromResponse = responseJSON['url'] as String; + return urlFromResponse; + } + + @override + Future calculateAmount(String amount, String sourceCurrency) async { + final quoteUrl = baseApiUrl + _ordersSuffix + _quoteSuffix; + final body = { + 'amount': amount, + 'sourceCurrency': sourceCurrency, + 'destCurrency': walletTypeToCryptoCurrency(walletType).title, + 'dest': walletTypeToString(walletType).toLowerCase() + ':' + walletAddress, + 'accountId': _accountId, + 'country': 'US' //FIXME + }; + + final response = await post(quoteUrl, + headers: { + 'Authorization': 'Bearer $_secretKey', + 'Content-Type': 'application/json', + 'cache-control': 'no-cache' + }, + body: json.encode(body)); + + if (response.statusCode != 200) { + throw BuyException( + description: description, + text: 'Quote is not found!'); + } + + final responseJSON = json.decode(response.body) as Map; + final sourceAmount = responseJSON['sourceAmount'] as double; + final destAmount = responseJSON['destAmount'] as double; + + return BuyAmount(sourceAmount: sourceAmount, destAmount: destAmount); + } + + @override + Future findOrderById(String id) async { + final orderUrl = baseApiUrl + _ordersSuffix + '/$id'; + final orderResponse = await get(orderUrl); + + if (orderResponse.statusCode != 200) { + throw BuyException( + description: description, + text: 'Order $id is not found!'); + } + + final orderResponseJSON = + json.decode(orderResponse.body) as Map; + final transferId = orderResponseJSON['transferId'] as String; + final from = orderResponseJSON['sourceCurrency'] as String; + final to = orderResponseJSON['destCurrency'] as String; + final status = orderResponseJSON['status'] as String; + final state = TradeState.deserialize(raw: status.toLowerCase()); + final createdAtRaw = orderResponseJSON['createdAt'] as int; + final createdAt = + DateTime.fromMillisecondsSinceEpoch(createdAtRaw).toLocal(); + + final transferUrl = + baseApiUrl + _transferSuffix + transferId + _trackSuffix; + final transferResponse = await get(transferUrl); + + if (transferResponse.statusCode != 200) { + throw BuyException( + description: description, + text: 'Transfer $transferId is not found!'); + } + + final transferResponseJSON = + json.decode(transferResponse.body) as Map; + final amount = transferResponseJSON['destAmount'] as double; + + return Order( + id: id, + provider: description, + transferId: transferId, + from: from, + to: to, + state: state, + createdAt: createdAt, + amount: amount.toString(), + receiveAddress: walletAddress, + walletId: walletId + ); + } +} \ No newline at end of file diff --git a/lib/di.dart b/lib/di.dart index 66ac22231..8663b31de 100644 --- a/lib/di.dart +++ b/lib/di.dart @@ -4,7 +4,7 @@ import 'package:cake_wallet/core/wallet_service.dart'; import 'package:cake_wallet/entities/biometric_auth.dart'; import 'package:cake_wallet/entities/contact_record.dart'; import 'package:cake_wallet/entities/load_current_wallet.dart'; -import 'package:cake_wallet/entities/order.dart'; +import 'package:cake_wallet/buy/order.dart'; import 'package:cake_wallet/entities/transaction_description.dart'; import 'package:cake_wallet/entities/transaction_info.dart'; import 'package:cake_wallet/entities/wyre_service.dart'; @@ -15,7 +15,7 @@ import 'package:cake_wallet/exchange/trade.dart'; import 'package:cake_wallet/reactions/on_authentication_state_change.dart'; import 'package:cake_wallet/src/screens/backup/backup_page.dart'; import 'package:cake_wallet/src/screens/backup/edit_backup_password_page.dart'; - +import 'package:cake_wallet/src/screens/buy/pre_order_page.dart'; import 'package:cake_wallet/src/screens/contact/contact_list_page.dart'; import 'package:cake_wallet/src/screens/contact/contact_page.dart'; import 'package:cake_wallet/src/screens/exchange_trade/exchange_confirm_page.dart'; @@ -61,6 +61,8 @@ import 'package:cake_wallet/src/screens/subaddress/address_edit_or_create_page.d import 'package:cake_wallet/src/screens/wallet_list/wallet_list_page.dart'; import 'package:cake_wallet/store/wallet_list_store.dart'; import 'package:cake_wallet/view_model/backup_view_model.dart'; +import 'package:cake_wallet/view_model/buy/buy_amount_view_model.dart'; +import 'package:cake_wallet/view_model/buy/buy_view_model.dart'; import 'package:cake_wallet/view_model/contact_list/contact_list_view_model.dart'; import 'package:cake_wallet/view_model/contact_list/contact_view_model.dart'; import 'package:cake_wallet/view_model/edit_backup_password_view_model.dart'; @@ -546,6 +548,19 @@ Future setup( WyrePage(getIt.get(), ordersStore: getIt.get(), url: url)); + getIt.registerFactory(() => BuyAmountViewModel()); + + getIt.registerFactory(() { + final wallet = getIt.get().wallet; + + return BuyViewModel(ordersSource, getIt.get(), + getIt.get(), wallet: wallet); + }); + + getIt.registerFactory(() { + return PreOrderPage(buyViewModel: getIt.get()); + }); + getIt.registerFactoryParam( (order, _) => OrderDetailsViewModel( wyreViewModel: getIt.get(), diff --git a/lib/entities/wyre_service.dart b/lib/entities/wyre_service.dart index b83bfce68..f45f33d44 100644 --- a/lib/entities/wyre_service.dart +++ b/lib/entities/wyre_service.dart @@ -5,7 +5,7 @@ import 'package:cake_wallet/exchange/trade_state.dart'; import 'package:flutter/foundation.dart'; import 'package:http/http.dart'; import 'package:cake_wallet/.secrets.g.dart' as secrets; -import 'package:cake_wallet/entities/order.dart'; +import 'package:cake_wallet/buy/order.dart'; import 'package:cake_wallet/entities/wallet_type.dart'; class WyreService { @@ -26,6 +26,7 @@ class WyreService { static const _trackProductUrl = 'https://dash.sendwyre.com/track/'; static const _ordersSuffix = '/v3/orders'; static const _reserveSuffix = '/reserve'; + static const _quoteSuffix = '/quote/partner'; static const _timeStampSuffix = '?timestamp='; static const _transferSuffix = '/v2/transfer/'; static const _trackSuffix = '/track'; @@ -47,10 +48,12 @@ class WyreService { final secretKey = secrets.wyreSecretKey; final accountId = secrets.wyreAccountId; final body = { + 'amount': '1', + 'sourceCurrency': 'USD', 'destCurrency': walletTypeToCryptoCurrency(walletType).title, 'dest': walletTypeToString(walletType).toLowerCase() + ':' + walletAddress, 'referrerAccountId': accountId, - 'lockFields': ['destCurrency', 'dest'] + 'lockFields': ['amount', 'sourceCurrency', 'destCurrency', 'dest'] }; final response = await post(url, @@ -113,4 +116,36 @@ class WyreService { walletId: walletId ); } + + Future getAmount() async { + final quoteUrl = baseApiUrl + _ordersSuffix + _quoteSuffix; + + final secretKey = secrets.wyreSecretKey; + final accountId = secrets.wyreAccountId; + final body = { + 'amount': '1', + 'sourceCurrency': 'USD', + 'destCurrency': walletTypeToCryptoCurrency(walletType).title, + 'dest': walletTypeToString(walletType).toLowerCase() + ':' + walletAddress, + 'accountId': accountId, + 'country': 'US' + }; + + final response = await post(quoteUrl, + headers: { + 'Authorization': 'Bearer $secretKey', + 'Content-Type': 'application/json', + 'cache-control': 'no-cache' + }, + body: json.encode(body)); + + if (response.statusCode != 200) { + throw WyreException('Quote is not found! '); + } + + final responseJSON = json.decode(response.body) as Map; + final amount = responseJSON['destAmount'] as double; + + return amount; + } } \ No newline at end of file diff --git a/lib/main.dart b/lib/main.dart index 48b54c416..80b48f50b 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,5 +1,5 @@ import 'package:cake_wallet/entities/language_service.dart'; -import 'package:cake_wallet/entities/order.dart'; +import 'package:cake_wallet/buy/order.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:hive/hive.dart'; diff --git a/lib/palette.dart b/lib/palette.dart index 336f7b7f9..a26a27163 100644 --- a/lib/palette.dart +++ b/lib/palette.dart @@ -23,6 +23,7 @@ class Palette { static const Color persianRed = Color.fromRGBO(206, 55, 55, 1.0); static const Color blueCraiola = Color.fromRGBO(69, 110, 255, 1.0); static const Color blueGreyCraiola = Color.fromRGBO(106, 177, 207, 1.0); + static const Color greyBlueCraiola = Color.fromRGBO(116, 139, 219, 1.0); static const Color darkBlueCraiola = Color.fromRGBO(53, 86, 136, 1.0); static const Color pinkFlamingo = Color.fromRGBO(240, 60, 243, 1.0); static const Color redHat = Color.fromRGBO(209, 68, 37, 1.0); diff --git a/lib/router.dart b/lib/router.dart index 0474758e3..f8a3d454e 100644 --- a/lib/router.dart +++ b/lib/router.dart @@ -1,8 +1,9 @@ import 'package:cake_wallet/entities/contact_record.dart'; -import 'package:cake_wallet/entities/order.dart'; +import 'package:cake_wallet/buy/order.dart'; import 'package:cake_wallet/entities/transaction_description.dart'; import 'package:cake_wallet/src/screens/backup/backup_page.dart'; import 'package:cake_wallet/src/screens/backup/edit_backup_password_page.dart'; +import 'package:cake_wallet/src/screens/buy/pre_order_page.dart'; import 'package:cake_wallet/src/screens/order_details/order_details_page.dart'; import 'package:cake_wallet/src/screens/pin_code/pin_code_widget.dart'; import 'package:cake_wallet/src/screens/restore/restore_from_backup_page.dart'; @@ -297,6 +298,11 @@ Route createRoute(RouteSettings settings) { builder: (_) => getIt.get(param1: settings.arguments as Order)); + case Routes.preOrder: + return MaterialPageRoute( + builder: (_) => + getIt.get()); + case Routes.wyre: return MaterialPageRoute( builder: (_) => diff --git a/lib/routes.dart b/lib/routes.dart index 2bf8ee7b7..88b9b8440 100644 --- a/lib/routes.dart +++ b/lib/routes.dart @@ -54,4 +54,5 @@ class Routes { static const support = '/support'; static const orderDetails = '/order_details'; static const wyre = '/wyre'; + static const preOrder = '/pre_order'; } \ No newline at end of file diff --git a/lib/src/screens/buy/pre_order_page.dart b/lib/src/screens/buy/pre_order_page.dart new file mode 100644 index 000000000..e42b64790 --- /dev/null +++ b/lib/src/screens/buy/pre_order_page.dart @@ -0,0 +1,210 @@ +import 'dart:ui'; +import 'package:cake_wallet/buy/buy_amount.dart'; +import 'package:cake_wallet/src/screens/buy/widgets/buy_list_item.dart'; +import 'package:cake_wallet/src/widgets/keyboard_done_button.dart'; +import 'package:cake_wallet/view_model/buy/buy_view_model.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_mobx/flutter_mobx.dart'; +import 'package:keyboard_actions/keyboard_actions.dart'; +import 'package:cake_wallet/routes.dart'; +import 'package:cake_wallet/src/screens/base_page.dart'; +import 'package:cake_wallet/src/widgets/primary_button.dart'; +import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart'; +import 'package:cake_wallet/generated/i18n.dart'; +import 'package:cake_wallet/src/widgets/base_text_form_field.dart'; + +class PreOrderPage extends BasePage { + PreOrderPage({@required this.buyViewModel}) + : _amountFocus = FocusNode(), + _amountController = TextEditingController() { + + _amountController.addListener(() { + final amount = _amountController.text; + + if (amount != buyViewModel.buyAmountViewModel.amount) { + buyViewModel.buyAmountViewModel.amount = amount; + } + + if (buyViewModel.buyAmountViewModel.doubleAmount == 0.0) { + buyViewModel.selectedProvider = null; + } + }); + } + + final BuyViewModel buyViewModel; + final FocusNode _amountFocus; + final TextEditingController _amountController; + + @override + String get title => 'Buy Bitcoin'; + + @override + Color get titleColor => Colors.white; + + @override + bool get resizeToAvoidBottomInset => false; + + @override + bool get extendBodyBehindAppBar => true; + + @override + AppBarStyle get appBarStyle => AppBarStyle.transparent; + + @override + Widget body(BuildContext context) { + return KeyboardActions( + config: KeyboardActionsConfig( + keyboardActionsPlatform: KeyboardActionsPlatform.IOS, + keyboardBarColor: Theme.of(context).accentTextTheme.body2 + .backgroundColor, + nextFocus: false, + actions: [ + KeyboardActionsItem( + focusNode: _amountFocus, + toolbarButtons: [(_) => KeyboardDoneButton()], + ), + ]), + child: Container( + height: 0, + color: Theme.of(context).backgroundColor, + child: ScrollableWithBottomSection( + contentPadding: EdgeInsets.only(bottom: 24), + content: Column( + children: [ + Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.only( + bottomLeft: Radius.circular(24), + bottomRight: Radius.circular(24)), + gradient: LinearGradient(colors: [ + Theme.of(context).primaryTextTheme.subhead.color, + Theme.of(context) + .primaryTextTheme + .subhead + .decorationColor, + ], begin: Alignment.topLeft, end: Alignment.bottomRight), + ), + child: Padding( + padding: EdgeInsets.fromLTRB(100, 100, 100, 65), + child: BaseTextFormField( + focusNode: _amountFocus, + controller: _amountController, + keyboardType: + TextInputType.numberWithOptions( + signed: false, decimal: true), + inputFormatters: [ + FilteringTextInputFormatter + .allow(RegExp('^([0-9]+([.\,][0-9]{0,2})?|[.\,][0-9]{1,2})\$')) + ], + prefixIcon: Padding( + padding: EdgeInsets.only(top: 2), + child: + Text(buyViewModel.fiatCurrency.title + ': ', + style: TextStyle( + fontSize: 36, + fontWeight: FontWeight.w600, + color: Colors.white, + )), + ), + hintText: '0.00', + borderColor: Theme.of(context) + .primaryTextTheme + .headline + .color, + textStyle: TextStyle( + fontSize: 36, + fontWeight: FontWeight.w500, + color: Colors.white), + placeholderTextStyle: TextStyle( + color: Theme.of(context) + .primaryTextTheme + .headline + .decorationColor, + fontWeight: FontWeight.w500, + fontSize: 36), + ) + ) + ), + Padding( + padding: EdgeInsets.only(top: 38, bottom: 18), + child: Text( + 'Buy with:', + textAlign: TextAlign.center, + style: TextStyle( + color: Theme.of(context).primaryTextTheme.title.color, + fontSize: 18, + fontWeight: FontWeight.w500 + ), + ) + ), + ...buyViewModel.items.map( + (item) => Observer(builder: (_) => FutureBuilder( + future: item.buyAmount, + builder: (context, AsyncSnapshot snapshot) { + double sourceAmount; + double destAmount; + + if (snapshot.hasData) { + sourceAmount = snapshot.data.sourceAmount; + destAmount = snapshot.data.destAmount; + } else { + sourceAmount = 0.0; + destAmount = 0.0; + } + + return Padding( + padding: + EdgeInsets.only(left: 15, top: 20, right: 15), + child: Observer(builder: (_) => BuyListItem( + selectedProvider: buyViewModel.selectedProvider, + provider: item.provider, + sourceAmount: sourceAmount, + sourceCurrency: buyViewModel.fiatCurrency, + destAmount: destAmount, + destCurrency: buyViewModel.cryptoCurrency, + onTap: + buyViewModel.buyAmountViewModel + .doubleAmount == 0.0 ? null : () { + buyViewModel.selectedProvider = item.provider; + sourceAmount > 0 + ? buyViewModel.isDisabled = false + : buyViewModel.isDisabled = true; + } + )) + ); + } + ),) + ) + ], + ), + bottomSectionPadding: + EdgeInsets.only(left: 24, right: 24, bottom: 24), + bottomSection: Observer(builder: (_) { + return LoadingPrimaryButton( + onPressed: buyViewModel.isRunning + ? null + : () { + buyViewModel.isRunning = true; + + // FIXME: Start WebView + + buyViewModel.isRunning = false; + }, + text: buyViewModel.selectedProvider == null + ? 'Buy' + : 'Buy with ${buyViewModel.selectedProvider + .description.title}', + color: Theme.of(context).accentTextTheme.body2.color, + textColor: Colors.white, + isLoading: buyViewModel.isRunning, + isDisabled: (buyViewModel.selectedProvider == null) || + buyViewModel.isDisabled + ); + }) + ) + ) + ); + } +} \ No newline at end of file diff --git a/lib/src/screens/buy/widgets/buy_list_item.dart b/lib/src/screens/buy/widgets/buy_list_item.dart new file mode 100644 index 000000000..fe198a350 --- /dev/null +++ b/lib/src/screens/buy/widgets/buy_list_item.dart @@ -0,0 +1,131 @@ +import 'package:cake_wallet/buy/buy_provider_description.dart'; +import 'package:cake_wallet/buy/buy_provider.dart'; +import 'package:cake_wallet/entities/crypto_currency.dart'; +import 'package:cake_wallet/entities/fiat_currency.dart'; +import 'package:cake_wallet/palette.dart'; +import 'package:flutter/material.dart'; + +class BuyListItem extends StatelessWidget { + BuyListItem({ + @required this.selectedProvider, + @required this.provider, + @required this.sourceAmount, + @required this.sourceCurrency, + @required this.destAmount, + @required this.destCurrency, + @required this.onTap + }); + + final _wyreIcon = + Image.asset('assets/images/wyre-icon.png', width: 36, height: 36); + final _mooonPayIcon = + Image.asset('assets/images/wyre-icon.png', width: 36, height: 36); + + final BuyProvider selectedProvider; + final BuyProvider provider; + final double sourceAmount; + final FiatCurrency sourceCurrency; + final double destAmount; + final CryptoCurrency destCurrency; + final void Function() onTap; + + @override + Widget build(BuildContext context) { + final providerIcon = _getProviderIcon(provider.description); + + final backgroundColor = selectedProvider != null + ? selectedProvider.description == provider.description + ? Palette.greyBlueCraiola + : Palette.shadowWhite + : Palette.shadowWhite; + + final primaryTextColor = selectedProvider != null + ? selectedProvider.description == provider.description + ? Colors.white + : Palette.darkGray + : Palette.darkGray; + + final secondaryTextColor = selectedProvider != null + ? selectedProvider.description == provider.description + ? Colors.white + : Palette.darkBlueCraiola + : Palette.darkBlueCraiola; + + return GestureDetector( + onTap: () => onTap?.call(), + child: Container( + height: 102, + padding: EdgeInsets.only( + left: 20, + right: 20 + ), + decoration: BoxDecoration( + borderRadius: BorderRadius.all(Radius.circular(25)), + color: backgroundColor + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + mainAxisSize: MainAxisSize.max, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + if (providerIcon != null) Padding( + padding: EdgeInsets.only(right: 10), + child: providerIcon + ), + Text( + provider.description.title, + style: TextStyle( + color: primaryTextColor, + fontSize: 24, + fontWeight: FontWeight.w500 + ), + ) + ], + ), + Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + Text( + '${destAmount?.toString()} ${destCurrency.title}', + style: TextStyle( + color: secondaryTextColor, + fontSize: 18, + fontWeight: FontWeight.w500 + ), + ), + Padding( + padding: EdgeInsets.only(top: 5), + child: Text( + '${sourceAmount?.toString()} ${sourceCurrency.title}', + style: TextStyle( + color: primaryTextColor, + fontSize: 18, + fontWeight: FontWeight.w500 + ), + ), + ) + ], + ) + ], + ), + ) + ); + } + + Image _getProviderIcon(BuyProviderDescription providerDescription) { + switch (providerDescription) { + case BuyProviderDescription.wyre: + return _wyreIcon; + case BuyProviderDescription.moonPay: + //return _mooonPayIcon; + return null; + default: + return null; + } + } +} \ No newline at end of file diff --git a/lib/src/screens/dashboard/dashboard_page.dart b/lib/src/screens/dashboard/dashboard_page.dart index 172483ed0..9ea094dda 100644 --- a/lib/src/screens/dashboard/dashboard_page.dart +++ b/lib/src/screens/dashboard/dashboard_page.dart @@ -125,39 +125,13 @@ class DashboardPage extends BasePage { image: exchangeImage, title: S.of(context).exchange, route: Routes.exchange), - if (walletViewModel.type == WalletType.bitcoin) Observer( - builder: (_) => Stack( - clipBehavior: Clip.none, - alignment: Alignment.topCenter, - children: [ - if (walletViewModel.isRunningWebView) Positioned( - top: -5, - child: SpinKitRing( - color: Theme.of(context).buttonColor, - lineWidth: 3, - size: 70.0, - ), - ), - ActionButton( - image: buyImage, - title: S.of(context).buy, - onClick: walletViewModel.isRunningWebView - ? null - : () async { - try { - walletViewModel.isRunningWebView = true; - final url = - await walletViewModel.wyreViewModel.wyreUrl; - await Navigator.of(context) - .pushNamed(Routes.wyre, arguments: url); - walletViewModel.isRunningWebView = false; - } catch(e) { - print(e.toString()); - walletViewModel.isRunningWebView = false; - } - }) - ], - )), + if (walletViewModel.type == WalletType.bitcoin) ActionButton( + image: buyImage, + title: S.of(context).buy, + onClick: () { + Navigator.of(context).pushNamed(Routes.preOrder); + }, + ), ], )), ) diff --git a/lib/src/screens/send/send_page.dart b/lib/src/screens/send/send_page.dart index 9fae0ec23..eb4153676 100644 --- a/lib/src/screens/send/send_page.dart +++ b/lib/src/screens/send/send_page.dart @@ -67,7 +67,7 @@ class SendPage extends BasePage { Color get titleColor => Colors.white; @override - bool get resizeToAvoidBottomPadding => false; + bool get resizeToAvoidBottomInset => false; @override bool get extendBodyBehindAppBar => true; diff --git a/lib/store/dashboard/orders_store.dart b/lib/store/dashboard/orders_store.dart index 01c2b13a9..d3fc94bda 100644 --- a/lib/store/dashboard/orders_store.dart +++ b/lib/store/dashboard/orders_store.dart @@ -1,5 +1,5 @@ import 'dart:async'; -import 'package:cake_wallet/entities/order.dart'; +import 'package:cake_wallet/buy/order.dart'; import 'package:cake_wallet/view_model/dashboard/order_list_item.dart'; import 'package:hive/hive.dart'; import 'package:mobx/mobx.dart'; diff --git a/lib/view_model/buy/buy_amount_view_model.dart b/lib/view_model/buy/buy_amount_view_model.dart new file mode 100644 index 000000000..e027166f9 --- /dev/null +++ b/lib/view_model/buy/buy_amount_view_model.dart @@ -0,0 +1,28 @@ +import 'package:mobx/mobx.dart'; +import 'package:cake_wallet/entities/fiat_currency.dart'; + +part 'buy_amount_view_model.g.dart'; + +class BuyAmountViewModel = BuyAmountViewModelBase with _$BuyAmountViewModel; + +abstract class BuyAmountViewModelBase with Store { + BuyAmountViewModelBase() : amount = ''; + + @observable + String amount; + + FiatCurrency get fiatCurrency => FiatCurrency.usd; + + @computed + double get doubleAmount { + double _amount; + + try { + _amount = double.parse(amount.replaceAll(',', '.')) ?? 0.0; + } catch (e) { + _amount = 0.0; + } + + return _amount; + } +} \ No newline at end of file diff --git a/lib/view_model/buy/buy_item.dart b/lib/view_model/buy/buy_item.dart new file mode 100644 index 000000000..8d97597c4 --- /dev/null +++ b/lib/view_model/buy/buy_item.dart @@ -0,0 +1,29 @@ +import 'package:cake_wallet/buy/buy_amount.dart'; +import 'package:cake_wallet/buy/buy_provider.dart'; +import 'package:cake_wallet/entities/fiat_currency.dart'; +import 'package:cake_wallet/view_model/buy/buy_amount_view_model.dart'; + +class BuyItem { + BuyItem({this.provider, this.buyAmountViewModel}); + + final BuyProvider provider; + final BuyAmountViewModel buyAmountViewModel; + + double get amount => buyAmountViewModel.doubleAmount; + + FiatCurrency get fiatCurrency => buyAmountViewModel.fiatCurrency; + + Future get buyAmount async { + BuyAmount _buyAmount; + + try { + _buyAmount = await provider + .calculateAmount(amount?.toString(), fiatCurrency.title); + } catch (e) { + _buyAmount = BuyAmount(sourceAmount: 0.0, destAmount: 0.0); + print(e.toString()); + } + + return _buyAmount; + } +} \ No newline at end of file diff --git a/lib/view_model/buy/buy_view_model.dart b/lib/view_model/buy/buy_view_model.dart new file mode 100644 index 000000000..6b24cadfa --- /dev/null +++ b/lib/view_model/buy/buy_view_model.dart @@ -0,0 +1,81 @@ +import 'package:cake_wallet/buy/buy_provider.dart'; +import 'package:cake_wallet/buy/moonpay/moonpay_buy_provider.dart'; +import 'package:cake_wallet/buy/wyre/wyre_buy_provider.dart'; +import 'package:cake_wallet/entities/crypto_currency.dart'; +import 'package:cake_wallet/entities/fiat_currency.dart'; +import 'package:cake_wallet/entities/wallet_type.dart'; +import 'package:cake_wallet/view_model/buy/buy_item.dart'; +import 'package:flutter/foundation.dart'; +import 'package:hive/hive.dart'; +import 'package:cake_wallet/buy/order.dart'; +import 'package:cake_wallet/store/dashboard/orders_store.dart'; +import 'package:mobx/mobx.dart'; +import 'package:cake_wallet/core/wallet_base.dart'; +import 'buy_amount_view_model.dart'; + +part 'buy_view_model.g.dart'; + +class BuyViewModel = BuyViewModelBase with _$BuyViewModel; + +abstract class BuyViewModelBase with Store { + BuyViewModelBase(this.ordersSource, this.ordersStore, this.buyAmountViewModel, + {@required this.wallet}) { + providerList = [ + WyreBuyProvider(wallet: wallet), + MoonPayBuyProvider(wallet: wallet) + ]; + items = providerList.map((provider) => + BuyItem(provider: provider, buyAmountViewModel: buyAmountViewModel)) + .toList(); + isRunning = false; + isDisabled = true; + } + + final Box ordersSource; + final OrdersStore ordersStore; + final BuyAmountViewModel buyAmountViewModel; + final WalletBase wallet; + + @observable + List providerList; + + @observable + BuyProvider selectedProvider; + + @observable + List items; + + @observable + bool isRunning; + + @observable + bool isDisabled; + + WalletType get type => wallet.type; + + double get doubleAmount => buyAmountViewModel.doubleAmount; + + FiatCurrency get fiatCurrency => buyAmountViewModel.fiatCurrency; + + CryptoCurrency get cryptoCurrency => walletTypeToCryptoCurrency(type); + + Future createOrder() async { + try { + final url = await selectedProvider + ?.requestUrl(doubleAmount?.toString(), fiatCurrency.title); + // FIXME: Start WebView + } catch (e) { + print(e.toString()); + } + } + + Future saveOrder(String orderId) async { + try { + final order = await selectedProvider?.findOrderById(orderId); + await ordersSource.add(order); + ordersStore.setOrder(order); + } catch (e) { + print(e.toString()); + } + } +} \ No newline at end of file diff --git a/lib/view_model/dashboard/dashboard_view_model.dart b/lib/view_model/dashboard/dashboard_view_model.dart index 66fc53269..d471c866a 100644 --- a/lib/view_model/dashboard/dashboard_view_model.dart +++ b/lib/view_model/dashboard/dashboard_view_model.dart @@ -4,7 +4,7 @@ import 'dart:io'; import 'package:cake_wallet/bitcoin/bitcoin_transaction_info.dart'; import 'package:cake_wallet/bitcoin/bitcoin_wallet.dart'; import 'package:cake_wallet/entities/balance.dart'; -import 'package:cake_wallet/entities/order.dart'; +import 'package:cake_wallet/buy/order.dart'; import 'package:cake_wallet/entities/transaction_history.dart'; import 'package:cake_wallet/exchange/trade_state.dart'; import 'package:cake_wallet/monero/account.dart'; @@ -95,8 +95,6 @@ abstract class DashboardViewModelBase with Store { ] }; - isRunningWebView = false; - name = appStore.wallet?.name; wallet ??= appStore.wallet; type = wallet.type; @@ -162,9 +160,6 @@ abstract class DashboardViewModelBase with Store { @observable String subname; - @observable - bool isRunningWebView; - @computed String get address => wallet.address; diff --git a/lib/view_model/dashboard/order_list_item.dart b/lib/view_model/dashboard/order_list_item.dart index 7eeff4bd1..e4bb2cbd1 100644 --- a/lib/view_model/dashboard/order_list_item.dart +++ b/lib/view_model/dashboard/order_list_item.dart @@ -1,4 +1,4 @@ -import 'package:cake_wallet/entities/order.dart'; +import 'package:cake_wallet/buy/order.dart'; import 'package:cake_wallet/store/settings_store.dart'; import 'package:cake_wallet/view_model/dashboard/action_list_item.dart'; import 'package:cake_wallet/entities/balance_display_mode.dart'; diff --git a/lib/view_model/order_details_view_model.dart b/lib/view_model/order_details_view_model.dart index 44367a19d..275247670 100644 --- a/lib/view_model/order_details_view_model.dart +++ b/lib/view_model/order_details_view_model.dart @@ -1,5 +1,5 @@ import 'dart:async'; -import 'package:cake_wallet/entities/order.dart'; +import 'package:cake_wallet/buy/order.dart'; import 'package:cake_wallet/utils/date_formatter.dart'; import 'package:cake_wallet/view_model/wyre_view_model.dart'; import 'package:mobx/mobx.dart'; diff --git a/lib/view_model/wyre_view_model.dart b/lib/view_model/wyre_view_model.dart index 8624cd0e6..2d5ab3315 100644 --- a/lib/view_model/wyre_view_model.dart +++ b/lib/view_model/wyre_view_model.dart @@ -1,7 +1,7 @@ import 'package:cake_wallet/entities/wyre_service.dart'; import 'package:flutter/foundation.dart'; import 'package:hive/hive.dart'; -import 'package:cake_wallet/entities/order.dart'; +import 'package:cake_wallet/buy/order.dart'; import 'package:cake_wallet/store/dashboard/orders_store.dart'; import 'package:mobx/mobx.dart'; From 19ffffa6ccd9462cc2722b841a8c236971801448 Mon Sep 17 00:00:00 2001 From: OleksandrSobol Date: Tue, 13 Apr 2021 21:40:44 +0300 Subject: [PATCH 02/17] CAKE-306 | applied buy_webview_page.dart to the app; fixed order_details_view_model.dart and order_row.dart; applied localization to the pre_order_page.dart; deleted unused files --- lib/buy/buy_provider.dart | 1 + lib/buy/get_buy_provider_icon.dart | 23 +++ lib/buy/moonpay/moonpay_buy_provider.dart | 5 + lib/buy/wyre/wyre_buy_provider.dart | 9 +- lib/di.dart | 35 ++-- lib/entities/wyre_exception.dart | 8 - lib/entities/wyre_service.dart | 151 ------------------ lib/router.dart | 6 +- lib/routes.dart | 2 +- lib/src/screens/buy/buy_webview_page.dart | 108 +++++++++++++ lib/src/screens/buy/pre_order_page.dart | 32 ++-- .../screens/buy/widgets/buy_list_item.dart | 21 +-- .../screens/dashboard/widgets/order_row.dart | 10 +- .../dashboard/widgets/transactions_page.dart | 1 + lib/src/screens/wyre/wyre_page.dart | 97 ----------- lib/view_model/buy/buy_view_model.dart | 14 +- .../dashboard/dashboard_view_model.dart | 6 +- lib/view_model/order_details_view_model.dart | 85 ++++++---- lib/view_model/wyre_view_model.dart | 34 ---- res/values/strings_de.arb | 5 +- res/values/strings_en.arb | 5 +- res/values/strings_es.arb | 5 +- res/values/strings_hi.arb | 5 +- res/values/strings_ja.arb | 5 +- res/values/strings_ko.arb | 5 +- res/values/strings_nl.arb | 5 +- res/values/strings_pl.arb | 5 +- res/values/strings_pt.arb | 5 +- res/values/strings_ru.arb | 5 +- res/values/strings_uk.arb | 5 +- res/values/strings_zh.arb | 5 +- 31 files changed, 310 insertions(+), 398 deletions(-) create mode 100644 lib/buy/get_buy_provider_icon.dart delete mode 100644 lib/entities/wyre_exception.dart delete mode 100644 lib/entities/wyre_service.dart create mode 100644 lib/src/screens/buy/buy_webview_page.dart delete mode 100644 lib/src/screens/wyre/wyre_page.dart delete mode 100644 lib/view_model/wyre_view_model.dart diff --git a/lib/buy/buy_provider.dart b/lib/buy/buy_provider.dart index 78ad54451..b908d7bc5 100644 --- a/lib/buy/buy_provider.dart +++ b/lib/buy/buy_provider.dart @@ -12,6 +12,7 @@ abstract class BuyProvider { String get title; BuyProviderDescription get description; + String get trackUrl; WalletType get walletType => wallet.type; String get walletAddress => wallet.address; diff --git a/lib/buy/get_buy_provider_icon.dart b/lib/buy/get_buy_provider_icon.dart new file mode 100644 index 000000000..d58fe19d4 --- /dev/null +++ b/lib/buy/get_buy_provider_icon.dart @@ -0,0 +1,23 @@ +import 'package:flutter/material.dart'; +import 'package:cake_wallet/buy/buy_provider_description.dart'; + +Image getBuyProviderIcon(BuyProviderDescription providerDescription) { + final _wyreIcon = + Image.asset('assets/images/wyre-icon.png', width: 36, height: 36); + final _moonPayIcon = + Image.asset('assets/images/wyre-icon.png', width: 36, height: 36); + + if (providerDescription != null) { + switch (providerDescription) { + case BuyProviderDescription.wyre: + return _wyreIcon; + case BuyProviderDescription.moonPay: + //return _moonPayIcon; + return null; + default: + return null; + } + } else { + return null; + } +} \ No newline at end of file diff --git a/lib/buy/moonpay/moonpay_buy_provider.dart b/lib/buy/moonpay/moonpay_buy_provider.dart index 972b2d2a4..2fde667e4 100644 --- a/lib/buy/moonpay/moonpay_buy_provider.dart +++ b/lib/buy/moonpay/moonpay_buy_provider.dart @@ -34,6 +34,11 @@ class MoonPayBuyProvider extends BuyProvider { String get currencyCode => walletTypeToCryptoCurrency(walletType).title.toLowerCase(); + @override + String get trackUrl => isTestEnvironment + ? '' + : ''; // FIXME + String baseApiUrl; @override diff --git a/lib/buy/wyre/wyre_buy_provider.dart b/lib/buy/wyre/wyre_buy_provider.dart index 0b94816b2..3a09be0f6 100644 --- a/lib/buy/wyre/wyre_buy_provider.dart +++ b/lib/buy/wyre/wyre_buy_provider.dart @@ -16,9 +16,6 @@ class WyreBuyProvider extends BuyProvider { baseApiUrl = isTestEnvironment ? _baseTestApiUrl : _baseProductApiUrl; - trackUrl = isTestEnvironment - ? _trackTestUrl - : _trackProductUrl; } static const _baseTestApiUrl = 'https://api.testwyre.com'; @@ -40,8 +37,12 @@ class WyreBuyProvider extends BuyProvider { @override BuyProviderDescription get description => BuyProviderDescription.wyre; + @override + String get trackUrl => isTestEnvironment + ? _trackTestUrl + : _trackProductUrl; + String baseApiUrl; - String trackUrl; @override Future requestUrl(String amount, String sourceCurrency) async { diff --git a/lib/di.dart b/lib/di.dart index 8663b31de..0b107fde4 100644 --- a/lib/di.dart +++ b/lib/di.dart @@ -7,7 +7,6 @@ import 'package:cake_wallet/entities/load_current_wallet.dart'; import 'package:cake_wallet/buy/order.dart'; import 'package:cake_wallet/entities/transaction_description.dart'; import 'package:cake_wallet/entities/transaction_info.dart'; -import 'package:cake_wallet/entities/wyre_service.dart'; import 'package:cake_wallet/monero/monero_wallet_service.dart'; import 'package:cake_wallet/entities/contact.dart'; import 'package:cake_wallet/entities/node.dart'; @@ -15,6 +14,7 @@ import 'package:cake_wallet/exchange/trade.dart'; import 'package:cake_wallet/reactions/on_authentication_state_change.dart'; import 'package:cake_wallet/src/screens/backup/backup_page.dart'; import 'package:cake_wallet/src/screens/backup/edit_backup_password_page.dart'; +import 'package:cake_wallet/src/screens/buy/buy_webview_page.dart'; import 'package:cake_wallet/src/screens/buy/pre_order_page.dart'; import 'package:cake_wallet/src/screens/contact/contact_list_page.dart'; import 'package:cake_wallet/src/screens/contact/contact_page.dart'; @@ -42,7 +42,6 @@ import 'package:cake_wallet/src/screens/transaction_details/transaction_details_ import 'package:cake_wallet/src/screens/wallet_keys/wallet_keys_page.dart'; import 'package:cake_wallet/src/screens/exchange/exchange_page.dart'; import 'package:cake_wallet/src/screens/exchange/exchange_template_page.dart'; -import 'package:cake_wallet/src/screens/wyre/wyre_page.dart'; import 'package:cake_wallet/store/dashboard/orders_store.dart'; import 'package:cake_wallet/store/node_list_store.dart'; import 'package:cake_wallet/store/secret_store.dart'; @@ -91,7 +90,6 @@ import 'package:cake_wallet/view_model/wallet_list/wallet_list_view_model.dart'; import 'package:cake_wallet/view_model/wallet_restore_view_model.dart'; import 'package:cake_wallet/view_model/wallet_seed_view_model.dart'; import 'package:cake_wallet/view_model/exchange/exchange_view_model.dart'; -import 'package:cake_wallet/view_model/wyre_view_model.dart'; import 'package:flutter/widgets.dart'; import 'package:get_it/get_it.dart'; import 'package:hive/hive.dart'; @@ -241,8 +239,7 @@ Future setup( transactionFilterStore: getIt.get(), settingsStore: settingsStore, ordersSource: _ordersSource, - ordersStore: getIt.get(), - wyreViewModel: getIt.get())); + ordersStore: getIt.get())); getIt.registerFactory(() => AuthService( secureStorage: getIt.get(), @@ -534,20 +531,6 @@ Future setup( getIt.registerFactoryParam((Trade trade, _) => TradeDetailsPage(getIt.get(param1: trade))); - getIt.registerFactory(() { - final wallet = getIt.get().wallet; - return WyreService(wallet: wallet); - }); - - getIt.registerFactory(() { - return WyreViewModel(ordersSource, getIt.get(), - wyreService: getIt.get()); - }); - - getIt.registerFactoryParam((String url, _) => - WyrePage(getIt.get(), - ordersStore: getIt.get(), url: url)); - getIt.registerFactory(() => BuyAmountViewModel()); getIt.registerFactory(() { @@ -561,10 +544,18 @@ Future setup( return PreOrderPage(buyViewModel: getIt.get()); }); + getIt.registerFactoryParam((String url, _) => + BuyWebViewPage(getIt.get(), + ordersStore: getIt.get(), url: url)); + getIt.registerFactoryParam( - (order, _) => OrderDetailsViewModel( - wyreViewModel: getIt.get(), - orderForDetails: order)); + (order, _) { + final wallet = getIt.get().wallet; + + return OrderDetailsViewModel( + wallet: wallet, + orderForDetails: order); + }); getIt.registerFactoryParam((Order order, _) => OrderDetailsPage(getIt.get(param1: order))); diff --git a/lib/entities/wyre_exception.dart b/lib/entities/wyre_exception.dart deleted file mode 100644 index 85d9f5b60..000000000 --- a/lib/entities/wyre_exception.dart +++ /dev/null @@ -1,8 +0,0 @@ -class WyreException implements Exception { - WyreException(this.description); - - String description; - - @override - String toString() => description; -} \ No newline at end of file diff --git a/lib/entities/wyre_service.dart b/lib/entities/wyre_service.dart deleted file mode 100644 index f45f33d44..000000000 --- a/lib/entities/wyre_service.dart +++ /dev/null @@ -1,151 +0,0 @@ -import 'dart:convert'; -import 'package:cake_wallet/core/wallet_base.dart'; -import 'package:cake_wallet/entities/wyre_exception.dart'; -import 'package:cake_wallet/exchange/trade_state.dart'; -import 'package:flutter/foundation.dart'; -import 'package:http/http.dart'; -import 'package:cake_wallet/.secrets.g.dart' as secrets; -import 'package:cake_wallet/buy/order.dart'; -import 'package:cake_wallet/entities/wallet_type.dart'; - -class WyreService { - WyreService({ - @required this.wallet, - this.isTestEnvironment = false}) { - baseApiUrl = isTestEnvironment - ? _baseTestApiUrl - : _baseProductApiUrl; - trackUrl = isTestEnvironment - ? _trackTestUrl - : _trackProductUrl; - } - - static const _baseTestApiUrl = 'https://api.testwyre.com'; - static const _baseProductApiUrl = 'https://api.sendwyre.com'; - static const _trackTestUrl = 'https://dash.testwyre.com/track/'; - static const _trackProductUrl = 'https://dash.sendwyre.com/track/'; - static const _ordersSuffix = '/v3/orders'; - static const _reserveSuffix = '/reserve'; - static const _quoteSuffix = '/quote/partner'; - static const _timeStampSuffix = '?timestamp='; - static const _transferSuffix = '/v2/transfer/'; - static const _trackSuffix = '/track'; - - final bool isTestEnvironment; - final WalletBase wallet; - - WalletType get walletType => wallet.type; - String get walletAddress => wallet.address; - String get walletId => wallet.id; - - String baseApiUrl; - String trackUrl; - - Future getWyreUrl() async { - final timestamp = DateTime.now().millisecondsSinceEpoch.toString(); - final url = baseApiUrl + _ordersSuffix + _reserveSuffix + - _timeStampSuffix + timestamp; - final secretKey = secrets.wyreSecretKey; - final accountId = secrets.wyreAccountId; - final body = { - 'amount': '1', - 'sourceCurrency': 'USD', - 'destCurrency': walletTypeToCryptoCurrency(walletType).title, - 'dest': walletTypeToString(walletType).toLowerCase() + ':' + walletAddress, - 'referrerAccountId': accountId, - 'lockFields': ['amount', 'sourceCurrency', 'destCurrency', 'dest'] - }; - - final response = await post(url, - headers: { - 'Authorization': 'Bearer $secretKey', - 'Content-Type': 'application/json', - 'cache-control': 'no-cache' - }, - body: json.encode(body)); - - if (response.statusCode != 200) { - throw WyreException('Url $url is not found!'); - } - - final responseJSON = json.decode(response.body) as Map; - final urlFromResponse = responseJSON['url'] as String; - return urlFromResponse; - } - - Future findOrderById(String id) async { - final orderUrl = baseApiUrl + _ordersSuffix + '/$id'; - final orderResponse = await get(orderUrl); - - if (orderResponse.statusCode != 200) { - throw WyreException('Order $id is not found!'); - } - - final orderResponseJSON = - json.decode(orderResponse.body) as Map; - final transferId = orderResponseJSON['transferId'] as String; - final from = orderResponseJSON['sourceCurrency'] as String; - final to = orderResponseJSON['destCurrency'] as String; - final status = orderResponseJSON['status'] as String; - final state = TradeState.deserialize(raw: status.toLowerCase()); - final createdAtRaw = orderResponseJSON['createdAt'] as int; - final createdAt = - DateTime.fromMillisecondsSinceEpoch(createdAtRaw).toLocal(); - - final transferUrl = - baseApiUrl + _transferSuffix + transferId + _trackSuffix; - final transferResponse = await get(transferUrl); - - if (transferResponse.statusCode != 200) { - throw WyreException('Transfer $transferId is not found!'); - } - - final transferResponseJSON = - json.decode(transferResponse.body) as Map; - final amount = transferResponseJSON['destAmount'] as double; - - return Order( - id: id, - transferId: transferId, - from: from, - to: to, - state: state, - createdAt: createdAt, - amount: amount.toString(), - receiveAddress: walletAddress, - walletId: walletId - ); - } - - Future getAmount() async { - final quoteUrl = baseApiUrl + _ordersSuffix + _quoteSuffix; - - final secretKey = secrets.wyreSecretKey; - final accountId = secrets.wyreAccountId; - final body = { - 'amount': '1', - 'sourceCurrency': 'USD', - 'destCurrency': walletTypeToCryptoCurrency(walletType).title, - 'dest': walletTypeToString(walletType).toLowerCase() + ':' + walletAddress, - 'accountId': accountId, - 'country': 'US' - }; - - final response = await post(quoteUrl, - headers: { - 'Authorization': 'Bearer $secretKey', - 'Content-Type': 'application/json', - 'cache-control': 'no-cache' - }, - body: json.encode(body)); - - if (response.statusCode != 200) { - throw WyreException('Quote is not found! '); - } - - final responseJSON = json.decode(response.body) as Map; - final amount = responseJSON['destAmount'] as double; - - return amount; - } -} \ No newline at end of file diff --git a/lib/router.dart b/lib/router.dart index f8a3d454e..a3421fa69 100644 --- a/lib/router.dart +++ b/lib/router.dart @@ -3,6 +3,7 @@ import 'package:cake_wallet/buy/order.dart'; import 'package:cake_wallet/entities/transaction_description.dart'; import 'package:cake_wallet/src/screens/backup/backup_page.dart'; import 'package:cake_wallet/src/screens/backup/edit_backup_password_page.dart'; +import 'package:cake_wallet/src/screens/buy/buy_webview_page.dart'; import 'package:cake_wallet/src/screens/buy/pre_order_page.dart'; import 'package:cake_wallet/src/screens/order_details/order_details_page.dart'; import 'package:cake_wallet/src/screens/pin_code/pin_code_widget.dart'; @@ -10,7 +11,6 @@ import 'package:cake_wallet/src/screens/restore/restore_from_backup_page.dart'; import 'package:cake_wallet/src/screens/restore/wallet_restore_page.dart'; import 'package:cake_wallet/src/screens/seed/pre_seed_page.dart'; import 'package:cake_wallet/src/screens/support/support_page.dart'; -import 'package:cake_wallet/src/screens/wyre/wyre_page.dart'; import 'package:cake_wallet/store/settings_store.dart'; import 'package:cake_wallet/view_model/monero_account_list/account_list_item.dart'; import 'package:flutter/cupertino.dart'; @@ -303,10 +303,10 @@ Route createRoute(RouteSettings settings) { builder: (_) => getIt.get()); - case Routes.wyre: + case Routes.buyWebView: return MaterialPageRoute( builder: (_) => - getIt.get(param1: settings.arguments as String)); + getIt.get(param1: settings.arguments as String)); case Routes.restoreWalletFromSeedDetails: final args = settings.arguments as List; diff --git a/lib/routes.dart b/lib/routes.dart index 88b9b8440..6ac75bceb 100644 --- a/lib/routes.dart +++ b/lib/routes.dart @@ -53,6 +53,6 @@ class Routes { static const restoreFromBackup = '/restore_from_backup'; static const support = '/support'; static const orderDetails = '/order_details'; - static const wyre = '/wyre'; static const preOrder = '/pre_order'; + static const buyWebView = '/buy_web_view'; } \ No newline at end of file diff --git a/lib/src/screens/buy/buy_webview_page.dart b/lib/src/screens/buy/buy_webview_page.dart new file mode 100644 index 000000000..91e93f902 --- /dev/null +++ b/lib/src/screens/buy/buy_webview_page.dart @@ -0,0 +1,108 @@ +import 'dart:async'; +import 'dart:io'; +import 'package:cake_wallet/buy/buy_provider.dart'; +import 'package:cake_wallet/buy/moonpay/moonpay_buy_provider.dart'; +import 'package:cake_wallet/buy/wyre/wyre_buy_provider.dart'; +import 'package:cake_wallet/generated/i18n.dart'; +import 'package:cake_wallet/palette.dart'; +import 'package:cake_wallet/src/screens/base_page.dart'; +import 'package:cake_wallet/store/dashboard/orders_store.dart'; +import 'package:cake_wallet/view_model/buy/buy_view_model.dart'; +import 'package:flutter/material.dart'; +import 'package:webview_flutter/webview_flutter.dart'; + +class BuyWebViewPage extends BasePage { + BuyWebViewPage(this.buyViewModel, + {@required this.ordersStore, @required this.url}); + + final OrdersStore ordersStore; + final String url; + final BuyViewModel buyViewModel; + + @override + String get title => S.current.buy; + + @override + Color get backgroundDarkColor => Colors.white; + + @override + Color get titleColor => Palette.darkBlueCraiola; + + @override + Widget body(BuildContext context) => + BuyWebViewPageBody(buyViewModel, ordersStore: ordersStore, url: url); +} + +class BuyWebViewPageBody extends StatefulWidget { + BuyWebViewPageBody(this.buyViewModel, {this.ordersStore, this.url}); + + final OrdersStore ordersStore; + final String url; + final BuyViewModel buyViewModel; + + @override + BuyWebViewPageBodyState createState() => BuyWebViewPageBodyState(); +} + +class BuyWebViewPageBodyState extends State { + String orderId; + WebViewController _webViewController; + GlobalKey _webViewkey; + Timer _timer; + bool _isSaving; + BuyProvider _provider; + + @override + void initState() { + super.initState(); + _webViewkey = GlobalKey(); + _isSaving = false; + widget.ordersStore.orderId = ''; + _provider = widget.buyViewModel.selectedProvider; + + if (Platform.isAndroid) WebView.platform = SurfaceAndroidWebView(); + + if (_provider is WyreBuyProvider) { + _timer?.cancel(); + _timer = Timer.periodic(Duration(seconds: 1), (timer) async { + + try { + if (_webViewController == null || _isSaving) { + return; + } + + final url = await _webViewController.currentUrl(); + + if (url.contains('completed')) { + final urlParts = url.split('/'); + orderId = urlParts.last; + widget.ordersStore.orderId = orderId; + + if (orderId.isNotEmpty) { + _isSaving = true; + await widget.buyViewModel.saveOrder(orderId); + timer.cancel(); + } + } + } catch (e) { + _isSaving = false; + print(e); + } + }); + } + + if (_provider is MoonPayBuyProvider) { + // FIXME: fetch orderId + } + } + + @override + Widget build(BuildContext context) { + return WebView( + key: _webViewkey, + initialUrl: widget.url, + javascriptMode: JavascriptMode.unrestricted, + onWebViewCreated: (WebViewController controller) => + setState(() => _webViewController = controller)); + } +} diff --git a/lib/src/screens/buy/pre_order_page.dart b/lib/src/screens/buy/pre_order_page.dart index e42b64790..0cf6a1f88 100644 --- a/lib/src/screens/buy/pre_order_page.dart +++ b/lib/src/screens/buy/pre_order_page.dart @@ -14,6 +14,7 @@ import 'package:cake_wallet/src/widgets/primary_button.dart'; import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart'; import 'package:cake_wallet/generated/i18n.dart'; import 'package:cake_wallet/src/widgets/base_text_form_field.dart'; +import 'package:mobx/mobx.dart'; class PreOrderPage extends BasePage { PreOrderPage({@required this.buyViewModel}) @@ -31,14 +32,22 @@ class PreOrderPage extends BasePage { buyViewModel.selectedProvider = null; } }); + + reaction((_) => buyViewModel.buyAmountViewModel.amount, (String amount) { + if (_amountController.text != amount) { + _amountController.text = amount; + } + }); } + static const _amountPattern = '^([0-9]+([.\,][0-9]{0,2})?|[.\,][0-9]{1,2})\$'; + final BuyViewModel buyViewModel; final FocusNode _amountFocus; final TextEditingController _amountController; @override - String get title => 'Buy Bitcoin'; + String get title => S.current.buy_bitcoin; @override Color get titleColor => Colors.white; @@ -96,7 +105,7 @@ class PreOrderPage extends BasePage { signed: false, decimal: true), inputFormatters: [ FilteringTextInputFormatter - .allow(RegExp('^([0-9]+([.\,][0-9]{0,2})?|[.\,][0-9]{1,2})\$')) + .allow(RegExp(_amountPattern)) ], prefixIcon: Padding( padding: EdgeInsets.only(top: 2), @@ -130,7 +139,7 @@ class PreOrderPage extends BasePage { Padding( padding: EdgeInsets.only(top: 38, bottom: 18), child: Text( - 'Buy with:', + S.of(context).buy_with + ':', textAlign: TextAlign.center, style: TextStyle( color: Theme.of(context).primaryTextTheme.title.color, @@ -185,16 +194,21 @@ class PreOrderPage extends BasePage { return LoadingPrimaryButton( onPressed: buyViewModel.isRunning ? null - : () { + : () async { buyViewModel.isRunning = true; - - // FIXME: Start WebView - + final url = + await buyViewModel.fetchUrl(); + if (url.isNotEmpty) { + await Navigator.of(context) + .pushNamed(Routes.buyWebView, arguments: url); + buyViewModel.reset(); + } buyViewModel.isRunning = false; }, text: buyViewModel.selectedProvider == null - ? 'Buy' - : 'Buy with ${buyViewModel.selectedProvider + ? S.of(context).buy + : S.of(context).buy_with + + ' ${buyViewModel.selectedProvider .description.title}', color: Theme.of(context).accentTextTheme.body2.color, textColor: Colors.white, diff --git a/lib/src/screens/buy/widgets/buy_list_item.dart b/lib/src/screens/buy/widgets/buy_list_item.dart index fe198a350..8ee3950aa 100644 --- a/lib/src/screens/buy/widgets/buy_list_item.dart +++ b/lib/src/screens/buy/widgets/buy_list_item.dart @@ -1,5 +1,5 @@ -import 'package:cake_wallet/buy/buy_provider_description.dart'; import 'package:cake_wallet/buy/buy_provider.dart'; +import 'package:cake_wallet/buy/get_buy_provider_icon.dart'; import 'package:cake_wallet/entities/crypto_currency.dart'; import 'package:cake_wallet/entities/fiat_currency.dart'; import 'package:cake_wallet/palette.dart'; @@ -16,11 +16,6 @@ class BuyListItem extends StatelessWidget { @required this.onTap }); - final _wyreIcon = - Image.asset('assets/images/wyre-icon.png', width: 36, height: 36); - final _mooonPayIcon = - Image.asset('assets/images/wyre-icon.png', width: 36, height: 36); - final BuyProvider selectedProvider; final BuyProvider provider; final double sourceAmount; @@ -31,7 +26,7 @@ class BuyListItem extends StatelessWidget { @override Widget build(BuildContext context) { - final providerIcon = _getProviderIcon(provider.description); + final providerIcon = getBuyProviderIcon(provider.description); final backgroundColor = selectedProvider != null ? selectedProvider.description == provider.description @@ -116,16 +111,4 @@ class BuyListItem extends StatelessWidget { ) ); } - - Image _getProviderIcon(BuyProviderDescription providerDescription) { - switch (providerDescription) { - case BuyProviderDescription.wyre: - return _wyreIcon; - case BuyProviderDescription.moonPay: - //return _mooonPayIcon; - return null; - default: - return null; - } - } } \ No newline at end of file diff --git a/lib/src/screens/dashboard/widgets/order_row.dart b/lib/src/screens/dashboard/widgets/order_row.dart index cfb2c88eb..49afa7a25 100644 --- a/lib/src/screens/dashboard/widgets/order_row.dart +++ b/lib/src/screens/dashboard/widgets/order_row.dart @@ -1,22 +1,26 @@ +import 'package:cake_wallet/buy/buy_provider_description.dart'; +import 'package:cake_wallet/buy/get_buy_provider_icon.dart'; import 'package:flutter/material.dart'; class OrderRow extends StatelessWidget { OrderRow({ @required this.onTap, + @required this.provider, this.from, this.to, this.createdAtFormattedDate, this.formattedAmount}); final VoidCallback onTap; + final BuyProviderDescription provider; final String from; final String to; final String createdAtFormattedDate; final String formattedAmount; - final wyreImage = - Image.asset('assets/images/wyre-icon.png', width: 36, height: 36); @override Widget build(BuildContext context) { + final providerIcon = getBuyProviderIcon(provider); + return InkWell( onTap: onTap, child: Container( @@ -26,7 +30,7 @@ class OrderRow extends StatelessWidget { mainAxisSize: MainAxisSize.max, crossAxisAlignment: CrossAxisAlignment.center, children: [ - wyreImage, + providerIcon ?? Offstage(), SizedBox(width: 12), Expanded( child: Column( diff --git a/lib/src/screens/dashboard/widgets/transactions_page.dart b/lib/src/screens/dashboard/widgets/transactions_page.dart index 482d23a75..942177b40 100644 --- a/lib/src/screens/dashboard/widgets/transactions_page.dart +++ b/lib/src/screens/dashboard/widgets/transactions_page.dart @@ -84,6 +84,7 @@ class TransactionsPage extends StatelessWidget { onTap: () => Navigator.of(context).pushNamed( Routes.orderDetails, arguments: order), + provider: order.provider, from: order.from, to: order.to, createdAtFormattedDate: diff --git a/lib/src/screens/wyre/wyre_page.dart b/lib/src/screens/wyre/wyre_page.dart deleted file mode 100644 index 1ba49da64..000000000 --- a/lib/src/screens/wyre/wyre_page.dart +++ /dev/null @@ -1,97 +0,0 @@ -import 'dart:async'; -import 'dart:io'; -import 'package:cake_wallet/generated/i18n.dart'; -import 'package:cake_wallet/palette.dart'; -import 'package:cake_wallet/src/screens/base_page.dart'; -import 'package:cake_wallet/store/dashboard/orders_store.dart'; -import 'package:cake_wallet/view_model/wyre_view_model.dart'; -import 'package:flutter/material.dart'; -import 'package:webview_flutter/webview_flutter.dart'; - -class WyrePage extends BasePage { - WyrePage(this.wyreViewModel, - {@required this.ordersStore, @required this.url}); - - final OrdersStore ordersStore; - final String url; - final WyreViewModel wyreViewModel; - - @override - String get title => S.current.buy; - - @override - Color get backgroundDarkColor => Colors.white; - - @override - Color get titleColor => Palette.darkBlueCraiola; - - @override - Widget body(BuildContext context) => - WyrePageBody(wyreViewModel, ordersStore: ordersStore, url: url); -} - -class WyrePageBody extends StatefulWidget { - WyrePageBody(this.wyreViewModel, {this.ordersStore, this.url}); - - final OrdersStore ordersStore; - final String url; - final WyreViewModel wyreViewModel; - - @override - WyrePageBodyState createState() => WyrePageBodyState(); -} - -class WyrePageBodyState extends State { - String orderId; - WebViewController _webViewController; - GlobalKey _webViewkey; - Timer _timer; - bool _isSaving; - - @override - void initState() { - super.initState(); - _webViewkey = GlobalKey(); - _isSaving = false; - widget.ordersStore.orderId = ''; - - if (Platform.isAndroid) WebView.platform = SurfaceAndroidWebView(); - - _timer?.cancel(); - _timer = Timer.periodic(Duration(seconds: 1), (timer) async { - - try { - if (_webViewController == null || _isSaving) { - return; - } - - final url = await _webViewController.currentUrl(); - - if (url.contains('completed')) { - final urlParts = url.split('/'); - orderId = urlParts.last; - widget.ordersStore.orderId = orderId; - - if (orderId.isNotEmpty) { - _isSaving = true; - await widget.wyreViewModel.saveOrder(orderId); - timer.cancel(); - } - } - } catch (e) { - _isSaving = false; - print(e); - } - }); - } - - @override - Widget build(BuildContext context) { - return WebView( - key: _webViewkey, - initialUrl: widget.url, - javascriptMode: JavascriptMode.unrestricted, - onWebViewCreated: (WebViewController controller) => - setState(() => _webViewController = controller)); - } -} diff --git a/lib/view_model/buy/buy_view_model.dart b/lib/view_model/buy/buy_view_model.dart index 6b24cadfa..10dd9a35a 100644 --- a/lib/view_model/buy/buy_view_model.dart +++ b/lib/view_model/buy/buy_view_model.dart @@ -59,14 +59,17 @@ abstract class BuyViewModelBase with Store { CryptoCurrency get cryptoCurrency => walletTypeToCryptoCurrency(type); - Future createOrder() async { + Future fetchUrl() async { + String _url = ''; + try { - final url = await selectedProvider + _url = await selectedProvider ?.requestUrl(doubleAmount?.toString(), fiatCurrency.title); - // FIXME: Start WebView } catch (e) { print(e.toString()); } + + return _url; } Future saveOrder(String orderId) async { @@ -78,4 +81,9 @@ abstract class BuyViewModelBase with Store { print(e.toString()); } } + + void reset() { + buyAmountViewModel.amount = ''; + selectedProvider = null; + } } \ No newline at end of file diff --git a/lib/view_model/dashboard/dashboard_view_model.dart b/lib/view_model/dashboard/dashboard_view_model.dart index d471c866a..10e6a5afd 100644 --- a/lib/view_model/dashboard/dashboard_view_model.dart +++ b/lib/view_model/dashboard/dashboard_view_model.dart @@ -28,7 +28,6 @@ import 'package:cake_wallet/view_model/dashboard/trade_list_item.dart'; import 'package:cake_wallet/view_model/dashboard/transaction_list_item.dart'; import 'package:cake_wallet/view_model/dashboard/action_list_item.dart'; import 'package:cake_wallet/view_model/dashboard/action_list_display_mode.dart'; -import 'package:cake_wallet/view_model/wyre_view_model.dart'; import 'package:crypto/crypto.dart'; import 'package:flutter/services.dart'; import 'package:hive/hive.dart'; @@ -59,8 +58,7 @@ abstract class DashboardViewModelBase with Store { this.transactionFilterStore, this.settingsStore, this.ordersSource, - this.ordersStore, - this.wyreViewModel}) { + this.ordersStore}) { filterItems = { S.current.transactions: [ FilterItem( @@ -230,8 +228,6 @@ abstract class DashboardViewModelBase with Store { TransactionFilterStore transactionFilterStore; - WyreViewModel wyreViewModel; - Map> filterItems; bool get isBuyEnabled => settingsStore.isBitcoinBuyEnabled; diff --git a/lib/view_model/order_details_view_model.dart b/lib/view_model/order_details_view_model.dart index 275247670..8e8ed3c18 100644 --- a/lib/view_model/order_details_view_model.dart +++ b/lib/view_model/order_details_view_model.dart @@ -1,12 +1,16 @@ import 'dart:async'; +import 'package:cake_wallet/buy/buy_provider.dart'; +import 'package:cake_wallet/buy/buy_provider_description.dart'; import 'package:cake_wallet/buy/order.dart'; import 'package:cake_wallet/utils/date_formatter.dart'; -import 'package:cake_wallet/view_model/wyre_view_model.dart'; import 'package:mobx/mobx.dart'; import 'package:cake_wallet/generated/i18n.dart'; import 'package:cake_wallet/src/screens/transaction_details/standart_list_item.dart'; import 'package:cake_wallet/src/screens/trade_details/track_trade_list_item.dart'; import 'package:url_launcher/url_launcher.dart'; +import 'package:cake_wallet/core/wallet_base.dart'; +import 'package:cake_wallet/buy/moonpay/moonpay_buy_provider.dart'; +import 'package:cake_wallet/buy/wyre/wyre_buy_provider.dart'; part 'order_details_view_model.g.dart'; @@ -14,16 +18,27 @@ class OrderDetailsViewModel = OrderDetailsViewModelBase with _$OrderDetailsViewModel; abstract class OrderDetailsViewModelBase with Store { - OrderDetailsViewModelBase({this.wyreViewModel, Order orderForDetails}) { + OrderDetailsViewModelBase({WalletBase wallet, Order orderForDetails}) { order = orderForDetails; + if (order.provider != null) { + switch (order.provider) { + case BuyProviderDescription.wyre: + _provider = WyreBuyProvider(wallet: wallet); + break; + case BuyProviderDescription.moonPay: + _provider = MoonPayBuyProvider(wallet: wallet); + break; + } + } + items = ObservableList(); _updateItems(); _updateOrder(); - _timer = Timer.periodic(Duration(seconds: 20), (_) async => _updateOrder()); + timer = Timer.periodic(Duration(seconds: 20), (_) async => _updateOrder()); } @observable @@ -32,21 +47,20 @@ abstract class OrderDetailsViewModelBase with Store { @observable ObservableList items; - WyreViewModel wyreViewModel; + BuyProvider _provider; - Timer _timer; + Timer timer; @action Future _updateOrder() async { try { - final updatedOrder = - await wyreViewModel.wyreService.findOrderById(order.id); - - updatedOrder.receiveAddress = order.receiveAddress; - updatedOrder.walletId = order.walletId; - order = updatedOrder; - - _updateItems(); + if (_provider != null) { + final updatedOrder = await _provider.findOrderById(order.id); + updatedOrder.receiveAddress = order.receiveAddress; + updatedOrder.walletId = order.walletId; + order = updatedOrder; + _updateItems(); + } } catch (e) { print(e.toString()); } @@ -54,8 +68,6 @@ abstract class OrderDetailsViewModelBase with Store { void _updateItems() { final dateFormat = DateFormatter.withCurrentLocal(); - final buildURL = - wyreViewModel.trackUrl + '${order.transferId}'; items?.clear(); @@ -68,18 +80,37 @@ abstract class OrderDetailsViewModelBase with Store { value: order.state != null ? order.state.toString() : S.current.trade_details_fetching), - TrackTradeListItem( - title: 'Track', - value: buildURL, - onTap: () { - launch(buildURL); - }), - StandartListItem( - title: S.current.trade_details_created_at, - value: dateFormat.format(order.createdAt).toString()), - StandartListItem( - title: S.current.trade_details_pair, - value: '${order.from} → ${order.to}') ]); + + if (order.provider != null) { + items.add( + StandartListItem( + title: 'Buy provider', + value: order.provider.title) + ); + } + + if (_provider.trackUrl.isNotEmpty) { + final buildURL = _provider.trackUrl + '${order.transferId}'; + items.add( + TrackTradeListItem( + title: 'Track', + value: buildURL, + onTap: () => launch(buildURL) + ) + ); + } + + items.add( + StandartListItem( + title: S.current.trade_details_created_at, + value: dateFormat.format(order.createdAt).toString()) + ); + + items.add( + StandartListItem( + title: S.current.trade_details_pair, + value: '${order.from} → ${order.to}') + ); } } \ No newline at end of file diff --git a/lib/view_model/wyre_view_model.dart b/lib/view_model/wyre_view_model.dart deleted file mode 100644 index 2d5ab3315..000000000 --- a/lib/view_model/wyre_view_model.dart +++ /dev/null @@ -1,34 +0,0 @@ -import 'package:cake_wallet/entities/wyre_service.dart'; -import 'package:flutter/foundation.dart'; -import 'package:hive/hive.dart'; -import 'package:cake_wallet/buy/order.dart'; -import 'package:cake_wallet/store/dashboard/orders_store.dart'; -import 'package:mobx/mobx.dart'; - -part 'wyre_view_model.g.dart'; - -class WyreViewModel = WyreViewModelBase with _$WyreViewModel; - -abstract class WyreViewModelBase with Store { - WyreViewModelBase(this.ordersSource, this.ordersStore, - {@required this.wyreService}); - - Future get wyreUrl => wyreService.getWyreUrl(); - - String get trackUrl => wyreService.trackUrl; - - final Box ordersSource; - final OrdersStore ordersStore; - - final WyreService wyreService; - - Future saveOrder(String orderId) async { - try { - final order = await wyreService.findOrderById(orderId); - await ordersSource.add(order); - ordersStore.setOrder(order); - } catch (e) { - print(e.toString()); - } - } -} \ No newline at end of file diff --git a/res/values/strings_de.arb b/res/values/strings_de.arb index 6d1d66b84..14c692c0d 100644 --- a/res/values/strings_de.arb +++ b/res/values/strings_de.arb @@ -469,5 +469,8 @@ "unconfirmed" : "Unbestätigt", "displayable" : "Anzeigebar", - "submit_request" : "Einen Antrag stellen" + "submit_request" : "Einen Antrag stellen", + + "buy_bitcoin" : "Bitcoin kaufen", + "buy_with" : "Kaufen mit" } \ No newline at end of file diff --git a/res/values/strings_en.arb b/res/values/strings_en.arb index b0d98b7c9..b2cebcca3 100644 --- a/res/values/strings_en.arb +++ b/res/values/strings_en.arb @@ -469,5 +469,8 @@ "unconfirmed" : "Unconfirmed", "displayable" : "Displayable", - "submit_request" : "submit a request" + "submit_request" : "submit a request", + + "buy_bitcoin" : "Buy Bitcoin", + "buy_with" : "Buy with" } \ No newline at end of file diff --git a/res/values/strings_es.arb b/res/values/strings_es.arb index 190ad4304..20c6e1586 100644 --- a/res/values/strings_es.arb +++ b/res/values/strings_es.arb @@ -469,5 +469,8 @@ "unconfirmed" : "Inconfirmado", "displayable" : "Visualizable", - "submit_request" : "presentar una solicitud" + "submit_request" : "presentar una solicitud", + + "buy_bitcoin" : "Comprar Bitcoin", + "buy_with" : "Compra con" } \ No newline at end of file diff --git a/res/values/strings_hi.arb b/res/values/strings_hi.arb index 6eac14e99..e93615549 100644 --- a/res/values/strings_hi.arb +++ b/res/values/strings_hi.arb @@ -469,5 +469,8 @@ "unconfirmed" : "अपुष्ट", "displayable" : "प्रदर्शन योग्य", - "submit_request" : "एक अनुरोध सबमिट करें" + "submit_request" : "एक अनुरोध सबमिट करें", + + "buy_bitcoin" : "बिटकॉइन खरीदें", + "buy_with" : "के साथ खरीदें" } \ No newline at end of file diff --git a/res/values/strings_ja.arb b/res/values/strings_ja.arb index 291d5afd3..113e60f46 100644 --- a/res/values/strings_ja.arb +++ b/res/values/strings_ja.arb @@ -469,5 +469,8 @@ "unconfirmed" : "未確認", "displayable" : "表示可能", - "submit_request" : "リクエストを送信する" + "submit_request" : "リクエストを送信する", + + "buy_bitcoin" : "ビットコインを購入する", + "buy_with" : "で購入" } \ No newline at end of file diff --git a/res/values/strings_ko.arb b/res/values/strings_ko.arb index 781df8d8a..e225f4746 100644 --- a/res/values/strings_ko.arb +++ b/res/values/strings_ko.arb @@ -469,5 +469,8 @@ "unconfirmed" : "미확인", "displayable" : "표시 가능", - "submit_request" : "요청을 제출" + "submit_request" : "요청을 제출", + + "buy_bitcoin" : "비트 코인 구매", + "buy_with" : "구매" } \ No newline at end of file diff --git a/res/values/strings_nl.arb b/res/values/strings_nl.arb index 9a6183efb..04c239ff4 100644 --- a/res/values/strings_nl.arb +++ b/res/values/strings_nl.arb @@ -469,5 +469,8 @@ "unconfirmed" : "Niet bevestigd", "displayable" : "Weer te geven", - "submit_request" : "een verzoek indienen" + "submit_request" : "een verzoek indienen", + + "buy_bitcoin" : "Koop Bitcoin", + "buy_with" : "Koop met" } \ No newline at end of file diff --git a/res/values/strings_pl.arb b/res/values/strings_pl.arb index 6db66958e..542223c18 100644 --- a/res/values/strings_pl.arb +++ b/res/values/strings_pl.arb @@ -469,5 +469,8 @@ "unconfirmed" : "Niepotwierdzony", "displayable" : "Wyświetlane", - "submit_request" : "złożyć wniosek" + "submit_request" : "złożyć wniosek", + + "buy_bitcoin" : "Kup Bitcoin", + "buy_with" : "Kup za pomocą" } \ No newline at end of file diff --git a/res/values/strings_pt.arb b/res/values/strings_pt.arb index 631be8f42..ea56dfca9 100644 --- a/res/values/strings_pt.arb +++ b/res/values/strings_pt.arb @@ -469,5 +469,8 @@ "unconfirmed" : "Não confirmado", "displayable" : "Exibível", - "submit_request" : "enviar um pedido" + "submit_request" : "enviar um pedido", + + "buy_bitcoin" : "Compre Bitcoin", + "buy_with" : "Compre com" } \ No newline at end of file diff --git a/res/values/strings_ru.arb b/res/values/strings_ru.arb index 333a823d8..319a1753f 100644 --- a/res/values/strings_ru.arb +++ b/res/values/strings_ru.arb @@ -469,5 +469,8 @@ "unconfirmed" : "Неподтвержденный", "displayable" : "Отображаемый", - "submit_request" : "отправить запрос" + "submit_request" : "отправить запрос", + + "buy_bitcoin" : "Купить Bitcoin", + "buy_with" : "Купить с помощью" } \ No newline at end of file diff --git a/res/values/strings_uk.arb b/res/values/strings_uk.arb index cc10a2d89..06082f250 100644 --- a/res/values/strings_uk.arb +++ b/res/values/strings_uk.arb @@ -469,5 +469,8 @@ "unconfirmed" : "Непідтверджений", "displayable" : "Відображуваний", - "submit_request" : "надіслати запит" + "submit_request" : "надіслати запит", + + "buy_bitcoin" : "Купити Bitcoin", + "buy_with" : "Купити за допомогою" } \ No newline at end of file diff --git a/res/values/strings_zh.arb b/res/values/strings_zh.arb index 639babcbc..39f93ecb1 100644 --- a/res/values/strings_zh.arb +++ b/res/values/strings_zh.arb @@ -469,5 +469,8 @@ "unconfirmed" : "未经证实", "displayable" : "可显示", - "submit_request" : "提交請求" + "submit_request" : "提交請求", + + "buy_bitcoin" : "購買比特幣", + "buy_with" : "與一起購買" } \ No newline at end of file From 697fc7f5a59132a677c9af6b681a728a2640592e Mon Sep 17 00:00:00 2001 From: OleksandrSobol Date: Wed, 14 Apr 2021 21:23:10 +0300 Subject: [PATCH 03/17] CAKE-306 | reworked buy_list_item.dart and pre_order_page.dart; added clear button to pre_order_page.dart; fixed order_row.dart and order_details_view_model.dart; added MoonPay statuses to trade_state.dart --- assets/images/moonpay-icon.png | Bin 0 -> 2015 bytes lib/buy/get_buy_provider_icon.dart | 5 +- lib/buy/moonpay/moonpay_buy_provider.dart | 5 +- lib/exchange/trade_state.dart | 14 +++ lib/src/screens/buy/buy_webview_page.dart | 18 +++- lib/src/screens/buy/pre_order_page.dart | 86 ++++++++++------- .../screens/buy/widgets/buy_list_item.dart | 91 +++++++++--------- .../screens/dashboard/widgets/order_row.dart | 6 +- lib/src/widgets/base_text_form_field.dart | 10 +- lib/view_model/order_details_view_model.dart | 5 +- 10 files changed, 148 insertions(+), 92 deletions(-) create mode 100644 assets/images/moonpay-icon.png diff --git a/assets/images/moonpay-icon.png b/assets/images/moonpay-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..1fbc2b6fc67fbb9ceab8b7134328fe7416db7980 GIT binary patch literal 2015 zcmY*a3piWZ7C!N4Ns+cRG1U@jOi?7F7@3Gyij>5wLzGD*go*LUka~4|De4;P5uJ|e z)H~?IBdW$si!@%<2S(Milu||AGQBNTq%|k4Ywq3qTYLR`?SK7iuk)Sropg7qlPYvS z6aWBKvNOpO^k~pfN;|;S()V;K=oCWiD0TpFBWIVCsR-_ofzF;307&=}0M48RfORl* zW*z`eVF6$v008j$0D#~Y-1Z=V2WQzdaxjGg7=l;{P=xFS(jhX6jkGYqnqW~xC=!VzLEO;9^&OAL#bYqB zv9TtxW+stQAsACDD=Q2Zhr!{DL56X3d<2gfXB-iI=(EUwbx7>!z$gxv$BB$U%5<5m z$WuIP6iQZTdwjkpj}!b)rHJUQw!j84vJ;G{2^O=h4W<%gQM_9ehYePi=@U%}pP2s_ z+p0sr$cq2ZWj=TMDGIhqgc2~@kBtcZfh6yoOwt~pKf%f`n1ia3FR4h;S>KDoIe=@>dw z3LBHFuiUvVs;$+$@Xan?IV&5RIqS)ZiP=G0&6%mGR^pz62Prt#h)nd@&(TJW`%=Ze)a}n*@D1PiPd2d&Q*?eQf{zj=nyDH?Z6N^=!}O zoo-Q4^@7~8co!F!PN}qcC>}A;m7`-`{GzS0vXbVQ)`EN6+6pnZwDj`vK}4qIm8GK_-=!PgWZQirWx;H2jW<)WH=m7yXe2W z<@9Pci^ck=dO+ftH##|qSX*0b&gkxTrTWw9bo*J=skyn3w~(AnP2CUV^78V!?8Zh5 zx;Z`8c!j3*!8V;SvGL%+an-v$Jz0Nu zE8ILoLk;iqY|YHf2x*rFRngamhP<<{wzoSJj?>m*^nA@nX-bN!Fxbz_h@r?`7m~c- z=!3a}+;CH~=j!ynQ!z1)Q&Up|PoGk7R`m@vR!5Ox7me>8e)dOKLVP?~EEbQa3A-{g zM@M~Ut%$_gL`(I!g|8aPR;HmyGu*Q<9RGEUft^|DmFy!Aiwg?!GomgSK)jYFgWT71 zp|*|ih>or<^`F;RHk%mZV`DL2J$|D?=@vH`r`#<+N`2^)wABDJz=}GLb(d z3CzNDEeDsC+Rq^{^V8GGP9FCz<>uz)-6iN7Bc+zgLv}sVwYMRL1_q^9uj+gAhePw; zej%c_lx1WhG^B#}JO58`{=;ReGCf+&<0rDmoW7jAaJ4dM_fiqy*X2c>HC@f;n@;wjXD|t})zkK6ew}y3g6MTuDuBs8eiogk*0ohghli zACxXGF51%p-;4eCRllaM0!a)5$x{`6@zpjR$v_H9Td^gyiV>f`8>G?V6 QKeFEz`52W{YafvOH-BM2B>(^b literal 0 HcmV?d00001 diff --git a/lib/buy/get_buy_provider_icon.dart b/lib/buy/get_buy_provider_icon.dart index d58fe19d4..bbd599e24 100644 --- a/lib/buy/get_buy_provider_icon.dart +++ b/lib/buy/get_buy_provider_icon.dart @@ -5,15 +5,14 @@ Image getBuyProviderIcon(BuyProviderDescription providerDescription) { final _wyreIcon = Image.asset('assets/images/wyre-icon.png', width: 36, height: 36); final _moonPayIcon = - Image.asset('assets/images/wyre-icon.png', width: 36, height: 36); + Image.asset('assets/images/moonpay-icon.png', width: 36, height: 34); if (providerDescription != null) { switch (providerDescription) { case BuyProviderDescription.wyre: return _wyreIcon; case BuyProviderDescription.moonPay: - //return _moonPayIcon; - return null; + return _moonPayIcon; default: return null; } diff --git a/lib/buy/moonpay/moonpay_buy_provider.dart b/lib/buy/moonpay/moonpay_buy_provider.dart index 2fde667e4..5b2fd7326 100644 --- a/lib/buy/moonpay/moonpay_buy_provider.dart +++ b/lib/buy/moonpay/moonpay_buy_provider.dart @@ -94,8 +94,9 @@ class MoonPayBuyProvider extends BuyProvider { final responseJSON = json.decode(response.body) as Map; final status = responseJSON['status'] as String; - final state = TradeState.deserialize(raw: status.toLowerCase()); - final createdAt = responseJSON['createdAt'] as DateTime; + final state = TradeState.deserialize(raw: status); + final createdAtRaw = responseJSON['createdAt'] as String; + final createdAt = DateTime.parse(createdAtRaw).toLocal(); final amount = responseJSON['quoteCurrencyAmount'] as double; return Order( diff --git a/lib/exchange/trade_state.dart b/lib/exchange/trade_state.dart index 7095626b9..9f08b4df2 100644 --- a/lib/exchange/trade_state.dart +++ b/lib/exchange/trade_state.dart @@ -27,6 +27,12 @@ class TradeState extends EnumerableItem with Serializable { static const finished = TradeState(raw: 'finished', title: 'Finished'); static const waiting = TradeState(raw: 'waiting', title: 'Waiting'); static const processing = TradeState(raw: 'processing', title: 'Processing'); + static const waitingPayment = + TradeState(raw: 'waitingPayment', title: 'Waiting payment'); + static const waitingAuthorization = + TradeState(raw: 'waitingAuthorization', title: 'Waiting authorization'); + static const failed = TradeState(raw: 'failed', title: 'Failed'); + static const completed = TradeState(raw: 'completed', title: 'Completed'); static TradeState deserialize({String raw}) { switch (raw) { @@ -62,6 +68,14 @@ class TradeState extends EnumerableItem with Serializable { return waiting; case 'processing': return processing; + case 'waitingPayment': + return waitingPayment; + case 'waitingAuthorization': + return waitingAuthorization; + case 'failed': + return failed; + case 'completed': + return completed; default: return null; } diff --git a/lib/src/screens/buy/buy_webview_page.dart b/lib/src/screens/buy/buy_webview_page.dart index 91e93f902..d5ab59482 100644 --- a/lib/src/screens/buy/buy_webview_page.dart +++ b/lib/src/screens/buy/buy_webview_page.dart @@ -92,7 +92,23 @@ class BuyWebViewPageBodyState extends State { } if (_provider is MoonPayBuyProvider) { - // FIXME: fetch orderId + /*_timer?.cancel(); + _timer = Timer.periodic(Duration(seconds: 1), (timer) async { + + try { + if (_webViewController == null || _isSaving) { + return; + } + + final url = await _webViewController.currentUrl(); + print('MoonPay Url = $url'); + + timer.cancel(); + } catch (e) { + _isSaving = false; + print(e); + } + });*/ } } diff --git a/lib/src/screens/buy/pre_order_page.dart b/lib/src/screens/buy/pre_order_page.dart index 0cf6a1f88..44162d7ec 100644 --- a/lib/src/screens/buy/pre_order_page.dart +++ b/lib/src/screens/buy/pre_order_page.dart @@ -14,6 +14,7 @@ import 'package:cake_wallet/src/widgets/primary_button.dart'; import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart'; import 'package:cake_wallet/generated/i18n.dart'; import 'package:cake_wallet/src/widgets/base_text_form_field.dart'; +import 'package:cake_wallet/src/widgets/trail_button.dart'; import 'package:mobx/mobx.dart'; class PreOrderPage extends BasePage { @@ -61,6 +62,13 @@ class PreOrderPage extends BasePage { @override AppBarStyle get appBarStyle => AppBarStyle.transparent; + @override + Widget trailing(context) => TrailButton( + caption: S.of(context).clear, + onPressed: () { + buyViewModel.reset(); + }); + @override Widget body(BuildContext context) { return KeyboardActions( @@ -96,43 +104,49 @@ class PreOrderPage extends BasePage { ], begin: Alignment.topLeft, end: Alignment.bottomRight), ), child: Padding( - padding: EdgeInsets.fromLTRB(100, 100, 100, 65), - child: BaseTextFormField( - focusNode: _amountFocus, - controller: _amountController, - keyboardType: - TextInputType.numberWithOptions( - signed: false, decimal: true), - inputFormatters: [ - FilteringTextInputFormatter - .allow(RegExp(_amountPattern)) - ], - prefixIcon: Padding( - padding: EdgeInsets.only(top: 2), - child: - Text(buyViewModel.fiatCurrency.title + ': ', - style: TextStyle( - fontSize: 36, - fontWeight: FontWeight.w600, - color: Colors.white, - )), - ), - hintText: '0.00', - borderColor: Theme.of(context) - .primaryTextTheme - .headline - .color, - textStyle: TextStyle( - fontSize: 36, - fontWeight: FontWeight.w500, - color: Colors.white), - placeholderTextStyle: TextStyle( - color: Theme.of(context) + padding: EdgeInsets.only(top: 100, bottom: 65), + child: Center( + child: Container( + width: 165, + child: BaseTextFormField( + focusNode: _amountFocus, + controller: _amountController, + keyboardType: + TextInputType.numberWithOptions( + signed: false, decimal: true), + inputFormatters: [ + FilteringTextInputFormatter + .allow(RegExp(_amountPattern)) + ], + prefixIcon: Padding( + padding: EdgeInsets.only(top: 2), + child: + Text(buyViewModel.fiatCurrency.title + ': ', + style: TextStyle( + fontSize: 36, + fontWeight: FontWeight.w600, + color: Colors.white, + )), + ), + hintText: '0.00', + borderColor: Theme.of(context) .primaryTextTheme - .headline + .body2 .decorationColor, - fontWeight: FontWeight.w500, - fontSize: 36), + borderWidth: 0.5, + textStyle: TextStyle( + fontSize: 36, + fontWeight: FontWeight.w500, + color: Colors.white), + placeholderTextStyle: TextStyle( + color: Theme.of(context) + .primaryTextTheme + .headline + .decorationColor, + fontWeight: FontWeight.w500, + fontSize: 36), + ) + ) ) ) ), @@ -144,7 +158,7 @@ class PreOrderPage extends BasePage { style: TextStyle( color: Theme.of(context).primaryTextTheme.title.color, fontSize: 18, - fontWeight: FontWeight.w500 + fontWeight: FontWeight.bold ), ) ), diff --git a/lib/src/screens/buy/widgets/buy_list_item.dart b/lib/src/screens/buy/widgets/buy_list_item.dart index 8ee3950aa..7c18893a6 100644 --- a/lib/src/screens/buy/widgets/buy_list_item.dart +++ b/lib/src/screens/buy/widgets/buy_list_item.dart @@ -52,62 +52,67 @@ class BuyListItem extends StatelessWidget { height: 102, padding: EdgeInsets.only( left: 20, + //top: 33, right: 20 ), decoration: BoxDecoration( borderRadius: BorderRadius.all(Radius.circular(25)), color: backgroundColor ), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - mainAxisSize: MainAxisSize.max, - crossAxisAlignment: CrossAxisAlignment.center, + child: Stack( children: [ - Row( - mainAxisAlignment: MainAxisAlignment.start, - mainAxisSize: MainAxisSize.min, - children: [ - if (providerIcon != null) Padding( - padding: EdgeInsets.only(right: 10), - child: providerIcon - ), - Text( - provider.description.title, - style: TextStyle( - color: primaryTextColor, - fontSize: 24, - fontWeight: FontWeight.w500 + Positioned( + top: 33, + left: 0, + right: 0, + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + mainAxisSize: MainAxisSize.max, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + if (providerIcon != null) Padding( + padding: EdgeInsets.only(right: 10), + child: providerIcon + ), + Text( + provider.description.title, + style: TextStyle( + color: secondaryTextColor, + fontSize: 20, + fontWeight: FontWeight.bold + ), + ) + ], ), - ) - ], - ), - Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.end, - children: [ - Text( - '${destAmount?.toString()} ${destCurrency.title}', - style: TextStyle( - color: secondaryTextColor, - fontSize: 18, - fontWeight: FontWeight.w500 - ), - ), - Padding( - padding: EdgeInsets.only(top: 5), - child: Text( - '${sourceAmount?.toString()} ${sourceCurrency.title}', + Text( + '${destAmount?.toString()} ${destCurrency.title}', style: TextStyle( - color: primaryTextColor, - fontSize: 18, - fontWeight: FontWeight.w500 + color: secondaryTextColor, + fontSize: 20, + fontWeight: FontWeight.bold ), ), - ) - ], + ], + ) + ), + Positioned( + top: 65, + right: 0, + child: Text( + '${sourceAmount?.toString()} ${sourceCurrency.title}', + style: TextStyle( + color: primaryTextColor, + fontSize: 16, + fontWeight: FontWeight.bold + ), + ), ) ], - ), + ) ) ); } diff --git a/lib/src/screens/dashboard/widgets/order_row.dart b/lib/src/screens/dashboard/widgets/order_row.dart index 49afa7a25..56a69122e 100644 --- a/lib/src/screens/dashboard/widgets/order_row.dart +++ b/lib/src/screens/dashboard/widgets/order_row.dart @@ -30,8 +30,10 @@ class OrderRow extends StatelessWidget { mainAxisSize: MainAxisSize.max, crossAxisAlignment: CrossAxisAlignment.center, children: [ - providerIcon ?? Offstage(), - SizedBox(width: 12), + if (providerIcon != null) Padding( + padding: EdgeInsets.only(right: 12), + child: providerIcon, + ), Expanded( child: Column( mainAxisSize: MainAxisSize.min, diff --git a/lib/src/widgets/base_text_form_field.dart b/lib/src/widgets/base_text_form_field.dart index b9148bf2c..385cba8cd 100644 --- a/lib/src/widgets/base_text_form_field.dart +++ b/lib/src/widgets/base_text_form_field.dart @@ -26,7 +26,8 @@ class BaseTextFormField extends StatelessWidget { this.placeholderTextStyle, this.maxLength, this.focusNode, - this.initialValue}); + this.initialValue, + this.borderWidth = 1.0}); final TextEditingController controller; final TextInputType keyboardType; @@ -52,6 +53,7 @@ class BaseTextFormField extends StatelessWidget { final bool readOnly; final bool enableInteractiveSelection; final String initialValue; + final double borderWidth; @override Widget build(BuildContext context) { @@ -88,17 +90,17 @@ class BaseTextFormField extends StatelessWidget { borderSide: BorderSide( color: borderColor ?? Theme.of(context).primaryTextTheme.title.backgroundColor, - width: 1.0)), + width: borderWidth)), disabledBorder: UnderlineInputBorder( borderSide: BorderSide( color: borderColor ?? Theme.of(context).primaryTextTheme.title.backgroundColor, - width: 1.0)), + width: borderWidth)), enabledBorder: UnderlineInputBorder( borderSide: BorderSide( color: borderColor ?? Theme.of(context).primaryTextTheme.title.backgroundColor, - width: 1.0))), + width: borderWidth))), validator: validator, ); } diff --git a/lib/view_model/order_details_view_model.dart b/lib/view_model/order_details_view_model.dart index 8e8ed3c18..328b0c93c 100644 --- a/lib/view_model/order_details_view_model.dart +++ b/lib/view_model/order_details_view_model.dart @@ -58,6 +58,9 @@ abstract class OrderDetailsViewModelBase with Store { final updatedOrder = await _provider.findOrderById(order.id); updatedOrder.receiveAddress = order.receiveAddress; updatedOrder.walletId = order.walletId; + if (order.provider != null) { + updatedOrder.providerRaw = order.provider.raw; + } order = updatedOrder; _updateItems(); } @@ -90,7 +93,7 @@ abstract class OrderDetailsViewModelBase with Store { ); } - if (_provider.trackUrl.isNotEmpty) { + if (_provider?.trackUrl?.isNotEmpty ?? false) { final buildURL = _provider.trackUrl + '${order.transferId}'; items.add( TrackTradeListItem( From 1c976bfaa12e6868b0773e7585992d014626a4a9 Mon Sep 17 00:00:00 2001 From: OleksandrSobol Date: Thu, 15 Apr 2021 20:10:23 +0300 Subject: [PATCH 04/17] CAKE-306 | applied _saveOrder() to buy_webview_page.dart; fixed moonpay_buy_provider.dart and wyre_buy_provider.dart; canceled timers on exchange_trade_page.dart, order_details_page.dart and trade_details_page.dart; reworked pre_order_page.dart --- lib/buy/moonpay/moonpay_buy_provider.dart | 9 +- lib/buy/wyre/wyre_buy_provider.dart | 3 +- lib/di.dart | 11 +- lib/router.dart | 5 +- lib/src/screens/buy/buy_webview_page.dart | 78 +++--- lib/src/screens/buy/pre_order_page.dart | 227 +++++++++--------- .../exchange_trade/exchange_trade_page.dart | 6 + .../order_details/order_details_page.dart | 29 ++- .../trade_details/trade_details_page.dart | 29 ++- lib/view_model/buy/buy_view_model.dart | 4 + .../exchange/exchange_trade_view_model.dart | 4 +- lib/view_model/trade_details_view_model.dart | 4 +- 12 files changed, 238 insertions(+), 171 deletions(-) diff --git a/lib/buy/moonpay/moonpay_buy_provider.dart b/lib/buy/moonpay/moonpay_buy_provider.dart index 5b2fd7326..240d6389e 100644 --- a/lib/buy/moonpay/moonpay_buy_provider.dart +++ b/lib/buy/moonpay/moonpay_buy_provider.dart @@ -23,6 +23,7 @@ class MoonPayBuyProvider extends BuyProvider { static const _currenciesSuffix = '/v3/currencies'; static const _quoteSuffix = '/buy_quote'; static const _transactionsSuffix = '/v1/transactions'; + static const _fiatCurrency = 'USD'; static const _apiKey = secrets.moonPayApiKey; @override @@ -35,9 +36,7 @@ class MoonPayBuyProvider extends BuyProvider { walletTypeToCryptoCurrency(walletType).title.toLowerCase(); @override - String get trackUrl => isTestEnvironment - ? '' - : ''; // FIXME + String get trackUrl => baseApiUrl + '/transaction_receipt?transactionId='; String baseApiUrl; @@ -103,8 +102,8 @@ class MoonPayBuyProvider extends BuyProvider { id: id, provider: description, transferId: id, - from: 'USD', //FIXME - to: 'BTC', //FIXME + from: _fiatCurrency, + to: currencyCode.toUpperCase(), state: state, createdAt: createdAt, amount: amount.toString(), diff --git a/lib/buy/wyre/wyre_buy_provider.dart b/lib/buy/wyre/wyre_buy_provider.dart index 3a09be0f6..77055c325 100644 --- a/lib/buy/wyre/wyre_buy_provider.dart +++ b/lib/buy/wyre/wyre_buy_provider.dart @@ -28,6 +28,7 @@ class WyreBuyProvider extends BuyProvider { static const _timeStampSuffix = '?timestamp='; static const _transferSuffix = '/v2/transfer/'; static const _trackSuffix = '/track'; + static const _countryCode = 'US'; static const _secretKey = secrets.wyreSecretKey; static const _accountId = secrets.wyreAccountId; @@ -86,7 +87,7 @@ class WyreBuyProvider extends BuyProvider { 'destCurrency': walletTypeToCryptoCurrency(walletType).title, 'dest': walletTypeToString(walletType).toLowerCase() + ':' + walletAddress, 'accountId': _accountId, - 'country': 'US' //FIXME + 'country': _countryCode }; final response = await post(quoteUrl, diff --git a/lib/di.dart b/lib/di.dart index 0b107fde4..2a33e6b11 100644 --- a/lib/di.dart +++ b/lib/di.dart @@ -544,9 +544,14 @@ Future setup( return PreOrderPage(buyViewModel: getIt.get()); }); - getIt.registerFactoryParam((String url, _) => - BuyWebViewPage(getIt.get(), - ordersStore: getIt.get(), url: url)); + getIt.registerFactoryParam( + (List args, _) { + final url = args.first as String; + final buyViewModel = args[1] as BuyViewModel; + + return BuyWebViewPage(buyViewModel: buyViewModel, + ordersStore: getIt.get(), url: url); + }); getIt.registerFactoryParam( (order, _) { diff --git a/lib/router.dart b/lib/router.dart index a3421fa69..e8650e34e 100644 --- a/lib/router.dart +++ b/lib/router.dart @@ -12,6 +12,7 @@ import 'package:cake_wallet/src/screens/restore/wallet_restore_page.dart'; import 'package:cake_wallet/src/screens/seed/pre_seed_page.dart'; import 'package:cake_wallet/src/screens/support/support_page.dart'; import 'package:cake_wallet/store/settings_store.dart'; +import 'package:cake_wallet/view_model/buy/buy_view_model.dart'; import 'package:cake_wallet/view_model/monero_account_list/account_list_item.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; @@ -304,9 +305,11 @@ Route createRoute(RouteSettings settings) { getIt.get()); case Routes.buyWebView: + final args = settings.arguments as List; + return MaterialPageRoute( builder: (_) => - getIt.get(param1: settings.arguments as String)); + getIt.get(param1: args)); case Routes.restoreWalletFromSeedDetails: final args = settings.arguments as List; diff --git a/lib/src/screens/buy/buy_webview_page.dart b/lib/src/screens/buy/buy_webview_page.dart index d5ab59482..9d2ba3698 100644 --- a/lib/src/screens/buy/buy_webview_page.dart +++ b/lib/src/screens/buy/buy_webview_page.dart @@ -12,8 +12,8 @@ import 'package:flutter/material.dart'; import 'package:webview_flutter/webview_flutter.dart'; class BuyWebViewPage extends BasePage { - BuyWebViewPage(this.buyViewModel, - {@required this.ordersStore, @required this.url}); + BuyWebViewPage({@required this.buyViewModel, + @required this.ordersStore, @required this.url}); final OrdersStore ordersStore; final String url; @@ -63,52 +63,11 @@ class BuyWebViewPageBodyState extends State { if (Platform.isAndroid) WebView.platform = SurfaceAndroidWebView(); if (_provider is WyreBuyProvider) { - _timer?.cancel(); - _timer = Timer.periodic(Duration(seconds: 1), (timer) async { - - try { - if (_webViewController == null || _isSaving) { - return; - } - - final url = await _webViewController.currentUrl(); - - if (url.contains('completed')) { - final urlParts = url.split('/'); - orderId = urlParts.last; - widget.ordersStore.orderId = orderId; - - if (orderId.isNotEmpty) { - _isSaving = true; - await widget.buyViewModel.saveOrder(orderId); - timer.cancel(); - } - } - } catch (e) { - _isSaving = false; - print(e); - } - }); + _saveOrder(keyword: 'completed', splitSymbol: '/'); } if (_provider is MoonPayBuyProvider) { - /*_timer?.cancel(); - _timer = Timer.periodic(Duration(seconds: 1), (timer) async { - - try { - if (_webViewController == null || _isSaving) { - return; - } - - final url = await _webViewController.currentUrl(); - print('MoonPay Url = $url'); - - timer.cancel(); - } catch (e) { - _isSaving = false; - print(e); - } - });*/ + _saveOrder(keyword: 'transactionId', splitSymbol: '='); } } @@ -121,4 +80,33 @@ class BuyWebViewPageBodyState extends State { onWebViewCreated: (WebViewController controller) => setState(() => _webViewController = controller)); } + + void _saveOrder({String keyword, String splitSymbol}) { + _timer?.cancel(); + _timer = Timer.periodic(Duration(seconds: 1), (timer) async { + + try { + if (_webViewController == null || _isSaving) { + return; + } + + final url = await _webViewController.currentUrl(); + + if (url.contains(keyword)) { + final urlParts = url.split(splitSymbol); + orderId = urlParts.last; + widget.ordersStore.orderId = orderId; + + if (orderId.isNotEmpty) { + _isSaving = true; + await widget.buyViewModel.saveOrder(orderId); + timer.cancel(); + } + } + } catch (e) { + _isSaving = false; + print(e); + } + }); + } } diff --git a/lib/src/screens/buy/pre_order_page.dart b/lib/src/screens/buy/pre_order_page.dart index 44162d7ec..dbe03b07e 100644 --- a/lib/src/screens/buy/pre_order_page.dart +++ b/lib/src/screens/buy/pre_order_page.dart @@ -27,9 +27,6 @@ class PreOrderPage extends BasePage { if (amount != buyViewModel.buyAmountViewModel.amount) { buyViewModel.buyAmountViewModel.amount = amount; - } - - if (buyViewModel.buyAmountViewModel.doubleAmount == 0.0) { buyViewModel.selectedProvider = null; } }); @@ -38,6 +35,12 @@ class PreOrderPage extends BasePage { if (_amountController.text != amount) { _amountController.text = amount; } + if (amount.isEmpty) { + buyViewModel.selectedProvider = null; + buyViewModel.isShowProviderButtons = false; + } else { + buyViewModel.isShowProviderButtons = true; + } }); } @@ -88,120 +91,123 @@ class PreOrderPage extends BasePage { color: Theme.of(context).backgroundColor, child: ScrollableWithBottomSection( contentPadding: EdgeInsets.only(bottom: 24), - content: Column( + content: Observer(builder: (_) => Column( children: [ Container( - decoration: BoxDecoration( - borderRadius: BorderRadius.only( - bottomLeft: Radius.circular(24), - bottomRight: Radius.circular(24)), - gradient: LinearGradient(colors: [ - Theme.of(context).primaryTextTheme.subhead.color, - Theme.of(context) - .primaryTextTheme - .subhead - .decorationColor, - ], begin: Alignment.topLeft, end: Alignment.bottomRight), - ), - child: Padding( - padding: EdgeInsets.only(top: 100, bottom: 65), - child: Center( - child: Container( - width: 165, - child: BaseTextFormField( - focusNode: _amountFocus, - controller: _amountController, - keyboardType: - TextInputType.numberWithOptions( - signed: false, decimal: true), - inputFormatters: [ - FilteringTextInputFormatter - .allow(RegExp(_amountPattern)) - ], - prefixIcon: Padding( - padding: EdgeInsets.only(top: 2), - child: - Text(buyViewModel.fiatCurrency.title + ': ', - style: TextStyle( - fontSize: 36, - fontWeight: FontWeight.w600, - color: Colors.white, - )), - ), - hintText: '0.00', - borderColor: Theme.of(context) - .primaryTextTheme - .body2 - .decorationColor, - borderWidth: 0.5, - textStyle: TextStyle( - fontSize: 36, - fontWeight: FontWeight.w500, - color: Colors.white), - placeholderTextStyle: TextStyle( - color: Theme.of(context) - .primaryTextTheme - .headline - .decorationColor, - fontWeight: FontWeight.w500, - fontSize: 36), - ) - ) - ) - ) - ), - Padding( - padding: EdgeInsets.only(top: 38, bottom: 18), - child: Text( - S.of(context).buy_with + ':', - textAlign: TextAlign.center, - style: TextStyle( - color: Theme.of(context).primaryTextTheme.title.color, - fontSize: 18, - fontWeight: FontWeight.bold + decoration: BoxDecoration( + borderRadius: BorderRadius.only( + bottomLeft: Radius.circular(24), + bottomRight: Radius.circular(24)), + gradient: LinearGradient(colors: [ + Theme.of(context).primaryTextTheme.subhead.color, + Theme.of(context) + .primaryTextTheme + .subhead + .decorationColor, + ], begin: Alignment.topLeft, end: Alignment.bottomRight), ), - ) + child: Padding( + padding: EdgeInsets.only(top: 100, bottom: 65), + child: Center( + child: Container( + width: 165, + child: BaseTextFormField( + focusNode: _amountFocus, + controller: _amountController, + keyboardType: + TextInputType.numberWithOptions( + signed: false, decimal: true), + inputFormatters: [ + FilteringTextInputFormatter + .allow(RegExp(_amountPattern)) + ], + prefixIcon: Padding( + padding: EdgeInsets.only(top: 2), + child: + Text(buyViewModel.fiatCurrency.title + ': ', + style: TextStyle( + fontSize: 36, + fontWeight: FontWeight.w600, + color: Colors.white, + )), + ), + hintText: '0.00', + borderColor: Theme.of(context) + .primaryTextTheme + .body2 + .decorationColor, + borderWidth: 0.5, + textStyle: TextStyle( + fontSize: 36, + fontWeight: FontWeight.w500, + color: Colors.white), + placeholderTextStyle: TextStyle( + color: Theme.of(context) + .primaryTextTheme + .headline + .decorationColor, + fontWeight: FontWeight.w500, + fontSize: 36), + ) + ) + ) + ) ), - ...buyViewModel.items.map( - (item) => Observer(builder: (_) => FutureBuilder( - future: item.buyAmount, - builder: (context, AsyncSnapshot snapshot) { - double sourceAmount; - double destAmount; + if (buyViewModel.isShowProviderButtons) Padding( + padding: EdgeInsets.only(top: 38, bottom: 18), + child: Text( + S.of(context).buy_with + ':', + textAlign: TextAlign.center, + style: TextStyle( + color: Theme.of(context).primaryTextTheme.title.color, + fontSize: 18, + fontWeight: FontWeight.bold + ), + ) + ), + if (buyViewModel.isShowProviderButtons) + ...buyViewModel.items.map( + (item) => Observer(builder: (_) => FutureBuilder( + future: item.buyAmount, + builder: (context, AsyncSnapshot snapshot) { + double sourceAmount; + double destAmount; - if (snapshot.hasData) { - sourceAmount = snapshot.data.sourceAmount; - destAmount = snapshot.data.destAmount; - } else { - sourceAmount = 0.0; - destAmount = 0.0; + if (snapshot.hasData) { + sourceAmount = snapshot.data.sourceAmount; + destAmount = snapshot.data.destAmount; + } else { + sourceAmount = 0.0; + destAmount = 0.0; + } + + return Padding( + padding: + EdgeInsets.only(left: 15, top: 20, right: 15), + child: Observer(builder: (_) { + return BuyListItem( + selectedProvider: buyViewModel.selectedProvider, + provider: item.provider, + sourceAmount: sourceAmount, + sourceCurrency: buyViewModel.fiatCurrency, + destAmount: destAmount, + destCurrency: buyViewModel.cryptoCurrency, + onTap: + buyViewModel.buyAmountViewModel + .doubleAmount == 0.0 ? null : () { + buyViewModel.selectedProvider = item.provider; + sourceAmount > 0 + ? buyViewModel.isDisabled = false + : buyViewModel.isDisabled = true; + } + ); + }) + ); } - - return Padding( - padding: - EdgeInsets.only(left: 15, top: 20, right: 15), - child: Observer(builder: (_) => BuyListItem( - selectedProvider: buyViewModel.selectedProvider, - provider: item.provider, - sourceAmount: sourceAmount, - sourceCurrency: buyViewModel.fiatCurrency, - destAmount: destAmount, - destCurrency: buyViewModel.cryptoCurrency, - onTap: - buyViewModel.buyAmountViewModel - .doubleAmount == 0.0 ? null : () { - buyViewModel.selectedProvider = item.provider; - sourceAmount > 0 - ? buyViewModel.isDisabled = false - : buyViewModel.isDisabled = true; - } - )) - ); - } - ),) + )) ) ], - ), + )), bottomSectionPadding: EdgeInsets.only(left: 24, right: 24, bottom: 24), bottomSection: Observer(builder: (_) { @@ -214,7 +220,8 @@ class PreOrderPage extends BasePage { await buyViewModel.fetchUrl(); if (url.isNotEmpty) { await Navigator.of(context) - .pushNamed(Routes.buyWebView, arguments: url); + .pushNamed(Routes.buyWebView, + arguments: [url, buyViewModel]); buyViewModel.reset(); } buyViewModel.isRunning = false; diff --git a/lib/src/screens/exchange_trade/exchange_trade_page.dart b/lib/src/screens/exchange_trade/exchange_trade_page.dart index b4374cbb5..0a3b10e18 100644 --- a/lib/src/screens/exchange_trade/exchange_trade_page.dart +++ b/lib/src/screens/exchange_trade/exchange_trade_page.dart @@ -98,6 +98,12 @@ class ExchangeTradeState extends State { showInformation(widget.exchangeTradeViewModel, context); } + @override + void dispose() { + super.dispose(); + widget.exchangeTradeViewModel.timer?.cancel(); + } + @override Widget build(BuildContext context) { final copyImage = Image.asset('assets/images/copy_content.png', diff --git a/lib/src/screens/order_details/order_details_page.dart b/lib/src/screens/order_details/order_details_page.dart index 4122a1af5..293cf5e3d 100644 --- a/lib/src/screens/order_details/order_details_page.dart +++ b/lib/src/screens/order_details/order_details_page.dart @@ -19,7 +19,33 @@ class OrderDetailsPage extends BasePage { final OrderDetailsViewModel orderDetailsViewModel; @override - Widget body(BuildContext context) { + Widget body(BuildContext context) => + OrderDetailsPageBody(orderDetailsViewModel); +} + +class OrderDetailsPageBody extends StatefulWidget { + OrderDetailsPageBody(this.orderDetailsViewModel); + + final OrderDetailsViewModel orderDetailsViewModel; + + @override + OrderDetailsPageBodyState createState() => + OrderDetailsPageBodyState(orderDetailsViewModel); +} + +class OrderDetailsPageBodyState extends State { + OrderDetailsPageBodyState(this.orderDetailsViewModel); + + final OrderDetailsViewModel orderDetailsViewModel; + + @override + void dispose() { + super.dispose(); + orderDetailsViewModel.timer?.cancel(); + } + + @override + Widget build(BuildContext context) { return Observer(builder: (_) { return SectionStandardList( sectionCount: 1, @@ -44,4 +70,5 @@ class OrderDetailsPage extends BasePage { }); }); } + } \ No newline at end of file diff --git a/lib/src/screens/trade_details/trade_details_page.dart b/lib/src/screens/trade_details/trade_details_page.dart index e691e7f84..abf8e2873 100644 --- a/lib/src/screens/trade_details/trade_details_page.dart +++ b/lib/src/screens/trade_details/trade_details_page.dart @@ -19,7 +19,33 @@ class TradeDetailsPage extends BasePage { final TradeDetailsViewModel tradeDetailsViewModel; @override - Widget body(BuildContext context) { + Widget body(BuildContext context) => + TradeDetailsPageBody(tradeDetailsViewModel); +} + +class TradeDetailsPageBody extends StatefulWidget { + TradeDetailsPageBody(this.tradeDetailsViewModel); + + final TradeDetailsViewModel tradeDetailsViewModel; + + @override + TradeDetailsPageBodyState createState() => + TradeDetailsPageBodyState(tradeDetailsViewModel); +} + +class TradeDetailsPageBodyState extends State { + TradeDetailsPageBodyState(this.tradeDetailsViewModel); + + final TradeDetailsViewModel tradeDetailsViewModel; + + @override + void dispose() { + super.dispose(); + tradeDetailsViewModel.timer?.cancel(); + } + + @override + Widget build(BuildContext context) { return Observer(builder: (_) { return SectionStandardList( sectionCount: 1, @@ -44,4 +70,5 @@ class TradeDetailsPage extends BasePage { }); }); } + } diff --git a/lib/view_model/buy/buy_view_model.dart b/lib/view_model/buy/buy_view_model.dart index 10dd9a35a..70d1cc556 100644 --- a/lib/view_model/buy/buy_view_model.dart +++ b/lib/view_model/buy/buy_view_model.dart @@ -29,6 +29,7 @@ abstract class BuyViewModelBase with Store { .toList(); isRunning = false; isDisabled = true; + isShowProviderButtons = false; } final Box ordersSource; @@ -51,6 +52,9 @@ abstract class BuyViewModelBase with Store { @observable bool isDisabled; + @observable + bool isShowProviderButtons; + WalletType get type => wallet.type; double get doubleAmount => buyAmountViewModel.doubleAmount; diff --git a/lib/view_model/exchange/exchange_trade_view_model.dart b/lib/view_model/exchange/exchange_trade_view_model.dart index a739b207f..7882c3dca 100644 --- a/lib/view_model/exchange/exchange_trade_view_model.dart +++ b/lib/view_model/exchange/exchange_trade_view_model.dart @@ -45,7 +45,7 @@ abstract class ExchangeTradeViewModelBase with Store { _updateTrade(); - _timer = Timer.periodic(Duration(seconds: 20), (_) async => _updateTrade()); + timer = Timer.periodic(Duration(seconds: 20), (_) async => _updateTrade()); } final WalletBase wallet; @@ -71,7 +71,7 @@ abstract class ExchangeTradeViewModelBase with Store { ExchangeProvider _provider; - Timer _timer; + Timer timer; @action Future confirmSending() async { diff --git a/lib/view_model/trade_details_view_model.dart b/lib/view_model/trade_details_view_model.dart index 9c88cb981..a367c17f4 100644 --- a/lib/view_model/trade_details_view_model.dart +++ b/lib/view_model/trade_details_view_model.dart @@ -39,7 +39,7 @@ abstract class TradeDetailsViewModelBase with Store { _updateTrade(); - _timer = Timer.periodic(Duration(seconds: 20), (_) async => _updateTrade()); + timer = Timer.periodic(Duration(seconds: 20), (_) async => _updateTrade()); } final Box trades; @@ -52,7 +52,7 @@ abstract class TradeDetailsViewModelBase with Store { ExchangeProvider _provider; - Timer _timer; + Timer timer; @action Future _updateTrade() async { From 9627590ba571dd9979117015ab815a66847dbdad Mon Sep 17 00:00:00 2001 From: OleksandrSobol Date: Fri, 30 Apr 2021 11:08:25 +0300 Subject: [PATCH 05/17] CAKE-306 | fixed findOrderById() in the moonpay_buy_provider.dart; fixed saveOrder() in the buy_view_model.dart --- lib/buy/moonpay/moonpay_buy_provider.dart | 27 ++++++++++++++----- lib/buy/wyre/wyre_buy_provider.dart | 2 +- lib/di.dart | 4 +-- lib/view_model/buy/buy_view_model.dart | 4 ++- .../dashboard/dashboard_view_model.dart | 3 --- lib/view_model/order_details_view_model.dart | 9 +++++-- 6 files changed, 33 insertions(+), 16 deletions(-) diff --git a/lib/buy/moonpay/moonpay_buy_provider.dart b/lib/buy/moonpay/moonpay_buy_provider.dart index 240d6389e..c53eb9c0e 100644 --- a/lib/buy/moonpay/moonpay_buy_provider.dart +++ b/lib/buy/moonpay/moonpay_buy_provider.dart @@ -1,5 +1,6 @@ import 'dart:convert'; import 'package:cake_wallet/buy/buy_exception.dart'; +import 'package:hive/hive.dart'; import 'package:http/http.dart'; import 'package:cake_wallet/buy/buy_amount.dart'; import 'package:cake_wallet/buy/buy_provider.dart'; @@ -11,9 +12,10 @@ import 'package:cake_wallet/exchange/trade_state.dart'; import 'package:cake_wallet/.secrets.g.dart' as secrets; class MoonPayBuyProvider extends BuyProvider { - MoonPayBuyProvider({WalletBase wallet, bool isTestEnvironment = false}) + MoonPayBuyProvider({WalletBase wallet, this.ordersSource, + bool isTestEnvironment = false}) : super(wallet: wallet, isTestEnvironment: isTestEnvironment) { - baseApiUrl = isTestEnvironment + baseApiUrl = isTestEnvironment ? _baseTestApiUrl : _baseProductApiUrl; } @@ -23,7 +25,6 @@ class MoonPayBuyProvider extends BuyProvider { static const _currenciesSuffix = '/v3/currencies'; static const _quoteSuffix = '/buy_quote'; static const _transactionsSuffix = '/v1/transactions'; - static const _fiatCurrency = 'USD'; static const _apiKey = secrets.moonPayApiKey; @override @@ -38,6 +39,7 @@ class MoonPayBuyProvider extends BuyProvider { @override String get trackUrl => baseApiUrl + '/transaction_receipt?transactionId='; + final Box ordersSource; String baseApiUrl; @override @@ -58,7 +60,7 @@ class MoonPayBuyProvider extends BuyProvider { @override Future calculateAmount(String amount, String sourceCurrency) async { - final url = baseApiUrl + _currenciesSuffix + '/$currencyCode' + + final url = _baseProductApiUrl + _currenciesSuffix + '/$currencyCode' + _quoteSuffix + '/?apiKey=' + _apiKey + '&baseCurrencyAmount=' + amount + '&baseCurrencyCode' + sourceCurrency.toLowerCase(); @@ -80,7 +82,7 @@ class MoonPayBuyProvider extends BuyProvider { @override Future findOrderById(String id) async { - final url = baseApiUrl + _transactionsSuffix + '/$id' + + final url = _baseProductApiUrl + _transactionsSuffix + '/$id' + '?apiKey=' + _apiKey; final response = await get(url); @@ -98,12 +100,23 @@ class MoonPayBuyProvider extends BuyProvider { final createdAt = DateTime.parse(createdAtRaw).toLocal(); final amount = responseJSON['quoteCurrencyAmount'] as double; + var from = ''; + var to = ''; + + for (final order in ordersSource.values) { + if (order.id == id) { + from = order.from; + to = order.to; + break; + } + } + return Order( id: id, provider: description, transferId: id, - from: _fiatCurrency, - to: currencyCode.toUpperCase(), + from: from, + to: to, state: state, createdAt: createdAt, amount: amount.toString(), diff --git a/lib/buy/wyre/wyre_buy_provider.dart b/lib/buy/wyre/wyre_buy_provider.dart index 77055c325..f1100d130 100644 --- a/lib/buy/wyre/wyre_buy_provider.dart +++ b/lib/buy/wyre/wyre_buy_provider.dart @@ -80,7 +80,7 @@ class WyreBuyProvider extends BuyProvider { @override Future calculateAmount(String amount, String sourceCurrency) async { - final quoteUrl = baseApiUrl + _ordersSuffix + _quoteSuffix; + final quoteUrl = _baseProductApiUrl + _ordersSuffix + _quoteSuffix; final body = { 'amount': amount, 'sourceCurrency': sourceCurrency, diff --git a/lib/di.dart b/lib/di.dart index 2a33e6b11..60a20e52a 100644 --- a/lib/di.dart +++ b/lib/di.dart @@ -238,7 +238,6 @@ Future setup( tradeFilterStore: getIt.get(), transactionFilterStore: getIt.get(), settingsStore: settingsStore, - ordersSource: _ordersSource, ordersStore: getIt.get())); getIt.registerFactory(() => AuthService( @@ -536,7 +535,7 @@ Future setup( getIt.registerFactory(() { final wallet = getIt.get().wallet; - return BuyViewModel(ordersSource, getIt.get(), + return BuyViewModel(_ordersSource, getIt.get(), getIt.get(), wallet: wallet); }); @@ -559,6 +558,7 @@ Future setup( return OrderDetailsViewModel( wallet: wallet, + ordersSource: _ordersSource, orderForDetails: order); }); diff --git a/lib/view_model/buy/buy_view_model.dart b/lib/view_model/buy/buy_view_model.dart index 70d1cc556..d0f2c2f6e 100644 --- a/lib/view_model/buy/buy_view_model.dart +++ b/lib/view_model/buy/buy_view_model.dart @@ -22,7 +22,7 @@ abstract class BuyViewModelBase with Store { {@required this.wallet}) { providerList = [ WyreBuyProvider(wallet: wallet), - MoonPayBuyProvider(wallet: wallet) + MoonPayBuyProvider(wallet: wallet, ordersSource: ordersSource) ]; items = providerList.map((provider) => BuyItem(provider: provider, buyAmountViewModel: buyAmountViewModel)) @@ -79,6 +79,8 @@ abstract class BuyViewModelBase with Store { Future saveOrder(String orderId) async { try { final order = await selectedProvider?.findOrderById(orderId); + order.from = fiatCurrency.title; + order.to = cryptoCurrency.title; await ordersSource.add(order); ordersStore.setOrder(order); } catch (e) { diff --git a/lib/view_model/dashboard/dashboard_view_model.dart b/lib/view_model/dashboard/dashboard_view_model.dart index 10e6a5afd..ff1d2411d 100644 --- a/lib/view_model/dashboard/dashboard_view_model.dart +++ b/lib/view_model/dashboard/dashboard_view_model.dart @@ -57,7 +57,6 @@ abstract class DashboardViewModelBase with Store { this.tradeFilterStore, this.transactionFilterStore, this.settingsStore, - this.ordersSource, this.ordersStore}) { filterItems = { S.current.transactions: [ @@ -212,8 +211,6 @@ abstract class DashboardViewModelBase with Store { bool get hasRescan => wallet.type == WalletType.monero; - Box ordersSource; - BalanceViewModel balanceViewModel; AppStore appStore; diff --git a/lib/view_model/order_details_view_model.dart b/lib/view_model/order_details_view_model.dart index 328b0c93c..0fb92a761 100644 --- a/lib/view_model/order_details_view_model.dart +++ b/lib/view_model/order_details_view_model.dart @@ -3,6 +3,7 @@ import 'package:cake_wallet/buy/buy_provider.dart'; import 'package:cake_wallet/buy/buy_provider_description.dart'; import 'package:cake_wallet/buy/order.dart'; import 'package:cake_wallet/utils/date_formatter.dart'; +import 'package:hive/hive.dart'; import 'package:mobx/mobx.dart'; import 'package:cake_wallet/generated/i18n.dart'; import 'package:cake_wallet/src/screens/transaction_details/standart_list_item.dart'; @@ -18,7 +19,8 @@ class OrderDetailsViewModel = OrderDetailsViewModelBase with _$OrderDetailsViewModel; abstract class OrderDetailsViewModelBase with Store { - OrderDetailsViewModelBase({WalletBase wallet, Order orderForDetails}) { + OrderDetailsViewModelBase({WalletBase wallet, this.ordersSource, + Order orderForDetails}) { order = orderForDetails; if (order.provider != null) { @@ -27,7 +29,8 @@ abstract class OrderDetailsViewModelBase with Store { _provider = WyreBuyProvider(wallet: wallet); break; case BuyProviderDescription.moonPay: - _provider = MoonPayBuyProvider(wallet: wallet); + _provider = + MoonPayBuyProvider(wallet: wallet, ordersSource: ordersSource); break; } } @@ -41,6 +44,8 @@ abstract class OrderDetailsViewModelBase with Store { timer = Timer.periodic(Duration(seconds: 20), (_) async => _updateOrder()); } + final Box ordersSource; + @observable Order order; From ae7ccacf29f6e25d73290c2e734ef4f1e44ae922 Mon Sep 17 00:00:00 2001 From: OleksandrSobol Date: Fri, 30 Apr 2021 13:12:00 +0300 Subject: [PATCH 06/17] CAKE-306 | fixed moonpay_buy_provider.dart, buy_view_model.dart and order_details_view_model.dart --- lib/buy/moonpay/moonpay_buy_provider.dart | 18 +----------------- lib/di.dart | 1 - lib/view_model/buy/buy_view_model.dart | 2 +- lib/view_model/order_details_view_model.dart | 11 ++++------- 4 files changed, 6 insertions(+), 26 deletions(-) diff --git a/lib/buy/moonpay/moonpay_buy_provider.dart b/lib/buy/moonpay/moonpay_buy_provider.dart index c53eb9c0e..eef0e941f 100644 --- a/lib/buy/moonpay/moonpay_buy_provider.dart +++ b/lib/buy/moonpay/moonpay_buy_provider.dart @@ -1,6 +1,5 @@ import 'dart:convert'; import 'package:cake_wallet/buy/buy_exception.dart'; -import 'package:hive/hive.dart'; import 'package:http/http.dart'; import 'package:cake_wallet/buy/buy_amount.dart'; import 'package:cake_wallet/buy/buy_provider.dart'; @@ -12,8 +11,7 @@ import 'package:cake_wallet/exchange/trade_state.dart'; import 'package:cake_wallet/.secrets.g.dart' as secrets; class MoonPayBuyProvider extends BuyProvider { - MoonPayBuyProvider({WalletBase wallet, this.ordersSource, - bool isTestEnvironment = false}) + MoonPayBuyProvider({WalletBase wallet, bool isTestEnvironment = false}) : super(wallet: wallet, isTestEnvironment: isTestEnvironment) { baseApiUrl = isTestEnvironment ? _baseTestApiUrl @@ -39,7 +37,6 @@ class MoonPayBuyProvider extends BuyProvider { @override String get trackUrl => baseApiUrl + '/transaction_receipt?transactionId='; - final Box ordersSource; String baseApiUrl; @override @@ -100,23 +97,10 @@ class MoonPayBuyProvider extends BuyProvider { final createdAt = DateTime.parse(createdAtRaw).toLocal(); final amount = responseJSON['quoteCurrencyAmount'] as double; - var from = ''; - var to = ''; - - for (final order in ordersSource.values) { - if (order.id == id) { - from = order.from; - to = order.to; - break; - } - } - return Order( id: id, provider: description, transferId: id, - from: from, - to: to, state: state, createdAt: createdAt, amount: amount.toString(), diff --git a/lib/di.dart b/lib/di.dart index 60a20e52a..e66d8516a 100644 --- a/lib/di.dart +++ b/lib/di.dart @@ -558,7 +558,6 @@ Future setup( return OrderDetailsViewModel( wallet: wallet, - ordersSource: _ordersSource, orderForDetails: order); }); diff --git a/lib/view_model/buy/buy_view_model.dart b/lib/view_model/buy/buy_view_model.dart index d0f2c2f6e..90dbd6cfa 100644 --- a/lib/view_model/buy/buy_view_model.dart +++ b/lib/view_model/buy/buy_view_model.dart @@ -22,7 +22,7 @@ abstract class BuyViewModelBase with Store { {@required this.wallet}) { providerList = [ WyreBuyProvider(wallet: wallet), - MoonPayBuyProvider(wallet: wallet, ordersSource: ordersSource) + MoonPayBuyProvider(wallet: wallet) ]; items = providerList.map((provider) => BuyItem(provider: provider, buyAmountViewModel: buyAmountViewModel)) diff --git a/lib/view_model/order_details_view_model.dart b/lib/view_model/order_details_view_model.dart index 0fb92a761..d50469d4a 100644 --- a/lib/view_model/order_details_view_model.dart +++ b/lib/view_model/order_details_view_model.dart @@ -3,7 +3,6 @@ import 'package:cake_wallet/buy/buy_provider.dart'; import 'package:cake_wallet/buy/buy_provider_description.dart'; import 'package:cake_wallet/buy/order.dart'; import 'package:cake_wallet/utils/date_formatter.dart'; -import 'package:hive/hive.dart'; import 'package:mobx/mobx.dart'; import 'package:cake_wallet/generated/i18n.dart'; import 'package:cake_wallet/src/screens/transaction_details/standart_list_item.dart'; @@ -19,8 +18,7 @@ class OrderDetailsViewModel = OrderDetailsViewModelBase with _$OrderDetailsViewModel; abstract class OrderDetailsViewModelBase with Store { - OrderDetailsViewModelBase({WalletBase wallet, this.ordersSource, - Order orderForDetails}) { + OrderDetailsViewModelBase({WalletBase wallet, Order orderForDetails}) { order = orderForDetails; if (order.provider != null) { @@ -29,8 +27,7 @@ abstract class OrderDetailsViewModelBase with Store { _provider = WyreBuyProvider(wallet: wallet); break; case BuyProviderDescription.moonPay: - _provider = - MoonPayBuyProvider(wallet: wallet, ordersSource: ordersSource); + _provider = MoonPayBuyProvider(wallet: wallet); break; } } @@ -44,8 +41,6 @@ abstract class OrderDetailsViewModelBase with Store { timer = Timer.periodic(Duration(seconds: 20), (_) async => _updateOrder()); } - final Box ordersSource; - @observable Order order; @@ -61,6 +56,8 @@ abstract class OrderDetailsViewModelBase with Store { try { if (_provider != null) { final updatedOrder = await _provider.findOrderById(order.id); + updatedOrder.from = order.from; + updatedOrder.to = order.to; updatedOrder.receiveAddress = order.receiveAddress; updatedOrder.walletId = order.walletId; if (order.provider != null) { From bb92fb3288bbf4b5b6d55e99b896e88454fb8aee Mon Sep 17 00:00:00 2001 From: OleksandrSobol Date: Fri, 28 May 2021 17:58:10 +0300 Subject: [PATCH 07/17] CAKE-306 | merged main branch into current and resolved problems --- pubspec.lock | 6 +++--- pubspec.yaml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/pubspec.lock b/pubspec.lock index 3ff596074..207f4b18a 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -70,7 +70,7 @@ packages: path: "." ref: cake resolved-ref: "02fef082f20af13de00b4e64efb93a2c1e5e1cf2" - url: "git@github.com:cake-tech/bech32.git" + url: "https://github.com/cake-tech/bech32.git" source: git version: "0.2.0" bip32: @@ -92,8 +92,8 @@ packages: description: path: "." ref: cake - resolved-ref: b3ab2926c665f0e68b74a4a5f31059f7fcd817b7 - url: "git@github.com:cake-tech/bitcoin_flutter.git" + resolved-ref: cbabfd87b6ce3cae6051a3e86ddb56e7a934e188 + url: "https://github.com/cake-tech/bitcoin_flutter.git" source: git version: "2.0.2" boolean_selector: diff --git a/pubspec.yaml b/pubspec.yaml index 4d1b63ceb..cf4cf4bfe 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -151,4 +151,4 @@ flutter: # weight: 700 # # For details regarding fonts from package dependencies, - # see https://flutter.dev/custom-fonts/#from-packages + # see https://flutter.dev/custom-fonts/#from-packages \ No newline at end of file From 9e1c151f85ce2d1d9b48fd1c40ac0057891b3b45 Mon Sep 17 00:00:00 2001 From: OleksandrSobol Date: Tue, 1 Jun 2021 21:03:35 +0300 Subject: [PATCH 08/17] CAKE-306 | fixed moonpay_buy_provider.dart; added onEnabled() method to moonpay_buy_provider.dart; checked is MoonPayBuyProvider enabled; added isMoonPayEnabled to settings_store.dart --- lib/buy/buy_amount.dart | 6 +- lib/buy/moonpay/moonpay_buy_provider.dart | 67 ++++++++++++++++++----- lib/di.dart | 13 ++++- lib/src/screens/buy/pre_order_page.dart | 46 +++++++++++++--- lib/store/settings_store.dart | 17 ++++++ lib/view_model/buy/buy_view_model.dart | 16 ++++-- 6 files changed, 135 insertions(+), 30 deletions(-) diff --git a/lib/buy/buy_amount.dart b/lib/buy/buy_amount.dart index d049c1e03..3cf3543d8 100644 --- a/lib/buy/buy_amount.dart +++ b/lib/buy/buy_amount.dart @@ -1,8 +1,12 @@ import 'package:flutter/foundation.dart'; class BuyAmount { - BuyAmount({@required this.sourceAmount, @required this.destAmount}); + BuyAmount({ + @required this.sourceAmount, + @required this.destAmount, + this.minAmount = 0}); final double sourceAmount; final double destAmount; + final int minAmount; } \ No newline at end of file diff --git a/lib/buy/moonpay/moonpay_buy_provider.dart b/lib/buy/moonpay/moonpay_buy_provider.dart index eef0e941f..22b349c79 100644 --- a/lib/buy/moonpay/moonpay_buy_provider.dart +++ b/lib/buy/moonpay/moonpay_buy_provider.dart @@ -1,4 +1,5 @@ import 'dart:convert'; +import 'package:crypto/crypto.dart'; import 'package:cake_wallet/buy/buy_exception.dart'; import 'package:http/http.dart'; import 'package:cake_wallet/buy/buy_amount.dart'; @@ -13,17 +14,18 @@ import 'package:cake_wallet/.secrets.g.dart' as secrets; class MoonPayBuyProvider extends BuyProvider { MoonPayBuyProvider({WalletBase wallet, bool isTestEnvironment = false}) : super(wallet: wallet, isTestEnvironment: isTestEnvironment) { - baseApiUrl = isTestEnvironment - ? _baseTestApiUrl - : _baseProductApiUrl; + baseUrl = isTestEnvironment ? _baseTestUrl : _baseProductUrl; } - static const _baseTestApiUrl = 'https://buy-staging.moonpay.com'; - static const _baseProductApiUrl = 'https://api.moonpay.com'; + static const _baseTestUrl = 'https://buy-staging.moonpay.com'; + static const _baseProductUrl = 'https://buy.moonpay.com'; + static const _apiUrl = 'https://api.moonpay.com'; static const _currenciesSuffix = '/v3/currencies'; static const _quoteSuffix = '/buy_quote'; static const _transactionsSuffix = '/v1/transactions'; + static const _countrySuffix = '/v3/countries'; static const _apiKey = secrets.moonPayApiKey; + static const _secretKey = secrets.moonPaySecretKey; @override String get title => 'MoonPay'; @@ -35,9 +37,9 @@ class MoonPayBuyProvider extends BuyProvider { walletTypeToCryptoCurrency(walletType).title.toLowerCase(); @override - String get trackUrl => baseApiUrl + '/transaction_receipt?transactionId='; + String get trackUrl => baseUrl + '/transaction_receipt?transactionId='; - String baseApiUrl; + String baseUrl; @override Future requestUrl(String amount, String sourceCurrency) async { @@ -45,22 +47,32 @@ class MoonPayBuyProvider extends BuyProvider { 'credit_debit_card%2Capple_pay%2Cgoogle_pay%2Csamsung_pay' '%2Csepa_bank_transfer%2Cgbp_bank_transfer%2Cgbp_open_banking_payment'; - final originalUrl = baseApiUrl + '?apiKey=' + _apiKey + '¤cyCode=' + + final suffix = '?apiKey=' + _apiKey + '¤cyCode=' + currencyCode + '&enabledPaymentMethods=' + enabledPaymentMethods + '&walletAddress=' + walletAddress + '&baseCurrencyCode=' + sourceCurrency.toLowerCase() + '&baseCurrencyAmount=' + amount + '&lockAmount=true' + '&showAllCurrencies=false' + '&showWalletAddressForm=false'; - return originalUrl; + final originalUrl = baseUrl + suffix; + + final messageBytes = utf8.encode(suffix); + final key = utf8.encode(_secretKey); + final hmac = Hmac(sha256, key); + final digest = hmac.convert(messageBytes); + final signature = base64.encode(digest.bytes); + final urlWithSignature = originalUrl + + '&signature=${Uri.encodeComponent(signature)}'; + + return isTestEnvironment ? originalUrl : urlWithSignature; } @override Future calculateAmount(String amount, String sourceCurrency) async { - final url = _baseProductApiUrl + _currenciesSuffix + '/$currencyCode' + + final url = _apiUrl + _currenciesSuffix + '/$currencyCode' + _quoteSuffix + '/?apiKey=' + _apiKey + '&baseCurrencyAmount=' + amount + - '&baseCurrencyCode' + sourceCurrency.toLowerCase(); + '&baseCurrencyCode=' + sourceCurrency.toLowerCase(); final response = await get(url); @@ -73,13 +85,17 @@ class MoonPayBuyProvider extends BuyProvider { final responseJSON = json.decode(response.body) as Map; final sourceAmount = responseJSON['totalAmount'] as double; final destAmount = responseJSON['quoteCurrencyAmount'] as double; + final minSourceAmount = responseJSON['baseCurrency']['minAmount'] as int; - return BuyAmount(sourceAmount: sourceAmount, destAmount: destAmount); + return BuyAmount( + sourceAmount: sourceAmount, + destAmount: destAmount, + minAmount: minSourceAmount); } @override Future findOrderById(String id) async { - final url = _baseProductApiUrl + _transactionsSuffix + '/$id' + + final url = _apiUrl + _transactionsSuffix + '/$id' + '?apiKey=' + _apiKey; final response = await get(url); @@ -108,4 +124,29 @@ class MoonPayBuyProvider extends BuyProvider { walletId: walletId ); } + + static Future onEnabled(String deviceCountryCode) async { + final url = _apiUrl + _countrySuffix; + var isBuyEnable = false; + + final response = await get(url); + + try { + final responseJSON = json.decode(response.body) as List; + + for (var element in responseJSON) { + final countryMap = element as Map; + final countryCode = countryMap['alpha2'] as String; + if (countryCode == deviceCountryCode) { + isBuyEnable = countryMap['isBuyAllowed'] as bool; + break; + } + } + } catch (e) { + isBuyEnable = false; + print(e.toString()); + } + + return isBuyEnable; + } } \ No newline at end of file diff --git a/lib/di.dart b/lib/di.dart index 675366209..c00cb129e 100644 --- a/lib/di.dart +++ b/lib/di.dart @@ -1,5 +1,6 @@ import 'package:cake_wallet/bitcoin/bitcoin_wallet_service.dart'; import 'package:cake_wallet/bitcoin/litecoin_wallet_service.dart'; +import 'package:cake_wallet/buy/moonpay/moonpay_buy_provider.dart'; import 'package:cake_wallet/core/backup_service.dart'; import 'package:cake_wallet/core/wallet_service.dart'; import 'package:cake_wallet/entities/biometric_auth.dart'; @@ -91,6 +92,7 @@ import 'package:cake_wallet/view_model/wallet_list/wallet_list_view_model.dart'; import 'package:cake_wallet/view_model/wallet_restore_view_model.dart'; import 'package:cake_wallet/view_model/wallet_seed_view_model.dart'; import 'package:cake_wallet/view_model/exchange/exchange_view_model.dart'; +import 'package:devicelocale/devicelocale.dart'; import 'package:flutter/widgets.dart'; import 'package:get_it/get_it.dart'; import 'package:hive/hive.dart'; @@ -152,8 +154,14 @@ Future setup( final isBitcoinBuyEnabled = (secrets.wyreSecretKey?.isNotEmpty ?? false) && (secrets.wyreApiKey?.isNotEmpty ?? false) && (secrets.wyreAccountId?.isNotEmpty ?? false); + + final locale = await Devicelocale.currentLocale; + final deviceCountryCode = locale.split('_').last; + final isMoonPayEnabled = await MoonPayBuyProvider.onEnabled(deviceCountryCode); + final settingsStore = await SettingsStoreBase.load( - nodeSource: _nodeSource, isBitcoinBuyEnabled: isBitcoinBuyEnabled); + nodeSource: _nodeSource, isBitcoinBuyEnabled: isBitcoinBuyEnabled, + isMoonPayEnabled: isMoonPayEnabled); if (_isSetupFinished) { return; @@ -541,7 +549,8 @@ Future setup( final wallet = getIt.get().wallet; return BuyViewModel(_ordersSource, getIt.get(), - getIt.get(), wallet: wallet); + getIt.get(), getIt.get(), + wallet: wallet); }); getIt.registerFactory(() { diff --git a/lib/src/screens/buy/pre_order_page.dart b/lib/src/screens/buy/pre_order_page.dart index dbe03b07e..dd4832cc1 100644 --- a/lib/src/screens/buy/pre_order_page.dart +++ b/lib/src/screens/buy/pre_order_page.dart @@ -1,7 +1,11 @@ import 'dart:ui'; import 'package:cake_wallet/buy/buy_amount.dart'; +import 'package:cake_wallet/buy/buy_provider.dart'; +import 'package:cake_wallet/buy/moonpay/moonpay_buy_provider.dart'; import 'package:cake_wallet/src/screens/buy/widgets/buy_list_item.dart'; +import 'package:cake_wallet/src/widgets/alert_with_one_action.dart'; import 'package:cake_wallet/src/widgets/keyboard_done_button.dart'; +import 'package:cake_wallet/utils/show_pop_up.dart'; import 'package:cake_wallet/view_model/buy/buy_view_model.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; @@ -172,13 +176,16 @@ class PreOrderPage extends BasePage { builder: (context, AsyncSnapshot snapshot) { double sourceAmount; double destAmount; + int minAmount; if (snapshot.hasData) { sourceAmount = snapshot.data.sourceAmount; destAmount = snapshot.data.destAmount; + minAmount = snapshot.data.minAmount; } else { sourceAmount = 0.0; destAmount = 0.0; + minAmount = 0; } return Padding( @@ -192,14 +199,15 @@ class PreOrderPage extends BasePage { sourceCurrency: buyViewModel.fiatCurrency, destAmount: destAmount, destCurrency: buyViewModel.cryptoCurrency, - onTap: - buyViewModel.buyAmountViewModel - .doubleAmount == 0.0 ? null : () { - buyViewModel.selectedProvider = item.provider; - sourceAmount > 0 - ? buyViewModel.isDisabled = false - : buyViewModel.isDisabled = true; - } + onTap: ((buyViewModel.buyAmountViewModel + .doubleAmount != 0.0) && + (snapshot.hasData)) ? () => + onSelectBuyProvider( + context: context, + provider: item.provider, + sourceAmount: sourceAmount, + minAmount: minAmount + ) : null ); }) ); @@ -242,4 +250,26 @@ class PreOrderPage extends BasePage { ) ); } + + void onSelectBuyProvider({BuildContext context, BuyProvider provider, + double sourceAmount, int minAmount}) { + + if ((provider is MoonPayBuyProvider)&& + (buyViewModel.buyAmountViewModel.doubleAmount < minAmount)) { + showPopUp( + context: context, + builder: (BuildContext context) { + return AlertWithOneAction( + alertTitle: 'MoonPay', + alertContent: 'Value of the amount must be more than $minAmount ${buyViewModel.fiatCurrency.toString()}', + buttonText: S.of(context).ok, + buttonAction: () => Navigator.of(context).pop()); + }); + return; + } + buyViewModel.selectedProvider = provider; + sourceAmount > 0 + ? buyViewModel.isDisabled = false + : buyViewModel.isDisabled = true; + } } \ No newline at end of file diff --git a/lib/store/settings_store.dart b/lib/store/settings_store.dart index da2410378..2b26b2dd8 100644 --- a/lib/store/settings_store.dart +++ b/lib/store/settings_store.dart @@ -1,4 +1,5 @@ import 'package:cake_wallet/bitcoin/bitcoin_transaction_priority.dart'; +import 'package:cake_wallet/buy/moonpay/moonpay_buy_provider.dart'; import 'package:cake_wallet/entities/preferences_key.dart'; import 'package:cake_wallet/entities/transaction_priority.dart'; import 'package:cake_wallet/themes/theme_base.dart'; @@ -18,6 +19,7 @@ import 'package:cake_wallet/entities/fiat_currency.dart'; import 'package:cake_wallet/entities/node.dart'; import 'package:cake_wallet/entities/monero_transaction_priority.dart'; import 'package:cake_wallet/entities/action_list_display_mode.dart'; +import 'package:cake_wallet/.secrets.g.dart' as secrets; part 'settings_store.g.dart'; @@ -39,6 +41,7 @@ abstract class SettingsStoreBase with Store { @required TransactionPriority initialBitcoinTransactionPriority, @required TransactionPriority initialMoneroTransactionPriority, @required this.isBitcoinBuyEnabled, + @required this.isMoonPayEnabled, this.actionlistDisplayMode}) { fiatCurrency = initialFiatCurrency; balanceDisplayMode = initialBalanceDisplayMode; @@ -147,9 +150,12 @@ abstract class SettingsStoreBase with Store { bool isBitcoinBuyEnabled; + bool isMoonPayEnabled; + static Future load( {@required Box nodeSource, @required bool isBitcoinBuyEnabled, + @required bool isMoonPayEnabled, FiatCurrency initialFiatCurrency = FiatCurrency.usd, MoneroTransactionPriority initialMoneroTransactionPriority = MoneroTransactionPriority.slow, @@ -221,6 +227,7 @@ abstract class SettingsStoreBase with Store { }, appVersion: packageInfo.version, isBitcoinBuyEnabled: isBitcoinBuyEnabled, + isMoonPayEnabled: isMoonPayEnabled, initialFiatCurrency: currentFiatCurrency, initialBalanceDisplayMode: currentBalanceDisplayMode, initialSaveRecipientAddress: shouldSaveRecipientAddress, @@ -242,8 +249,18 @@ abstract class SettingsStoreBase with Store { BitcoinTransactionPriority.medium, BalanceDisplayMode initialBalanceDisplayMode = BalanceDisplayMode.availableBalance}) async { + final isBitcoinBuyEnabled = (secrets.wyreSecretKey?.isNotEmpty ?? false) && + (secrets.wyreApiKey?.isNotEmpty ?? false) && + (secrets.wyreAccountId?.isNotEmpty ?? false); + + final locale = await Devicelocale.currentLocale; + final deviceCountryCode = locale.split('_').last; + final isMoonPayEnabled = await MoonPayBuyProvider.onEnabled(deviceCountryCode); + final settings = await SettingsStoreBase.load( nodeSource: nodeSource, + isBitcoinBuyEnabled: isBitcoinBuyEnabled, + isMoonPayEnabled: isMoonPayEnabled, initialBalanceDisplayMode: initialBalanceDisplayMode, initialFiatCurrency: initialFiatCurrency, initialMoneroTransactionPriority: initialMoneroTransactionPriority, diff --git a/lib/view_model/buy/buy_view_model.dart b/lib/view_model/buy/buy_view_model.dart index 90dbd6cfa..68afb9577 100644 --- a/lib/view_model/buy/buy_view_model.dart +++ b/lib/view_model/buy/buy_view_model.dart @@ -4,6 +4,7 @@ import 'package:cake_wallet/buy/wyre/wyre_buy_provider.dart'; import 'package:cake_wallet/entities/crypto_currency.dart'; import 'package:cake_wallet/entities/fiat_currency.dart'; import 'package:cake_wallet/entities/wallet_type.dart'; +import 'package:cake_wallet/store/settings_store.dart'; import 'package:cake_wallet/view_model/buy/buy_item.dart'; import 'package:flutter/foundation.dart'; import 'package:hive/hive.dart'; @@ -18,12 +19,14 @@ part 'buy_view_model.g.dart'; class BuyViewModel = BuyViewModelBase with _$BuyViewModel; abstract class BuyViewModelBase with Store { - BuyViewModelBase(this.ordersSource, this.ordersStore, this.buyAmountViewModel, - {@required this.wallet}) { - providerList = [ - WyreBuyProvider(wallet: wallet), - MoonPayBuyProvider(wallet: wallet) - ]; + BuyViewModelBase(this.ordersSource, this.ordersStore, this.settingsStore, + this.buyAmountViewModel, {@required this.wallet}) { + providerList = [WyreBuyProvider(wallet: wallet)]; + + if (settingsStore.isMoonPayEnabled ?? false) { + providerList.add(MoonPayBuyProvider(wallet: wallet)); + } + items = providerList.map((provider) => BuyItem(provider: provider, buyAmountViewModel: buyAmountViewModel)) .toList(); @@ -34,6 +37,7 @@ abstract class BuyViewModelBase with Store { final Box ordersSource; final OrdersStore ordersStore; + final SettingsStore settingsStore; final BuyAmountViewModel buyAmountViewModel; final WalletBase wallet; From 1bf6ef607765272239b8839b7f4191ad879a2ec9 Mon Sep 17 00:00:00 2001 From: OleksandrSobol Date: Wed, 2 Jun 2021 09:56:06 +0300 Subject: [PATCH 09/17] CAKE-306 | applied localization to MoonPay alert --- lib/src/screens/buy/pre_order_page.dart | 4 +++- res/values/strings_de.arb | 3 ++- res/values/strings_en.arb | 3 ++- res/values/strings_es.arb | 3 ++- res/values/strings_hi.arb | 3 ++- res/values/strings_hr.arb | 3 ++- res/values/strings_it.arb | 3 ++- res/values/strings_ja.arb | 3 ++- res/values/strings_ko.arb | 3 ++- res/values/strings_nl.arb | 3 ++- res/values/strings_pl.arb | 3 ++- res/values/strings_pt.arb | 3 ++- res/values/strings_ru.arb | 3 ++- res/values/strings_uk.arb | 3 ++- res/values/strings_zh.arb | 3 ++- 15 files changed, 31 insertions(+), 15 deletions(-) diff --git a/lib/src/screens/buy/pre_order_page.dart b/lib/src/screens/buy/pre_order_page.dart index dd4832cc1..39728c058 100644 --- a/lib/src/screens/buy/pre_order_page.dart +++ b/lib/src/screens/buy/pre_order_page.dart @@ -261,7 +261,9 @@ class PreOrderPage extends BasePage { builder: (BuildContext context) { return AlertWithOneAction( alertTitle: 'MoonPay', - alertContent: 'Value of the amount must be more than $minAmount ${buyViewModel.fiatCurrency.toString()}', + alertContent: S.of(context).moonpay_alert_text( + minAmount.toString(), + buyViewModel.fiatCurrency.toString()), buttonText: S.of(context).ok, buttonAction: () => Navigator.of(context).pop()); }); diff --git a/res/values/strings_de.arb b/res/values/strings_de.arb index 6d689104f..d5b8a1a8d 100644 --- a/res/values/strings_de.arb +++ b/res/values/strings_de.arb @@ -477,5 +477,6 @@ "understand" : "I understand", "buy_bitcoin" : "Bitcoin kaufen", - "buy_with" : "Kaufen mit" + "buy_with" : "Kaufen mit", + "moonpay_alert_text" : "Der Wert des Betrags muss größer oder gleich sein ${minAmount} ${fiatCurrency}" } \ No newline at end of file diff --git a/res/values/strings_en.arb b/res/values/strings_en.arb index 2c4c2e28f..f2a045708 100644 --- a/res/values/strings_en.arb +++ b/res/values/strings_en.arb @@ -477,5 +477,6 @@ "understand" : "I understand", "buy_bitcoin" : "Buy Bitcoin", - "buy_with" : "Buy with" + "buy_with" : "Buy with", + "moonpay_alert_text" : "Value of the amount must be more or equal to ${minAmount} ${fiatCurrency}" } \ No newline at end of file diff --git a/res/values/strings_es.arb b/res/values/strings_es.arb index 2e07285db..5fc627bcd 100644 --- a/res/values/strings_es.arb +++ b/res/values/strings_es.arb @@ -477,5 +477,6 @@ "understand" : "I understand", "buy_bitcoin" : "Comprar Bitcoin", - "buy_with" : "Compra con" + "buy_with" : "Compra con", + "moonpay_alert_text" : "El valor de la cantidad debe ser mayor o igual a ${minAmount} ${fiatCurrency}" } \ No newline at end of file diff --git a/res/values/strings_hi.arb b/res/values/strings_hi.arb index 2430f2bce..7b2af2856 100644 --- a/res/values/strings_hi.arb +++ b/res/values/strings_hi.arb @@ -477,5 +477,6 @@ "understand" : "I understand", "buy_bitcoin" : "बिटकॉइन खरीदें", - "buy_with" : "के साथ खरीदें" + "buy_with" : "के साथ खरीदें", + "moonpay_alert_text" : "राशि का मूल्य अधिक है या करने के लिए बराबर होना चाहिए ${minAmount} ${fiatCurrency}" } \ No newline at end of file diff --git a/res/values/strings_hr.arb b/res/values/strings_hr.arb index d449fca1b..d0bce1c55 100644 --- a/res/values/strings_hr.arb +++ b/res/values/strings_hr.arb @@ -474,5 +474,6 @@ "buy_alert_content" : "Currently we only support the purchase of Bitcoin. To buy Bitcoin, please create or switch to your Bitcoin wallet", "outdated_electrum_wallet_desceription" : "New Bitcoin wallets created in Cake now have a 24-word seed. It is mandatory that you create a new Bitcoin wallet and transfer all of your funds to the new 24-word wallet, and stop using wallets with a 12-word seed. Please do this immediately to secure your funds.", - "understand" : "I understand" + "understand" : "I understand", + "moonpay_alert_text" : "Vrijednost iznosa mora biti veća ili jednaka ${minAmount} ${fiatCurrency}" } \ No newline at end of file diff --git a/res/values/strings_it.arb b/res/values/strings_it.arb index e8c4da26d..6d54c2b78 100644 --- a/res/values/strings_it.arb +++ b/res/values/strings_it.arb @@ -474,5 +474,6 @@ "buy_alert_content" : "Currently we only support the purchase of Bitcoin. To buy Bitcoin, please create or switch to your Bitcoin wallet", "outdated_electrum_wallet_desceription" : "New Bitcoin wallets created in Cake now have a 24-word seed. It is mandatory that you create a new Bitcoin wallet and transfer all of your funds to the new 24-word wallet, and stop using wallets with a 12-word seed. Please do this immediately to secure your funds.", - "understand" : "I understand" + "understand" : "I understand", + "moonpay_alert_text" : "Il valore dell'importo deve essere maggiore o uguale a ${minAmount} ${fiatCurrency}" } \ No newline at end of file diff --git a/res/values/strings_ja.arb b/res/values/strings_ja.arb index 1eeb07c27..bb1189653 100644 --- a/res/values/strings_ja.arb +++ b/res/values/strings_ja.arb @@ -477,5 +477,6 @@ "understand" : "I understand", "buy_bitcoin" : "ビットコインを購入する", - "buy_with" : "で購入" + "buy_with" : "で購入", + "moonpay_alert_text" : "金額の値は以上でなければなりません ${minAmount} ${fiatCurrency}" } \ No newline at end of file diff --git a/res/values/strings_ko.arb b/res/values/strings_ko.arb index 91fd84b90..0774fe4de 100644 --- a/res/values/strings_ko.arb +++ b/res/values/strings_ko.arb @@ -477,5 +477,6 @@ "understand" : "I understand", "buy_bitcoin" : "비트 코인 구매", - "buy_with" : "구매" + "buy_with" : "구매", + "moonpay_alert_text" : "금액은 다음보다 크거나 같아야합니다 ${minAmount} ${fiatCurrency}" } \ No newline at end of file diff --git a/res/values/strings_nl.arb b/res/values/strings_nl.arb index 653078810..82f9ec961 100644 --- a/res/values/strings_nl.arb +++ b/res/values/strings_nl.arb @@ -477,5 +477,6 @@ "understand" : "I understand", "buy_bitcoin" : "Koop Bitcoin", - "buy_with" : "Koop met" + "buy_with" : "Koop met", + "moonpay_alert_text" : "Waarde van het bedrag moet meer of gelijk zijn aan ${minAmount} ${fiatCurrency}" } \ No newline at end of file diff --git a/res/values/strings_pl.arb b/res/values/strings_pl.arb index 7b299afbf..dbef4aab6 100644 --- a/res/values/strings_pl.arb +++ b/res/values/strings_pl.arb @@ -477,5 +477,6 @@ "understand" : "I understand", "buy_bitcoin" : "Kup Bitcoin", - "buy_with" : "Kup za pomocą" + "buy_with" : "Kup za pomocą", + "moonpay_alert_text" : "Wartość kwoty musi być większa lub równa ${minAmount} ${fiatCurrency}" } \ No newline at end of file diff --git a/res/values/strings_pt.arb b/res/values/strings_pt.arb index cc0d22ed2..270b02909 100644 --- a/res/values/strings_pt.arb +++ b/res/values/strings_pt.arb @@ -477,5 +477,6 @@ "understand" : "I understand", "buy_bitcoin" : "Compre Bitcoin", - "buy_with" : "Compre com" + "buy_with" : "Compre com", + "moonpay_alert_text" : "O valor do montante deve ser maior ou igual a ${minAmount} ${fiatCurrency}" } \ No newline at end of file diff --git a/res/values/strings_ru.arb b/res/values/strings_ru.arb index 63831b3d0..f29625aec 100644 --- a/res/values/strings_ru.arb +++ b/res/values/strings_ru.arb @@ -477,5 +477,6 @@ "understand" : "I understand", "buy_bitcoin" : "Купить Bitcoin", - "buy_with" : "Купить с помощью" + "buy_with" : "Купить с помощью", + "moonpay_alert_text" : "Сумма должна быть больше или равна ${minAmount} ${fiatCurrency}" } \ No newline at end of file diff --git a/res/values/strings_uk.arb b/res/values/strings_uk.arb index 879b52102..b101f81cd 100644 --- a/res/values/strings_uk.arb +++ b/res/values/strings_uk.arb @@ -477,5 +477,6 @@ "understand" : "I understand", "buy_bitcoin" : "Купити Bitcoin", - "buy_with" : "Купити за допомогою" + "buy_with" : "Купити за допомогою", + "moonpay_alert_text" : "Значення суми має бути більшим або дорівнювати ${minAmount} ${fiatCurrency}" } \ No newline at end of file diff --git a/res/values/strings_zh.arb b/res/values/strings_zh.arb index 674a1ec48..9363e621c 100644 --- a/res/values/strings_zh.arb +++ b/res/values/strings_zh.arb @@ -474,5 +474,6 @@ "understand" : "I understand", "buy_bitcoin" : "購買比特幣", - "buy_with" : "與一起購買" + "buy_with" : "與一起購買", + "moonpay_alert_text" : "金額的價值必須大於或等於 ${minAmount} ${fiatCurrency}" } \ No newline at end of file From c8debf46efe52a6fc5246561ad021fc2b1149580 Mon Sep 17 00:00:00 2001 From: OleksandrSobol Date: Wed, 2 Jun 2021 10:03:14 +0300 Subject: [PATCH 10/17] CAKE-306 | fixed pre_order_page.dart --- lib/src/screens/buy/pre_order_page.dart | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/lib/src/screens/buy/pre_order_page.dart b/lib/src/screens/buy/pre_order_page.dart index 39728c058..1385ba3e5 100644 --- a/lib/src/screens/buy/pre_order_page.dart +++ b/lib/src/screens/buy/pre_order_page.dart @@ -199,9 +199,8 @@ class PreOrderPage extends BasePage { sourceCurrency: buyViewModel.fiatCurrency, destAmount: destAmount, destCurrency: buyViewModel.cryptoCurrency, - onTap: ((buyViewModel.buyAmountViewModel - .doubleAmount != 0.0) && - (snapshot.hasData)) ? () => + onTap: ((buyViewModel.doubleAmount != 0.0) + && (snapshot.hasData)) ? () => onSelectBuyProvider( context: context, provider: item.provider, From 6950ec54607c83bf56480f7505f4788542d4d195 Mon Sep 17 00:00:00 2001 From: OleksandrSobol Date: Wed, 2 Jun 2021 10:27:28 +0300 Subject: [PATCH 11/17] CAKE-306 | merged main branch into current --- lib/src/screens/buy/pre_order_page.dart | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/src/screens/buy/pre_order_page.dart b/lib/src/screens/buy/pre_order_page.dart index 1385ba3e5..66aecfb2b 100644 --- a/lib/src/screens/buy/pre_order_page.dart +++ b/lib/src/screens/buy/pre_order_page.dart @@ -171,7 +171,8 @@ class PreOrderPage extends BasePage { ), if (buyViewModel.isShowProviderButtons) ...buyViewModel.items.map( - (item) => Observer(builder: (_) => FutureBuilder( + (item) => Observer(builder: (_) => + FutureBuilder( future: item.buyAmount, builder: (context, AsyncSnapshot snapshot) { double sourceAmount; @@ -193,7 +194,8 @@ class PreOrderPage extends BasePage { EdgeInsets.only(left: 15, top: 20, right: 15), child: Observer(builder: (_) { return BuyListItem( - selectedProvider: buyViewModel.selectedProvider, + selectedProvider: + buyViewModel.selectedProvider, provider: item.provider, sourceAmount: sourceAmount, sourceCurrency: buyViewModel.fiatCurrency, From 087579410e35094680d27372fad02bca09604b7d Mon Sep 17 00:00:00 2001 From: OleksandrSobol Date: Wed, 2 Jun 2021 12:28:18 +0300 Subject: [PATCH 12/17] CAKE-306 | increased width of the amount text field in the pre_order_page.dart --- lib/src/screens/buy/pre_order_page.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/src/screens/buy/pre_order_page.dart b/lib/src/screens/buy/pre_order_page.dart index 66aecfb2b..ebda2dc8a 100644 --- a/lib/src/screens/buy/pre_order_page.dart +++ b/lib/src/screens/buy/pre_order_page.dart @@ -114,7 +114,7 @@ class PreOrderPage extends BasePage { padding: EdgeInsets.only(top: 100, bottom: 65), child: Center( child: Container( - width: 165, + width: 185, child: BaseTextFormField( focusNode: _amountFocus, controller: _amountController, From 90d53c9f6e1393c827176e4fbaac32f036489b3c Mon Sep 17 00:00:00 2001 From: OleksandrSobol Date: Wed, 2 Jun 2021 16:52:58 +0300 Subject: [PATCH 13/17] CAKE-306 | fixed MoonPay icon for buy_list_item.dart and order_row.dart --- assets/images/moonpay-icon.png | Bin 2015 -> 1381 bytes lib/buy/get_buy_provider_icon.dart | 14 ++++++++--- lib/di.dart | 12 ++++++--- lib/src/screens/buy/pre_order_page.dart | 2 +- .../screens/buy/widgets/buy_list_item.dart | 23 ++++++++---------- .../screens/dashboard/widgets/order_row.dart | 9 ++++++- lib/store/settings_store.dart | 12 ++++++--- 7 files changed, 47 insertions(+), 25 deletions(-) diff --git a/assets/images/moonpay-icon.png b/assets/images/moonpay-icon.png index 1fbc2b6fc67fbb9ceab8b7134328fe7416db7980..3c0260108f3a8593b1d87f89a552499980d5ea2d 100644 GIT binary patch delta 873 zcmV-v1D5>X59JCWiBL{Q4GJ0x0000DNk~Le0000Y0000a2nGNE0PG&<-;p7a2}l3{ z0096107#L$9V#NA0{{d700031001Na0001f06F&n000SaNLh0L01FcU01FcV0GgZ_ zvjPEU0e=GKNklf7d&~_OT?3?cMpOW z6?_4Gg`7nZ6kkAzkw8$6t6W|g!wimUMIVn619)Ta= zH`oOCl7U?fhmJ*`9kAITD$QINVFS$(;B}pB7v~% z!f0YfU|>$i_gPol^}f#5Y@@d?q6W75$i}5~L#J2*z0j;@F zQ?A-gfRi|d{T9Tc`@Lh39m`7w^G3#V%;Gx_4w*mnQ+4IYOF-xe^l|VBNVL`EqG7^* z;%0o0@2C}>j#6uP1G(7OXe$6b2NIDOR)2ok!X+=jSh>RKa$iyt5Z_zn+}2ME@!O`5 zD=LUi?n1Z)&W-?@JyT)%FPp#ixO!YkhnJiAt|}Kf)lZFmF+hZ1AD_iOZOM{XW9wq& zc`o2Q5>M3G9^muN`Y?`zz#l+9Kdkm$Q4T$Cl2Rt(0Zz!rqiD}jS7RV&-6B1nyML0O z!z3yb@msM<@EXv3+h!%Ci1~>9MUIq|iO}4k%U1||W5G@?l`Ee29bMwYtn0)e?JkI* za7xe@flJd9)z`T8=Z-#x&WIg)l#>?%!%mUlfn`^6JquD2ue)U=l5hBUD+g?2webv)nssbF5dzK^lH6$%s|$W@s| zSZk{0hVdnLq$-Ju;`i8p^Ea}J*x2`9@4z1>{+vv34X26#0000 zN8*2?+uK{7X=7tUw6wH{*RNlTu&^-E)YK#v7k?Ku3esQC_@Tf|PEHCU;>X+DTLc6I z==R9S2qSR7TQWTxNibv>25oI^QCwUcMMOl9zrR0y|NfmY78)8#PEJm=yu3`GK7AtN z*^pQ$9v>g)9%&U?TU*1TBm=G8zLF7MRzP`@Bp#dl`(#gco(2!OD5K!>u&6@{La&oeek)nA^OH0N4 z{JhqA7u3g(A2pxl!ltICSVo7vGV{T~0W~)_Q&(3PZ-MBQp})dadOmCC5ice}6x>kzbsho&OjNl)>4hPk&NsYAVTe zKr=HlJcpUp4J4`7)>e+F!^1{7uIhJlz~$gBps2Ek!>+E z;}c%cb#`_};o;%b-Q7(xIkL2^IW!<_mo3I8;JC+nd3i~`zP{XtqsNSl+1XjNvJZIWcM=Gcw?8GJk*Yb}2zKp{I^3%b%Q_n2K_gDFIXN-(*nwkqSVz7H-7H z#}m%4zZLG*Fr(f9ArFBRKyFyPjH03jiZ{S(51;5X2P>^v|qKrdguw6FX?frPL`nE?g$X;1WFBIv=H zDE~-cFuA$8G&VNICxM#7%<>P4d<6=ExJ%)tiE|E7h)GfBC<0J*Rw3!3AcIfhmReq3 z&KHHCpdh=pO6kPvHDUWe(lSE&6xYO(k^efu6-Y*;2o4U`#ecpmD?~y<0vGkNT-E&T z?d?Kd09lvP$qhSR_W*%kDZt$ff1jwrAHdXuA|3_3YO7y)eG;-B*V8Ipy2FkSy=79V zrn~!%twLhMJqpF#^z=06EHD=o6i{AX9@paX>Z21Md80& diff --git a/lib/buy/get_buy_provider_icon.dart b/lib/buy/get_buy_provider_icon.dart index bbd599e24..f284b6f04 100644 --- a/lib/buy/get_buy_provider_icon.dart +++ b/lib/buy/get_buy_provider_icon.dart @@ -1,18 +1,24 @@ import 'package:flutter/material.dart'; import 'package:cake_wallet/buy/buy_provider_description.dart'; -Image getBuyProviderIcon(BuyProviderDescription providerDescription) { +Image getBuyProviderIcon(BuyProviderDescription providerDescription, + {bool isWhiteIconColor = false}) { + final _wyreIcon = Image.asset('assets/images/wyre-icon.png', width: 36, height: 36); - final _moonPayIcon = - Image.asset('assets/images/moonpay-icon.png', width: 36, height: 34); + final _moonPayWhiteIcon = + Image.asset('assets/images/moonpay-icon.png', color: Colors.white, + width: 36, height: 34); + final _moonPayBlackIcon = + Image.asset('assets/images/moonpay-icon.png', color: Colors.black, + width: 36, height: 34); if (providerDescription != null) { switch (providerDescription) { case BuyProviderDescription.wyre: return _wyreIcon; case BuyProviderDescription.moonPay: - return _moonPayIcon; + return isWhiteIconColor ? _moonPayWhiteIcon : _moonPayBlackIcon; default: return null; } diff --git a/lib/di.dart b/lib/di.dart index c00cb129e..58523c474 100644 --- a/lib/di.dart +++ b/lib/di.dart @@ -155,9 +155,15 @@ Future setup( (secrets.wyreApiKey?.isNotEmpty ?? false) && (secrets.wyreAccountId?.isNotEmpty ?? false); - final locale = await Devicelocale.currentLocale; - final deviceCountryCode = locale.split('_').last; - final isMoonPayEnabled = await MoonPayBuyProvider.onEnabled(deviceCountryCode); + var isMoonPayEnabled = false; + try { + final locale = await Devicelocale.currentLocale; + final deviceCountryCode = locale.split('_').last; + isMoonPayEnabled = await MoonPayBuyProvider.onEnabled(deviceCountryCode); + } catch (e) { + isMoonPayEnabled = false; + print(e.toString()); + } final settingsStore = await SettingsStoreBase.load( nodeSource: _nodeSource, isBitcoinBuyEnabled: isBitcoinBuyEnabled, diff --git a/lib/src/screens/buy/pre_order_page.dart b/lib/src/screens/buy/pre_order_page.dart index ebda2dc8a..cf6cca586 100644 --- a/lib/src/screens/buy/pre_order_page.dart +++ b/lib/src/screens/buy/pre_order_page.dart @@ -114,7 +114,7 @@ class PreOrderPage extends BasePage { padding: EdgeInsets.only(top: 100, bottom: 65), child: Center( child: Container( - width: 185, + width: 210, child: BaseTextFormField( focusNode: _amountFocus, controller: _amountController, diff --git a/lib/src/screens/buy/widgets/buy_list_item.dart b/lib/src/screens/buy/widgets/buy_list_item.dart index 7c18893a6..97eb88ec6 100644 --- a/lib/src/screens/buy/widgets/buy_list_item.dart +++ b/lib/src/screens/buy/widgets/buy_list_item.dart @@ -26,25 +26,22 @@ class BuyListItem extends StatelessWidget { @override Widget build(BuildContext context) { - final providerIcon = getBuyProviderIcon(provider.description); + final isSelected = selectedProvider?.description == provider.description; - final backgroundColor = selectedProvider != null - ? selectedProvider.description == provider.description + final providerIcon = getBuyProviderIcon(provider.description, + isWhiteIconColor: isSelected); + + final backgroundColor = isSelected ? Palette.greyBlueCraiola - : Palette.shadowWhite - : Palette.shadowWhite; + : Palette.shadowWhite; - final primaryTextColor = selectedProvider != null - ? selectedProvider.description == provider.description + final primaryTextColor = isSelected ? Colors.white - : Palette.darkGray - : Palette.darkGray; + : Palette.darkGray; - final secondaryTextColor = selectedProvider != null - ? selectedProvider.description == provider.description + final secondaryTextColor = isSelected ? Colors.white - : Palette.darkBlueCraiola - : Palette.darkBlueCraiola; + : Palette.darkBlueCraiola; return GestureDetector( onTap: () => onTap?.call(), diff --git a/lib/src/screens/dashboard/widgets/order_row.dart b/lib/src/screens/dashboard/widgets/order_row.dart index 56a69122e..9a7a61525 100644 --- a/lib/src/screens/dashboard/widgets/order_row.dart +++ b/lib/src/screens/dashboard/widgets/order_row.dart @@ -1,6 +1,9 @@ import 'package:cake_wallet/buy/buy_provider_description.dart'; import 'package:cake_wallet/buy/get_buy_provider_icon.dart'; +import 'package:cake_wallet/themes/theme_base.dart'; import 'package:flutter/material.dart'; +import 'package:cake_wallet/di.dart'; +import 'package:cake_wallet/store/settings_store.dart'; class OrderRow extends StatelessWidget { OrderRow({ @@ -19,7 +22,11 @@ class OrderRow extends StatelessWidget { @override Widget build(BuildContext context) { - final providerIcon = getBuyProviderIcon(provider); + final currentTheme = getIt.get().currentTheme; + final isWhiteIconColor = currentTheme.type != ThemeType.light; + + final providerIcon = getBuyProviderIcon(provider, + isWhiteIconColor: isWhiteIconColor); return InkWell( onTap: onTap, diff --git a/lib/store/settings_store.dart b/lib/store/settings_store.dart index 2b26b2dd8..be71b9f2c 100644 --- a/lib/store/settings_store.dart +++ b/lib/store/settings_store.dart @@ -253,9 +253,15 @@ abstract class SettingsStoreBase with Store { (secrets.wyreApiKey?.isNotEmpty ?? false) && (secrets.wyreAccountId?.isNotEmpty ?? false); - final locale = await Devicelocale.currentLocale; - final deviceCountryCode = locale.split('_').last; - final isMoonPayEnabled = await MoonPayBuyProvider.onEnabled(deviceCountryCode); + var isMoonPayEnabled = false; + try { + final locale = await Devicelocale.currentLocale; + final deviceCountryCode = locale.split('_').last; + isMoonPayEnabled = await MoonPayBuyProvider.onEnabled(deviceCountryCode); + } catch (e) { + isMoonPayEnabled = false; + print(e.toString()); + } final settings = await SettingsStoreBase.load( nodeSource: nodeSource, From ee6b8767d694cc67c3a9d2a3da574ea9e689ce00 Mon Sep 17 00:00:00 2001 From: OleksandrSobol Date: Wed, 2 Jun 2021 20:09:39 +0300 Subject: [PATCH 14/17] CAKE-306 | fixed settings_store.dart and di.dart --- lib/di.dart | 6 +++--- lib/store/settings_store.dart | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/di.dart b/lib/di.dart index 58523c474..b5bb7bfd3 100644 --- a/lib/di.dart +++ b/lib/di.dart @@ -155,7 +155,7 @@ Future setup( (secrets.wyreApiKey?.isNotEmpty ?? false) && (secrets.wyreAccountId?.isNotEmpty ?? false); - var isMoonPayEnabled = false; + /*var isMoonPayEnabled = false; try { final locale = await Devicelocale.currentLocale; final deviceCountryCode = locale.split('_').last; @@ -163,11 +163,11 @@ Future setup( } catch (e) { isMoonPayEnabled = false; print(e.toString()); - } + }*/ final settingsStore = await SettingsStoreBase.load( nodeSource: _nodeSource, isBitcoinBuyEnabled: isBitcoinBuyEnabled, - isMoonPayEnabled: isMoonPayEnabled); + isMoonPayEnabled: true); if (_isSetupFinished) { return; diff --git a/lib/store/settings_store.dart b/lib/store/settings_store.dart index be71b9f2c..207a731c9 100644 --- a/lib/store/settings_store.dart +++ b/lib/store/settings_store.dart @@ -253,7 +253,7 @@ abstract class SettingsStoreBase with Store { (secrets.wyreApiKey?.isNotEmpty ?? false) && (secrets.wyreAccountId?.isNotEmpty ?? false); - var isMoonPayEnabled = false; + /*var isMoonPayEnabled = false; try { final locale = await Devicelocale.currentLocale; final deviceCountryCode = locale.split('_').last; @@ -261,12 +261,12 @@ abstract class SettingsStoreBase with Store { } catch (e) { isMoonPayEnabled = false; print(e.toString()); - } + }*/ final settings = await SettingsStoreBase.load( nodeSource: nodeSource, isBitcoinBuyEnabled: isBitcoinBuyEnabled, - isMoonPayEnabled: isMoonPayEnabled, + isMoonPayEnabled: true, initialBalanceDisplayMode: initialBalanceDisplayMode, initialFiatCurrency: initialFiatCurrency, initialMoneroTransactionPriority: initialMoneroTransactionPriority, From 6ac8dabf87a8402bbc548367251680a83f077564 Mon Sep 17 00:00:00 2001 From: OleksandrSobol Date: Mon, 7 Jun 2021 14:28:53 +0300 Subject: [PATCH 15/17] CAKE-306 | fixed onEnabled() method in the moonpay_buy_provider.dart --- lib/buy/moonpay/moonpay_buy_provider.dart | 18 +++++------------- lib/di.dart | 10 ++++------ lib/store/settings_store.dart | 10 ++++------ 3 files changed, 13 insertions(+), 25 deletions(-) diff --git a/lib/buy/moonpay/moonpay_buy_provider.dart b/lib/buy/moonpay/moonpay_buy_provider.dart index 22b349c79..65effe483 100644 --- a/lib/buy/moonpay/moonpay_buy_provider.dart +++ b/lib/buy/moonpay/moonpay_buy_provider.dart @@ -23,7 +23,7 @@ class MoonPayBuyProvider extends BuyProvider { static const _currenciesSuffix = '/v3/currencies'; static const _quoteSuffix = '/buy_quote'; static const _transactionsSuffix = '/v1/transactions'; - static const _countrySuffix = '/v3/countries'; + static const _ipAddressSuffix = '/v4/ip_address'; static const _apiKey = secrets.moonPayApiKey; static const _secretKey = secrets.moonPaySecretKey; @@ -125,23 +125,15 @@ class MoonPayBuyProvider extends BuyProvider { ); } - static Future onEnabled(String deviceCountryCode) async { - final url = _apiUrl + _countrySuffix; + static Future onEnabled() async { + final url = _apiUrl + _ipAddressSuffix + '?apiKey=' + _apiKey; var isBuyEnable = false; final response = await get(url); try { - final responseJSON = json.decode(response.body) as List; - - for (var element in responseJSON) { - final countryMap = element as Map; - final countryCode = countryMap['alpha2'] as String; - if (countryCode == deviceCountryCode) { - isBuyEnable = countryMap['isBuyAllowed'] as bool; - break; - } - } + final responseJSON = json.decode(response.body) as Map; + isBuyEnable = responseJSON['isBuyAllowed'] as bool; } catch (e) { isBuyEnable = false; print(e.toString()); diff --git a/lib/di.dart b/lib/di.dart index b5bb7bfd3..a4e922f7c 100644 --- a/lib/di.dart +++ b/lib/di.dart @@ -155,19 +155,17 @@ Future setup( (secrets.wyreApiKey?.isNotEmpty ?? false) && (secrets.wyreAccountId?.isNotEmpty ?? false); - /*var isMoonPayEnabled = false; + var isMoonPayEnabled = false; try { - final locale = await Devicelocale.currentLocale; - final deviceCountryCode = locale.split('_').last; - isMoonPayEnabled = await MoonPayBuyProvider.onEnabled(deviceCountryCode); + isMoonPayEnabled = await MoonPayBuyProvider.onEnabled(); } catch (e) { isMoonPayEnabled = false; print(e.toString()); - }*/ + } final settingsStore = await SettingsStoreBase.load( nodeSource: _nodeSource, isBitcoinBuyEnabled: isBitcoinBuyEnabled, - isMoonPayEnabled: true); + isMoonPayEnabled: isMoonPayEnabled); if (_isSetupFinished) { return; diff --git a/lib/store/settings_store.dart b/lib/store/settings_store.dart index 207a731c9..206c3141b 100644 --- a/lib/store/settings_store.dart +++ b/lib/store/settings_store.dart @@ -253,20 +253,18 @@ abstract class SettingsStoreBase with Store { (secrets.wyreApiKey?.isNotEmpty ?? false) && (secrets.wyreAccountId?.isNotEmpty ?? false); - /*var isMoonPayEnabled = false; + var isMoonPayEnabled = false; try { - final locale = await Devicelocale.currentLocale; - final deviceCountryCode = locale.split('_').last; - isMoonPayEnabled = await MoonPayBuyProvider.onEnabled(deviceCountryCode); + isMoonPayEnabled = await MoonPayBuyProvider.onEnabled(); } catch (e) { isMoonPayEnabled = false; print(e.toString()); - }*/ + } final settings = await SettingsStoreBase.load( nodeSource: nodeSource, isBitcoinBuyEnabled: isBitcoinBuyEnabled, - isMoonPayEnabled: true, + isMoonPayEnabled: isMoonPayEnabled, initialBalanceDisplayMode: initialBalanceDisplayMode, initialFiatCurrency: initialFiatCurrency, initialMoneroTransactionPriority: initialMoneroTransactionPriority, From d5d1afd89b62344332e0c2473d7154267041d41f Mon Sep 17 00:00:00 2001 From: OleksandrSobol Date: Mon, 7 Jun 2021 16:40:25 +0300 Subject: [PATCH 16/17] CAKE-306 | merged main branch into current; added _fetchBuyItems() method to buy_view_model.dart; deleted isMoonPayEnabled parameter from settings_store.dart --- lib/di.dart | 14 +-------- lib/entities/push_notifications_service.dart | 2 +- lib/store/settings_store.dart | 16 ---------- lib/view_model/buy/buy_view_model.dart | 31 +++++++++++++------- res/values/strings_hr.arb | 2 ++ res/values/strings_it.arb | 2 ++ 6 files changed, 27 insertions(+), 40 deletions(-) diff --git a/lib/di.dart b/lib/di.dart index eda81d899..0de43a76e 100644 --- a/lib/di.dart +++ b/lib/di.dart @@ -1,11 +1,9 @@ import 'package:cake_wallet/bitcoin/bitcoin_wallet_service.dart'; import 'package:cake_wallet/bitcoin/litecoin_wallet_service.dart'; -import 'package:cake_wallet/buy/moonpay/moonpay_buy_provider.dart'; import 'package:cake_wallet/core/backup_service.dart'; import 'package:cake_wallet/core/wallet_service.dart'; import 'package:cake_wallet/entities/biometric_auth.dart'; import 'package:cake_wallet/entities/contact_record.dart'; -import 'package:cake_wallet/entities/load_current_wallet.dart'; import 'package:cake_wallet/buy/order.dart'; import 'package:cake_wallet/entities/transaction_description.dart'; import 'package:cake_wallet/entities/transaction_info.dart'; @@ -92,7 +90,6 @@ import 'package:cake_wallet/view_model/wallet_list/wallet_list_view_model.dart'; import 'package:cake_wallet/view_model/wallet_restore_view_model.dart'; import 'package:cake_wallet/view_model/wallet_seed_view_model.dart'; import 'package:cake_wallet/view_model/exchange/exchange_view_model.dart'; -import 'package:devicelocale/devicelocale.dart'; import 'package:flutter/widgets.dart'; import 'package:get_it/get_it.dart'; import 'package:hive/hive.dart'; @@ -156,17 +153,8 @@ Future setup( (secrets.wyreApiKey?.isNotEmpty ?? false) && (secrets.wyreAccountId?.isNotEmpty ?? false); - var isMoonPayEnabled = false; - try { - isMoonPayEnabled = await MoonPayBuyProvider.onEnabled(); - } catch (e) { - isMoonPayEnabled = false; - print(e.toString()); - } - final settingsStore = await SettingsStoreBase.load( - nodeSource: _nodeSource, isBitcoinBuyEnabled: isBitcoinBuyEnabled, - isMoonPayEnabled: isMoonPayEnabled); + nodeSource: _nodeSource, isBitcoinBuyEnabled: isBitcoinBuyEnabled); if (_isSetupFinished) { return; diff --git a/lib/entities/push_notifications_service.dart b/lib/entities/push_notifications_service.dart index 5fe69153c..5fb68bae4 100644 --- a/lib/entities/push_notifications_service.dart +++ b/lib/entities/push_notifications_service.dart @@ -21,7 +21,7 @@ class PushNotificationsService { _firebaseMessaging.requestNotificationPermissions(); _firebaseMessaging.configure( onMessage: (message) async { - Map alert = {}; + Map alert = {}; String msg = ''; String title = ''; diff --git a/lib/store/settings_store.dart b/lib/store/settings_store.dart index 206c3141b..66124a108 100644 --- a/lib/store/settings_store.dart +++ b/lib/store/settings_store.dart @@ -1,5 +1,4 @@ import 'package:cake_wallet/bitcoin/bitcoin_transaction_priority.dart'; -import 'package:cake_wallet/buy/moonpay/moonpay_buy_provider.dart'; import 'package:cake_wallet/entities/preferences_key.dart'; import 'package:cake_wallet/entities/transaction_priority.dart'; import 'package:cake_wallet/themes/theme_base.dart'; @@ -9,7 +8,6 @@ import 'package:flutter/material.dart'; import 'package:hive/hive.dart'; import 'package:mobx/mobx.dart'; import 'package:package_info/package_info.dart'; -import 'package:devicelocale/devicelocale.dart'; import 'package:cake_wallet/di.dart'; import 'package:cake_wallet/entities/wallet_type.dart'; import 'package:shared_preferences/shared_preferences.dart'; @@ -41,7 +39,6 @@ abstract class SettingsStoreBase with Store { @required TransactionPriority initialBitcoinTransactionPriority, @required TransactionPriority initialMoneroTransactionPriority, @required this.isBitcoinBuyEnabled, - @required this.isMoonPayEnabled, this.actionlistDisplayMode}) { fiatCurrency = initialFiatCurrency; balanceDisplayMode = initialBalanceDisplayMode; @@ -150,12 +147,9 @@ abstract class SettingsStoreBase with Store { bool isBitcoinBuyEnabled; - bool isMoonPayEnabled; - static Future load( {@required Box nodeSource, @required bool isBitcoinBuyEnabled, - @required bool isMoonPayEnabled, FiatCurrency initialFiatCurrency = FiatCurrency.usd, MoneroTransactionPriority initialMoneroTransactionPriority = MoneroTransactionPriority.slow, @@ -227,7 +221,6 @@ abstract class SettingsStoreBase with Store { }, appVersion: packageInfo.version, isBitcoinBuyEnabled: isBitcoinBuyEnabled, - isMoonPayEnabled: isMoonPayEnabled, initialFiatCurrency: currentFiatCurrency, initialBalanceDisplayMode: currentBalanceDisplayMode, initialSaveRecipientAddress: shouldSaveRecipientAddress, @@ -253,18 +246,9 @@ abstract class SettingsStoreBase with Store { (secrets.wyreApiKey?.isNotEmpty ?? false) && (secrets.wyreAccountId?.isNotEmpty ?? false); - var isMoonPayEnabled = false; - try { - isMoonPayEnabled = await MoonPayBuyProvider.onEnabled(); - } catch (e) { - isMoonPayEnabled = false; - print(e.toString()); - } - final settings = await SettingsStoreBase.load( nodeSource: nodeSource, isBitcoinBuyEnabled: isBitcoinBuyEnabled, - isMoonPayEnabled: isMoonPayEnabled, initialBalanceDisplayMode: initialBalanceDisplayMode, initialFiatCurrency: initialFiatCurrency, initialMoneroTransactionPriority: initialMoneroTransactionPriority, diff --git a/lib/view_model/buy/buy_view_model.dart b/lib/view_model/buy/buy_view_model.dart index 68afb9577..95739edd0 100644 --- a/lib/view_model/buy/buy_view_model.dart +++ b/lib/view_model/buy/buy_view_model.dart @@ -21,15 +21,9 @@ class BuyViewModel = BuyViewModelBase with _$BuyViewModel; abstract class BuyViewModelBase with Store { BuyViewModelBase(this.ordersSource, this.ordersStore, this.settingsStore, this.buyAmountViewModel, {@required this.wallet}) { - providerList = [WyreBuyProvider(wallet: wallet)]; - if (settingsStore.isMoonPayEnabled ?? false) { - providerList.add(MoonPayBuyProvider(wallet: wallet)); - } + _fetchBuyItems(); - items = providerList.map((provider) => - BuyItem(provider: provider, buyAmountViewModel: buyAmountViewModel)) - .toList(); isRunning = false; isDisabled = true; isShowProviderButtons = false; @@ -41,9 +35,6 @@ abstract class BuyViewModelBase with Store { final BuyAmountViewModel buyAmountViewModel; final WalletBase wallet; - @observable - List providerList; - @observable BuyProvider selectedProvider; @@ -96,4 +87,24 @@ abstract class BuyViewModelBase with Store { buyAmountViewModel.amount = ''; selectedProvider = null; } + + Future _fetchBuyItems() async { + final List _providerList = [WyreBuyProvider(wallet: wallet)]; + + var isMoonPayEnabled = false; + try { + isMoonPayEnabled = await MoonPayBuyProvider.onEnabled(); + } catch (e) { + isMoonPayEnabled = false; + print(e.toString()); + } + + if (isMoonPayEnabled) { + _providerList.add(MoonPayBuyProvider(wallet: wallet)); + } + + items = _providerList.map((provider) => + BuyItem(provider: provider, buyAmountViewModel: buyAmountViewModel)) + .toList(); + } } \ No newline at end of file diff --git a/res/values/strings_hr.arb b/res/values/strings_hr.arb index 0eb24134c..1d3233d0e 100644 --- a/res/values/strings_hr.arb +++ b/res/values/strings_hr.arb @@ -478,5 +478,7 @@ "apk_update" : "APK ažuriranje", + "buy_bitcoin" : "Kupite Bitcoin", + "buy_with" : "Kupite s", "moonpay_alert_text" : "Vrijednost iznosa mora biti veća ili jednaka ${minAmount} ${fiatCurrency}" } \ No newline at end of file diff --git a/res/values/strings_it.arb b/res/values/strings_it.arb index 7f8dc6499..00d88efd8 100644 --- a/res/values/strings_it.arb +++ b/res/values/strings_it.arb @@ -478,5 +478,7 @@ "apk_update" : "Aggiornamento APK", + "buy_bitcoin" : "Acquista Bitcoin", + "buy_with" : "Acquista con", "moonpay_alert_text" : "Il valore dell'importo deve essere maggiore o uguale a ${minAmount} ${fiatCurrency}" } \ No newline at end of file From fcb1c891f6f845f49661dce5ed5a012de3386ff4 Mon Sep 17 00:00:00 2001 From: M Date: Mon, 7 Jun 2021 17:01:21 +0300 Subject: [PATCH 17/17] Changed web client for moonpay. --- lib/src/screens/buy/pre_order_page.dart | 38 ++++++++++++++++--------- 1 file changed, 24 insertions(+), 14 deletions(-) diff --git a/lib/src/screens/buy/pre_order_page.dart b/lib/src/screens/buy/pre_order_page.dart index cf6cca586..54eeebb6f 100644 --- a/lib/src/screens/buy/pre_order_page.dart +++ b/lib/src/screens/buy/pre_order_page.dart @@ -20,6 +20,7 @@ import 'package:cake_wallet/generated/i18n.dart'; import 'package:cake_wallet/src/widgets/base_text_form_field.dart'; import 'package:cake_wallet/src/widgets/trail_button.dart'; import 'package:mobx/mobx.dart'; +import 'package:url_launcher/url_launcher.dart'; class PreOrderPage extends BasePage { PreOrderPage({@required this.buyViewModel}) @@ -221,20 +222,7 @@ class PreOrderPage extends BasePage { EdgeInsets.only(left: 24, right: 24, bottom: 24), bottomSection: Observer(builder: (_) { return LoadingPrimaryButton( - onPressed: buyViewModel.isRunning - ? null - : () async { - buyViewModel.isRunning = true; - final url = - await buyViewModel.fetchUrl(); - if (url.isNotEmpty) { - await Navigator.of(context) - .pushNamed(Routes.buyWebView, - arguments: [url, buyViewModel]); - buyViewModel.reset(); - } - buyViewModel.isRunning = false; - }, + onPressed: () => onPresentProvider(context: context), text: buyViewModel.selectedProvider == null ? S.of(context).buy : S.of(context).buy_with + @@ -275,4 +263,26 @@ class PreOrderPage extends BasePage { ? buyViewModel.isDisabled = false : buyViewModel.isDisabled = true; } + + Future onPresentProvider({BuildContext context}) async { + if (buyViewModel.isRunning) { + return; + } + + buyViewModel.isRunning = true; + final url = await buyViewModel.fetchUrl(); + + if (url.isNotEmpty) { + if (buyViewModel.selectedProvider is MoonPayBuyProvider) { + if (await canLaunch(url)) await launch(url); + } else { + await Navigator.of(context) + .pushNamed(Routes.buyWebView, + arguments: [url, buyViewModel]); + } + } + + buyViewModel.reset(); + buyViewModel.isRunning = false; + } } \ No newline at end of file