diff --git a/.github/workflows/pr_test_build.yml b/.github/workflows/pr_test_build.yml index 30bf3058e..92d2ad8ba 100644 --- a/.github/workflows/pr_test_build.yml +++ b/.github/workflows/pr_test_build.yml @@ -128,6 +128,8 @@ jobs: echo "const payfuraApiKey = '${{ secrets.PAYFURA_API_KEY }}';" >> lib/.secrets.g.dart echo "const etherScanApiKey = '${{ secrets.ETHER_SCAN_API_KEY }}';" >> cw_ethereum/lib/.secrets.g.dart echo "const chatwootWebsiteToken = '${{ secrets.CHATWOOT_WEBSITE_TOKEN }}';" >> lib/.secrets.g.dart + echo "const robinhoodApplicationId = '${{ secrets.ROBINHOOD_APPLICATION_ID }}';" >> lib/.secrets.g.dart + echo "const robinhoodCIdApiSecret = '${{ secrets.ROBINHOOD_CID_CLIENT_SECRET }}';" >> lib/.secrets.g.dart - name: Rename app run: echo -e "id=com.cakewallet.test\nname=$GITHUB_HEAD_REF" > /opt/android/cake_wallet/android/app.properties diff --git a/assets/images/onramper_dark.png b/assets/images/onramper_dark.png new file mode 100644 index 000000000..62f37cd29 Binary files /dev/null and b/assets/images/onramper_dark.png differ diff --git a/assets/images/onramper_light.png b/assets/images/onramper_light.png new file mode 100644 index 000000000..f07fc3709 Binary files /dev/null and b/assets/images/onramper_light.png differ diff --git a/assets/images/robinhood_dark.png b/assets/images/robinhood_dark.png new file mode 100644 index 000000000..0d7273fc4 Binary files /dev/null and b/assets/images/robinhood_dark.png differ diff --git a/assets/images/robinhood_light.png b/assets/images/robinhood_light.png new file mode 100644 index 000000000..24aa345f1 Binary files /dev/null and b/assets/images/robinhood_light.png differ diff --git a/cw_bitcoin/lib/electrum_wallet.dart b/cw_bitcoin/lib/electrum_wallet.dart index f9437e668..1c0a1e4e3 100644 --- a/cw_bitcoin/lib/electrum_wallet.dart +++ b/cw_bitcoin/lib/electrum_wallet.dart @@ -740,4 +740,14 @@ abstract class ElectrumWalletBase extends WalletBase _onError = onError; + + @override + String signMessage(String message, {String? address = null}) { + final index = address != null + ? walletAddresses.addresses.firstWhere((element) => element.address == address).index + : null; + return index == null + ? base64Encode(hd.sign(message)) + : base64Encode(hd.derive(index).sign(message)); + } } diff --git a/cw_core/lib/wallet_base.dart b/cw_core/lib/wallet_base.dart index e24fdb4b0..f8db67b24 100644 --- a/cw_core/lib/wallet_base.dart +++ b/cw_core/lib/wallet_base.dart @@ -83,4 +83,6 @@ abstract class WalletBase< void setExceptionHandler(void Function(FlutterErrorDetails) onError) => null; Future renameWalletFiles(String newWalletName); + + String signMessage(String message, {String? address = null}) => throw UnimplementedError(); } diff --git a/cw_ethereum/lib/ethereum_wallet.dart b/cw_ethereum/lib/ethereum_wallet.dart index b5bbdb58a..012b33a4b 100644 --- a/cw_ethereum/lib/ethereum_wallet.dart +++ b/cw_ethereum/lib/ethereum_wallet.dart @@ -31,6 +31,7 @@ import 'package:hive/hive.dart'; import 'package:hex/hex.dart'; import 'package:mobx/mobx.dart'; import 'package:shared_preferences/shared_preferences.dart'; +import 'package:web3dart/crypto.dart'; import 'package:web3dart/web3dart.dart'; import 'package:bip39/bip39.dart' as bip39; import 'package:bip32/bip32.dart' as bip32; @@ -503,4 +504,8 @@ abstract class EthereumWalletBase _transactionsUpdateTimer?.cancel(); } } + + @override + String signMessage(String message, {String? address = null}) => + bytesToHex(_ethPrivateKey.signPersonalMessageToUint8List(ascii.encode(message))); } diff --git a/lib/buy/onramper/onramper_buy_provider.dart b/lib/buy/onramper/onramper_buy_provider.dart index cf4cbd124..2f0e47f02 100644 --- a/lib/buy/onramper/onramper_buy_provider.dart +++ b/lib/buy/onramper/onramper_buy_provider.dart @@ -1,10 +1,13 @@ import 'package:cake_wallet/.secrets.g.dart' as secrets; import 'package:cake_wallet/generated/i18n.dart'; +import 'package:cake_wallet/routes.dart'; import 'package:cake_wallet/store/settings_store.dart'; import 'package:cake_wallet/themes/extensions/cake_text_theme.dart'; +import 'package:cake_wallet/utils/device_info.dart'; import 'package:cw_core/crypto_currency.dart'; import 'package:cw_core/wallet_base.dart'; import 'package:flutter/material.dart'; +import 'package:url_launcher/url_launcher.dart'; class OnRamperBuyProvider { OnRamperBuyProvider({required SettingsStore settingsStore, required WalletBase wallet}) @@ -69,4 +72,13 @@ class OnRamperBuyProvider { 'cardColor': cardColor }); } + + Future launchProvider(BuildContext context) async { + final uri = requestUrl(context); + if (DeviceInfo.instance.isMobile) { + Navigator.of(context).pushNamed(Routes.webViewPage, arguments: [S.of(context).buy, uri]); + } else { + await launchUrl(uri); + } + } } diff --git a/lib/buy/robinhood/robinhood_buy_provider.dart b/lib/buy/robinhood/robinhood_buy_provider.dart new file mode 100644 index 000000000..0cb367298 --- /dev/null +++ b/lib/buy/robinhood/robinhood_buy_provider.dart @@ -0,0 +1,92 @@ +import 'dart:convert'; + +import 'package:cake_wallet/.secrets.g.dart' as secrets; +import 'package:cake_wallet/generated/i18n.dart'; +import 'package:cake_wallet/src/widgets/alert_with_one_action.dart'; +import 'package:cake_wallet/utils/exception_handler.dart'; +import 'package:cake_wallet/utils/show_pop_up.dart'; +import 'package:cw_core/wallet_base.dart'; +import 'package:cw_core/wallet_type.dart'; +import 'package:flutter/material.dart'; +import 'package:http/http.dart' as http; +import 'package:url_launcher/url_launcher.dart'; + +class RobinhoodBuyProvider { + RobinhoodBuyProvider({required WalletBase wallet}) + : this._wallet = wallet; + + final WalletBase _wallet; + + static const _baseUrl = 'applink.robinhood.com'; + static const _cIdBaseUrl = 'exchange-helper.cakewallet.com'; + + String get _applicationId => secrets.robinhoodApplicationId; + String get _apiSecret => secrets.robinhoodCIdApiSecret; + + bool get isAvailable => + [WalletType.bitcoin, WalletType.litecoin, WalletType.ethereum].contains(_wallet.type); + + String getSignature(String message) { + switch (_wallet.type) { + case WalletType.ethereum: + return _wallet.signMessage(message); + case WalletType.litecoin: + case WalletType.bitcoin: + return _wallet.signMessage(message, address: _wallet.walletAddresses.address); + default: + throw Exception("WalletType is not available for Robinhood"); + } + } + + Future getConnectId() async { + final walletAddress = _wallet.walletAddresses.address; + final valid_until = (DateTime.now().millisecondsSinceEpoch / 1000).round() + 10; + final message = "$_apiSecret:${valid_until}"; + + final signature = getSignature(message); + + final uri = Uri.https(_cIdBaseUrl, "/api/robinhood"); + + var response = await http.post(uri, + headers: {'Content-Type': 'application/json'}, + body: json + .encode({'valid_until': valid_until, 'wallet': walletAddress, 'signature': signature})); + + if (response.statusCode == 200) { + return (jsonDecode(response.body) as Map)['connectId'] as String; + } else { + throw Exception('Provider currently unavailable. Status: ${response.statusCode} ${response.body}'); + } + } + + Future requestUrl() async { + final connectId = await getConnectId(); + final networkName = _wallet.currency.fullName?.toUpperCase().replaceAll(" ", "_"); + + return Uri.https(_baseUrl, '/u/connect', { + 'applicationId': _applicationId, + 'connectId': connectId, + 'walletAddress': _wallet.walletAddresses.address, + 'userIdentifier': _wallet.walletAddresses.address, + 'supportedNetworks': networkName + }); + } + + Future launchProvider(BuildContext context) async { + try { + final uri = await requestUrl(); + await launchUrl(uri, mode: LaunchMode.externalApplication); + } catch (e, s) { + ExceptionHandler.onError(FlutterErrorDetails(exception: e, stack: s)); + await showPopUp( + context: context, + builder: (BuildContext context) { + return AlertWithOneAction( + alertTitle: "Robinhood Connect", + alertContent: S.of(context).buy_provider_unavailable, + buttonText: S.of(context).ok, + buttonAction: () => Navigator.of(context).pop()); + }); + } + } +} diff --git a/lib/core/backup_service.dart b/lib/core/backup_service.dart index 3f430b7e9..24b5558d5 100644 --- a/lib/core/backup_service.dart +++ b/lib/core/backup_service.dart @@ -208,6 +208,7 @@ class BackupService { final isAppSecure = data[PreferencesKey.isAppSecureKey] as bool?; final disableBuy = data[PreferencesKey.disableBuyKey] as bool?; final disableSell = data[PreferencesKey.disableSellKey] as bool?; + final defaultBuyProvider = data[PreferencesKey.defaultBuyProvider] as int?; final currentTransactionPriorityKeyLegacy = data[PreferencesKey.currentTransactionPriorityKeyLegacy] as int?; final allowBiometricalAuthentication = @@ -276,6 +277,9 @@ class BackupService { if (disableSell != null) await _sharedPreferences.setBool(PreferencesKey.disableSellKey, disableSell); + if (defaultBuyProvider != null) + await _sharedPreferences.setInt(PreferencesKey.defaultBuyProvider, defaultBuyProvider); + if (currentTransactionPriorityKeyLegacy != null) await _sharedPreferences.setInt( PreferencesKey.currentTransactionPriorityKeyLegacy, currentTransactionPriorityKeyLegacy); @@ -476,6 +480,7 @@ class BackupService { _sharedPreferences.getBool(PreferencesKey.shouldSaveRecipientAddressKey), PreferencesKey.disableBuyKey: _sharedPreferences.getBool(PreferencesKey.disableBuyKey), PreferencesKey.disableSellKey: _sharedPreferences.getBool(PreferencesKey.disableSellKey), + PreferencesKey.defaultBuyProvider: _sharedPreferences.getInt(PreferencesKey.defaultBuyProvider), PreferencesKey.isDarkThemeLegacy: _sharedPreferences.getBool(PreferencesKey.isDarkThemeLegacy), PreferencesKey.currentPinLength: _sharedPreferences.getInt(PreferencesKey.currentPinLength), diff --git a/lib/di.dart b/lib/di.dart index dd3e6c495..a308b989a 100644 --- a/lib/di.dart +++ b/lib/di.dart @@ -3,6 +3,7 @@ import 'package:cake_wallet/anonpay/anonpay_info_base.dart'; import 'package:cake_wallet/anonpay/anonpay_invoice_info.dart'; import 'package:cake_wallet/buy/onramper/onramper_buy_provider.dart'; import 'package:cake_wallet/buy/payfura/payfura_buy_provider.dart'; +import 'package:cake_wallet/buy/robinhood/robinhood_buy_provider.dart'; import 'package:cake_wallet/core/yat_service.dart'; import 'package:cake_wallet/entities/background_tasks.dart'; import 'package:cake_wallet/entities/auto_generate_subaddress_status.dart'; @@ -15,6 +16,7 @@ import 'package:cake_wallet/ionia/ionia_gift_card.dart'; import 'package:cake_wallet/ionia/ionia_tip.dart'; import 'package:cake_wallet/routes.dart'; import 'package:cake_wallet/src/screens/anonpay_details/anonpay_details_page.dart'; +import 'package:cake_wallet/src/screens/buy/buy_options_page.dart'; import 'package:cake_wallet/src/screens/buy/webview_page.dart'; import 'package:cake_wallet/src/screens/dashboard/desktop_dashboard_page.dart'; import 'package:cake_wallet/src/screens/dashboard/desktop_widgets/desktop_sidebar_wrapper.dart'; @@ -95,7 +97,6 @@ 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'; import 'package:cake_wallet/src/screens/exchange_trade/exchange_confirm_page.dart'; @@ -688,6 +689,9 @@ Future setup({ editingNode: editingNode, isSelected: isSelected)); + getIt.registerFactory( + () => RobinhoodBuyProvider(wallet: getIt.get().wallet!)); + getIt.registerFactory(() => OnRamperBuyProvider( settingsStore: getIt.get().settingsStore, wallet: getIt.get().wallet!, @@ -816,6 +820,8 @@ Future setup({ getIt.registerFactory(() => BuyAmountViewModel()); + getIt.registerFactory(() => BuyOptionsPage()); + getIt.registerFactory(() { final wallet = getIt.get().wallet; @@ -824,10 +830,6 @@ Future setup({ wallet: wallet!); }); - getIt.registerFactory(() { - return PreOrderPage(buyViewModel: getIt.get()); - }); - getIt.registerFactoryParam((List args, _) { final url = args.first as String; final buyViewModel = args[1] as BuyViewModel; diff --git a/lib/entities/buy_provider_types.dart b/lib/entities/buy_provider_types.dart new file mode 100644 index 000000000..90c070e86 --- /dev/null +++ b/lib/entities/buy_provider_types.dart @@ -0,0 +1,19 @@ +import 'package:cake_wallet/generated/i18n.dart'; + +enum BuyProviderType { + AskEachTime, + Robinhood, + Onramper; + + @override + String toString() { + switch (this) { + case BuyProviderType.AskEachTime: + return S.current.ask_each_time; + case BuyProviderType.Robinhood: + return "Robinhood"; + case BuyProviderType.Onramper: + return "Onramper"; + } + } +} diff --git a/lib/entities/main_actions.dart b/lib/entities/main_actions.dart index 2d91983d4..4d2abced9 100644 --- a/lib/entities/main_actions.dart +++ b/lib/entities/main_actions.dart @@ -1,6 +1,8 @@ import 'package:cake_wallet/buy/moonpay/moonpay_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:cake_wallet/entities/buy_provider_types.dart'; import 'package:cake_wallet/generated/i18n.dart'; import 'package:cake_wallet/routes.dart'; import 'package:cake_wallet/src/widgets/alert_with_one_action.dart'; @@ -41,23 +43,30 @@ class MainActions { isEnabled: (viewModel) => viewModel.isEnabledBuyAction, canShow: (viewModel) => viewModel.hasBuyAction, onTap: (BuildContext context, DashboardViewModel viewModel) async { + final defaultBuyProvider = viewModel.defaultBuyProvider; final walletType = viewModel.type; + if (!viewModel.isEnabledBuyAction) return; + switch (walletType) { case WalletType.bitcoin: case WalletType.litecoin: case WalletType.ethereum: - case WalletType.monero: - if (viewModel.isEnabledBuyAction) { - final uri = getIt.get().requestUrl(context); - if (DeviceInfo.instance.isMobile) { - Navigator.of(context) - .pushNamed(Routes.webViewPage, arguments: [S.of(context).buy, uri]); - } else { - await launchUrl(uri); - } + switch (defaultBuyProvider) { + case BuyProviderType.AskEachTime: + Navigator.pushNamed(context, Routes.buy); + break; + case BuyProviderType.Onramper: + await getIt.get().launchProvider(context); + break; + case BuyProviderType.Robinhood: + await getIt.get().launchProvider(context); + break; } break; + case WalletType.monero: + await getIt.get().launchProvider(context); + break; default: await showPopUp( context: context, diff --git a/lib/entities/preferences_key.dart b/lib/entities/preferences_key.dart index 7b4d3d0dc..f699f89f8 100644 --- a/lib/entities/preferences_key.dart +++ b/lib/entities/preferences_key.dart @@ -13,6 +13,7 @@ class PreferencesKey { static const isAppSecureKey = 'is_app_secure'; static const disableBuyKey = 'disable_buy'; static const disableSellKey = 'disable_sell'; + static const defaultBuyProvider = 'default_buy_provider'; static const currentFiatApiModeKey = 'current_fiat_api_mode'; static const allowBiometricalAuthenticationKey = 'allow_biometrical_authentication'; diff --git a/lib/router.dart b/lib/router.dart index 0ef91e16f..c73cf72e0 100644 --- a/lib/router.dart +++ b/lib/router.dart @@ -7,9 +7,9 @@ import 'package:cake_wallet/entities/qr_view_data.dart'; import 'package:cake_wallet/src/screens/anonpay_details/anonpay_details_page.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_options_page.dart'; import 'package:cake_wallet/src/screens/buy/buy_webview_page.dart'; import 'package:cake_wallet/src/screens/buy/webview_page.dart'; -import 'package:cake_wallet/src/screens/buy/pre_order_page.dart'; import 'package:cake_wallet/src/screens/dashboard/edit_token_page.dart'; import 'package:cake_wallet/src/screens/dashboard/home_settings_page.dart'; import 'package:cake_wallet/src/screens/restore/sweeping_wallet_page.dart'; @@ -383,10 +383,8 @@ Route createRoute(RouteSettings settings) { builder: (_) => getIt.get(param1: settings.arguments as Order)); - case Routes.preOrder: - return MaterialPageRoute( - builder: (_) => - getIt.get()); + case Routes.buy: + return MaterialPageRoute(builder: (_) => getIt.get()); case Routes.buyWebView: final args = settings.arguments as List; diff --git a/lib/routes.dart b/lib/routes.dart index bcb4c38b5..cb479ddf6 100644 --- a/lib/routes.dart +++ b/lib/routes.dart @@ -50,7 +50,7 @@ class Routes { static const supportLiveChat = '/support/live_chat'; static const supportOtherLinks = '/support/other'; static const orderDetails = '/order_details'; - static const preOrder = '/pre_order'; + static const buy = '/buy'; static const buyWebView = '/buy_web_view'; static const unspentCoinsList = '/unspent_coins_list'; static const unspentCoinsDetails = '/unspent_coins_details'; diff --git a/lib/src/screens/buy/buy_options_page.dart b/lib/src/screens/buy/buy_options_page.dart new file mode 100644 index 000000000..45edfc921 --- /dev/null +++ b/lib/src/screens/buy/buy_options_page.dart @@ -0,0 +1,76 @@ +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:cake_wallet/generated/i18n.dart'; +import 'package:cake_wallet/src/screens/base_page.dart'; +import 'package:cake_wallet/src/widgets/option_tile.dart'; +import 'package:cake_wallet/themes/extensions/option_tile_theme.dart'; +import 'package:cake_wallet/themes/extensions/transaction_trade_theme.dart'; +import 'package:flutter/material.dart'; + +class BuyOptionsPage extends BasePage { + final iconDarkRobinhood = 'assets/images/robinhood_dark.png'; + final iconLightRobinhood = 'assets/images/robinhood_light.png'; + final iconDarkOnramper = 'assets/images/onramper_dark.png'; + final iconLightOnramper = 'assets/images/onramper_light.png'; + + @override + String get title => S.current.buy; + + @override + AppBarStyle get appBarStyle => AppBarStyle.regular; + + @override + Widget body(BuildContext context) { + final isLightMode = Theme.of(context).extension()?.useDarkImage ?? false; + final iconRobinhood = + Image.asset(isLightMode ? iconLightRobinhood : iconDarkRobinhood, height: 40, width: 40); + final iconOnramper = + Image.asset(isLightMode ? iconLightOnramper : iconDarkOnramper, height: 40, width: 40); + + return Container( + child: Center( + child: ConstrainedBox( + constraints: BoxConstraints(maxWidth: 330), + child: Column( + children: [ + Padding( + padding: EdgeInsets.only(top: 24), + child: OptionTile( + image: iconRobinhood, + title: "Robinhood Connect", + description: S.of(context).robinhood_option_description, + onPressed: () async => + await getIt.get().launchProvider(context), + ), + ), + Padding( + padding: EdgeInsets.only(top: 24), + child: OptionTile( + image: iconOnramper, + title: "Onramper", + description: S.of(context).onramper_option_description, + onPressed: () async => + await getIt.get().launchProvider(context), + ), + ), + Spacer(), + Padding( + padding: EdgeInsets.fromLTRB(24, 24, 24, 32), + child: Text( + S.of(context).select_buy_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/pre_order_page.dart b/lib/src/screens/buy/pre_order_page.dart deleted file mode 100644 index 67b9a18a1..000000000 --- a/lib/src/screens/buy/pre_order_page.dart +++ /dev/null @@ -1,304 +0,0 @@ -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/entities/fiat_currency.dart'; -import 'package:cake_wallet/src/widgets/picker.dart'; -import 'package:cake_wallet/themes/extensions/cake_text_theme.dart'; -import 'package:cake_wallet/themes/extensions/exchange_page_theme.dart'; -import 'package:cake_wallet/themes/extensions/keyboard_theme.dart'; -import 'package:cake_wallet/themes/extensions/send_page_theme.dart'; -import 'package:cw_core/wallet_type.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/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'; -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}) - : _amountFocus = FocusNode(), - _amountController = TextEditingController() { - _amountController.addListener(() { - final amount = _amountController.text; - - if (amount != buyViewModel.buyAmountViewModel.amount) { - buyViewModel.buyAmountViewModel.amount = amount; - buyViewModel.selectedProvider = null; - } - }); - - reaction((_) => buyViewModel.buyAmountViewModel.amount, (String amount) { - if (_amountController.text != amount) { - _amountController.text = amount; - } - if (amount.isEmpty) { - buyViewModel.selectedProvider = null; - buyViewModel.isShowProviderButtons = false; - } else { - buyViewModel.isShowProviderButtons = true; - } - }); - } - - 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 => S.current.buy + ' ' + walletTypeToString(buyViewModel.wallet.type); - - @override - bool get resizeToAvoidBottomInset => false; - - @override - bool get extendBodyBehindAppBar => true; - - @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( - config: KeyboardActionsConfig( - keyboardActionsPlatform: KeyboardActionsPlatform.IOS, - keyboardBarColor: Theme.of(context).extension()!.keyboardBarColor, - nextFocus: false, - actions: [ - KeyboardActionsItem( - focusNode: _amountFocus, - toolbarButtons: [(_) => KeyboardDoneButton()], - ), - ]), - child: Container( - height: 0, - color: Theme.of(context).colorScheme.background, - child: ScrollableWithBottomSection( - contentPadding: EdgeInsets.only(bottom: 24), - 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).extension()!.firstGradientColor, - Theme.of(context).extension()!.secondGradientColor, - ], begin: Alignment.topLeft, end: Alignment.bottomRight), - ), - child: Padding( - padding: EdgeInsets.only(top: 100, bottom: 65), - child: Center( - child: Container( - width: 210, - child: BaseTextFormField( - focusNode: _amountFocus, - controller: _amountController, - keyboardType: TextInputType.numberWithOptions(signed: false, decimal: true), - inputFormatters: [FilteringTextInputFormatter.allow(RegExp(_amountPattern))], - prefixIcon: GestureDetector( - onTap: () { - showPopUp( - context: context, - builder: (_) => Picker( - hintText: S.current.search_currency, - items: FiatCurrency.currenciesAvailableToBuyWith, - selectedAtIndex: - FiatCurrency.currenciesAvailableToBuyWith.indexOf(buyViewModel.fiatCurrency), - onItemSelected: (FiatCurrency selectedCurrency) { - buyViewModel.buyAmountViewModel.fiatCurrency = selectedCurrency; - }, - images: FiatCurrency.currenciesAvailableToBuyWith - .map((e) => Image.asset("assets/images/flags/${e.countryCode}.png")) - .toList(), - isGridView: true, - matchingCriteria: (FiatCurrency currency, String searchText) { - return currency.title.toLowerCase().contains(searchText) || - currency.fullName.toLowerCase().contains(searchText); - }, - ), - ); - }, - child: Padding( - padding: EdgeInsets.only(top: 2), - child: Row( - mainAxisSize: MainAxisSize.min, - children: [ - Icon(Icons.keyboard_arrow_down, color: Colors.white), - Text( - buyViewModel.fiatCurrency.title + ': ', - style: TextStyle( - fontSize: 36, - fontWeight: FontWeight.w600, - color: Colors.white, - ), - ), - ], - ), - ), - ), - hintText: '0.00', - borderColor: Theme.of(context).extension()!.textFieldBorderBottomPanelColor, - borderWidth: 0.5, - textStyle: TextStyle(fontSize: 36, fontWeight: FontWeight.w500, color: Colors.white), - placeholderTextStyle: TextStyle( - color: Theme.of(context).extension()!.textFieldHintColor, - fontWeight: FontWeight.w500, - fontSize: 36, - ), - ), - ), - ), - ), - ), - 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).extension()!.titleColor, - 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; - double achAmount; - int minAmount; - - if (snapshot.hasData && snapshot.data != null) { - sourceAmount = snapshot.data!.sourceAmount; - destAmount = snapshot.data!.destAmount; - minAmount = snapshot.data!.minAmount; - achAmount = snapshot.data!.achSourceAmount ?? 0; - } else { - sourceAmount = 0.0; - destAmount = 0.0; - minAmount = 0; - achAmount = 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, - achSourceAmount: achAmount, - onTap: ((buyViewModel.doubleAmount != 0.0) - && (snapshot.hasData)) ? () => - onSelectBuyProvider( - context: context, - provider: item.provider, - sourceAmount: sourceAmount, - minAmount: minAmount - ) : null - ); - }) - ); - } - )) - ) - ], - )), - bottomSectionPadding: - EdgeInsets.only(left: 24, right: 24, bottom: 24), - bottomSection: Observer(builder: (_) { - return LoadingPrimaryButton( - onPressed: () => onPresentProvider(context: context), - text: buyViewModel.selectedProvider == null - ? S.of(context).buy - : S.of(context).buy_with + - ' ${buyViewModel.selectedProvider!.description.title}', - color: Theme.of(context).primaryColor, - textColor: Colors.white, - isLoading: buyViewModel.isRunning, - isDisabled: (buyViewModel.selectedProvider == null) || - buyViewModel.isDisabled - ); - }) - ) - ) - ); - } - - void onSelectBuyProvider({required BuildContext context, required BuyProvider provider, - required double sourceAmount, required int minAmount}) { - - if ((provider is MoonPayBuyProvider)&& - (buyViewModel.buyAmountViewModel.doubleAmount < minAmount)) { - showPopUp( - context: context, - builder: (BuildContext context) { - return AlertWithOneAction( - alertTitle: 'MoonPay', - alertContent: S.of(context).moonpay_alert_text( - minAmount.toString(), - 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; - } - - Future onPresentProvider({required 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; - } -} diff --git a/lib/src/screens/restore/restore_options_page.dart b/lib/src/screens/restore/restore_options_page.dart index 3adad4379..191bddfec 100644 --- a/lib/src/screens/restore/restore_options_page.dart +++ b/lib/src/screens/restore/restore_options_page.dart @@ -2,6 +2,7 @@ import 'package:cake_wallet/core/execution_state.dart'; import 'package:cake_wallet/di.dart'; import 'package:cake_wallet/src/screens/pin_code/pin_code_widget.dart'; import 'package:cake_wallet/src/widgets/alert_with_one_action.dart'; +import 'package:cake_wallet/src/widgets/option_tile.dart'; import 'package:cake_wallet/utils/show_pop_up.dart'; import 'package:cake_wallet/view_model/restore/restore_from_qr_vm.dart'; import 'package:cake_wallet/view_model/restore/wallet_restore_from_qr_code.dart'; @@ -9,7 +10,6 @@ import 'package:cake_wallet/utils/responsive_layout_util.dart'; import 'package:flutter/material.dart'; import 'package:cake_wallet/routes.dart'; import 'package:flutter/cupertino.dart'; -import 'package:cake_wallet/src/screens/restore/widgets/restore_button.dart'; import 'package:cake_wallet/src/screens/base_page.dart'; import 'package:cake_wallet/generated/i18n.dart'; @@ -35,7 +35,7 @@ class RestoreOptionsPage extends BasePage { child: SingleChildScrollView( child: Column( children: [ - RestoreButton( + OptionTile( onPressed: () => Navigator.pushNamed( context, Routes.restoreWalletFromSeedKeys, arguments: isNewInstall), @@ -45,7 +45,7 @@ class RestoreOptionsPage extends BasePage { if (isNewInstall) Padding( padding: EdgeInsets.only(top: 24), - child: RestoreButton( + child: OptionTile( onPressed: () => Navigator.pushNamed(context, Routes.restoreFromBackup), image: imageBackup, title: S.of(context).restore_title_from_backup, @@ -53,7 +53,7 @@ class RestoreOptionsPage extends BasePage { ), Padding( padding: EdgeInsets.only(top: 24), - child: RestoreButton( + child: OptionTile( onPressed: () async { bool isPinSet = false; if (isNewInstall) { diff --git a/lib/src/screens/restore/widgets/restore_button.dart b/lib/src/screens/restore/widgets/restore_button.dart deleted file mode 100644 index c196de059..000000000 --- a/lib/src/screens/restore/widgets/restore_button.dart +++ /dev/null @@ -1,74 +0,0 @@ -import 'package:cake_wallet/themes/extensions/cake_text_theme.dart'; -import 'package:flutter/material.dart'; -import 'package:cake_wallet/themes/extensions/transaction_trade_theme.dart'; - -class RestoreButton extends StatelessWidget { - const RestoreButton( - {required this.onPressed, - required this.image, - required this.title, - required this.description}); - - final VoidCallback onPressed; - final Image image; - final String title; - final String description; - - @override - Widget build(BuildContext context) { - return GestureDetector( - onTap: onPressed, - child: Container( - width: double.infinity, - height: 170, - padding: EdgeInsets.all(24), - alignment: Alignment.topLeft, - decoration: BoxDecoration( - borderRadius: BorderRadius.all(Radius.circular(12)), - color: Theme.of(context).cardColor, - ), - child: Row( - mainAxisSize: MainAxisSize.max, - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - image, - Expanded( - child: Padding( - padding: EdgeInsets.only(left: 16), - child: Column( - mainAxisSize: MainAxisSize.max, - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - title, - style: TextStyle( - fontSize: 16, - fontWeight: FontWeight.w500, - color: Theme.of(context).extension()!.titleColor, - ), - ), - Padding( - padding: EdgeInsets.only(top: 5), - child: Text( - description, - style: TextStyle( - fontSize: 14, - fontWeight: FontWeight.normal, - color: Theme.of(context) - .extension()! - .detailsTitlesColor, - ), - ), - ) - ], - ), - ), - ) - ], - ), - ), - ); - } -} diff --git a/lib/src/screens/settings/other_settings_page.dart b/lib/src/screens/settings/other_settings_page.dart index aea58eb73..0ba4618ac 100644 --- a/lib/src/screens/settings/other_settings_page.dart +++ b/lib/src/screens/settings/other_settings_page.dart @@ -1,3 +1,5 @@ +import 'package:cake_wallet/buy/buy_provider.dart'; +import 'package:cake_wallet/entities/buy_provider_types.dart'; import 'package:cake_wallet/entities/priority_for_wallet_type.dart'; import 'package:cake_wallet/generated/i18n.dart'; import 'package:cake_wallet/routes.dart'; @@ -33,6 +35,13 @@ class OtherSettingsPage extends BasePage { selectedItem: _otherSettingsViewModel.transactionPriority, onItemSelected: _otherSettingsViewModel.onDisplayPrioritySelected, ), + SettingsPickerCell( + title: S.current.default_buy_provider, + items: BuyProviderType.values, + displayItem: _otherSettingsViewModel.getBuyProviderType, + selectedItem: _otherSettingsViewModel.buyProviderType, + onItemSelected: _otherSettingsViewModel.onBuyProviderTypeSelected, + ), SettingsCellWithArrow( title: S.current.settings_terms_and_conditions, handler: (BuildContext context) => diff --git a/lib/src/screens/settings/widgets/settings_version_cell.dart b/lib/src/screens/settings/widgets/settings_version_cell.dart index 8ab1a1672..1607012dc 100644 --- a/lib/src/screens/settings/widgets/settings_version_cell.dart +++ b/lib/src/screens/settings/widgets/settings_version_cell.dart @@ -25,4 +25,4 @@ class SettingsVersionCell extends StatelessWidget { ), ); } -} \ No newline at end of file +} diff --git a/lib/src/screens/support/support_page.dart b/lib/src/screens/support/support_page.dart index 883677832..471ff15b0 100644 --- a/lib/src/screens/support/support_page.dart +++ b/lib/src/screens/support/support_page.dart @@ -1,7 +1,7 @@ import 'package:cake_wallet/generated/i18n.dart'; import 'package:cake_wallet/routes.dart'; -import 'package:cake_wallet/src/screens/support/widgets/support_tiles.dart'; import 'package:cake_wallet/src/screens/base_page.dart'; +import 'package:cake_wallet/src/widgets/option_tile.dart'; import 'package:cake_wallet/utils/device_info.dart'; import 'package:cake_wallet/view_model/support_view_model.dart'; import 'package:flutter/material.dart'; @@ -32,7 +32,7 @@ class SupportPage extends BasePage { children: [ Padding( padding: EdgeInsets.only(top: 24), - child: SupportTile( + child: OptionTile( image: imageLiveSupport, title: S.of(context).support_title_live_chat, description: S.of(context).support_description_live_chat, @@ -47,7 +47,7 @@ class SupportPage extends BasePage { ), Padding( padding: EdgeInsets.only(top: 24), - child: SupportTile( + child: OptionTile( image: imageWalletGuides, title: S.of(context).support_title_guides, description: S.of(context).support_description_guides, @@ -56,7 +56,7 @@ class SupportPage extends BasePage { ), Padding( padding: EdgeInsets.only(top: 24), - child: SupportTile( + child: OptionTile( image: imageMoreLinks, title: S.of(context).support_title_other_links, description: S.of(context).support_description_other_links, diff --git a/lib/src/screens/support/widgets/support_tiles.dart b/lib/src/widgets/option_tile.dart similarity index 82% rename from lib/src/screens/support/widgets/support_tiles.dart rename to lib/src/widgets/option_tile.dart index 7107efac6..6a2825003 100644 --- a/lib/src/screens/support/widgets/support_tiles.dart +++ b/lib/src/widgets/option_tile.dart @@ -1,9 +1,8 @@ -import 'package:cake_wallet/themes/extensions/cake_text_theme.dart'; -import 'package:cake_wallet/themes/extensions/transaction_trade_theme.dart'; +import 'package:cake_wallet/themes/extensions/option_tile_theme.dart'; import 'package:flutter/material.dart'; -class SupportTile extends StatelessWidget { - const SupportTile( +class OptionTile extends StatelessWidget { + const OptionTile( {required this.onPressed, required this.image, required this.title, @@ -45,7 +44,7 @@ class SupportTile extends StatelessWidget { style: TextStyle( fontSize: 20, fontWeight: FontWeight.w500, - color: Theme.of(context).extension()!.titleColor, + color: Theme.of(context).extension()!.titleColor, ), ), Padding( @@ -55,7 +54,7 @@ class SupportTile extends StatelessWidget { style: TextStyle( fontSize: 16, fontWeight: FontWeight.normal, - color: Theme.of(context).extension()!.detailsTitlesColor, + color: Theme.of(context).extension()!.descriptionColor, ), ), ) diff --git a/lib/store/settings_store.dart b/lib/store/settings_store.dart index f512063a4..2690dcb64 100644 --- a/lib/store/settings_store.dart +++ b/lib/store/settings_store.dart @@ -2,6 +2,7 @@ import 'dart:io'; import 'package:cake_wallet/bitcoin/bitcoin.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/cake_2fa_preset_options.dart'; import 'package:cake_wallet/entities/background_tasks.dart'; import 'package:cake_wallet/entities/exchange_api_mode.dart'; @@ -47,6 +48,7 @@ abstract class SettingsStoreBase with Store { required bool initialAppSecure, required bool initialDisableBuy, required bool initialDisableSell, + required BuyProviderType initialDefaultBuyProvider, required FiatApiMode initialFiatMode, required bool initialAllowBiometricalAuthentication, required String initialTotpSecretKey, @@ -99,6 +101,7 @@ abstract class SettingsStoreBase with Store { isAppSecure = initialAppSecure, disableBuy = initialDisableBuy, disableSell = initialDisableSell, + defaultBuyProvider = initialDefaultBuyProvider, shouldShowMarketPlaceInDashboard = initialShouldShowMarketPlaceInDashboard, exchangeStatus = initialExchangeStatus, currentTheme = initialTheme, @@ -200,6 +203,11 @@ abstract class SettingsStoreBase with Store { (bool disableSell) => sharedPreferences.setBool(PreferencesKey.disableSellKey, disableSell)); + reaction( + (_) => defaultBuyProvider, + (BuyProviderType defaultBuyProvider) => + sharedPreferences.setInt(PreferencesKey.defaultBuyProvider, defaultBuyProvider.index)); + reaction( (_) => autoGenerateSubaddressStatus, (AutoGenerateSubaddressStatus autoGenerateSubaddressStatus) => sharedPreferences.setInt( @@ -380,6 +388,9 @@ abstract class SettingsStoreBase with Store { @observable bool disableSell; + @observable + BuyProviderType defaultBuyProvider; + @observable bool allowBiometricalAuthentication; @@ -535,6 +546,7 @@ abstract class SettingsStoreBase with Store { final isAppSecure = sharedPreferences.getBool(PreferencesKey.isAppSecureKey) ?? false; final disableBuy = sharedPreferences.getBool(PreferencesKey.disableBuyKey) ?? false; final disableSell = sharedPreferences.getBool(PreferencesKey.disableSellKey) ?? false; + final defaultBuyProvider = BuyProviderType.values[sharedPreferences.getInt(PreferencesKey.defaultBuyProvider) ?? 0]; final currentFiatApiMode = FiatApiMode.deserialize( raw: sharedPreferences.getInt(PreferencesKey.currentFiatApiModeKey) ?? FiatApiMode.enabled.raw); @@ -661,6 +673,7 @@ abstract class SettingsStoreBase with Store { initialAppSecure: isAppSecure, initialDisableBuy: disableBuy, initialDisableSell: disableSell, + initialDefaultBuyProvider: defaultBuyProvider, initialFiatMode: currentFiatApiMode, initialAllowBiometricalAuthentication: allowBiometricalAuthentication, initialCake2FAPresetOptions: selectedCake2FAPreset, @@ -747,6 +760,7 @@ abstract class SettingsStoreBase with Store { isAppSecure = sharedPreferences.getBool(PreferencesKey.isAppSecureKey) ?? isAppSecure; disableBuy = sharedPreferences.getBool(PreferencesKey.disableBuyKey) ?? disableBuy; disableSell = sharedPreferences.getBool(PreferencesKey.disableSellKey) ?? disableSell; + defaultBuyProvider = BuyProviderType.values[sharedPreferences.getInt(PreferencesKey.defaultBuyProvider) ?? 0]; allowBiometricalAuthentication = sharedPreferences.getBool(PreferencesKey.allowBiometricalAuthenticationKey) ?? allowBiometricalAuthentication; diff --git a/lib/themes/dark_theme.dart b/lib/themes/dark_theme.dart index 747dc2527..4e18628fa 100644 --- a/lib/themes/dark_theme.dart +++ b/lib/themes/dark_theme.dart @@ -12,6 +12,7 @@ import 'package:cake_wallet/themes/extensions/info_theme.dart'; import 'package:cake_wallet/themes/extensions/keyboard_theme.dart'; import 'package:cake_wallet/themes/extensions/menu_theme.dart'; import 'package:cake_wallet/themes/extensions/new_wallet_theme.dart'; +import 'package:cake_wallet/themes/extensions/option_tile_theme.dart'; import 'package:cake_wallet/themes/extensions/order_theme.dart'; import 'package:cake_wallet/themes/extensions/picker_theme.dart'; import 'package:cake_wallet/themes/extensions/pin_code_theme.dart'; @@ -215,6 +216,10 @@ class DarkTheme extends ThemeBase { qrCodeColor: PaletteDark.lightBlueGrey, qrWidgetCopyButtonColor: PaletteDark.lightBlueGrey); + @override + OptionTileTheme get optionTileTheme => OptionTileTheme( + titleColor: primaryTextColor, descriptionColor: primaryTextColor, useDarkImage: false); + @override ThemeData get themeData => super.themeData.copyWith( dividerColor: PaletteDark.dividerColor, diff --git a/lib/themes/extensions/option_tile_theme.dart b/lib/themes/extensions/option_tile_theme.dart new file mode 100644 index 000000000..6c125a4ac --- /dev/null +++ b/lib/themes/extensions/option_tile_theme.dart @@ -0,0 +1,30 @@ +import 'package:flutter/material.dart'; + +class OptionTileTheme extends ThemeExtension { + final Color titleColor; + final Color descriptionColor; + final bool useDarkImage; + + OptionTileTheme( + {required this.titleColor, required this.descriptionColor, this.useDarkImage = false}); + + @override + OptionTileTheme copyWith({Color? titleColor, Color? descriptionColor, bool? useDarkImage}) => + OptionTileTheme( + titleColor: titleColor ?? this.titleColor, + descriptionColor: descriptionColor ?? this.descriptionColor, + useDarkImage: useDarkImage ?? this.useDarkImage); + + @override + OptionTileTheme lerp(ThemeExtension? other, double t) { + if (other is! OptionTileTheme) { + return this; + } + + return OptionTileTheme( + titleColor: Color.lerp(titleColor, other.titleColor, t) ?? titleColor, + descriptionColor: + Color.lerp(descriptionColor, other.descriptionColor, t) ?? descriptionColor, + useDarkImage: other.useDarkImage); + } +} diff --git a/lib/themes/high_contrast_theme.dart b/lib/themes/high_contrast_theme.dart index 0483adb38..bef959a70 100644 --- a/lib/themes/high_contrast_theme.dart +++ b/lib/themes/high_contrast_theme.dart @@ -9,6 +9,7 @@ import 'package:cake_wallet/themes/extensions/exchange_page_theme.dart'; import 'package:cake_wallet/themes/extensions/filter_theme.dart'; import 'package:cake_wallet/themes/extensions/indicator_dot_theme.dart'; import 'package:cake_wallet/themes/extensions/menu_theme.dart'; +import 'package:cake_wallet/themes/extensions/option_tile_theme.dart'; import 'package:cake_wallet/themes/extensions/picker_theme.dart'; import 'package:cake_wallet/themes/extensions/receive_page_theme.dart'; import 'package:cake_wallet/themes/extensions/send_page_theme.dart'; @@ -103,6 +104,10 @@ class HighContrastTheme extends MoneroLightTheme { ReceivePageTheme get receivePageTheme => super.receivePageTheme.copyWith( tilesTextColor: Colors.white, iconsBackgroundColor: Colors.grey, iconsColor: Colors.black); + @override + OptionTileTheme get optionTileTheme => OptionTileTheme( + titleColor: Colors.white, descriptionColor: Colors.white, useDarkImage: false); + @override ThemeData get themeData => super.themeData.copyWith( disabledColor: Colors.grey, diff --git a/lib/themes/light_theme.dart b/lib/themes/light_theme.dart index f24551e66..492b2c145 100644 --- a/lib/themes/light_theme.dart +++ b/lib/themes/light_theme.dart @@ -12,6 +12,7 @@ import 'package:cake_wallet/themes/extensions/info_theme.dart'; import 'package:cake_wallet/themes/extensions/keyboard_theme.dart'; import 'package:cake_wallet/themes/extensions/menu_theme.dart'; import 'package:cake_wallet/themes/extensions/new_wallet_theme.dart'; +import 'package:cake_wallet/themes/extensions/option_tile_theme.dart'; import 'package:cake_wallet/themes/extensions/order_theme.dart'; import 'package:cake_wallet/themes/extensions/picker_theme.dart'; import 'package:cake_wallet/themes/extensions/pin_code_theme.dart'; @@ -215,6 +216,10 @@ class LightTheme extends ThemeBase { qrCodeColor: Colors.white, qrWidgetCopyButtonColor: PaletteDark.lightBlueGrey); + @override + OptionTileTheme get optionTileTheme => OptionTileTheme( + titleColor: primaryTextColor, descriptionColor: primaryTextColor, useDarkImage: true); + @override ThemeData get themeData => super.themeData.copyWith( dividerColor: Palette.paleBlue, diff --git a/lib/themes/theme_base.dart b/lib/themes/theme_base.dart index b5f42e7de..3bba6f65f 100644 --- a/lib/themes/theme_base.dart +++ b/lib/themes/theme_base.dart @@ -12,6 +12,7 @@ import 'package:cake_wallet/themes/extensions/info_theme.dart'; import 'package:cake_wallet/themes/extensions/keyboard_theme.dart'; import 'package:cake_wallet/themes/extensions/menu_theme.dart'; import 'package:cake_wallet/themes/extensions/new_wallet_theme.dart'; +import 'package:cake_wallet/themes/extensions/option_tile_theme.dart'; import 'package:cake_wallet/themes/extensions/order_theme.dart'; import 'package:cake_wallet/themes/extensions/picker_theme.dart'; import 'package:cake_wallet/themes/extensions/pin_code_theme.dart'; @@ -114,6 +115,8 @@ abstract class ThemeBase { QRCodeTheme get qrCodeTheme; + OptionTileTheme get optionTileTheme; + ThemeData get themeData => generatedThemeData.copyWith( primaryColor: primaryColor, cardColor: containerColor, @@ -144,6 +147,7 @@ abstract class ThemeBase { accountListTheme, receivePageTheme, qrCodeTheme, + optionTileTheme ], scrollbarTheme: generatedThemeData.scrollbarTheme.copyWith( thumbColor: MaterialStateProperty.all(scrollbarTheme.thumbColor), diff --git a/lib/view_model/dashboard/dashboard_view_model.dart b/lib/view_model/dashboard/dashboard_view_model.dart index 009fe0350..9998eb8be 100644 --- a/lib/view_model/dashboard/dashboard_view_model.dart +++ b/lib/view_model/dashboard/dashboard_view_model.dart @@ -1,4 +1,5 @@ import 'package:cake_wallet/entities/auto_generate_subaddress_status.dart'; +import 'package:cake_wallet/entities/buy_provider_types.dart'; import 'package:cake_wallet/entities/exchange_api_mode.dart'; import 'package:cake_wallet/store/anonpay/anonpay_transactions_store.dart'; import 'package:cake_wallet/view_model/dashboard/anonpay_transaction_list_item.dart'; @@ -277,6 +278,9 @@ abstract class DashboardViewModelBase with Store { Map> filterItems; + + BuyProviderType get defaultBuyProvider => settingsStore.defaultBuyProvider; + bool get isBuyEnabled => settingsStore.isBitcoinBuyEnabled; bool get shouldShowYatPopup => settingsStore.shouldShowYatPopup; diff --git a/lib/view_model/settings/other_settings_view_model.dart b/lib/view_model/settings/other_settings_view_model.dart index c57af50d9..ef8abe8e6 100644 --- a/lib/view_model/settings/other_settings_view_model.dart +++ b/lib/view_model/settings/other_settings_view_model.dart @@ -1,4 +1,5 @@ import 'package:cake_wallet/bitcoin/bitcoin.dart'; +import 'package:cake_wallet/entities/buy_provider_types.dart'; import 'package:cake_wallet/entities/priority_for_wallet_type.dart'; import 'package:cake_wallet/store/settings_store.dart'; import 'package:cw_core/balance.dart'; @@ -48,6 +49,9 @@ abstract class OtherSettingsViewModelBase with Store { return priority; } + @computed + BuyProviderType get buyProviderType { return _settingsStore.defaultBuyProvider; } + String getDisplayPriority(dynamic priority) { final _priority = priority as TransactionPriority; @@ -59,6 +63,16 @@ abstract class OtherSettingsViewModelBase with Store { return priority.toString(); } + String getBuyProviderType (dynamic buyProviderType) { + final _buyProviderType = buyProviderType as BuyProviderType; + + return _buyProviderType.toString(); + } + void onDisplayPrioritySelected(TransactionPriority priority) => _settingsStore.priority[_wallet.type] = priority; + + void onBuyProviderTypeSelected(BuyProviderType buyProviderType) => + _settingsStore.defaultBuyProvider = buyProviderType; + } diff --git a/res/values/strings_ar.arb b/res/values/strings_ar.arb index ae3f669ca..7c7b0535b 100644 --- a/res/values/strings_ar.arb +++ b/res/values/strings_ar.arb @@ -682,7 +682,11 @@ "support_title_other_links": "روابط دعم أخرى", "support_description_other_links": "انضم إلى مجتمعاتنا أو تصل إلينا شركائنا من خلال أساليب أخرى", "select_destination": ".ﻲﻃﺎﻴﺘﺣﻻﺍ ﺦﺴﻨﻟﺍ ﻒﻠﻣ ﺔﻬﺟﻭ ﺪﻳﺪﺤﺗ ءﺎﺟﺮﻟﺍ", + "auto_generate_subaddresses": "تلقائي توليد subddresses", "save_to_downloads": "ﺕﻼﻳﺰﻨﺘﻟﺍ ﻲﻓ ﻆﻔﺣ", - "support_description_other_links": "انضم إلى مجتمعاتنا أو تصل إلينا شركائنا من خلال أساليب أخرى", - "auto_generate_subaddresses": "تلقائي توليد subddresses" -} + "select_buy_provider_notice": "حدد مزود شراء أعلاه. يمكنك تخطي هذه الشاشة عن طريق تعيين مزود شراء الافتراضي في إعدادات التطبيق.", + "onramper_option_description": "شراء بسرعة التشفير مع العديد من طرق الدفع. متوفر في معظم البلدان. ينتشر وتختلف الرسوم.", + "default_buy_provider": "مزود شراء الافتراضي", + "ask_each_time": "اسأل في كل مرة", + "buy_provider_unavailable": "مزود حاليا غير متوفر." +} \ No newline at end of file diff --git a/res/values/strings_bg.arb b/res/values/strings_bg.arb index ac2a08088..9f4c21c44 100644 --- a/res/values/strings_bg.arb +++ b/res/values/strings_bg.arb @@ -679,5 +679,10 @@ "support_title_other_links": "Други връзки за поддръжка", "support_description_other_links": "Присъединете се към нашите общности или се свържете с нас нашите партньори чрез други методи", "select_destination": "Моля, изберете дестинация за архивния файл.", - "save_to_downloads": "Запазване в Изтегляния" -} + "save_to_downloads": "Запазване в Изтегляния", + "select_buy_provider_notice": "Изберете доставчик на покупка по -горе. Можете да пропуснете този екран, като зададете вашия доставчик по подразбиране по подразбиране в настройките на приложението.", + "onramper_option_description": "Бързо купувайте криптовалута с много методи за плащане. Предлага се в повечето страни. Разпространенията и таксите варират.", + "default_buy_provider": "Доставчик по подразбиране купува", + "ask_each_time": "Питайте всеки път", + "buy_provider_unavailable": "Понастоящем доставчик не е наличен." +} \ No newline at end of file diff --git a/res/values/strings_cs.arb b/res/values/strings_cs.arb index 38e01ca6b..82d0fd9ed 100644 --- a/res/values/strings_cs.arb +++ b/res/values/strings_cs.arb @@ -679,5 +679,10 @@ "support_title_other_links": "Další odkazy na podporu", "support_description_other_links": "Připojte se k našim komunitám nebo se k nám oslovte další metody", "select_destination": "Vyberte cíl pro záložní soubor.", - "save_to_downloads": "Uložit do Stažených souborů" -} + "save_to_downloads": "Uložit do Stažených souborů", + "select_buy_provider_notice": "Vyberte výše uvedeného poskytovatele nákupu. Tuto obrazovku můžete přeskočit nastavením výchozího poskytovatele nákupu v nastavení aplikace.", + "onramper_option_description": "Rychle si koupte krypto s mnoha metodami plateb. K dispozici ve většině zemí. Rozpětí a poplatky se liší.", + "default_buy_provider": "Výchozí poskytovatel nákupu", + "ask_each_time": "Zeptejte se pokaždé", + "buy_provider_unavailable": "Poskytovatel aktuálně nedostupný." +} \ No newline at end of file diff --git a/res/values/strings_de.arb b/res/values/strings_de.arb index afc5fba82..e5bb7ce51 100644 --- a/res/values/strings_de.arb +++ b/res/values/strings_de.arb @@ -364,8 +364,8 @@ "enter_your_note": "Geben Sie Ihre Bemerkung ein…", "note_optional": "Bemerkung (optional)", "note_tap_to_change": "Bemerkung (zum Ändern tippen)", - "view_in_block_explorer": "View in Block Explorer", - "view_transaction_on": "View Transaction on ", + "view_in_block_explorer": "In Block Explorer anzeigen", + "view_transaction_on": "Anzeigen der Transaktion auf ", "transaction_key": "Transaktionsschlüssel", "confirmations": "Bestätigungen", "recipient_address": "Empfängeradresse", @@ -405,11 +405,11 @@ "moonpay_alert_text": "Der Wert des Betrags muss größer oder gleich ${minAmount} ${fiatCurrency} sein", "outdated_electrum_wallet_receive_warning": "Wenn diese Wallet einen 12-Wort-Seed hat und in Cake erstellt wurde, zahlen Sie KEINE Bitcoins in diese Wallet ein. Alle auf diese Wallet übertragenen BTC können verloren gehen. Erstellen Sie eine neue 24-Wort-Wallet (tippen Sie auf das Menü oben rechts, wählen Sie Wallets, wählen Sie Neue Wallet erstellen und dann Bitcoin) und verschieben Sie Ihre BTC SOFORT dorthin. Neue (24-Wort-)BTC-Wallets von Cake sind sicher", "do_not_show_me": "Zeig mir das nicht noch einmal", - "unspent_coins_title": "Nicht ausgegebene Münzen", - "unspent_coins_details_title": "Details zu nicht ausgegebenen Münzen", + "unspent_coins_title": "Nicht ausgegebene Coins", + "unspent_coins_details_title": "Details zu nicht ausgegebenen Coins", "freeze": "Einfrieren", "frozen": "Gefroren", - "coin_control": "Münzkontrolle (optional)", + "coin_control": "Coin Control (optional)", "address_detected": "Adresse erkannt", "address_from_domain": "Diese Adresse ist von ${domain} auf Unstoppable Domains", "add_receiver": "Fügen Sie einen weiteren Empfänger hinzu (optional)", @@ -551,7 +551,7 @@ "custom_redeem_amount": "Benutzerdefinierter Einlösungsbetrag", "add_custom_redemption": "Benutzerdefinierte Einlösung hinzufügen", "remaining": "Rest", - "delete_wallet": "Geldbörse löschen", + "delete_wallet": "Wallet löschen", "delete_wallet_confirm_message": "Sind Sie sicher, dass Sie das ${wallet_name} Wallet löschen möchten?", "low_fee": "Niedrige Gebühr", "low_fee_alert": "Sie verwenden derzeit eine niedrige Netzwerkgebührenpriorität. Dies kann zu langen Wartezeiten, unterschiedlichen Kursen oder stornierten Trades führen. Wir empfehlen, für ein besseres Erlebnis eine höhere Gebühr festzulegen.", @@ -560,7 +560,7 @@ "do_not_share_warning_text": "Teilen Sie diese nicht mit anderen, einschließlich Support.\n\nIhr Geld kann und wird gestohlen werden!", "help": "hilfe", "all_transactions": "Alle Transaktionen", - "all_trades": "Alle Gewerke", + "all_trades": "Alle Trades", "connection_sync": "Verbindung und Synchronisierung", "security_and_backup": "Sicherheit und Datensicherung", "create_backup": "Backup erstellen", @@ -583,7 +583,7 @@ "unmatched_currencies": "Die Währung Ihres aktuellen Wallets stimmt nicht mit der des gescannten QR überein", "orbot_running_alert": "Bitte stellen Sie sicher, dass Orbot läuft, bevor Sie sich mit diesem Knoten verbinden.", "contact_list_contacts": "Kontakte", - "contact_list_wallets": "Meine Geldbörsen", + "contact_list_wallets": "Meine Wallets", "bitcoin_payments_require_1_confirmation": "Bitcoin-Zahlungen erfordern 1 Bestätigung, was 20 Minuten oder länger dauern kann. Danke für Ihre Geduld! Sie erhalten eine E-Mail, wenn die Zahlung bestätigt ist.", "send_to_this_address": "Senden Sie ${currency} ${tag}an diese Adresse", "arrive_in_this_address": "${currency} ${tag}wird an dieser Adresse ankommen", @@ -592,8 +592,8 @@ "scan_qr_code": "QR-Code scannen", "cold_or_recover_wallet": "Fügen Sie eine Cold Wallet hinzu oder stellen Sie eine Paper Wallet wieder her", "please_wait": "Warten Sie mal", - "sweeping_wallet": "Kehre Geldbörse", - "sweeping_wallet_alert": "Das sollte nicht lange dauern. VERLASSEN SIE DIESEN BILDSCHIRM NICHT, ANDERNFALLS KÖNNEN DIE SWEPT-GELDER VERLOREN GEHEN", + "sweeping_wallet": "Wallet leeren", + "sweeping_wallet_alert": "Das sollte nicht lange dauern. VERLASSEN SIE DIESEN BILDSCHIRM NICHT, ANDERNFALLS KÖNNEN DIE GELDER VERLOREN GEHEN", "decimal_places_error": "Zu viele Nachkommastellen", "edit_node": "Knoten bearbeiten", "frozen_balance": "Gefrorenes Guthaben", @@ -630,7 +630,7 @@ "add_secret_code": "Fügen Sie diesen Geheimcode einem anderen Gerät hinzu", "totp_secret_code": "TOTP-Geheimcode", "important_note": "Wichtiger Hinweis", - "setup_2fa_text": "Cake 2FA ist NICHT so sicher wie eine Kühllagerung. 2FA schützt vor grundlegenden Arten von Angriffen, z. B. wenn Ihr Freund Ihren Fingerabdruck bereitstellt, während Sie schlafen.\n\n Cake 2FA schützt NICHT vor einem kompromittierten Gerät durch einen raffinierten Angreifer.\n\n Wenn Sie den Zugriff auf Ihre 2FA-Codes verlieren , VERLIEREN SIE DEN ZUGANG ZU DIESEM WALLET. Sie müssen Ihre Wallet aus mnemonic Seed wiederherstellen. SIE MÜSSEN DESHALB IHRE MNEMONISCHEN SEEDS SICHERN! Außerdem kann jemand mit Zugriff auf Ihre mnemonischen Seed(s) Ihr Geld stehlen und Cake 2FA umgehen.\n\n Cake-Supportmitarbeiter können Ihnen nicht helfen, wenn Sie den Zugriff auf Ihre mnemonischen Seed(s) verlieren, da Cake Wallet eine Wallet ohne treuhänderische Verwahrung ist.", + "setup_2fa_text": "Cake 2FA ist NICHT so sicher wie eine Cold Wallet. 2FA schützt vor grundlegenden Arten von Angriffen, z.B. wenn Ihr Freund Ihren Fingerabdruck verwendet, während Sie schlafen.\n\n Cake 2FA schützt NICHT vor einem kompromittierten Gerät durch einen raffinierten Angreifer.\n\n Wenn Sie den Zugriff auf Ihre 2FA-Codes verlieren , VERLIEREN SIE DEN ZUGANG ZU DIESEM WALLET. Sie müssen Ihre Wallet aus mnemonic Seed wiederherstellen. SIE MÜSSEN DESHALB IHRE MNEMONISCHEN SEEDS SICHERN! Außerdem kann jemand mit Zugriff auf Ihre mnemonischen Seed(s) Ihr Geld stehlen und Cake 2FA umgehen.\n\n Cake-Supportmitarbeiter können Ihnen nicht helfen, wenn Sie den Zugriff auf Ihre mnemonischen Seed(s) verlieren, da Cake Wallet eine Wallet ohne treuhänderische Verwahrung ist.", "setup_totp_recommended": "TOTP einrichten (empfohlen)", "disable_buy": "Kaufaktion deaktivieren", "disable_sell": "Verkaufsaktion deaktivieren", @@ -641,7 +641,7 @@ "matrix_green_dark_theme": "Matrix Green Dark Theme", "monero_light_theme": "Monero Light-Thema", "auto_generate_subaddresses": "Unteradressen automatisch generieren", - "cake_2fa_preset" : "Cake 2FA-Voreinstellung", + "cake_2fa_preset": "Cake 2FA-Voreinstellung", "narrow": "Eng", "normal": "Normal", "aggressive": "Übereifrig", @@ -675,7 +675,7 @@ "alphabetical": "Alphabetisch", "generate_name": "Namen generieren", "balance_page": "Balance-Seite", - "share": "Aktie", + "share": "Teilen", "slidable": "Verschiebbar", "manage_nodes": "Knoten verwalten", "etherscan_history": "Etherscan-Geschichte", @@ -687,5 +687,10 @@ "support_title_other_links": "Andere Support-Links", "support_description_other_links": "Treten Sie unseren Communities bei oder erreichen Sie uns oder unsere Partner über andere Methoden", "select_destination": "Bitte wählen Sie das Ziel für die Sicherungsdatei aus.", - "save_to_downloads": "Unter „Downloads“ speichern" -} + "save_to_downloads": "Unter „Downloads“ speichern", + "select_buy_provider_notice": "Wählen Sie oben einen Anbieter kaufen. Sie können diese Seite überspringen, indem Sie Ihren Standard-Kaufanbieter in den App-Einstellungen festlegen.", + "onramper_option_description": "Kaufen Sie schnell Krypto mit vielen Zahlungsmethoden. In den meisten Ländern erhältlich. Spreads und Gebühren variieren.", + "default_buy_provider": "Standard-Kaufanbieter", + "ask_each_time": "Jedes Mal fragen", + "buy_provider_unavailable": "Anbieter derzeit nicht verfügbar." +} \ No newline at end of file diff --git a/res/values/strings_en.arb b/res/values/strings_en.arb index 4698701de..8cf6e2acb 100644 --- a/res/values/strings_en.arb +++ b/res/values/strings_en.arb @@ -687,5 +687,11 @@ "support_title_other_links": "Other support links", "support_description_other_links": "Join our communities or reach us our our partners through other methods", "select_destination": "Please select destination for the backup file.", - "save_to_downloads": "Save to Downloads" -} + "save_to_downloads": "Save to Downloads", + "select_buy_provider_notice": "Select a buy provider above. You can skip this screen by setting your default buy provider in app settings.", + "onramper_option_description": "Quickly buy crypto with many payment methods. Available in most countries. Spreads and fees vary.", + "default_buy_provider": "Default Buy Provider", + "ask_each_time": "Ask each time", + "robinhood_option_description": "Buy and transfer instantly using your debit card, bank account, or Robinhood balance. USA only.", + "buy_provider_unavailable": "Provider currently unavailable." +} \ No newline at end of file diff --git a/res/values/strings_es.arb b/res/values/strings_es.arb index 8a4519255..d84b21b94 100644 --- a/res/values/strings_es.arb +++ b/res/values/strings_es.arb @@ -687,5 +687,10 @@ "support_title_other_links": "Otros enlaces de soporte", "support_description_other_links": "Únase a nuestras comunidades o comuníquese con nosotros nuestros socios a través de otros métodos", "select_destination": "Seleccione el destino del archivo de copia de seguridad.", - "save_to_downloads": "Guardar en Descargas" -} + "save_to_downloads": "Guardar en Descargas", + "select_buy_provider_notice": "Seleccione un proveedor de compra arriba. Puede omitir esta pantalla configurando su proveedor de compra predeterminado en la configuración de la aplicación.", + "onramper_option_description": "Compre rápidamente cripto con muchos métodos de pago. Disponible en la mayoría de los países. Los diferenciales y las tarifas varían.", + "default_buy_provider": "Proveedor de compra predeterminado", + "ask_each_time": "Pregunta cada vez", + "buy_provider_unavailable": "Proveedor actualmente no disponible." +} \ No newline at end of file diff --git a/res/values/strings_fr.arb b/res/values/strings_fr.arb index 357a73110..2daea4733 100644 --- a/res/values/strings_fr.arb +++ b/res/values/strings_fr.arb @@ -687,5 +687,10 @@ "support_title_other_links": "Autres liens d'assistance", "support_description_other_links": "Rejoignez nos communautés ou contactez-nous nos partenaires à travers d'autres méthodes", "select_destination": "Veuillez sélectionner la destination du fichier de sauvegarde.", - "save_to_downloads": "Enregistrer dans les téléchargements" -} + "save_to_downloads": "Enregistrer dans les téléchargements", + "select_buy_provider_notice": "Sélectionnez un fournisseur d'achat ci-dessus. Vous pouvez ignorer cet écran en définissant votre fournisseur d'achat par défaut dans les paramètres de l'application.", + "onramper_option_description": "Achetez rapidement la crypto avec de nombreux méthodes de paiement. Disponible dans la plupart des pays. Les écarts et les frais varient.", + "default_buy_provider": "Fournisseur d'achat par défaut", + "ask_each_time": "Demandez à chaque fois", + "buy_provider_unavailable": "Fournisseur actuellement indisponible." +} \ No newline at end of file diff --git a/res/values/strings_ha.arb b/res/values/strings_ha.arb index d344bfb15..666664ec2 100644 --- a/res/values/strings_ha.arb +++ b/res/values/strings_ha.arb @@ -665,5 +665,10 @@ "support_title_other_links": "Sauran hanyoyin tallafi", "support_description_other_links": "Kasance tare da al'ummominmu ko kuma ka kai mu abokanmu ta hanyar wasu hanyoyi", "select_destination": "Da fatan za a zaɓi wurin da za a yi wa madadin fayil ɗin.", - "save_to_downloads": "Ajiye zuwa Zazzagewa" -} + "save_to_downloads": "Ajiye zuwa Zazzagewa", + "select_buy_provider_notice": "Zaɓi mai ba da kyauta a sama. Zaka iya tsallake wannan allon ta hanyar saita mai ba da isasshen busasshen mai ba da isasshen busasshiyar saiti.", + "onramper_option_description": "Da sauri sayi Crypto tare da hanyoyin biyan kuɗi da yawa. Akwai a yawancin ƙasashe. Yaduwa da kudade sun bambanta.", + "default_buy_provider": "Tsohuwar Siyarwa", + "ask_each_time": "Tambaya kowane lokaci", + "buy_provider_unavailable": "Mai ba da kyauta a halin yanzu babu." +} \ No newline at end of file diff --git a/res/values/strings_hi.arb b/res/values/strings_hi.arb index e9ee219fe..9504baf36 100644 --- a/res/values/strings_hi.arb +++ b/res/values/strings_hi.arb @@ -687,5 +687,10 @@ "support_title_other_links": "अन्य समर्थन लिंक", "support_description_other_links": "हमारे समुदायों में शामिल हों या अन्य तरीकों के माध्यम से हमारे साथी तक पहुंचें", "select_destination": "कृपया बैकअप फ़ाइल के लिए गंतव्य का चयन करें।", - "save_to_downloads": "डाउनलोड में सहेजें" -} + "save_to_downloads": "डाउनलोड में सहेजें", + "select_buy_provider_notice": "ऊपर एक खरीद प्रदाता का चयन करें। आप इस स्क्रीन को ऐप सेटिंग्स में अपना डिफ़ॉल्ट बाय प्रदाता सेट करके छोड़ सकते हैं।", + "onramper_option_description": "जल्दी से कई भुगतान विधियों के साथ क्रिप्टो खरीदें। अधिकांश देशों में उपलब्ध है। फैलता है और फीस अलग -अलग होती है।", + "default_buy_provider": "डिफ़ॉल्ट खरीद प्रदाता", + "ask_each_time": "हर बार पूछें", + "buy_provider_unavailable": "वर्तमान में प्रदाता अनुपलब्ध है।" +} \ No newline at end of file diff --git a/res/values/strings_hr.arb b/res/values/strings_hr.arb index 91176248d..08549439a 100644 --- a/res/values/strings_hr.arb +++ b/res/values/strings_hr.arb @@ -687,5 +687,10 @@ "support_title_other_links": "Ostale veze za podršku", "support_description_other_links": "Pridružite se našim zajednicama ili nam dosegnu naše partnere drugim metodama", "select_destination": "Odaberite odredište za datoteku sigurnosne kopije.", - "save_to_downloads": "Spremi u Preuzimanja" -} + "save_to_downloads": "Spremi u Preuzimanja", + "select_buy_provider_notice": "Odaberite gornji davatelj kupnje. Ovaj zaslon možete preskočiti postavljanjem zadanog davatelja usluga kupnje u postavkama aplikacija.", + "onramper_option_description": "Brzo kupite kriptovalute s mnogim načinima plaćanja. Dostupno u većini zemalja. Širenja i naknade variraju.", + "default_buy_provider": "Zadani davatelj kupnje", + "ask_each_time": "Pitajte svaki put", + "buy_provider_unavailable": "Davatelj trenutno nije dostupan." +} \ No newline at end of file diff --git a/res/values/strings_id.arb b/res/values/strings_id.arb index daebfd579..031777aea 100644 --- a/res/values/strings_id.arb +++ b/res/values/strings_id.arb @@ -675,5 +675,10 @@ "support_title_other_links": "Tautan dukungan lainnya", "support_description_other_links": "Bergabunglah dengan komunitas kami atau hubungi kami mitra kami melalui metode lain", "select_destination": "Silakan pilih tujuan untuk file cadangan.", - "save_to_downloads": "Simpan ke Unduhan" -} + "save_to_downloads": "Simpan ke Unduhan", + "select_buy_provider_notice": "Pilih penyedia beli di atas. Anda dapat melewatkan layar ini dengan mengatur penyedia pembelian default Anda di pengaturan aplikasi.", + "onramper_option_description": "Beli crypto dengan cepat dengan banyak metode pembayaran. Tersedia di sebagian besar negara. Spread dan biaya bervariasi.", + "default_buy_provider": "Penyedia beli default", + "ask_each_time": "Tanyakan setiap kali", + "buy_provider_unavailable": "Penyedia saat ini tidak tersedia." +} \ No newline at end of file diff --git a/res/values/strings_it.arb b/res/values/strings_it.arb index 691abd15c..b14531b75 100644 --- a/res/values/strings_it.arb +++ b/res/values/strings_it.arb @@ -687,5 +687,10 @@ "support_title_other_links": "Altri collegamenti di supporto", "support_description_other_links": "Unisciti alle nostre comunità o raggiungici i nostri partner attraverso altri metodi", "select_destination": "Seleziona la destinazione per il file di backup.", - "save_to_downloads": "Salva in Download" -} + "save_to_downloads": "Salva in Download", + "select_buy_provider_notice": "Seleziona un fornitore di acquisto sopra. È possibile saltare questa schermata impostando il provider di acquisto predefinito nelle impostazioni dell'app.", + "onramper_option_description": "Acquista rapidamente la criptovaluta con molti metodi di pagamento. Disponibile nella maggior parte dei paesi. Gli spread e le commissioni variano.", + "default_buy_provider": "Provider di acquisto predefinito", + "ask_each_time": "Chiedi ogni volta", + "buy_provider_unavailable": "Provider attualmente non disponibile." +} \ No newline at end of file diff --git a/res/values/strings_ja.arb b/res/values/strings_ja.arb index 254d7bbba..baf9b8a6d 100644 --- a/res/values/strings_ja.arb +++ b/res/values/strings_ja.arb @@ -686,6 +686,11 @@ "support_title_other_links": "その他のサポートリンク", "support_description_other_links": "私たちのコミュニティに参加するか、他の方法を通して私たちのパートナーに連絡してください", "select_destination": "バックアップファイルの保存先を選択してください。", + "auto_generate_subaddresses": "Autoはサブアドレスを生成します", "save_to_downloads": "ダウンロードに保存", - "auto_generate_subaddresses": "Autoはサブアドレスを生成します" -} + "select_buy_provider_notice": "上記の購入プロバイダーを選択してください。デフォルトの購入プロバイダーをアプリ設定で設定して、この画面をスキップできます。", + "onramper_option_description": "多くの支払い方法で暗号をすばやく購入してください。ほとんどの国で利用可能です。スプレッドと料金は異なります。", + "default_buy_provider": "デフォルトの購入プロバイダー", + "ask_each_time": "毎回尋ねてください", + "buy_provider_unavailable": "現在、プロバイダーは利用できません。" +} \ No newline at end of file diff --git a/res/values/strings_ko.arb b/res/values/strings_ko.arb index 63a446fa3..3b9ed1e74 100644 --- a/res/values/strings_ko.arb +++ b/res/values/strings_ko.arb @@ -686,6 +686,11 @@ "support_title_other_links": "다른 지원 링크", "support_description_other_links": "다른 방법을 통해 커뮤니티에 가입하거나 파트너에게 연락하십시오.", "select_destination": "백업 파일의 대상을 선택하십시오.", + "auto_generate_subaddresses": "자동 생성 서브 아드 드레스", "save_to_downloads": "다운로드에 저장", - "auto_generate_subaddresses": "자동 생성 서브 아드 드레스" -} + "select_buy_provider_notice": "위의 구매 제공자를 선택하십시오. 앱 설정에서 기본 구매 제공자를 설정 하여이 화면을 건너 뛸 수 있습니다.", + "onramper_option_description": "많은 결제 방법으로 암호화를 신속하게 구입하십시오. 대부분의 국가에서 사용할 수 있습니다. 스프레드와 수수료는 다양합니다.", + "default_buy_provider": "기본 구매 제공자", + "ask_each_time": "매번 물어보십시오", + "buy_provider_unavailable": "제공자는 현재 사용할 수 없습니다." +} \ No newline at end of file diff --git a/res/values/strings_my.arb b/res/values/strings_my.arb index b131e66d8..04d2f58ef 100644 --- a/res/values/strings_my.arb +++ b/res/values/strings_my.arb @@ -684,6 +684,11 @@ "support_title_other_links": "အခြားအထောက်အပံ့လင့်များ", "support_description_other_links": "ကျွန်ုပ်တို့၏လူမှုအသိုင်းအဝိုင်းများသို့ 0 င်ရောက်ပါ", "select_destination": "အရန်ဖိုင်အတွက် ဦးတည်ရာကို ရွေးပါ။", + "auto_generate_subaddresses": "အော်တို Generate Subaddresses", "save_to_downloads": "ဒေါင်းလုဒ်များထံ သိမ်းဆည်းပါ။", - "auto_generate_subaddresses": "အော်တို Generate Subaddresses" -} + "select_buy_provider_notice": "အပေါ်ကဝယ်သူတစ် ဦး ကိုရွေးချယ်ပါ။ သင်၏ default 0 ယ်သူအား app settings တွင် setting လုပ်ခြင်းဖြင့်ဤ screen ကိုကျော်သွားနိုင်သည်။", + "onramper_option_description": "ငွေပေးချေမှုနည်းလမ်းများစွာဖြင့် Crypto ကိုလျင်မြန်စွာ 0 ယ်ပါ။ နိုင်ငံအများစုတွင်ရရှိနိုင်ပါသည်။ ဖြန့်ဖြူးနှင့်အခကြေးငွေကွဲပြားခြားနားသည်။", + "default_buy_provider": "Default Provider ကိုဝယ်ပါ", + "ask_each_time": "တစ်ခုချင်းစီကိုအချိန်မေးပါ", + "buy_provider_unavailable": "လက်ရှိတွင်လက်ရှိမရနိုင်ပါ။" +} \ No newline at end of file diff --git a/res/values/strings_nl.arb b/res/values/strings_nl.arb index fcd683fd1..676653dcd 100644 --- a/res/values/strings_nl.arb +++ b/res/values/strings_nl.arb @@ -687,5 +687,10 @@ "support_title_other_links": "Andere ondersteuningslinks", "support_description_other_links": "Word lid van onze gemeenschappen of bereik ons ​​onze partners via andere methoden", "select_destination": "Selecteer de bestemming voor het back-upbestand.", - "save_to_downloads": "Opslaan in downloads" -} + "save_to_downloads": "Opslaan in downloads", + "select_buy_provider_notice": "Selecteer hierboven een koopprovider. U kunt dit scherm overslaan door uw standaard kopenprovider in te stellen in app -instellingen.", + "onramper_option_description": "Koop snel crypto met veel betaalmethoden. Beschikbaar in de meeste landen. Spreads en vergoedingen variëren.", + "default_buy_provider": "Standaard Koopprovider", + "ask_each_time": "Vraag het elke keer", + "buy_provider_unavailable": "Provider momenteel niet beschikbaar." +} \ No newline at end of file diff --git a/res/values/strings_pl.arb b/res/values/strings_pl.arb index 17745ccb8..cae3d0075 100644 --- a/res/values/strings_pl.arb +++ b/res/values/strings_pl.arb @@ -687,5 +687,10 @@ "support_title_other_links": "Inne linki wsparcia", "support_description_other_links": "Dołącz do naszych społeczności lub skontaktuj się z nami naszymi partnerami za pomocą innych metod", "select_destination": "Wybierz miejsce docelowe dla pliku kopii zapasowej.", - "save_to_downloads": "Zapisz w Pobranych" -} + "save_to_downloads": "Zapisz w Pobranych", + "select_buy_provider_notice": "Wybierz powyższe dostawcę zakupu. Możesz pominąć ten ekran, ustawiając domyślnego dostawcę zakupu w ustawieniach aplikacji.", + "onramper_option_description": "Szybko kup kryptowaluty z wieloma metodami płatności. Dostępne w większości krajów. Spready i opłaty różnią się.", + "default_buy_provider": "Domyślny dostawca zakupu", + "ask_each_time": "Zapytaj za każdym razem", + "buy_provider_unavailable": "Dostawca obecnie niedostępny." +} \ No newline at end of file diff --git a/res/values/strings_pt.arb b/res/values/strings_pt.arb index 2148e6905..46c62c590 100644 --- a/res/values/strings_pt.arb +++ b/res/values/strings_pt.arb @@ -686,5 +686,10 @@ "support_title_other_links": "Outros links de suporte", "support_description_other_links": "Junte -se às nossas comunidades ou chegue a nós nossos parceiros por meio de outros métodos", "select_destination": "Selecione o destino para o arquivo de backup.", - "save_to_downloads": "Salvar em Downloads" -} + "save_to_downloads": "Salvar em Downloads", + "select_buy_provider_notice": "Selecione um provedor de compra acima. Você pode pular esta tela definindo seu provedor de compra padrão nas configurações de aplicativos.", + "onramper_option_description": "Compre rapidamente criptografia com muitos métodos de pagamento. Disponível na maioria dos países. Os spreads e taxas variam.", + "default_buy_provider": "Provedor de compra padrão", + "ask_each_time": "Pergunte cada vez", + "buy_provider_unavailable": "Provedor atualmente indisponível." +} \ No newline at end of file diff --git a/res/values/strings_ru.arb b/res/values/strings_ru.arb index e229ddee9..523b4c77e 100644 --- a/res/values/strings_ru.arb +++ b/res/values/strings_ru.arb @@ -686,6 +686,11 @@ "support_title_other_links": "Другие ссылки на поддержку", "support_description_other_links": "Присоединяйтесь к нашим сообществам или охватите нас наших партнеров с помощью других методов", "select_destination": "Пожалуйста, выберите место для файла резервной копии.", + "auto_generate_subaddresses": "Авто генерируйте Subaddresses", "save_to_downloads": "Сохранить в загрузках", - "auto_generate_subaddresses": "Авто генерируйте Subaddresses" -} + "select_buy_provider_notice": "Выберите поставщика покупки выше. Вы можете пропустить этот экран, установив поставщика покупки по умолчанию в настройках приложения.", + "onramper_option_description": "Быстро купите крипто со многими способами оплаты. Доступно в большинстве стран. Спреды и сборы различаются.", + "default_buy_provider": "По умолчанию поставщик покупки", + "ask_each_time": "Спросите каждый раз", + "buy_provider_unavailable": "Поставщик в настоящее время недоступен." +} \ No newline at end of file diff --git a/res/values/strings_th.arb b/res/values/strings_th.arb index 100929da2..68c13dd2d 100644 --- a/res/values/strings_th.arb +++ b/res/values/strings_th.arb @@ -684,6 +684,11 @@ "support_title_other_links": "ลิงค์สนับสนุนอื่น ๆ", "support_description_other_links": "เข้าร่วมชุมชนของเราหรือเข้าถึงเราพันธมิตรของเราผ่านวิธีการอื่น ๆ", "select_destination": "โปรดเลือกปลายทางสำหรับไฟล์สำรอง", + "auto_generate_subaddresses": "Auto สร้าง subaddresses", "save_to_downloads": "บันทึกลงดาวน์โหลด", - "auto_generate_subaddresses": "Auto สร้าง subaddresses" -} + "select_buy_provider_notice": "เลือกผู้ให้บริการซื้อด้านบน คุณสามารถข้ามหน้าจอนี้ได้โดยการตั้งค่าผู้ให้บริการซื้อเริ่มต้นในการตั้งค่าแอป", + "onramper_option_description": "ซื้อ crypto อย่างรวดเร็วด้วยวิธีการชำระเงินจำนวนมาก มีให้บริการในประเทศส่วนใหญ่ สเปรดและค่าธรรมเนียมแตกต่างกันไป", + "default_buy_provider": "ผู้ให้บริการซื้อเริ่มต้น", + "ask_each_time": "ถามทุกครั้ง", + "buy_provider_unavailable": "ผู้ให้บริการไม่สามารถใช้งานได้ในปัจจุบัน" +} \ No newline at end of file diff --git a/res/values/strings_tr.arb b/res/values/strings_tr.arb index e74d1a281..2694bb1bc 100644 --- a/res/values/strings_tr.arb +++ b/res/values/strings_tr.arb @@ -685,5 +685,10 @@ "support_title_other_links": "Diğer destek bağlantıları", "support_description_other_links": "Topluluklarımıza katılın veya ortaklarımıza diğer yöntemlerle bize ulaşın", "select_destination": "Lütfen yedekleme dosyası için hedef seçin.", - "save_to_downloads": "İndirilenlere Kaydet" -} + "save_to_downloads": "İndirilenlere Kaydet", + "select_buy_provider_notice": "Yukarıda bir satın alma sağlayıcısı seçin. App ayarlarında varsayılan satın alma sağlayıcınızı ayarlayarak bu ekranı atlayabilirsiniz.", + "onramper_option_description": "Birçok ödeme yöntemi ile hızlı bir şekilde kripto satın alın. Çoğu ülkede mevcuttur. Forma ve ücretler değişir.", + "default_buy_provider": "Varsayılan Satın Alma Sağlayıcısı", + "ask_each_time": "Her seferinde sor", + "buy_provider_unavailable": "Sağlayıcı şu anda kullanılamıyor." +} \ No newline at end of file diff --git a/res/values/strings_uk.arb b/res/values/strings_uk.arb index 313f6aeb0..38fe226b1 100644 --- a/res/values/strings_uk.arb +++ b/res/values/strings_uk.arb @@ -687,5 +687,10 @@ "support_title_other_links": "Інші посилання на підтримку", "support_description_other_links": "Приєднуйтесь до наших спільнот або досягайте нас нашими партнерами іншими методами", "select_destination": "Виберіть місце призначення для файлу резервної копії.", - "save_to_downloads": "Зберегти до завантажень" -} + "save_to_downloads": "Зберегти до завантажень", + "select_buy_provider_notice": "Виберіть постачальника купівлі вище. Ви можете пропустити цей екран, встановивши свого постачальника купівлі за замовчуванням у налаштуваннях додатків.", + "onramper_option_description": "Швидко купуйте криптовалюту з багатьма методами оплати. Доступний у більшості країн. Поширення та збори різняться.", + "default_buy_provider": "Постачальник покупки за замовчуванням", + "ask_each_time": "Запитайте кожен раз", + "buy_provider_unavailable": "В даний час постачальник недоступний." +} \ No newline at end of file diff --git a/res/values/strings_ur.arb b/res/values/strings_ur.arb index b8e0c0a00..1ecdb5c0e 100644 --- a/res/values/strings_ur.arb +++ b/res/values/strings_ur.arb @@ -678,6 +678,11 @@ "support_title_other_links": "دوسرے سپورٹ لنکس", "support_description_other_links": "ہماری برادریوں میں شامل ہوں یا دوسرے طریقوں سے ہمارے شراکت داروں تک پہنچیں", "select_destination": "۔ﮟﯾﺮﮐ ﺏﺎﺨﺘﻧﺍ ﺎﮐ ﻝﺰﻨﻣ ﮯﯿﻟ ﮯﮐ ﻞﺋﺎﻓ ﭖﺍ ﮏﯿﺑ ﻡﺮﮐ ﮦﺍﺮﺑ", + "auto_generate_subaddresses": "آٹو سب ایڈریس تیار کرتا ہے", "save_to_downloads": "۔ﮟﯾﺮﮐ ﻅﻮﻔﺤﻣ ﮟﯿﻣ ﺯﮈﻮﻟ ﻥﺅﺍﮈ", - "auto_generate_subaddresses": "آٹو سب ایڈریس تیار کرتا ہے" -} + "select_buy_provider_notice": "اوپر خریدنے والا خریدنے والا منتخب کریں۔ آپ ایپ کی ترتیبات میں اپنے پہلے سے طے شدہ خریدنے والے کو ترتیب دے کر اس اسکرین کو چھوڑ سکتے ہیں۔", + "onramper_option_description": "ادائیگی کے بہت سے طریقوں سے جلدی سے کرپٹو خریدیں۔ زیادہ تر ممالک میں دستیاب ہے۔ پھیلاؤ اور فیس مختلف ہوتی ہے۔", + "default_buy_provider": "پہلے سے طے شدہ خریدنے والا", + "ask_each_time": "ہر بار پوچھیں", + "buy_provider_unavailable": "فراہم کنندہ فی الحال دستیاب نہیں ہے۔" +} \ No newline at end of file diff --git a/res/values/strings_yo.arb b/res/values/strings_yo.arb index 143fd0ccd..62909f128 100644 --- a/res/values/strings_yo.arb +++ b/res/values/strings_yo.arb @@ -680,6 +680,11 @@ "matrix_green_dark_theme": "Matrix Green Dark Akori", "monero_light_theme": "Monero Light Akori", "select_destination": "Jọwọ yan ibi ti o nlo fun faili afẹyinti.", + "auto_generate_subaddresses": "Aṣiṣe Ibi-Afọwọkọ", "save_to_downloads": "Fipamọ si Awọn igbasilẹ", - "auto_generate_subaddresses": "Aṣiṣe Ibi-Afọwọkọ" -} + "select_buy_provider_notice": "Yan olupese Ra loke. O le skii iboju yii nipa ṣiṣeto olupese rẹ ni awọn eto App.", + "onramper_option_description": "Ni kiakia Ra Crypto pẹlu ọpọlọpọ awọn ọna isanwo. Wa ni ọpọlọpọ awọn orilẹ-ede. Itankale ati awọn idiyele yatọ.", + "default_buy_provider": "Aiyipada Ra Olupese", + "ask_each_time": "Beere lọwọ kọọkan", + "buy_provider_unavailable": "Olupese lọwọlọwọ ko si." +} \ No newline at end of file diff --git a/res/values/strings_zh.arb b/res/values/strings_zh.arb index 7e3ceae31..24295e0ca 100644 --- a/res/values/strings_zh.arb +++ b/res/values/strings_zh.arb @@ -685,6 +685,11 @@ "matrix_green_dark_theme": "矩阵绿暗主题", "monero_light_theme": "门罗币浅色主题", "select_destination": "请选择备份文件的目的地。", + "auto_generate_subaddresses": "自动生成子辅助", "save_to_downloads": "保存到下载", - "auto_generate_subaddresses": "自动生成子辅助" -} + "select_buy_provider_notice": "在上面选择买入提供商。您可以通过在应用程序设置中设置默认的购买提供商来跳过此屏幕。", + "onramper_option_description": "快速使用许多付款方式购买加密货币。在大多数国家 /地区可用。利差和费用各不相同。", + "default_buy_provider": "默认购买提供商", + "ask_each_time": "每次问", + "buy_provider_unavailable": "提供者目前不可用。" +} \ No newline at end of file diff --git a/tool/utils/secret_key.dart b/tool/utils/secret_key.dart index 7819a582c..8c86e1b7f 100644 --- a/tool/utils/secret_key.dart +++ b/tool/utils/secret_key.dart @@ -32,6 +32,8 @@ class SecretKey { SecretKey('fiatApiKey', () => ''), SecretKey('payfuraApiKey', () => ''), SecretKey('chatwootWebsiteToken', () => ''), + SecretKey('robinhoodApplicationId', () => ''), + SecretKey('robinhoodCIdApiSecret', () => ''), ]; static final ethereumSecrets = [