From 133fa2987a48fd1f6ac77dd57c202f64a6d75619 Mon Sep 17 00:00:00 2001 From: Omar Hatem Date: Mon, 1 Jan 2024 15:05:37 +0200 Subject: [PATCH] Generic fixes (#1245) * Remove error message if buy action is disabled * Fix wallet list selected wallet issue * Check if the widget is still mounted before showing popup * minor code readability enhancement [skip ci] * Code enhancement [skip ci] * Revert removing ask each time localization * Add Moonpay to sell flow Code Enhancements * remove error popup when sell option is disabled --- cw_ethereum/lib/ethereum_wallet_service.dart | 11 +- cw_polygon/lib/polygon_wallet_service.dart | 6 +- lib/buy/buy_provider.dart | 4 +- lib/buy/dfx/dfx_buy_provider.dart | 6 +- ...uy_provider.dart => moonpay_provider.dart} | 122 ++++++++++-------- lib/buy/onramper/onramper_buy_provider.dart | 5 +- lib/buy/robinhood/robinhood_buy_provider.dart | 12 +- lib/buy/wyre/wyre_buy_provider.dart | 55 +++----- lib/di.dart | 2 +- lib/entities/buy_provider_types.dart | 112 ---------------- lib/entities/main_actions.dart | 4 - lib/entities/provider_types.dart | 108 ++++++++++++++++ lib/src/screens/buy/buy_options_page.dart | 38 +++--- lib/src/screens/buy/buy_webview_page.dart | 2 +- .../screens/buy/widgets/buy_list_item.dart | 2 +- .../exchange_trade/exchange_trade_page.dart | 6 +- .../screens/wallet_list/wallet_list_page.dart | 5 +- lib/store/settings_store.dart | 22 ++-- lib/view_model/buy/buy_view_model.dart | 2 +- .../dashboard/dashboard_view_model.dart | 20 +-- lib/view_model/order_details_view_model.dart | 2 +- .../settings/other_settings_view_model.dart | 36 +++--- .../wallet_list/wallet_list_view_model.dart | 1 + 23 files changed, 270 insertions(+), 313 deletions(-) rename lib/buy/moonpay/{moonpay_buy_provider.dart => moonpay_provider.dart} (73%) delete mode 100644 lib/entities/buy_provider_types.dart create mode 100644 lib/entities/provider_types.dart diff --git a/cw_ethereum/lib/ethereum_wallet_service.dart b/cw_ethereum/lib/ethereum_wallet_service.dart index 8810d6014..0acc90bac 100644 --- a/cw_ethereum/lib/ethereum_wallet_service.dart +++ b/cw_ethereum/lib/ethereum_wallet_service.dart @@ -20,12 +20,7 @@ class EthereumWalletService extends WalletService create(EthereumNewWalletCredentials credentials) async { - - final strength = (credentials.seedPhraseLength == 12) - ? 128 - : (credentials.seedPhraseLength == 24) - ? 256 - : 128; + final strength = credentials.seedPhraseLength == 24 ? 256 : 128; final mnemonic = bip39.generateMnemonic(strength: strength); final wallet = EthereumWallet( @@ -67,8 +62,8 @@ class EthereumWalletService extends WalletService remove(String wallet) async { File(await pathForWalletDir(name: wallet, type: getType())).delete(recursive: true); - final walletInfo = walletInfoSource.values.firstWhereOrNull( - (info) => info.id == WalletBase.idFor(wallet, getType()))!; + final walletInfo = walletInfoSource.values + .firstWhereOrNull((info) => info.id == WalletBase.idFor(wallet, getType()))!; await walletInfoSource.delete(walletInfo.key); } diff --git a/cw_polygon/lib/polygon_wallet_service.dart b/cw_polygon/lib/polygon_wallet_service.dart index dafe3bab0..43c6269f6 100644 --- a/cw_polygon/lib/polygon_wallet_service.dart +++ b/cw_polygon/lib/polygon_wallet_service.dart @@ -20,11 +20,7 @@ class PolygonWalletService extends WalletService create(PolygonNewWalletCredentials credentials) async { - final strength = (credentials.seedPhraseLength == 12) - ? 128 - : (credentials.seedPhraseLength == 24) - ? 256 - : 128; + final strength = credentials.seedPhraseLength == 24 ? 256 : 128; final mnemonic = bip39.generateMnemonic(strength: strength); final wallet = PolygonWallet( diff --git a/lib/buy/buy_provider.dart b/lib/buy/buy_provider.dart index 93f4e8105..4e4c113f4 100644 --- a/lib/buy/buy_provider.dart +++ b/lib/buy/buy_provider.dart @@ -14,9 +14,7 @@ abstract class BuyProvider { String get title; - String get buyOptionDescription; - - String get sellOptionDescription; + String get providerDescription; String get lightIcon; diff --git a/lib/buy/dfx/dfx_buy_provider.dart b/lib/buy/dfx/dfx_buy_provider.dart index 18ac081ea..f74039caa 100644 --- a/lib/buy/dfx/dfx_buy_provider.dart +++ b/lib/buy/dfx/dfx_buy_provider.dart @@ -26,10 +26,7 @@ class DFXBuyProvider extends BuyProvider { String get title => 'DFX Connect'; @override - String get buyOptionDescription => S.current.dfx_option_description; - - @override - String get sellOptionDescription => S.current.dfx_option_description; + String get providerDescription => S.current.dfx_option_description; @override String get lightIcon => 'assets/images/dfx_light.png'; @@ -152,6 +149,7 @@ class DFXBuyProvider extends BuyProvider { } } + @override Future launchProvider(BuildContext context, bool? isBuyAction) async { try { final assetOut = this.assetOut; diff --git a/lib/buy/moonpay/moonpay_buy_provider.dart b/lib/buy/moonpay/moonpay_provider.dart similarity index 73% rename from lib/buy/moonpay/moonpay_buy_provider.dart rename to lib/buy/moonpay/moonpay_provider.dart index 0831e1313..0ccb73e1c 100644 --- a/lib/buy/moonpay/moonpay_buy_provider.dart +++ b/lib/buy/moonpay/moonpay_provider.dart @@ -21,19 +21,33 @@ import 'package:cake_wallet/.secrets.g.dart' as secrets; import 'package:cw_core/crypto_currency.dart'; import 'package:url_launcher/url_launcher.dart'; -class MoonPaySellProvider { - MoonPaySellProvider({required SettingsStore settingsStore, - required WalletBase wallet, this.isTest = false}) - : baseUrl = isTest ? _baseTestUrl : _baseProductUrl, +class MoonPaySellProvider extends BuyProvider { + MoonPaySellProvider({ + required SettingsStore settingsStore, + required WalletBase wallet, + bool isTestEnvironment = false, + }) : baseUrl = isTestEnvironment ? _baseTestUrl : _baseProductUrl, this._settingsStore = settingsStore, - this._wallet = wallet; + super(wallet: wallet, isTestEnvironment: isTestEnvironment); final SettingsStore _settingsStore; - final WalletBase _wallet; static const _baseTestUrl = 'sell-sandbox.moonpay.com'; static const _baseProductUrl = 'sell.moonpay.com'; + @override + String get providerDescription => + 'MoonPay offers a fast and simple way to buy and sell cryptocurrencies'; + + @override + String get title => 'MoonPay'; + + @override + String get lightIcon => 'assets/images/moonpay_light.png'; + + @override + String get darkIcon => 'assets/images/moonpay_dark.png'; + static String themeToMoonPayTheme(ThemeBase theme) { switch (theme.type) { case ThemeType.bright: @@ -45,11 +59,11 @@ class MoonPaySellProvider { } static String get _apiKey => secrets.moonPayApiKey; + static String get _secretKey => secrets.moonPaySecretKey; - final bool isTest; final String baseUrl; - Future requestUrl({ + Future requestMoonPayUrl({ required CryptoCurrency currency, required String refundWalletAddress, required SettingsStore settingsStore, @@ -78,7 +92,7 @@ class MoonPaySellProvider { final digest = hmac.convert(messageBytes); final signature = base64.encode(digest.bytes); - if (isTest) { + if (isTestEnvironment) { return originalUri; } @@ -88,11 +102,12 @@ class MoonPaySellProvider { return signedUri; } - Future launchProvider(BuildContext context) async { + @override + Future launchProvider(BuildContext context, bool? isBuyAction) async { try { - final uri = await requestUrl( - currency: _wallet.currency, - refundWalletAddress: _wallet.walletAddresses.address, + final uri = await requestMoonPayUrl( + currency: wallet.currency, + refundWalletAddress: wallet.walletAddresses.address, settingsStore: _settingsStore, ); @@ -137,35 +152,43 @@ class MoonPayBuyProvider extends BuyProvider { static const _secretKey = secrets.moonPaySecretKey; @override - String get title => 'Moon Pay'; + String get title => 'MoonPay'; @override - String get buyOptionDescription => ''; + String get providerDescription => + 'MoonPay offers a fast and simple way to buy and sell cryptocurrencies'; @override String get lightIcon => 'assets/images/moonpay_light.png'; @override String get darkIcon => 'assets/images/moonpay_dark.png'; - - String get currencyCode => - walletTypeToCryptoCurrency(wallet.type).title.toLowerCase(); + + String get currencyCode => walletTypeToCryptoCurrency(wallet.type).title.toLowerCase(); String get trackUrl => baseUrl + '/transaction_receipt?transactionId='; String baseUrl; Future requestUrl(String amount, String sourceCurrency) async { - final enabledPaymentMethods = - 'credit_debit_card%2Capple_pay%2Cgoogle_pay%2Csamsung_pay' + final enabledPaymentMethods = 'credit_debit_card%2Capple_pay%2Cgoogle_pay%2Csamsung_pay' '%2Csepa_bank_transfer%2Cgbp_bank_transfer%2Cgbp_open_banking_payment'; - final suffix = '?apiKey=' + _apiKey + '¤cyCode=' + - currencyCode + '&enabledPaymentMethods=' + enabledPaymentMethods + - '&walletAddress=' + wallet.walletAddresses.address + - '&baseCurrencyCode=' + sourceCurrency.toLowerCase() + - '&baseCurrencyAmount=' + amount + '&lockAmount=true' + - '&showAllCurrencies=false' + '&showWalletAddressForm=false'; + final suffix = '?apiKey=' + + _apiKey + + '¤cyCode=' + + currencyCode + + '&enabledPaymentMethods=' + + enabledPaymentMethods + + '&walletAddress=' + + wallet.walletAddresses.address + + '&baseCurrencyCode=' + + sourceCurrency.toLowerCase() + + '&baseCurrencyAmount=' + + amount + + '&lockAmount=true' + + '&showAllCurrencies=false' + + '&showWalletAddressForm=false'; final originalUrl = baseUrl + suffix; @@ -174,24 +197,27 @@ class MoonPayBuyProvider extends BuyProvider { final hmac = Hmac(sha256, key); final digest = hmac.convert(messageBytes); final signature = base64.encode(digest.bytes); - final urlWithSignature = originalUrl + - '&signature=${Uri.encodeComponent(signature)}'; + final urlWithSignature = originalUrl + '&signature=${Uri.encodeComponent(signature)}'; return isTestEnvironment ? originalUrl : urlWithSignature; } Future calculateAmount(String amount, String sourceCurrency) async { - final url = _apiUrl + _currenciesSuffix + '/$currencyCode' + - _quoteSuffix + '/?apiKey=' + _apiKey + - '&baseCurrencyAmount=' + amount + - '&baseCurrencyCode=' + sourceCurrency.toLowerCase(); + final url = _apiUrl + + _currenciesSuffix + + '/$currencyCode' + + _quoteSuffix + + '/?apiKey=' + + _apiKey + + '&baseCurrencyAmount=' + + amount + + '&baseCurrencyCode=' + + sourceCurrency.toLowerCase(); final uri = Uri.parse(url); final response = await get(uri); if (response.statusCode != 200) { - throw BuyException( - title: buyOptionDescription, - content: 'Quote is not found!'); + throw BuyException(title: providerDescription, content: 'Quote is not found!'); } final responseJSON = json.decode(response.body) as Map; @@ -200,21 +226,16 @@ class MoonPayBuyProvider extends BuyProvider { final minSourceAmount = responseJSON['baseCurrency']['minAmount'] as int; return BuyAmount( - sourceAmount: sourceAmount, - destAmount: destAmount, - minAmount: minSourceAmount); + sourceAmount: sourceAmount, destAmount: destAmount, minAmount: minSourceAmount); } Future findOrderById(String id) async { - final url = _apiUrl + _transactionsSuffix + '/$id' + - '?apiKey=' + _apiKey; + final url = _apiUrl + _transactionsSuffix + '/$id' + '?apiKey=' + _apiKey; final uri = Uri.parse(url); final response = await get(uri); if (response.statusCode != 200) { - throw BuyException( - title: buyOptionDescription, - content: 'Transaction $id is not found!'); + throw BuyException(title: providerDescription, content: 'Transaction $id is not found!'); } final responseJSON = json.decode(response.body) as Map; @@ -232,8 +253,7 @@ class MoonPayBuyProvider extends BuyProvider { createdAt: createdAt, amount: amount.toString(), receiveAddress: wallet.walletAddresses.address, - walletId: wallet.id - ); + walletId: wallet.id); } static Future onEnabled() async { @@ -254,12 +274,6 @@ class MoonPayBuyProvider extends BuyProvider { } @override - // TODO: implement sellOptionDescription - String get sellOptionDescription => throw UnimplementedError(); - - @override - Future launchProvider(BuildContext context, bool? isBuyAction) { - // TODO: implement launchProvider - throw UnimplementedError(); - } -} \ No newline at end of file + Future launchProvider(BuildContext context, bool? isBuyAction) => + throw UnimplementedError(); +} diff --git a/lib/buy/onramper/onramper_buy_provider.dart b/lib/buy/onramper/onramper_buy_provider.dart index e38d95df3..014edb813 100644 --- a/lib/buy/onramper/onramper_buy_provider.dart +++ b/lib/buy/onramper/onramper_buy_provider.dart @@ -23,10 +23,7 @@ class OnRamperBuyProvider extends BuyProvider { String get title => 'Onramper'; @override - String get buyOptionDescription => S.current.onramper_option_description; - - @override - String get sellOptionDescription => S.current.onramper_option_description; + String get providerDescription => S.current.onramper_option_description; @override String get lightIcon => 'assets/images/onramper_light.png'; diff --git a/lib/buy/robinhood/robinhood_buy_provider.dart b/lib/buy/robinhood/robinhood_buy_provider.dart index ff6c6a8cf..47c3ab1ea 100644 --- a/lib/buy/robinhood/robinhood_buy_provider.dart +++ b/lib/buy/robinhood/robinhood_buy_provider.dart @@ -1,10 +1,7 @@ import 'dart:convert'; import 'package:cake_wallet/.secrets.g.dart' as secrets; -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/generated/i18n.dart'; import 'package:cake_wallet/src/widgets/alert_with_one_action.dart'; import 'package:cake_wallet/utils/show_pop_up.dart'; @@ -14,22 +11,18 @@ import 'package:flutter/material.dart'; import 'package:http/http.dart' as http; import 'package:url_launcher/url_launcher.dart'; -class RobinhoodBuyProvider extends BuyProvider{ +class RobinhoodBuyProvider extends BuyProvider { RobinhoodBuyProvider({required WalletBase wallet, bool isTestEnvironment = false}) : super(wallet: wallet, isTestEnvironment: isTestEnvironment); static const _baseUrl = 'applink.robinhood.com'; static const _cIdBaseUrl = 'exchange-helper.cakewallet.com'; - @override String get title => 'Robinhood Connect'; @override - String get buyOptionDescription => S.current.robinhood_option_description; - - @override - String get sellOptionDescription => S.current.robinhood_option_description; + String get providerDescription => S.current.robinhood_option_description; @override String get lightIcon => 'assets/images/robinhood_light.png'; @@ -105,5 +98,4 @@ class RobinhoodBuyProvider extends BuyProvider{ }); } } - } diff --git a/lib/buy/wyre/wyre_buy_provider.dart b/lib/buy/wyre/wyre_buy_provider.dart index 80e48fd4f..4dd091c33 100644 --- a/lib/buy/wyre/wyre_buy_provider.dart +++ b/lib/buy/wyre/wyre_buy_provider.dart @@ -13,10 +13,8 @@ import 'package:cake_wallet/.secrets.g.dart' as secrets; class WyreBuyProvider extends BuyProvider { WyreBuyProvider({required WalletBase wallet, bool isTestEnvironment = false}) - : baseApiUrl = isTestEnvironment - ? _baseTestApiUrl - : _baseProductApiUrl, - super(wallet: wallet, isTestEnvironment: isTestEnvironment); + : baseApiUrl = isTestEnvironment ? _baseTestApiUrl : _baseProductApiUrl, + super(wallet: wallet, isTestEnvironment: isTestEnvironment); static const _baseTestApiUrl = 'https://api.testwyre.com'; static const _baseProductApiUrl = 'https://api.sendwyre.com'; @@ -36,7 +34,7 @@ class WyreBuyProvider extends BuyProvider { String get title => 'Wyre'; @override - String get buyOptionDescription => ''; + String get providerDescription => ''; @override String get lightIcon => 'assets/images/robinhood_light.png'; @@ -44,16 +42,13 @@ class WyreBuyProvider extends BuyProvider { @override String get darkIcon => 'assets/images/robinhood_dark.png'; - String get trackUrl => isTestEnvironment - ? _trackTestUrl - : _trackProductUrl; + String get trackUrl => isTestEnvironment ? _trackTestUrl : _trackProductUrl; String baseApiUrl; Future requestUrl(String amount, String sourceCurrency) async { final timestamp = DateTime.now().millisecondsSinceEpoch.toString(); - final url = baseApiUrl + _ordersSuffix + _reserveSuffix + - _timeStampSuffix + timestamp; + final url = baseApiUrl + _ordersSuffix + _reserveSuffix + _timeStampSuffix + timestamp; final uri = Uri.parse(url); final body = { 'amount': amount, @@ -72,9 +67,7 @@ class WyreBuyProvider extends BuyProvider { body: json.encode(body)); if (response.statusCode != 200) { - throw BuyException( - title: buyOptionDescription, - content: 'Url $url is not found!'); + throw BuyException(title: providerDescription, content: 'Url $url is not found!'); } final responseJSON = json.decode(response.body) as Map; @@ -102,9 +95,7 @@ class WyreBuyProvider extends BuyProvider { body: json.encode(body)); if (response.statusCode != 200) { - throw BuyException( - title: buyOptionDescription, - content: 'Quote is not found!'); + throw BuyException(title: providerDescription, content: 'Quote is not found!'); } final responseJSON = json.decode(response.body) as Map; @@ -112,7 +103,8 @@ class WyreBuyProvider extends BuyProvider { final destAmount = responseJSON['destAmount'] as double; final achAmount = responseJSON['sourceAmountWithoutFees'] as double; - return BuyAmount(sourceAmount: sourceAmount, destAmount: destAmount, achSourceAmount: achAmount); + return BuyAmount( + sourceAmount: sourceAmount, destAmount: destAmount, achSourceAmount: achAmount); } Future findOrderById(String id) async { @@ -121,35 +113,27 @@ class WyreBuyProvider extends BuyProvider { final orderResponse = await get(orderUri); if (orderResponse.statusCode != 200) { - throw BuyException( - title: buyOptionDescription, - content: 'Order $id is not found!'); + throw BuyException(title: providerDescription, content: 'Order $id is not found!'); } - final orderResponseJSON = - json.decode(orderResponse.body) as Map; + 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 createdAt = DateTime.fromMillisecondsSinceEpoch(createdAtRaw).toLocal(); - final transferUrl = - baseApiUrl + _transferSuffix + transferId + _trackSuffix; + final transferUrl = baseApiUrl + _transferSuffix + transferId + _trackSuffix; final transferUri = Uri.parse(transferUrl); final transferResponse = await get(transferUri); if (transferResponse.statusCode != 200) { - throw BuyException( - title: buyOptionDescription, - content: 'Transfer $transferId is not found!'); + throw BuyException(title: providerDescription, content: 'Transfer $transferId is not found!'); } - final transferResponseJSON = - json.decode(transferResponse.body) as Map; + final transferResponseJSON = json.decode(transferResponse.body) as Map; final amount = transferResponseJSON['destAmount'] as double; return Order( @@ -162,8 +146,7 @@ class WyreBuyProvider extends BuyProvider { createdAt: createdAt, amount: amount.toString(), receiveAddress: wallet.walletAddresses.address, - walletId: wallet.id - ); + walletId: wallet.id); } @override @@ -171,8 +154,4 @@ class WyreBuyProvider extends BuyProvider { // TODO: implement launchProvider throw UnimplementedError(); } - - @override - // TODO: implement sellOptionDescription - String get sellOptionDescription => throw UnimplementedError(); -} \ No newline at end of file +} diff --git a/lib/di.dart b/lib/di.dart index dd1001858..61a04bf1c 100644 --- a/lib/di.dart +++ b/lib/di.dart @@ -1,7 +1,7 @@ import 'package:cake_wallet/anonpay/anonpay_api.dart'; import 'package:cake_wallet/anonpay/anonpay_info_base.dart'; import 'package:cake_wallet/anonpay/anonpay_invoice_info.dart'; -import 'package:cake_wallet/buy/moonpay/moonpay_buy_provider.dart'; +import 'package:cake_wallet/buy/moonpay/moonpay_provider.dart'; import 'package:cake_wallet/buy/onramper/onramper_buy_provider.dart'; import 'package:cake_wallet/bitcoin_cash/bitcoin_cash.dart'; import 'package:cake_wallet/buy/payfura/payfura_buy_provider.dart'; diff --git a/lib/entities/buy_provider_types.dart b/lib/entities/buy_provider_types.dart deleted file mode 100644 index 1913d1b25..000000000 --- a/lib/entities/buy_provider_types.dart +++ /dev/null @@ -1,112 +0,0 @@ -import 'package:cake_wallet/buy/buy_provider.dart'; -import 'package:cake_wallet/buy/dfx/dfx_buy_provider.dart'; -import 'package:cake_wallet/buy/onramper/onramper_buy_provider.dart'; -import 'package:cake_wallet/buy/robinhood/robinhood_buy_provider.dart'; -import 'package:cake_wallet/di.dart'; -import 'package:cw_core/wallet_type.dart'; - -enum BuyProviderType { - askEachTime, - robinhood, - dfx, - onramper, -} - -extension BuyProviderTypeName on BuyProviderType { - String get name { - switch (this) { - case BuyProviderType.askEachTime: - return 'Ask each time'; - case BuyProviderType.robinhood: - return 'Robinhood Connect'; - case BuyProviderType.dfx: - return 'DFX Connect'; - case BuyProviderType.onramper: - return 'Onramper'; - default: - return this.toString().split('.').last; - } - } - - String get id { - switch (this) { - case BuyProviderType.askEachTime: - return 'ask_each_time_provider'; - case BuyProviderType.robinhood: - return 'robinhood_connect_provider'; - case BuyProviderType.dfx: - return 'dfx_connect_provider'; - case BuyProviderType.onramper: - return 'onramper_provider'; - default: - return this.toString().split('.').last.replaceAll('.', '_') - .toLowerCase() + '_provider'; - } - } -} - -class BuyProviderHelper { - static List getAvailableBuyProviderTypes( - WalletType walletType) { - switch (walletType) { - case WalletType.nano: - case WalletType.banano: - return [BuyProviderType.askEachTime, BuyProviderType.onramper]; - case WalletType.monero: - return [ - BuyProviderType.askEachTime, - BuyProviderType.onramper, - BuyProviderType.dfx - ]; - case WalletType.bitcoin: - case WalletType.ethereum: - return [ - BuyProviderType.askEachTime, - BuyProviderType.onramper, - BuyProviderType.dfx, - BuyProviderType.robinhood - ]; - case WalletType.litecoin: - case WalletType.bitcoinCash: - return [ - BuyProviderType.askEachTime, - BuyProviderType.onramper, - BuyProviderType.robinhood - ]; - default: - return []; - } - } - - static List getAvailableSellProviderTypes( - WalletType walletType) { - switch (walletType) { - case WalletType.nano: - case WalletType.banano: - return [BuyProviderType.askEachTime]; - case WalletType.monero: - return [BuyProviderType.askEachTime, BuyProviderType.dfx]; - case WalletType.bitcoin: - case WalletType.ethereum: - return [BuyProviderType.askEachTime, BuyProviderType.dfx]; - case WalletType.litecoin: - case WalletType.bitcoinCash: - return [BuyProviderType.askEachTime]; - default: - return []; - } - } - - static BuyProvider? getProviderByType(BuyProviderType type) { - switch (type) { - case BuyProviderType.robinhood: - return getIt.get(); - case BuyProviderType.dfx: - return getIt.get(); - case BuyProviderType.onramper: - return getIt.get(); - case BuyProviderType.askEachTime: - return null; - } - } -} diff --git a/lib/entities/main_actions.dart b/lib/entities/main_actions.dart index 0e4e05ae0..c1dd71cc9 100644 --- a/lib/entities/main_actions.dart +++ b/lib/entities/main_actions.dart @@ -37,8 +37,6 @@ class MainActions { canShow: (viewModel) => viewModel.hasBuyAction, onTap: (BuildContext context, DashboardViewModel viewModel) async { if (!viewModel.isEnabledBuyAction) { - await _showErrorDialog( - context, S.of(context).buy, S.of(context).unsupported_asset); return; } @@ -88,8 +86,6 @@ class MainActions { canShow: (viewModel) => viewModel.hasSellAction, onTap: (BuildContext context, DashboardViewModel viewModel) async { if (!viewModel.isEnabledSellAction) { - await _showErrorDialog( - context, S.of(context).sell, S.of(context).unsupported_asset); return; } diff --git a/lib/entities/provider_types.dart b/lib/entities/provider_types.dart new file mode 100644 index 000000000..4cb9a934f --- /dev/null +++ b/lib/entities/provider_types.dart @@ -0,0 +1,108 @@ +import 'package:cake_wallet/buy/buy_provider.dart'; +import 'package:cake_wallet/buy/dfx/dfx_buy_provider.dart'; +import 'package:cake_wallet/buy/moonpay/moonpay_provider.dart'; +import 'package:cake_wallet/buy/onramper/onramper_buy_provider.dart'; +import 'package:cake_wallet/buy/robinhood/robinhood_buy_provider.dart'; +import 'package:cake_wallet/di.dart'; +import 'package:cw_core/wallet_type.dart'; + +enum ProviderType { + askEachTime, + robinhood, + dfx, + onramper, + moonpaySell, +} + +extension ProviderTypeName on ProviderType { + String get title { + switch (this) { + case ProviderType.askEachTime: + return 'Ask each time'; + case ProviderType.robinhood: + return 'Robinhood Connect'; + case ProviderType.dfx: + return 'DFX Connect'; + case ProviderType.onramper: + return 'Onramper'; + case ProviderType.moonpaySell: + return 'MoonPay'; + } + } + + String get id { + switch (this) { + case ProviderType.askEachTime: + return 'ask_each_time_provider'; + case ProviderType.robinhood: + return 'robinhood_connect_provider'; + case ProviderType.dfx: + return 'dfx_connect_provider'; + case ProviderType.onramper: + return 'onramper_provider'; + case ProviderType.moonpaySell: + return 'moonpay_provider'; + } + } +} + +class ProvidersHelper { + static List getAvailableBuyProviderTypes(WalletType walletType) { + switch (walletType) { + case WalletType.nano: + case WalletType.banano: + return [ProviderType.askEachTime, ProviderType.onramper]; + case WalletType.monero: + return [ProviderType.askEachTime, ProviderType.onramper, ProviderType.dfx]; + case WalletType.bitcoin: + case WalletType.ethereum: + return [ + ProviderType.askEachTime, + ProviderType.onramper, + ProviderType.dfx, + ProviderType.robinhood, + ]; + case WalletType.litecoin: + case WalletType.bitcoinCash: + return [ProviderType.askEachTime, ProviderType.onramper, ProviderType.robinhood]; + case WalletType.none: + case WalletType.haven: + case WalletType.polygon: + return []; + } + } + + static List getAvailableSellProviderTypes(WalletType walletType) { + switch (walletType) { + case WalletType.monero: + return [ProviderType.askEachTime, ProviderType.dfx]; + case WalletType.bitcoin: + case WalletType.ethereum: + return [ProviderType.askEachTime, ProviderType.moonpaySell, ProviderType.dfx]; + case WalletType.litecoin: + case WalletType.bitcoinCash: + return [ProviderType.askEachTime, ProviderType.moonpaySell]; + case WalletType.nano: + case WalletType.banano: + case WalletType.none: + case WalletType.haven: + case WalletType.polygon: + return []; + } + } + + static BuyProvider? getProviderByType(ProviderType type) { + switch (type) { + case ProviderType.robinhood: + return getIt.get(); + case ProviderType.dfx: + return getIt.get(); + case ProviderType.onramper: + return getIt.get(); + case ProviderType.askEachTime: + return null; + case ProviderType.moonpaySell: + return getIt.get(); + } + } +} diff --git a/lib/src/screens/buy/buy_options_page.dart b/lib/src/screens/buy/buy_options_page.dart index 6ce8fe9f2..50f041d2e 100644 --- a/lib/src/screens/buy/buy_options_page.dart +++ b/lib/src/screens/buy/buy_options_page.dart @@ -1,5 +1,3 @@ -import 'package:cake_wallet/buy/buy_provider.dart'; -import 'package:cake_wallet/entities/buy_provider_types.dart'; import 'package:cake_wallet/generated/i18n.dart'; import 'package:cake_wallet/src/screens/base_page.dart'; import 'package:cake_wallet/src/widgets/option_tile.dart'; @@ -22,8 +20,7 @@ class BuySellOptionsPage extends BasePage { @override Widget body(BuildContext context) { - final isLightMode = - Theme.of(context).extension()?.useDarkImage ?? false; + final isLightMode = Theme.of(context).extension()?.useDarkImage ?? false; final availableProviders = isBuyAction ? dashboardViewModel.availableBuyProviders : dashboardViewModel.availableSellProviders; @@ -46,31 +43,26 @@ class BuySellOptionsPage extends BasePage { child: OptionTile( image: icon, title: provider.toString(), - description: isBuyAction - ? provider.buyOptionDescription - : provider.sellOptionDescription, - onPressed: () => - provider.launchProvider(context, isBuyAction), + description: provider.providerDescription, + onPressed: () => provider.launchProvider(context, isBuyAction), ), ); }).toList(), Spacer(), - Padding( - padding: EdgeInsets.fromLTRB(24, 24, 24, 32), - child: Text( - isBuyAction - ? S.of(context).select_buy_provider_notice - : S.of(context).select_sell_provider_notice, - textAlign: TextAlign.center, - style: TextStyle( - fontSize: 14, - fontWeight: FontWeight.normal, - color: Theme.of(context) - .extension()! - .detailsTitlesColor, - ), + Padding( + padding: EdgeInsets.fromLTRB(24, 24, 24, 32), + child: Text( + isBuyAction + ? S.of(context).select_buy_provider_notice + : S.of(context).select_sell_provider_notice, + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 14, + fontWeight: FontWeight.normal, + color: Theme.of(context).extension()!.detailsTitlesColor, ), ), + ), ], ), ), diff --git a/lib/src/screens/buy/buy_webview_page.dart b/lib/src/screens/buy/buy_webview_page.dart index a6ea2ab7b..6f7a39322 100644 --- a/lib/src/screens/buy/buy_webview_page.dart +++ b/lib/src/screens/buy/buy_webview_page.dart @@ -1,7 +1,7 @@ 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/moonpay/moonpay_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'; diff --git a/lib/src/screens/buy/widgets/buy_list_item.dart b/lib/src/screens/buy/widgets/buy_list_item.dart index d8adddfdc..d8c457ac0 100644 --- a/lib/src/screens/buy/widgets/buy_list_item.dart +++ b/lib/src/screens/buy/widgets/buy_list_item.dart @@ -29,7 +29,7 @@ class BuyListItem extends StatelessWidget { @override Widget build(BuildContext context) { - final isSelected = selectedProvider?.buyOptionDescription == provider.buyOptionDescription; + final isSelected = selectedProvider?.providerDescription == provider.providerDescription; final iconColor = isSelected ? Colors.white : Colors.black; final providerIcon = Image.asset('assets/images/wyre-icon.png', width: 36, height: 36); diff --git a/lib/src/screens/exchange_trade/exchange_trade_page.dart b/lib/src/screens/exchange_trade/exchange_trade_page.dart index 23595efdf..e72b0f0a8 100644 --- a/lib/src/screens/exchange_trade/exchange_trade_page.dart +++ b/lib/src/screens/exchange_trade/exchange_trade_page.dart @@ -299,7 +299,8 @@ class ExchangeTradeState extends State { } void transactionStatePopup() { - showPopUp( + if (this.mounted) { + showPopUp( context: context, builder: (BuildContext popupContext) { return Observer(builder: (_) { @@ -344,7 +345,7 @@ class ExchangeTradeState extends State { onPressed: () { Navigator.of(popupContext).pop(); RequestReviewHandler.requestReview(); - }, + }, text: S.of(popupContext).got_it, color: Theme.of(popupContext).primaryColor, textColor: Colors.white)) @@ -391,5 +392,6 @@ class ExchangeTradeState extends State { ); }); }); + } } } diff --git a/lib/src/screens/wallet_list/wallet_list_page.dart b/lib/src/screens/wallet_list/wallet_list_page.dart index 8dcd23d29..717bb0a94 100644 --- a/lib/src/screens/wallet_list/wallet_list_page.dart +++ b/lib/src/screens/wallet_list/wallet_list_page.dart @@ -1,6 +1,5 @@ import 'package:cake_wallet/entities/wallet_list_order_types.dart'; import 'package:cake_wallet/src/screens/dashboard/widgets/filter_list_widget.dart'; -import 'package:cake_wallet/src/screens/dashboard/widgets/filter_widget.dart'; import 'package:cake_wallet/src/screens/wallet_list/filtered_list.dart'; import 'package:cake_wallet/themes/extensions/cake_text_theme.dart'; import 'package:cake_wallet/core/auth_service.dart'; @@ -335,7 +334,9 @@ class WalletListBodyState extends State { // in desktop platforms the navigation tree is different if (responsiveLayoutUtil.shouldRenderMobileUI) { WidgetsBinding.instance.addPostFrameCallback((_) { - Navigator.of(context).pop(); + if (this.mounted) { + Navigator.of(context).pop(); + } }); } } catch (e) { diff --git a/lib/store/settings_store.dart b/lib/store/settings_store.dart index 20f310c83..7bccc4fc5 100644 --- a/lib/store/settings_store.dart +++ b/lib/store/settings_store.dart @@ -4,7 +4,7 @@ import 'package:cake_wallet/bitcoin/bitcoin.dart'; import 'package:cake_wallet/bitcoin_cash/bitcoin_cash.dart'; import 'package:cake_wallet/buy/buy_provider.dart'; import 'package:cake_wallet/entities/auto_generate_subaddress_status.dart'; -import 'package:cake_wallet/entities/buy_provider_types.dart'; +import 'package:cake_wallet/entities/provider_types.dart'; import 'package:cake_wallet/entities/cake_2fa_preset_options.dart'; import 'package:cake_wallet/entities/background_tasks.dart'; import 'package:cake_wallet/entities/exchange_api_mode.dart'; @@ -149,8 +149,8 @@ abstract class SettingsStoreBase with Store { currentSyncMode = initialSyncMode, currentSyncAll = initialSyncAll, priority = ObservableMap(), - defaultBuyProviders = ObservableMap(), - defaultSellProviders = ObservableMap() { + defaultBuyProviders = ObservableMap(), + defaultSellProviders = ObservableMap() { //this.nodes = ObservableMap.of(nodes); if (initialMoneroTransactionPriority != null) { @@ -187,10 +187,10 @@ abstract class SettingsStoreBase with Store { final key = 'buyProvider_${walletType.toString()}'; final providerId = sharedPreferences.getString(key); if (providerId != null) { - defaultBuyProviders[walletType] = BuyProviderType.values - .firstWhere((provider) => provider.id == providerId, orElse: () => BuyProviderType.askEachTime); + defaultBuyProviders[walletType] = ProviderType.values + .firstWhere((provider) => provider.id == providerId, orElse: () => ProviderType.askEachTime); } else { - defaultBuyProviders[walletType] = BuyProviderType.askEachTime; + defaultBuyProviders[walletType] = ProviderType.askEachTime; } }); @@ -198,10 +198,10 @@ abstract class SettingsStoreBase with Store { final key = 'sellProvider_${walletType.toString()}'; final providerId = sharedPreferences.getString(key); if (providerId != null) { - defaultSellProviders[walletType] = BuyProviderType.values - .firstWhere((provider) => provider.id == providerId, orElse: () => BuyProviderType.askEachTime); + defaultSellProviders[walletType] = ProviderType.values + .firstWhere((provider) => provider.id == providerId, orElse: () => ProviderType.askEachTime); } else { - defaultSellProviders[walletType] = BuyProviderType.askEachTime; + defaultSellProviders[walletType] = ProviderType.askEachTime; } }); @@ -617,10 +617,10 @@ abstract class SettingsStoreBase with Store { ObservableMap trocadorProviderStates = ObservableMap(); @observable - ObservableMap defaultBuyProviders; + ObservableMap defaultBuyProviders; @observable - ObservableMap defaultSellProviders; + ObservableMap defaultSellProviders; @observable SortBalanceBy sortBalanceBy; diff --git a/lib/view_model/buy/buy_view_model.dart b/lib/view_model/buy/buy_view_model.dart index c0e22dae2..d73396e1b 100644 --- a/lib/view_model/buy/buy_view_model.dart +++ b/lib/view_model/buy/buy_view_model.dart @@ -1,5 +1,5 @@ import 'package:cake_wallet/buy/buy_provider.dart'; -import 'package:cake_wallet/buy/moonpay/moonpay_buy_provider.dart'; +import 'package:cake_wallet/buy/moonpay/moonpay_provider.dart'; import 'package:cake_wallet/buy/wyre/wyre_buy_provider.dart'; import 'package:cw_core/crypto_currency.dart'; import 'package:cake_wallet/entities/fiat_currency.dart'; diff --git a/lib/view_model/dashboard/dashboard_view_model.dart b/lib/view_model/dashboard/dashboard_view_model.dart index f4c66b432..a794c2262 100644 --- a/lib/view_model/dashboard/dashboard_view_model.dart +++ b/lib/view_model/dashboard/dashboard_view_model.dart @@ -4,7 +4,7 @@ import 'package:cake_wallet/buy/buy_provider.dart'; import 'package:cake_wallet/core/key_service.dart'; import 'package:cake_wallet/entities/auto_generate_subaddress_status.dart'; import 'package:cake_wallet/entities/balance_display_mode.dart'; -import 'package:cake_wallet/entities/buy_provider_types.dart'; +import 'package:cake_wallet/entities/provider_types.dart'; import 'package:cake_wallet/entities/exchange_api_mode.dart'; import 'package:cake_wallet/exchange/exchange_provider_description.dart'; import 'package:cake_wallet/generated/i18n.dart'; @@ -42,7 +42,7 @@ import 'package:cw_core/wallet_type.dart'; import 'package:eth_sig_util/util/utils.dart'; import 'package:flutter/services.dart'; import 'package:mobx/mobx.dart'; -import 'package:cake_wallet/entities/buy_provider_types.dart'; +import 'package:cake_wallet/entities/provider_types.dart'; part 'dashboard_view_model.g.dart'; @@ -297,27 +297,27 @@ abstract class DashboardViewModelBase with Store { Map> filterItems; - BuyProvider? get defaultBuyProvider => BuyProviderHelper.getProviderByType( - settingsStore.defaultBuyProviders[wallet.type] ?? BuyProviderType.askEachTime); + BuyProvider? get defaultBuyProvider => ProvidersHelper.getProviderByType( + settingsStore.defaultBuyProviders[wallet.type] ?? ProviderType.askEachTime); - BuyProvider? get defaultSellProvider => BuyProviderHelper.getProviderByType( - settingsStore.defaultSellProviders[wallet.type] ?? BuyProviderType.askEachTime); + BuyProvider? get defaultSellProvider => ProvidersHelper.getProviderByType( + settingsStore.defaultSellProviders[wallet.type] ?? ProviderType.askEachTime); bool get isBuyEnabled => settingsStore.isBitcoinBuyEnabled; List get availableBuyProviders { - final providerTypes = BuyProviderHelper.getAvailableBuyProviderTypes(wallet.type); + final providerTypes = ProvidersHelper.getAvailableBuyProviderTypes(wallet.type); return providerTypes - .map((type) => BuyProviderHelper.getProviderByType(type)) + .map((type) => ProvidersHelper.getProviderByType(type)) .where((provider) => provider != null) .cast() .toList(); } List get availableSellProviders { - final providerTypes = BuyProviderHelper.getAvailableSellProviderTypes(wallet.type); + final providerTypes = ProvidersHelper.getAvailableSellProviderTypes(wallet.type); return providerTypes - .map((type) => BuyProviderHelper.getProviderByType(type)) + .map((type) => ProvidersHelper.getProviderByType(type)) .where((provider) => provider != null) .cast() .toList(); diff --git a/lib/view_model/order_details_view_model.dart b/lib/view_model/order_details_view_model.dart index 4ac848ecf..9b00bbb46 100644 --- a/lib/view_model/order_details_view_model.dart +++ b/lib/view_model/order_details_view_model.dart @@ -9,7 +9,7 @@ import 'package:cake_wallet/src/screens/transaction_details/standart_list_item.d import 'package:cake_wallet/src/screens/trade_details/track_trade_list_item.dart'; import 'package:url_launcher/url_launcher.dart'; import 'package:cw_core/wallet_base.dart'; -import 'package:cake_wallet/buy/moonpay/moonpay_buy_provider.dart'; +import 'package:cake_wallet/buy/moonpay/moonpay_provider.dart'; import 'package:cake_wallet/buy/wyre/wyre_buy_provider.dart'; part 'order_details_view_model.g.dart'; diff --git a/lib/view_model/settings/other_settings_view_model.dart b/lib/view_model/settings/other_settings_view_model.dart index 60ed550ef..e44eb8fc7 100644 --- a/lib/view_model/settings/other_settings_view_model.dart +++ b/lib/view_model/settings/other_settings_view_model.dart @@ -1,5 +1,5 @@ import 'package:cake_wallet/bitcoin/bitcoin.dart'; -import 'package:cake_wallet/entities/buy_provider_types.dart'; +import 'package:cake_wallet/entities/provider_types.dart'; import 'package:cake_wallet/entities/priority_for_wallet_type.dart'; import 'package:cake_wallet/generated/i18n.dart'; import 'package:cake_wallet/store/settings_store.dart'; @@ -64,20 +64,20 @@ abstract class OtherSettingsViewModelBase with Store { bool get isEnabledSellAction => !_settingsStore.disableSell && _wallet.type != WalletType.haven; - List get availableBuyProvidersTypes { - return BuyProviderHelper.getAvailableBuyProviderTypes(walletType); + List get availableBuyProvidersTypes { + return ProvidersHelper.getAvailableBuyProviderTypes(walletType); } - List get availableSellProvidersTypes => - BuyProviderHelper.getAvailableSellProviderTypes(walletType); + List get availableSellProvidersTypes => + ProvidersHelper.getAvailableSellProviderTypes(walletType); - BuyProviderType get buyProviderType => + ProviderType get buyProviderType => _settingsStore.defaultBuyProviders[walletType] ?? - BuyProviderType.askEachTime; + ProviderType.askEachTime; - BuyProviderType get sellProviderType => + ProviderType get sellProviderType => _settingsStore.defaultSellProviders[walletType] ?? - BuyProviderType.askEachTime; + ProviderType.askEachTime; String getDisplayPriority(dynamic priority) { final _priority = priority as TransactionPriority; @@ -93,28 +93,28 @@ abstract class OtherSettingsViewModelBase with Store { } String getBuyProviderType(dynamic buyProviderType) { - final _buyProviderType = buyProviderType as BuyProviderType; - return _buyProviderType == BuyProviderType.askEachTime + final _buyProviderType = buyProviderType as ProviderType; + return _buyProviderType == ProviderType.askEachTime ? S.current.ask_each_time - : _buyProviderType.name; + : _buyProviderType.title; } String getSellProviderType(dynamic sellProviderType) { - final _sellProviderType = sellProviderType as BuyProviderType; - return _sellProviderType == BuyProviderType.askEachTime + final _sellProviderType = sellProviderType as ProviderType; + return _sellProviderType == ProviderType.askEachTime ? S.current.ask_each_time - : _sellProviderType.name; + : _sellProviderType.title; } void onDisplayPrioritySelected(TransactionPriority priority) => _settingsStore.priority[_wallet.type] = priority; @action - BuyProviderType onBuyProviderTypeSelected(BuyProviderType buyProviderType) => + ProviderType onBuyProviderTypeSelected(ProviderType buyProviderType) => _settingsStore.defaultBuyProviders[walletType] = buyProviderType; @action - BuyProviderType onSellProviderTypeSelected( - BuyProviderType sellProviderType) => + ProviderType onSellProviderTypeSelected( + ProviderType sellProviderType) => _settingsStore.defaultSellProviders[walletType] = sellProviderType; } diff --git a/lib/view_model/wallet_list/wallet_list_view_model.dart b/lib/view_model/wallet_list/wallet_list_view_model.dart index 78211bb3e..407b6d3bc 100644 --- a/lib/view_model/wallet_list/wallet_list_view_model.dart +++ b/lib/view_model/wallet_list/wallet_list_view_model.dart @@ -22,6 +22,7 @@ abstract class WalletListViewModelBase with Store { ) : wallets = ObservableList() { setOrderType(_appStore.settingsStore.walletListOrder); reaction((_) => _appStore.wallet, (_) => updateList()); + updateList(); } @observable