Add additional Buy Provider (#1071)

* CW-466 Add Buy Options Page * CW-466 Add Buy Options * CW-466 Add Default Buy Provider to Other Settings * CW-466 Onramper is working from Buy Options * CW-466 Onramper is working from Buy Options * CW-466 Translation improvements * CW-466 Add Onramper & Robinhood Logos * CW-466 Implement Robinhood Flow * CW-466 Fix Robinhood Flow * CW-466 Add RH-Secrets * CW-466 Have RH Translation in English only * Add missing URI details * CW-466 Implement default Buy Provider * CW-466 Fix Padding Buy Provider Options * CW-466 Fix Bitcoin and Litecoin Signatures * CW-466 Fix Error Message * CW-466 Resolve requested changes * Add exception handler to robinhood API calls * CW-466 Fix Theming --------- Co-authored-by: Justin Ehrenhofer <justin.ehrenhofer@gmail.com> Co-authored-by: OmarHatem <omarh.ismail1@gmail.com>

Konstantin Ullrich committed Sep 14, 2023 at 21:14 UTC d972363417cc226e7d0acfef5b5388724e2546e2
59 files changed +548 -477
.github/workflows/pr_test_build.yml
+2
@@ -128,6 +128,8 @@ jobs:
128 echo "const payfuraApiKey = '${{ secrets.PAYFURA_API_KEY }}';" >> lib/.secrets.g.dart
129 echo "const etherScanApiKey = '${{ secrets.ETHER_SCAN_API_KEY }}';" >> cw_ethereum/lib/.secrets.g.dart
130 echo "const chatwootWebsiteToken = '${{ secrets.CHATWOOT_WEBSITE_TOKEN }}';" >> lib/.secrets.g.dart
131 + echo "const robinhoodApplicationId = '${{ secrets.ROBINHOOD_APPLICATION_ID }}';" >> lib/.secrets.g.dart
132 + echo "const robinhoodCIdApiSecret = '${{ secrets.ROBINHOOD_CID_CLIENT_SECRET }}';" >> lib/.secrets.g.dart
133
134 - name: Rename app
135 run: echo -e "id=com.cakewallet.test\nname=$GITHUB_HEAD_REF" > /opt/android/cake_wallet/android/app.properties
assets/images/onramper_dark.png
Binary files /dev/null and b/assets/images/onramper_dark.png differ
assets/images/onramper_light.png
Binary files /dev/null and b/assets/images/onramper_light.png differ
assets/images/robinhood_dark.png
Binary files /dev/null and b/assets/images/robinhood_dark.png differ
assets/images/robinhood_light.png
Binary files /dev/null and b/assets/images/robinhood_light.png differ
cw_bitcoin/lib/electrum_wallet.dart
+10
@@ -740,4 +740,14 @@ abstract class ElectrumWalletBase extends WalletBase<ElectrumBalance,
740
741 @override
742 void setExceptionHandler(void Function(FlutterErrorDetails) onError) => _onError = onError;
743 +
744 + @override
745 + String signMessage(String message, {String? address = null}) {
746 + final index = address != null
747 + ? walletAddresses.addresses.firstWhere((element) => element.address == address).index
748 + : null;
749 + return index == null
750 + ? base64Encode(hd.sign(message))
751 + : base64Encode(hd.derive(index).sign(message));
752 + }
753 }
cw_core/lib/wallet_base.dart
+2
@@ -83,4 +83,6 @@ abstract class WalletBase<
83 void setExceptionHandler(void Function(FlutterErrorDetails) onError) => null;
84
85 Future<void> renameWalletFiles(String newWalletName);
86 +
87 + String signMessage(String message, {String? address = null}) => throw UnimplementedError();
88 }
cw_ethereum/lib/ethereum_wallet.dart
+5
@@ -31,6 +31,7 @@ import 'package:hive/hive.dart';
31 import 'package:hex/hex.dart';
32 import 'package:mobx/mobx.dart';
33 import 'package:shared_preferences/shared_preferences.dart';
34 +import 'package:web3dart/crypto.dart';
35 import 'package:web3dart/web3dart.dart';
36 import 'package:bip39/bip39.dart' as bip39;
37 import 'package:bip32/bip32.dart' as bip32;
@@ -503,4 +504,8 @@ abstract class EthereumWalletBase
504 _transactionsUpdateTimer?.cancel();
505 }
506 }
507 +
508 + @override
509 + String signMessage(String message, {String? address = null}) =>
510 + bytesToHex(_ethPrivateKey.signPersonalMessageToUint8List(ascii.encode(message)));
511 }
lib/buy/onramper/onramper_buy_provider.dart
+12
@@ -1,10 +1,13 @@
1 import 'package:cake_wallet/.secrets.g.dart' as secrets;
2 import 'package:cake_wallet/generated/i18n.dart';
3 +import 'package:cake_wallet/routes.dart';
4 import 'package:cake_wallet/store/settings_store.dart';
5 import 'package:cake_wallet/themes/extensions/cake_text_theme.dart';
6 +import 'package:cake_wallet/utils/device_info.dart';
7 import 'package:cw_core/crypto_currency.dart';
8 import 'package:cw_core/wallet_base.dart';
9 import 'package:flutter/material.dart';
10 +import 'package:url_launcher/url_launcher.dart';
11
12 class OnRamperBuyProvider {
13 OnRamperBuyProvider({required SettingsStore settingsStore, required WalletBase wallet})
@@ -69,4 +72,13 @@ class OnRamperBuyProvider {
72 'cardColor': cardColor
73 });
74 }
75 +
76 + Future<void> launchProvider(BuildContext context) async {
77 + final uri = requestUrl(context);
78 + if (DeviceInfo.instance.isMobile) {
79 + Navigator.of(context).pushNamed(Routes.webViewPage, arguments: [S.of(context).buy, uri]);
80 + } else {
81 + await launchUrl(uri);
82 + }
83 + }
84 }
lib/buy/robinhood/robinhood_buy_provider.dart new
+92
@@ -0,0 +1,92 @@
1 +import 'dart:convert';
2 +
3 +import 'package:cake_wallet/.secrets.g.dart' as secrets;
4 +import 'package:cake_wallet/generated/i18n.dart';
5 +import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
6 +import 'package:cake_wallet/utils/exception_handler.dart';
7 +import 'package:cake_wallet/utils/show_pop_up.dart';
8 +import 'package:cw_core/wallet_base.dart';
9 +import 'package:cw_core/wallet_type.dart';
10 +import 'package:flutter/material.dart';
11 +import 'package:http/http.dart' as http;
12 +import 'package:url_launcher/url_launcher.dart';
13 +
14 +class RobinhoodBuyProvider {
15 + RobinhoodBuyProvider({required WalletBase wallet})
16 + : this._wallet = wallet;
17 +
18 + final WalletBase _wallet;
19 +
20 + static const _baseUrl = 'applink.robinhood.com';
21 + static const _cIdBaseUrl = 'exchange-helper.cakewallet.com';
22 +
23 + String get _applicationId => secrets.robinhoodApplicationId;
24 + String get _apiSecret => secrets.robinhoodCIdApiSecret;
25 +
26 + bool get isAvailable =>
27 + [WalletType.bitcoin, WalletType.litecoin, WalletType.ethereum].contains(_wallet.type);
28 +
29 + String getSignature(String message) {
30 + switch (_wallet.type) {
31 + case WalletType.ethereum:
32 + return _wallet.signMessage(message);
33 + case WalletType.litecoin:
34 + case WalletType.bitcoin:
35 + return _wallet.signMessage(message, address: _wallet.walletAddresses.address);
36 + default:
37 + throw Exception("WalletType is not available for Robinhood");
38 + }
39 + }
40 +
41 + Future<String> getConnectId() async {
42 + final walletAddress = _wallet.walletAddresses.address;
43 + final valid_until = (DateTime.now().millisecondsSinceEpoch / 1000).round() + 10;
44 + final message = "$_apiSecret:${valid_until}";
45 +
46 + final signature = getSignature(message);
47 +
48 + final uri = Uri.https(_cIdBaseUrl, "/api/robinhood");
49 +
50 + var response = await http.post(uri,
51 + headers: {'Content-Type': 'application/json'},
52 + body: json
53 + .encode({'valid_until': valid_until, 'wallet': walletAddress, 'signature': signature}));
54 +
55 + if (response.statusCode == 200) {
56 + return (jsonDecode(response.body) as Map<String, dynamic>)['connectId'] as String;
57 + } else {
58 + throw Exception('Provider currently unavailable. Status: ${response.statusCode} ${response.body}');
59 + }
60 + }
61 +
62 + Future<Uri> requestUrl() async {
63 + final connectId = await getConnectId();
64 + final networkName = _wallet.currency.fullName?.toUpperCase().replaceAll(" ", "_");
65 +
66 + return Uri.https(_baseUrl, '/u/connect', <String, dynamic>{
67 + 'applicationId': _applicationId,
68 + 'connectId': connectId,
69 + 'walletAddress': _wallet.walletAddresses.address,
70 + 'userIdentifier': _wallet.walletAddresses.address,
71 + 'supportedNetworks': networkName
72 + });
73 + }
74 +
75 + Future<void> launchProvider(BuildContext context) async {
76 + try {
77 + final uri = await requestUrl();
78 + await launchUrl(uri, mode: LaunchMode.externalApplication);
79 + } catch (e, s) {
80 + ExceptionHandler.onError(FlutterErrorDetails(exception: e, stack: s));
81 + await showPopUp<void>(
82 + context: context,
83 + builder: (BuildContext context) {
84 + return AlertWithOneAction(
85 + alertTitle: "Robinhood Connect",
86 + alertContent: S.of(context).buy_provider_unavailable,
87 + buttonText: S.of(context).ok,
88 + buttonAction: () => Navigator.of(context).pop());
89 + });
90 + }
91 + }
92 +}
lib/core/backup_service.dart
+5
@@ -208,6 +208,7 @@ class BackupService {
208 final isAppSecure = data[PreferencesKey.isAppSecureKey] as bool?;
209 final disableBuy = data[PreferencesKey.disableBuyKey] as bool?;
210 final disableSell = data[PreferencesKey.disableSellKey] as bool?;
211 + final defaultBuyProvider = data[PreferencesKey.defaultBuyProvider] as int?;
212 final currentTransactionPriorityKeyLegacy =
213 data[PreferencesKey.currentTransactionPriorityKeyLegacy] as int?;
214 final allowBiometricalAuthentication =
@@ -276,6 +277,9 @@ class BackupService {
277 if (disableSell != null)
278 await _sharedPreferences.setBool(PreferencesKey.disableSellKey, disableSell);
279
280 + if (defaultBuyProvider != null)
281 + await _sharedPreferences.setInt(PreferencesKey.defaultBuyProvider, defaultBuyProvider);
282 +
283 if (currentTransactionPriorityKeyLegacy != null)
284 await _sharedPreferences.setInt(
285 PreferencesKey.currentTransactionPriorityKeyLegacy, currentTransactionPriorityKeyLegacy);
@@ -476,6 +480,7 @@ class BackupService {
480 _sharedPreferences.getBool(PreferencesKey.shouldSaveRecipientAddressKey),
481 PreferencesKey.disableBuyKey: _sharedPreferences.getBool(PreferencesKey.disableBuyKey),
482 PreferencesKey.disableSellKey: _sharedPreferences.getBool(PreferencesKey.disableSellKey),
483 + PreferencesKey.defaultBuyProvider: _sharedPreferences.getInt(PreferencesKey.defaultBuyProvider),
484 PreferencesKey.isDarkThemeLegacy:
485 _sharedPreferences.getBool(PreferencesKey.isDarkThemeLegacy),
486 PreferencesKey.currentPinLength: _sharedPreferences.getInt(PreferencesKey.currentPinLength),
lib/di.dart
+7 -5
@@ -3,6 +3,7 @@ import 'package:cake_wallet/anonpay/anonpay_info_base.dart';
3 import 'package:cake_wallet/anonpay/anonpay_invoice_info.dart';
4 import 'package:cake_wallet/buy/onramper/onramper_buy_provider.dart';
5 import 'package:cake_wallet/buy/payfura/payfura_buy_provider.dart';
6 +import 'package:cake_wallet/buy/robinhood/robinhood_buy_provider.dart';
7 import 'package:cake_wallet/core/yat_service.dart';
8 import 'package:cake_wallet/entities/background_tasks.dart';
9 import 'package:cake_wallet/entities/auto_generate_subaddress_status.dart';
@@ -15,6 +16,7 @@ import 'package:cake_wallet/ionia/ionia_gift_card.dart';
16 import 'package:cake_wallet/ionia/ionia_tip.dart';
17 import 'package:cake_wallet/routes.dart';
18 import 'package:cake_wallet/src/screens/anonpay_details/anonpay_details_page.dart';
19 +import 'package:cake_wallet/src/screens/buy/buy_options_page.dart';
20 import 'package:cake_wallet/src/screens/buy/webview_page.dart';
21 import 'package:cake_wallet/src/screens/dashboard/desktop_dashboard_page.dart';
22 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';
97 import 'package:cake_wallet/src/screens/backup/backup_page.dart';
98 import 'package:cake_wallet/src/screens/backup/edit_backup_password_page.dart';
99 import 'package:cake_wallet/src/screens/buy/buy_webview_page.dart';
98 -import 'package:cake_wallet/src/screens/buy/pre_order_page.dart';
100 import 'package:cake_wallet/src/screens/contact/contact_list_page.dart';
101 import 'package:cake_wallet/src/screens/contact/contact_page.dart';
102 import 'package:cake_wallet/src/screens/exchange_trade/exchange_confirm_page.dart';
@@ -688,6 +689,9 @@ Future<void> setup({
689 editingNode: editingNode,
690 isSelected: isSelected));
691
692 + getIt.registerFactory<RobinhoodBuyProvider>(
693 + () => RobinhoodBuyProvider(wallet: getIt.get<AppStore>().wallet!));
694 +
695 getIt.registerFactory<OnRamperBuyProvider>(() => OnRamperBuyProvider(
696 settingsStore: getIt.get<AppStore>().settingsStore,
697 wallet: getIt.get<AppStore>().wallet!,
@@ -816,6 +820,8 @@ Future<void> setup({
820
821 getIt.registerFactory(() => BuyAmountViewModel());
822
823 + getIt.registerFactory(() => BuyOptionsPage());
824 +
825 getIt.registerFactory(() {
826 final wallet = getIt.get<AppStore>().wallet;
827
@@ -824,10 +830,6 @@ Future<void> setup({
830 wallet: wallet!);
831 });
832
827 - getIt.registerFactory(() {
828 - return PreOrderPage(buyViewModel: getIt.get<BuyViewModel>());
829 - });
830 -
833 getIt.registerFactoryParam<BuyWebViewPage, List, void>((List args, _) {
834 final url = args.first as String;
835 final buyViewModel = args[1] as BuyViewModel;
lib/entities/buy_provider_types.dart new
+19
@@ -0,0 +1,19 @@
1 +import 'package:cake_wallet/generated/i18n.dart';
2 +
3 +enum BuyProviderType {
4 + AskEachTime,
5 + Robinhood,
6 + Onramper;
7 +
8 + @override
9 + String toString() {
10 + switch (this) {
11 + case BuyProviderType.AskEachTime:
12 + return S.current.ask_each_time;
13 + case BuyProviderType.Robinhood:
14 + return "Robinhood";
15 + case BuyProviderType.Onramper:
16 + return "Onramper";
17 + }
18 + }
19 +}
lib/entities/main_actions.dart
+18 -9
@@ -1,6 +1,8 @@
1 import 'package:cake_wallet/buy/moonpay/moonpay_buy_provider.dart';
2 import 'package:cake_wallet/buy/onramper/onramper_buy_provider.dart';
3 +import 'package:cake_wallet/buy/robinhood/robinhood_buy_provider.dart';
4 import 'package:cake_wallet/di.dart';
5 +import 'package:cake_wallet/entities/buy_provider_types.dart';
6 import 'package:cake_wallet/generated/i18n.dart';
7 import 'package:cake_wallet/routes.dart';
8 import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
@@ -41,23 +43,30 @@ class MainActions {
43 isEnabled: (viewModel) => viewModel.isEnabledBuyAction,
44 canShow: (viewModel) => viewModel.hasBuyAction,
45 onTap: (BuildContext context, DashboardViewModel viewModel) async {
46 + final defaultBuyProvider = viewModel.defaultBuyProvider;
47 final walletType = viewModel.type;
48
49 + if (!viewModel.isEnabledBuyAction) return;
50 +
51 switch (walletType) {
52 case WalletType.bitcoin:
53 case WalletType.litecoin:
54 case WalletType.ethereum:
50 - case WalletType.monero:
51 - if (viewModel.isEnabledBuyAction) {
52 - final uri = getIt.get<OnRamperBuyProvider>().requestUrl(context);
53 - if (DeviceInfo.instance.isMobile) {
54 - Navigator.of(context)
55 - .pushNamed(Routes.webViewPage, arguments: [S.of(context).buy, uri]);
56 - } else {
57 - await launchUrl(uri);
58 - }
55 + switch (defaultBuyProvider) {
56 + case BuyProviderType.AskEachTime:
57 + Navigator.pushNamed(context, Routes.buy);
58 + break;
59 + case BuyProviderType.Onramper:
60 + await getIt.get<OnRamperBuyProvider>().launchProvider(context);
61 + break;
62 + case BuyProviderType.Robinhood:
63 + await getIt.get<RobinhoodBuyProvider>().launchProvider(context);
64 + break;
65 }
66 break;
67 + case WalletType.monero:
68 + await getIt.get<OnRamperBuyProvider>().launchProvider(context);
69 + break;
70 default:
71 await showPopUp<void>(
72 context: context,
lib/entities/preferences_key.dart
+1
@@ -13,6 +13,7 @@ class PreferencesKey {
13 static const isAppSecureKey = 'is_app_secure';
14 static const disableBuyKey = 'disable_buy';
15 static const disableSellKey = 'disable_sell';
16 + static const defaultBuyProvider = 'default_buy_provider';
17 static const currentFiatApiModeKey = 'current_fiat_api_mode';
18 static const allowBiometricalAuthenticationKey =
19 'allow_biometrical_authentication';
lib/router.dart
+3 -5
@@ -7,9 +7,9 @@ import 'package:cake_wallet/entities/qr_view_data.dart';
7 import 'package:cake_wallet/src/screens/anonpay_details/anonpay_details_page.dart';
8 import 'package:cake_wallet/src/screens/backup/backup_page.dart';
9 import 'package:cake_wallet/src/screens/backup/edit_backup_password_page.dart';
10 +import 'package:cake_wallet/src/screens/buy/buy_options_page.dart';
11 import 'package:cake_wallet/src/screens/buy/buy_webview_page.dart';
12 import 'package:cake_wallet/src/screens/buy/webview_page.dart';
12 -import 'package:cake_wallet/src/screens/buy/pre_order_page.dart';
13 import 'package:cake_wallet/src/screens/dashboard/edit_token_page.dart';
14 import 'package:cake_wallet/src/screens/dashboard/home_settings_page.dart';
15 import 'package:cake_wallet/src/screens/restore/sweeping_wallet_page.dart';
@@ -383,10 +383,8 @@ Route<dynamic> createRoute(RouteSettings settings) {
383 builder: (_) =>
384 getIt.get<OrderDetailsPage>(param1: settings.arguments as Order));
385
386 - case Routes.preOrder:
387 - return MaterialPageRoute<void>(
388 - builder: (_) =>
389 - getIt.get<PreOrderPage>());
386 + case Routes.buy:
387 + return MaterialPageRoute<void>(builder: (_) => getIt.get<BuyOptionsPage>());
388
389 case Routes.buyWebView:
390 final args = settings.arguments as List;
lib/routes.dart
+1 -1
@@ -50,7 +50,7 @@ class Routes {
50 static const supportLiveChat = '/support/live_chat';
51 static const supportOtherLinks = '/support/other';
52 static const orderDetails = '/order_details';
53 - static const preOrder = '/pre_order';
53 + static const buy = '/buy';
54 static const buyWebView = '/buy_web_view';
55 static const unspentCoinsList = '/unspent_coins_list';
56 static const unspentCoinsDetails = '/unspent_coins_details';
lib/src/screens/buy/buy_options_page.dart new
+76
@@ -0,0 +1,76 @@
1 +import 'package:cake_wallet/buy/onramper/onramper_buy_provider.dart';
2 +import 'package:cake_wallet/buy/robinhood/robinhood_buy_provider.dart';
3 +import 'package:cake_wallet/di.dart';
4 +import 'package:cake_wallet/generated/i18n.dart';
5 +import 'package:cake_wallet/src/screens/base_page.dart';
6 +import 'package:cake_wallet/src/widgets/option_tile.dart';
7 +import 'package:cake_wallet/themes/extensions/option_tile_theme.dart';
8 +import 'package:cake_wallet/themes/extensions/transaction_trade_theme.dart';
9 +import 'package:flutter/material.dart';
10 +
11 +class BuyOptionsPage extends BasePage {
12 + final iconDarkRobinhood = 'assets/images/robinhood_dark.png';
13 + final iconLightRobinhood = 'assets/images/robinhood_light.png';
14 + final iconDarkOnramper = 'assets/images/onramper_dark.png';
15 + final iconLightOnramper = 'assets/images/onramper_light.png';
16 +
17 + @override
18 + String get title => S.current.buy;
19 +
20 + @override
21 + AppBarStyle get appBarStyle => AppBarStyle.regular;
22 +
23 + @override
24 + Widget body(BuildContext context) {
25 + final isLightMode = Theme.of(context).extension<OptionTileTheme>()?.useDarkImage ?? false;
26 + final iconRobinhood =
27 + Image.asset(isLightMode ? iconLightRobinhood : iconDarkRobinhood, height: 40, width: 40);
28 + final iconOnramper =
29 + Image.asset(isLightMode ? iconLightOnramper : iconDarkOnramper, height: 40, width: 40);
30 +
31 + return Container(
32 + child: Center(
33 + child: ConstrainedBox(
34 + constraints: BoxConstraints(maxWidth: 330),
35 + child: Column(
36 + children: [
37 + Padding(
38 + padding: EdgeInsets.only(top: 24),
39 + child: OptionTile(
40 + image: iconRobinhood,
41 + title: "Robinhood Connect",
42 + description: S.of(context).robinhood_option_description,
43 + onPressed: () async =>
44 + await getIt.get<RobinhoodBuyProvider>().launchProvider(context),
45 + ),
46 + ),
47 + Padding(
48 + padding: EdgeInsets.only(top: 24),
49 + child: OptionTile(
50 + image: iconOnramper,
51 + title: "Onramper",
52 + description: S.of(context).onramper_option_description,
53 + onPressed: () async =>
54 + await getIt.get<OnRamperBuyProvider>().launchProvider(context),
55 + ),
56 + ),
57 + Spacer(),
58 + Padding(
59 + padding: EdgeInsets.fromLTRB(24, 24, 24, 32),
60 + child: Text(
61 + S.of(context).select_buy_provider_notice,
62 + textAlign: TextAlign.center,
63 + style: TextStyle(
64 + fontSize: 14,
65 + fontWeight: FontWeight.normal,
66 + color: Theme.of(context).extension<TransactionTradeTheme>()!.detailsTitlesColor,
67 + ),
68 + ),
69 + ),
70 + ],
71 + ),
72 + ),
73 + ),
74 + );
75 + }
76 +}
lib/src/screens/buy/pre_order_page.dart deleted
-304
@@ -1,304 +0,0 @@
1 -import 'package:cake_wallet/buy/buy_amount.dart';
2 -import 'package:cake_wallet/buy/buy_provider.dart';
3 -import 'package:cake_wallet/buy/moonpay/moonpay_buy_provider.dart';
4 -import 'package:cake_wallet/entities/fiat_currency.dart';
5 -import 'package:cake_wallet/src/widgets/picker.dart';
6 -import 'package:cake_wallet/themes/extensions/cake_text_theme.dart';
7 -import 'package:cake_wallet/themes/extensions/exchange_page_theme.dart';
8 -import 'package:cake_wallet/themes/extensions/keyboard_theme.dart';
9 -import 'package:cake_wallet/themes/extensions/send_page_theme.dart';
10 -import 'package:cw_core/wallet_type.dart';
11 -import 'package:cake_wallet/src/screens/buy/widgets/buy_list_item.dart';
12 -import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
13 -import 'package:cake_wallet/src/widgets/keyboard_done_button.dart';
14 -import 'package:cake_wallet/utils/show_pop_up.dart';
15 -import 'package:cake_wallet/view_model/buy/buy_view_model.dart';
16 -import 'package:flutter/material.dart';
17 -import 'package:flutter/services.dart';
18 -import 'package:flutter_mobx/flutter_mobx.dart';
19 -import 'package:keyboard_actions/keyboard_actions.dart';
20 -import 'package:cake_wallet/routes.dart';
21 -import 'package:cake_wallet/src/screens/base_page.dart';
22 -import 'package:cake_wallet/src/widgets/primary_button.dart';
23 -import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart';
24 -import 'package:cake_wallet/generated/i18n.dart';
25 -import 'package:cake_wallet/src/widgets/base_text_form_field.dart';
26 -import 'package:cake_wallet/src/widgets/trail_button.dart';
27 -import 'package:mobx/mobx.dart';
28 -import 'package:url_launcher/url_launcher.dart';
29 -
30 -class PreOrderPage extends BasePage {
31 - PreOrderPage({required this.buyViewModel})
32 - : _amountFocus = FocusNode(),
33 - _amountController = TextEditingController() {
34 - _amountController.addListener(() {
35 - final amount = _amountController.text;
36 -
37 - if (amount != buyViewModel.buyAmountViewModel.amount) {
38 - buyViewModel.buyAmountViewModel.amount = amount;
39 - buyViewModel.selectedProvider = null;
40 - }
41 - });
42 -
43 - reaction((_) => buyViewModel.buyAmountViewModel.amount, (String amount) {
44 - if (_amountController.text != amount) {
45 - _amountController.text = amount;
46 - }
47 - if (amount.isEmpty) {
48 - buyViewModel.selectedProvider = null;
49 - buyViewModel.isShowProviderButtons = false;
50 - } else {
51 - buyViewModel.isShowProviderButtons = true;
52 - }
53 - });
54 - }
55 -
56 - static const _amountPattern = '^([0-9]+([.\,][0-9]{0,2})?|[.\,][0-9]{1,2})\$';
57 -
58 - final BuyViewModel buyViewModel;
59 - final FocusNode _amountFocus;
60 - final TextEditingController _amountController;
61 -
62 - @override
63 - String get title => S.current.buy + ' ' + walletTypeToString(buyViewModel.wallet.type);
64 -
65 - @override
66 - bool get resizeToAvoidBottomInset => false;
67 -
68 - @override
69 - bool get extendBodyBehindAppBar => true;
70 -
71 - @override
72 - AppBarStyle get appBarStyle => AppBarStyle.transparent;
73 -
74 - @override
75 - Widget trailing(context) => TrailButton(
76 - caption: S.of(context).clear,
77 - onPressed: () => buyViewModel.reset());
78 -
79 - @override
80 - Widget body(BuildContext context) {
81 - return KeyboardActions(
82 - config: KeyboardActionsConfig(
83 - keyboardActionsPlatform: KeyboardActionsPlatform.IOS,
84 - keyboardBarColor: Theme.of(context).extension<KeyboardTheme>()!.keyboardBarColor,
85 - nextFocus: false,
86 - actions: [
87 - KeyboardActionsItem(
88 - focusNode: _amountFocus,
89 - toolbarButtons: [(_) => KeyboardDoneButton()],
90 - ),
91 - ]),
92 - child: Container(
93 - height: 0,
94 - color: Theme.of(context).colorScheme.background,
95 - child: ScrollableWithBottomSection(
96 - contentPadding: EdgeInsets.only(bottom: 24),
97 - content: Observer(builder: (_) => Column(
98 - children: [
99 - Container(
100 - decoration: BoxDecoration(
101 - borderRadius: BorderRadius.only(
102 - bottomLeft: Radius.circular(24),
103 - bottomRight: Radius.circular(24)),
104 - gradient: LinearGradient(colors: [
105 - Theme.of(context).extension<SendPageTheme>()!.firstGradientColor,
106 - Theme.of(context).extension<SendPageTheme>()!.secondGradientColor,
107 - ], begin: Alignment.topLeft, end: Alignment.bottomRight),
108 - ),
109 - child: Padding(
110 - padding: EdgeInsets.only(top: 100, bottom: 65),
111 - child: Center(
112 - child: Container(
113 - width: 210,
114 - child: BaseTextFormField(
115 - focusNode: _amountFocus,
116 - controller: _amountController,
117 - keyboardType: TextInputType.numberWithOptions(signed: false, decimal: true),
118 - inputFormatters: [FilteringTextInputFormatter.allow(RegExp(_amountPattern))],
119 - prefixIcon: GestureDetector(
120 - onTap: () {
121 - showPopUp<void>(
122 - context: context,
123 - builder: (_) => Picker(
124 - hintText: S.current.search_currency,
125 - items: FiatCurrency.currenciesAvailableToBuyWith,
126 - selectedAtIndex:
127 - FiatCurrency.currenciesAvailableToBuyWith.indexOf(buyViewModel.fiatCurrency),
128 - onItemSelected: (FiatCurrency selectedCurrency) {
129 - buyViewModel.buyAmountViewModel.fiatCurrency = selectedCurrency;
130 - },
131 - images: FiatCurrency.currenciesAvailableToBuyWith
132 - .map((e) => Image.asset("assets/images/flags/${e.countryCode}.png"))
133 - .toList(),
134 - isGridView: true,
135 - matchingCriteria: (FiatCurrency currency, String searchText) {
136 - return currency.title.toLowerCase().contains(searchText) ||
137 - currency.fullName.toLowerCase().contains(searchText);
138 - },
139 - ),
140 - );
141 - },
142 - child: Padding(
143 - padding: EdgeInsets.only(top: 2),
144 - child: Row(
145 - mainAxisSize: MainAxisSize.min,
146 - children: [
147 - Icon(Icons.keyboard_arrow_down, color: Colors.white),
148 - Text(
149 - buyViewModel.fiatCurrency.title + ': ',
150 - style: TextStyle(
151 - fontSize: 36,
152 - fontWeight: FontWeight.w600,
153 - color: Colors.white,
154 - ),
155 - ),
156 - ],
157 - ),
158 - ),
159 - ),
160 - hintText: '0.00',
161 - borderColor: Theme.of(context).extension<ExchangePageTheme>()!.textFieldBorderBottomPanelColor,
162 - borderWidth: 0.5,
163 - textStyle: TextStyle(fontSize: 36, fontWeight: FontWeight.w500, color: Colors.white),
164 - placeholderTextStyle: TextStyle(
165 - color: Theme.of(context).extension<SendPageTheme>()!.textFieldHintColor,
166 - fontWeight: FontWeight.w500,
167 - fontSize: 36,
168 - ),
169 - ),
170 - ),
171 - ),
172 - ),
173 - ),
174 - if (buyViewModel.isShowProviderButtons) Padding(
175 - padding: EdgeInsets.only(top: 38, bottom: 18),
176 - child: Text(
177 - S.of(context).buy_with + ':',
178 - textAlign: TextAlign.center,
179 - style: TextStyle(
180 - color: Theme.of(context).extension<CakeTextTheme>()!.titleColor,
181 - fontSize: 18,
182 - fontWeight: FontWeight.bold
183 - ),
184 - )
185 - ),
186 - if (buyViewModel.isShowProviderButtons)
187 - ...buyViewModel.items.map(
188 - (item) => Observer(builder: (_) =>
189 - FutureBuilder<BuyAmount>(
190 - future: item.buyAmount,
191 - builder: (context, AsyncSnapshot<BuyAmount> snapshot) {
192 - double sourceAmount;
193 - double destAmount;
194 - double achAmount;
195 - int minAmount;
196 -
197 - if (snapshot.hasData && snapshot.data != null) {
198 - sourceAmount = snapshot.data!.sourceAmount;
199 - destAmount = snapshot.data!.destAmount;
200 - minAmount = snapshot.data!.minAmount;
201 - achAmount = snapshot.data!.achSourceAmount ?? 0;
202 - } else {
203 - sourceAmount = 0.0;
204 - destAmount = 0.0;
205 - minAmount = 0;
206 - achAmount = 0;
207 - }
208 -
209 - return Padding(
210 - padding:
211 - EdgeInsets.only(left: 15, top: 20, right: 15),
212 - child: Observer(builder: (_) {
213 - return BuyListItem(
214 - selectedProvider:
215 - buyViewModel.selectedProvider,
216 - provider: item.provider,
217 - sourceAmount: sourceAmount,
218 - sourceCurrency: buyViewModel.fiatCurrency,
219 - destAmount: destAmount,
220 - destCurrency: buyViewModel.cryptoCurrency,
221 - achSourceAmount: achAmount,
222 - onTap: ((buyViewModel.doubleAmount != 0.0)
223 - && (snapshot.hasData)) ? () =>
224 - onSelectBuyProvider(
225 - context: context,
226 - provider: item.provider,
227 - sourceAmount: sourceAmount,
228 - minAmount: minAmount
229 - ) : null
230 - );
231 - })
232 - );
233 - }
234 - ))
235 - )
236 - ],
237 - )),
238 - bottomSectionPadding:
239 - EdgeInsets.only(left: 24, right: 24, bottom: 24),
240 - bottomSection: Observer(builder: (_) {
241 - return LoadingPrimaryButton(
242 - onPressed: () => onPresentProvider(context: context),
243 - text: buyViewModel.selectedProvider == null
244 - ? S.of(context).buy
245 - : S.of(context).buy_with +
246 - ' ${buyViewModel.selectedProvider!.description.title}',
247 - color: Theme.of(context).primaryColor,
248 - textColor: Colors.white,
249 - isLoading: buyViewModel.isRunning,
250 - isDisabled: (buyViewModel.selectedProvider == null) ||
251 - buyViewModel.isDisabled
252 - );
253 - })
254 - )
255 - )
256 - );
257 - }
258 -
259 - void onSelectBuyProvider({required BuildContext context, required BuyProvider provider,
260 - required double sourceAmount, required int minAmount}) {
261 -
262 - if ((provider is MoonPayBuyProvider)&&
263 - (buyViewModel.buyAmountViewModel.doubleAmount < minAmount)) {
264 - showPopUp<void>(
265 - context: context,
266 - builder: (BuildContext context) {
267 - return AlertWithOneAction(
268 - alertTitle: 'MoonPay',
269 - alertContent: S.of(context).moonpay_alert_text(
270 - minAmount.toString(),
271 - buyViewModel.fiatCurrency.toString()),
272 - buttonText: S.of(context).ok,
273 - buttonAction: () => Navigator.of(context).pop());
274 - });
275 - return;
276 - }
277 - buyViewModel.selectedProvider = provider;
278 - sourceAmount > 0
279 - ? buyViewModel.isDisabled = false
280 - : buyViewModel.isDisabled = true;
281 - }
282 -
283 - Future<void> onPresentProvider({required BuildContext context}) async {
284 - if (buyViewModel.isRunning) {
285 - return;
286 - }
287 -
288 - buyViewModel.isRunning = true;
289 - final url = await buyViewModel.fetchUrl();
290 -
291 - if (url.isNotEmpty) {
292 - if (buyViewModel.selectedProvider is MoonPayBuyProvider) {
293 - if (await canLaunch(url)) await launch(url);
294 - } else {
295 - await Navigator.of(context)
296 - .pushNamed(Routes.buyWebView,
297 - arguments: [url, buyViewModel]);
298 - }
299 - }
300 -
301 - buyViewModel.reset();
302 - buyViewModel.isRunning = false;
303 - }
304 -}
lib/src/screens/restore/restore_options_page.dart
+4 -4
@@ -2,6 +2,7 @@ import 'package:cake_wallet/core/execution_state.dart';
2 import 'package:cake_wallet/di.dart';
3 import 'package:cake_wallet/src/screens/pin_code/pin_code_widget.dart';
4 import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
5 +import 'package:cake_wallet/src/widgets/option_tile.dart';
6 import 'package:cake_wallet/utils/show_pop_up.dart';
7 import 'package:cake_wallet/view_model/restore/restore_from_qr_vm.dart';
8 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';
10 import 'package:flutter/material.dart';
11 import 'package:cake_wallet/routes.dart';
12 import 'package:flutter/cupertino.dart';
12 -import 'package:cake_wallet/src/screens/restore/widgets/restore_button.dart';
13 import 'package:cake_wallet/src/screens/base_page.dart';
14 import 'package:cake_wallet/generated/i18n.dart';
15
@@ -35,7 +35,7 @@ class RestoreOptionsPage extends BasePage {
35 child: SingleChildScrollView(
36 child: Column(
37 children: <Widget>[
38 - RestoreButton(
38 + OptionTile(
39 onPressed: () => Navigator.pushNamed(
40 context, Routes.restoreWalletFromSeedKeys,
41 arguments: isNewInstall),
@@ -45,7 +45,7 @@ class RestoreOptionsPage extends BasePage {
45 if (isNewInstall)
46 Padding(
47 padding: EdgeInsets.only(top: 24),
48 - child: RestoreButton(
48 + child: OptionTile(
49 onPressed: () => Navigator.pushNamed(context, Routes.restoreFromBackup),
50 image: imageBackup,
51 title: S.of(context).restore_title_from_backup,
@@ -53,7 +53,7 @@ class RestoreOptionsPage extends BasePage {
53 ),
54 Padding(
55 padding: EdgeInsets.only(top: 24),
56 - child: RestoreButton(
56 + child: OptionTile(
57 onPressed: () async {
58 bool isPinSet = false;
59 if (isNewInstall) {
lib/src/screens/restore/widgets/restore_button.dart deleted
-74
@@ -1,74 +0,0 @@
1 -import 'package:cake_wallet/themes/extensions/cake_text_theme.dart';
2 -import 'package:flutter/material.dart';
3 -import 'package:cake_wallet/themes/extensions/transaction_trade_theme.dart';
4 -
5 -class RestoreButton extends StatelessWidget {
6 - const RestoreButton(
7 - {required this.onPressed,
8 - required this.image,
9 - required this.title,
10 - required this.description});
11 -
12 - final VoidCallback onPressed;
13 - final Image image;
14 - final String title;
15 - final String description;
16 -
17 - @override
18 - Widget build(BuildContext context) {
19 - return GestureDetector(
20 - onTap: onPressed,
21 - child: Container(
22 - width: double.infinity,
23 - height: 170,
24 - padding: EdgeInsets.all(24),
25 - alignment: Alignment.topLeft,
26 - decoration: BoxDecoration(
27 - borderRadius: BorderRadius.all(Radius.circular(12)),
28 - color: Theme.of(context).cardColor,
29 - ),
30 - child: Row(
31 - mainAxisSize: MainAxisSize.max,
32 - mainAxisAlignment: MainAxisAlignment.start,
33 - crossAxisAlignment: CrossAxisAlignment.start,
34 - children: <Widget>[
35 - image,
36 - Expanded(
37 - child: Padding(
38 - padding: EdgeInsets.only(left: 16),
39 - child: Column(
40 - mainAxisSize: MainAxisSize.max,
41 - mainAxisAlignment: MainAxisAlignment.start,
42 - crossAxisAlignment: CrossAxisAlignment.start,
43 - children: <Widget>[
44 - Text(
45 - title,
46 - style: TextStyle(
47 - fontSize: 16,
48 - fontWeight: FontWeight.w500,
49 - color: Theme.of(context).extension<CakeTextTheme>()!.titleColor,
50 - ),
51 - ),
52 - Padding(
53 - padding: EdgeInsets.only(top: 5),
54 - child: Text(
55 - description,
56 - style: TextStyle(
57 - fontSize: 14,
58 - fontWeight: FontWeight.normal,
59 - color: Theme.of(context)
60 - .extension<TransactionTradeTheme>()!
61 - .detailsTitlesColor,
62 - ),
63 - ),
64 - )
65 - ],
66 - ),
67 - ),
68 - )
69 - ],
70 - ),
71 - ),
72 - );
73 - }
74 -}
lib/src/screens/settings/other_settings_page.dart
+9
@@ -1,3 +1,5 @@
1 +import 'package:cake_wallet/buy/buy_provider.dart';
2 +import 'package:cake_wallet/entities/buy_provider_types.dart';
3 import 'package:cake_wallet/entities/priority_for_wallet_type.dart';
4 import 'package:cake_wallet/generated/i18n.dart';
5 import 'package:cake_wallet/routes.dart';
@@ -33,6 +35,13 @@ class OtherSettingsPage extends BasePage {
35 selectedItem: _otherSettingsViewModel.transactionPriority,
36 onItemSelected: _otherSettingsViewModel.onDisplayPrioritySelected,
37 ),
38 + SettingsPickerCell(
39 + title: S.current.default_buy_provider,
40 + items: BuyProviderType.values,
41 + displayItem: _otherSettingsViewModel.getBuyProviderType,
42 + selectedItem: _otherSettingsViewModel.buyProviderType,
43 + onItemSelected: _otherSettingsViewModel.onBuyProviderTypeSelected,
44 + ),
45 SettingsCellWithArrow(
46 title: S.current.settings_terms_and_conditions,
47 handler: (BuildContext context) =>
lib/src/screens/settings/widgets/settings_version_cell.dart
+1 -1
@@ -25,4 +25,4 @@ class SettingsVersionCell extends StatelessWidget {
25 ),
26 );
27 }
28 -}
\ No newline at end of file
28 +}
lib/src/screens/support/support_page.dart
+4 -4
@@ -1,7 +1,7 @@
1 import 'package:cake_wallet/generated/i18n.dart';
2 import 'package:cake_wallet/routes.dart';
3 -import 'package:cake_wallet/src/screens/support/widgets/support_tiles.dart';
3 import 'package:cake_wallet/src/screens/base_page.dart';
4 +import 'package:cake_wallet/src/widgets/option_tile.dart';
5 import 'package:cake_wallet/utils/device_info.dart';
6 import 'package:cake_wallet/view_model/support_view_model.dart';
7 import 'package:flutter/material.dart';
@@ -32,7 +32,7 @@ class SupportPage extends BasePage {
32 children: [
33 Padding(
34 padding: EdgeInsets.only(top: 24),
35 - child: SupportTile(
35 + child: OptionTile(
36 image: imageLiveSupport,
37 title: S.of(context).support_title_live_chat,
38 description: S.of(context).support_description_live_chat,
@@ -47,7 +47,7 @@ class SupportPage extends BasePage {
47 ),
48 Padding(
49 padding: EdgeInsets.only(top: 24),
50 - child: SupportTile(
50 + child: OptionTile(
51 image: imageWalletGuides,
52 title: S.of(context).support_title_guides,
53 description: S.of(context).support_description_guides,
@@ -56,7 +56,7 @@ class SupportPage extends BasePage {
56 ),
57 Padding(
58 padding: EdgeInsets.only(top: 24),
59 - child: SupportTile(
59 + child: OptionTile(
60 image: imageMoreLinks,
61 title: S.of(context).support_title_other_links,
62 description: S.of(context).support_description_other_links,
lib/src/widgets/option_tile.dart renamed
+5 -6
@@ -1,9 +1,8 @@
1 -import 'package:cake_wallet/themes/extensions/cake_text_theme.dart';
2 -import 'package:cake_wallet/themes/extensions/transaction_trade_theme.dart';
1 +import 'package:cake_wallet/themes/extensions/option_tile_theme.dart';
2 import 'package:flutter/material.dart';
3
5 -class SupportTile extends StatelessWidget {
6 - const SupportTile(
4 +class OptionTile extends StatelessWidget {
5 + const OptionTile(
6 {required this.onPressed,
7 required this.image,
8 required this.title,
@@ -45,7 +44,7 @@ class SupportTile extends StatelessWidget {
44 style: TextStyle(
45 fontSize: 20,
46 fontWeight: FontWeight.w500,
48 - color: Theme.of(context).extension<CakeTextTheme>()!.titleColor,
47 + color: Theme.of(context).extension<OptionTileTheme>()!.titleColor,
48 ),
49 ),
50 Padding(
@@ -55,7 +54,7 @@ class SupportTile extends StatelessWidget {
54 style: TextStyle(
55 fontSize: 16,
56 fontWeight: FontWeight.normal,
58 - color: Theme.of(context).extension<TransactionTradeTheme>()!.detailsTitlesColor,
57 + color: Theme.of(context).extension<OptionTileTheme>()!.descriptionColor,
58 ),
59 ),
60 )
lib/store/settings_store.dart
+14
@@ -2,6 +2,7 @@ import 'dart:io';
2
3 import 'package:cake_wallet/bitcoin/bitcoin.dart';
4 import 'package:cake_wallet/entities/auto_generate_subaddress_status.dart';
5 +import 'package:cake_wallet/entities/buy_provider_types.dart';
6 import 'package:cake_wallet/entities/cake_2fa_preset_options.dart';
7 import 'package:cake_wallet/entities/background_tasks.dart';
8 import 'package:cake_wallet/entities/exchange_api_mode.dart';
@@ -47,6 +48,7 @@ abstract class SettingsStoreBase with Store {
48 required bool initialAppSecure,
49 required bool initialDisableBuy,
50 required bool initialDisableSell,
51 + required BuyProviderType initialDefaultBuyProvider,
52 required FiatApiMode initialFiatMode,
53 required bool initialAllowBiometricalAuthentication,
54 required String initialTotpSecretKey,
@@ -99,6 +101,7 @@ abstract class SettingsStoreBase with Store {
101 isAppSecure = initialAppSecure,
102 disableBuy = initialDisableBuy,
103 disableSell = initialDisableSell,
104 + defaultBuyProvider = initialDefaultBuyProvider,
105 shouldShowMarketPlaceInDashboard = initialShouldShowMarketPlaceInDashboard,
106 exchangeStatus = initialExchangeStatus,
107 currentTheme = initialTheme,
@@ -200,6 +203,11 @@ abstract class SettingsStoreBase with Store {
203 (bool disableSell) =>
204 sharedPreferences.setBool(PreferencesKey.disableSellKey, disableSell));
205
206 + reaction(
207 + (_) => defaultBuyProvider,
208 + (BuyProviderType defaultBuyProvider) =>
209 + sharedPreferences.setInt(PreferencesKey.defaultBuyProvider, defaultBuyProvider.index));
210 +
211 reaction(
212 (_) => autoGenerateSubaddressStatus,
213 (AutoGenerateSubaddressStatus autoGenerateSubaddressStatus) => sharedPreferences.setInt(
@@ -380,6 +388,9 @@ abstract class SettingsStoreBase with Store {
388 @observable
389 bool disableSell;
390
391 + @observable
392 + BuyProviderType defaultBuyProvider;
393 +
394 @observable
395 bool allowBiometricalAuthentication;
396
@@ -535,6 +546,7 @@ abstract class SettingsStoreBase with Store {
546 final isAppSecure = sharedPreferences.getBool(PreferencesKey.isAppSecureKey) ?? false;
547 final disableBuy = sharedPreferences.getBool(PreferencesKey.disableBuyKey) ?? false;
548 final disableSell = sharedPreferences.getBool(PreferencesKey.disableSellKey) ?? false;
549 + final defaultBuyProvider = BuyProviderType.values[sharedPreferences.getInt(PreferencesKey.defaultBuyProvider) ?? 0];
550 final currentFiatApiMode = FiatApiMode.deserialize(
551 raw: sharedPreferences.getInt(PreferencesKey.currentFiatApiModeKey) ??
552 FiatApiMode.enabled.raw);
@@ -661,6 +673,7 @@ abstract class SettingsStoreBase with Store {
673 initialAppSecure: isAppSecure,
674 initialDisableBuy: disableBuy,
675 initialDisableSell: disableSell,
676 + initialDefaultBuyProvider: defaultBuyProvider,
677 initialFiatMode: currentFiatApiMode,
678 initialAllowBiometricalAuthentication: allowBiometricalAuthentication,
679 initialCake2FAPresetOptions: selectedCake2FAPreset,
@@ -747,6 +760,7 @@ abstract class SettingsStoreBase with Store {
760 isAppSecure = sharedPreferences.getBool(PreferencesKey.isAppSecureKey) ?? isAppSecure;
761 disableBuy = sharedPreferences.getBool(PreferencesKey.disableBuyKey) ?? disableBuy;
762 disableSell = sharedPreferences.getBool(PreferencesKey.disableSellKey) ?? disableSell;
763 + defaultBuyProvider = BuyProviderType.values[sharedPreferences.getInt(PreferencesKey.defaultBuyProvider) ?? 0];
764 allowBiometricalAuthentication =
765 sharedPreferences.getBool(PreferencesKey.allowBiometricalAuthenticationKey) ??
766 allowBiometricalAuthentication;
lib/themes/dark_theme.dart
+5
@@ -12,6 +12,7 @@ import 'package:cake_wallet/themes/extensions/info_theme.dart';
12 import 'package:cake_wallet/themes/extensions/keyboard_theme.dart';
13 import 'package:cake_wallet/themes/extensions/menu_theme.dart';
14 import 'package:cake_wallet/themes/extensions/new_wallet_theme.dart';
15 +import 'package:cake_wallet/themes/extensions/option_tile_theme.dart';
16 import 'package:cake_wallet/themes/extensions/order_theme.dart';
17 import 'package:cake_wallet/themes/extensions/picker_theme.dart';
18 import 'package:cake_wallet/themes/extensions/pin_code_theme.dart';
@@ -215,6 +216,10 @@ class DarkTheme extends ThemeBase {
216 qrCodeColor: PaletteDark.lightBlueGrey,
217 qrWidgetCopyButtonColor: PaletteDark.lightBlueGrey);
218
219 + @override
220 + OptionTileTheme get optionTileTheme => OptionTileTheme(
221 + titleColor: primaryTextColor, descriptionColor: primaryTextColor, useDarkImage: false);
222 +
223 @override
224 ThemeData get themeData => super.themeData.copyWith(
225 dividerColor: PaletteDark.dividerColor,
lib/themes/extensions/option_tile_theme.dart new
+30
@@ -0,0 +1,30 @@
1 +import 'package:flutter/material.dart';
2 +
3 +class OptionTileTheme extends ThemeExtension<OptionTileTheme> {
4 + final Color titleColor;
5 + final Color descriptionColor;
6 + final bool useDarkImage;
7 +
8 + OptionTileTheme(
9 + {required this.titleColor, required this.descriptionColor, this.useDarkImage = false});
10 +
11 + @override
12 + OptionTileTheme copyWith({Color? titleColor, Color? descriptionColor, bool? useDarkImage}) =>
13 + OptionTileTheme(
14 + titleColor: titleColor ?? this.titleColor,
15 + descriptionColor: descriptionColor ?? this.descriptionColor,
16 + useDarkImage: useDarkImage ?? this.useDarkImage);
17 +
18 + @override
19 + OptionTileTheme lerp(ThemeExtension<OptionTileTheme>? other, double t) {
20 + if (other is! OptionTileTheme) {
21 + return this;
22 + }
23 +
24 + return OptionTileTheme(
25 + titleColor: Color.lerp(titleColor, other.titleColor, t) ?? titleColor,
26 + descriptionColor:
27 + Color.lerp(descriptionColor, other.descriptionColor, t) ?? descriptionColor,
28 + useDarkImage: other.useDarkImage);
29 + }
30 +}
lib/themes/high_contrast_theme.dart
+5
@@ -9,6 +9,7 @@ import 'package:cake_wallet/themes/extensions/exchange_page_theme.dart';
9 import 'package:cake_wallet/themes/extensions/filter_theme.dart';
10 import 'package:cake_wallet/themes/extensions/indicator_dot_theme.dart';
11 import 'package:cake_wallet/themes/extensions/menu_theme.dart';
12 +import 'package:cake_wallet/themes/extensions/option_tile_theme.dart';
13 import 'package:cake_wallet/themes/extensions/picker_theme.dart';
14 import 'package:cake_wallet/themes/extensions/receive_page_theme.dart';
15 import 'package:cake_wallet/themes/extensions/send_page_theme.dart';
@@ -103,6 +104,10 @@ class HighContrastTheme extends MoneroLightTheme {
104 ReceivePageTheme get receivePageTheme => super.receivePageTheme.copyWith(
105 tilesTextColor: Colors.white, iconsBackgroundColor: Colors.grey, iconsColor: Colors.black);
106
107 + @override
108 + OptionTileTheme get optionTileTheme => OptionTileTheme(
109 + titleColor: Colors.white, descriptionColor: Colors.white, useDarkImage: false);
110 +
111 @override
112 ThemeData get themeData => super.themeData.copyWith(
113 disabledColor: Colors.grey,
lib/themes/light_theme.dart
+5
@@ -12,6 +12,7 @@ import 'package:cake_wallet/themes/extensions/info_theme.dart';
12 import 'package:cake_wallet/themes/extensions/keyboard_theme.dart';
13 import 'package:cake_wallet/themes/extensions/menu_theme.dart';
14 import 'package:cake_wallet/themes/extensions/new_wallet_theme.dart';
15 +import 'package:cake_wallet/themes/extensions/option_tile_theme.dart';
16 import 'package:cake_wallet/themes/extensions/order_theme.dart';
17 import 'package:cake_wallet/themes/extensions/picker_theme.dart';
18 import 'package:cake_wallet/themes/extensions/pin_code_theme.dart';
@@ -215,6 +216,10 @@ class LightTheme extends ThemeBase {
216 qrCodeColor: Colors.white,
217 qrWidgetCopyButtonColor: PaletteDark.lightBlueGrey);
218
219 + @override
220 + OptionTileTheme get optionTileTheme => OptionTileTheme(
221 + titleColor: primaryTextColor, descriptionColor: primaryTextColor, useDarkImage: true);
222 +
223 @override
224 ThemeData get themeData => super.themeData.copyWith(
225 dividerColor: Palette.paleBlue,
lib/themes/theme_base.dart
+4
@@ -12,6 +12,7 @@ import 'package:cake_wallet/themes/extensions/info_theme.dart';
12 import 'package:cake_wallet/themes/extensions/keyboard_theme.dart';
13 import 'package:cake_wallet/themes/extensions/menu_theme.dart';
14 import 'package:cake_wallet/themes/extensions/new_wallet_theme.dart';
15 +import 'package:cake_wallet/themes/extensions/option_tile_theme.dart';
16 import 'package:cake_wallet/themes/extensions/order_theme.dart';
17 import 'package:cake_wallet/themes/extensions/picker_theme.dart';
18 import 'package:cake_wallet/themes/extensions/pin_code_theme.dart';
@@ -114,6 +115,8 @@ abstract class ThemeBase {
115
116 QRCodeTheme get qrCodeTheme;
117
118 + OptionTileTheme get optionTileTheme;
119 +
120 ThemeData get themeData => generatedThemeData.copyWith(
121 primaryColor: primaryColor,
122 cardColor: containerColor,
@@ -144,6 +147,7 @@ abstract class ThemeBase {
147 accountListTheme,
148 receivePageTheme,
149 qrCodeTheme,
150 + optionTileTheme
151 ],
152 scrollbarTheme: generatedThemeData.scrollbarTheme.copyWith(
153 thumbColor: MaterialStateProperty.all(scrollbarTheme.thumbColor),
lib/view_model/dashboard/dashboard_view_model.dart
+4
@@ -1,4 +1,5 @@
1 import 'package:cake_wallet/entities/auto_generate_subaddress_status.dart';
2 +import 'package:cake_wallet/entities/buy_provider_types.dart';
3 import 'package:cake_wallet/entities/exchange_api_mode.dart';
4 import 'package:cake_wallet/store/anonpay/anonpay_transactions_store.dart';
5 import 'package:cake_wallet/view_model/dashboard/anonpay_transaction_list_item.dart';
@@ -277,6 +278,9 @@ abstract class DashboardViewModelBase with Store {
278
279 Map<String, List<FilterItem>> filterItems;
280
281 +
282 + BuyProviderType get defaultBuyProvider => settingsStore.defaultBuyProvider;
283 +
284 bool get isBuyEnabled => settingsStore.isBitcoinBuyEnabled;
285
286 bool get shouldShowYatPopup => settingsStore.shouldShowYatPopup;
lib/view_model/settings/other_settings_view_model.dart
+14
@@ -1,4 +1,5 @@
1 import 'package:cake_wallet/bitcoin/bitcoin.dart';
2 +import 'package:cake_wallet/entities/buy_provider_types.dart';
3 import 'package:cake_wallet/entities/priority_for_wallet_type.dart';
4 import 'package:cake_wallet/store/settings_store.dart';
5 import 'package:cw_core/balance.dart';
@@ -48,6 +49,9 @@ abstract class OtherSettingsViewModelBase with Store {
49 return priority;
50 }
51
52 + @computed
53 + BuyProviderType get buyProviderType { return _settingsStore.defaultBuyProvider; }
54 +
55 String getDisplayPriority(dynamic priority) {
56 final _priority = priority as TransactionPriority;
57
@@ -59,6 +63,16 @@ abstract class OtherSettingsViewModelBase with Store {
63 return priority.toString();
64 }
65
66 + String getBuyProviderType (dynamic buyProviderType) {
67 + final _buyProviderType = buyProviderType as BuyProviderType;
68 +
69 + return _buyProviderType.toString();
70 + }
71 +
72 void onDisplayPrioritySelected(TransactionPriority priority) =>
73 _settingsStore.priority[_wallet.type] = priority;
74 +
75 + void onBuyProviderTypeSelected(BuyProviderType buyProviderType) =>
76 + _settingsStore.defaultBuyProvider = buyProviderType;
77 +
78 }
res/values/strings_ar.arb
+7 -3
@@ -682,7 +682,11 @@
682 "support_title_other_links": "روابط دعم أخرى",
683 "support_description_other_links": "انضم إلى مجتمعاتنا أو تصل إلينا شركائنا من خلال أساليب أخرى",
684 "select_destination": ".ﻲﻃﺎﻴﺘﺣﻻﺍ ﺦﺴﻨﻟﺍ ﻒﻠﻣ ﺔﻬﺟﻭ ﺪﻳﺪﺤﺗ ءﺎﺟﺮﻟﺍ",
685 + "auto_generate_subaddresses": "تلقائي توليد subddresses",
686 "save_to_downloads": "ﺕﻼﻳﺰﻨﺘﻟﺍ ﻲﻓ ﻆﻔﺣ",
686 - "support_description_other_links": "انضم إلى مجتمعاتنا أو تصل إلينا شركائنا من خلال أساليب أخرى",
687 - "auto_generate_subaddresses": "تلقائي توليد subddresses"
688 -}
687 + "select_buy_provider_notice": "حدد مزود شراء أعلاه. يمكنك تخطي هذه الشاشة عن طريق تعيين مزود شراء الافتراضي في إعدادات التطبيق.",
688 + "onramper_option_description": "شراء بسرعة التشفير مع العديد من طرق الدفع. متوفر في معظم البلدان. ينتشر وتختلف الرسوم.",
689 + "default_buy_provider": "مزود شراء الافتراضي",
690 + "ask_each_time": "اسأل في كل مرة",
691 + "buy_provider_unavailable": "مزود حاليا غير متوفر."
692 +}
\ No newline at end of file
res/values/strings_bg.arb
+7 -2
@@ -679,5 +679,10 @@
679 "support_title_other_links": "Други връзки за поддръжка",
680 "support_description_other_links": "Присъединете се към нашите общности или се свържете с нас нашите партньори чрез други методи",
681 "select_destination": "Моля, изберете дестинация за архивния файл.",
682 - "save_to_downloads": "Запазване в Изтегляния"
683 -}
682 + "save_to_downloads": "Запазване в Изтегляния",
683 + "select_buy_provider_notice": "Изберете доставчик на покупка по -горе. Можете да пропуснете този екран, като зададете вашия доставчик по подразбиране по подразбиране в настройките на приложението.",
684 + "onramper_option_description": "Бързо купувайте криптовалута с много методи за плащане. Предлага се в повечето страни. Разпространенията и таксите варират.",
685 + "default_buy_provider": "Доставчик по подразбиране купува",
686 + "ask_each_time": "Питайте всеки път",
687 + "buy_provider_unavailable": "Понастоящем доставчик не е наличен."
688 +}
\ No newline at end of file
res/values/strings_cs.arb
+7 -2
@@ -679,5 +679,10 @@
679 "support_title_other_links": "Další odkazy na podporu",
680 "support_description_other_links": "Připojte se k našim komunitám nebo se k nám oslovte další metody",
681 "select_destination": "Vyberte cíl pro záložní soubor.",
682 - "save_to_downloads": "Uložit do Stažených souborů"
683 -}
682 + "save_to_downloads": "Uložit do Stažených souborů",
683 + "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.",
684 + "onramper_option_description": "Rychle si koupte krypto s mnoha metodami plateb. K dispozici ve většině zemí. Rozpětí a poplatky se liší.",
685 + "default_buy_provider": "Výchozí poskytovatel nákupu",
686 + "ask_each_time": "Zeptejte se pokaždé",
687 + "buy_provider_unavailable": "Poskytovatel aktuálně nedostupný."
688 +}
\ No newline at end of file
res/values/strings_de.arb
+20 -15
@@ -364,8 +364,8 @@
364 "enter_your_note": "Geben Sie Ihre Bemerkung ein…",
365 "note_optional": "Bemerkung (optional)",
366 "note_tap_to_change": "Bemerkung (zum Ändern tippen)",
367 - "view_in_block_explorer": "View in Block Explorer",
368 - "view_transaction_on": "View Transaction on ",
367 + "view_in_block_explorer": "In Block Explorer anzeigen",
368 + "view_transaction_on": "Anzeigen der Transaktion auf ",
369 "transaction_key": "Transaktionsschlüssel",
370 "confirmations": "Bestätigungen",
371 "recipient_address": "Empfängeradresse",
@@ -405,11 +405,11 @@
405 "moonpay_alert_text": "Der Wert des Betrags muss größer oder gleich ${minAmount} ${fiatCurrency} sein",
406 "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",
407 "do_not_show_me": "Zeig mir das nicht noch einmal",
408 - "unspent_coins_title": "Nicht ausgegebene Münzen",
409 - "unspent_coins_details_title": "Details zu nicht ausgegebenen Münzen",
408 + "unspent_coins_title": "Nicht ausgegebene Coins",
409 + "unspent_coins_details_title": "Details zu nicht ausgegebenen Coins",
410 "freeze": "Einfrieren",
411 "frozen": "Gefroren",
412 - "coin_control": "Münzkontrolle (optional)",
412 + "coin_control": "Coin Control (optional)",
413 "address_detected": "Adresse erkannt",
414 "address_from_domain": "Diese Adresse ist von ${domain} auf Unstoppable Domains",
415 "add_receiver": "Fügen Sie einen weiteren Empfänger hinzu (optional)",
@@ -551,7 +551,7 @@
551 "custom_redeem_amount": "Benutzerdefinierter Einlösungsbetrag",
552 "add_custom_redemption": "Benutzerdefinierte Einlösung hinzufügen",
553 "remaining": "Rest",
554 - "delete_wallet": "Geldbörse löschen",
554 + "delete_wallet": "Wallet löschen",
555 "delete_wallet_confirm_message": "Sind Sie sicher, dass Sie das ${wallet_name} Wallet löschen möchten?",
556 "low_fee": "Niedrige Gebühr",
557 "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 @@
560 "do_not_share_warning_text": "Teilen Sie diese nicht mit anderen, einschließlich Support.\n\nIhr Geld kann und wird gestohlen werden!",
561 "help": "hilfe",
562 "all_transactions": "Alle Transaktionen",
563 - "all_trades": "Alle Gewerke",
563 + "all_trades": "Alle Trades",
564 "connection_sync": "Verbindung und Synchronisierung",
565 "security_and_backup": "Sicherheit und Datensicherung",
566 "create_backup": "Backup erstellen",
@@ -583,7 +583,7 @@
583 "unmatched_currencies": "Die Währung Ihres aktuellen Wallets stimmt nicht mit der des gescannten QR überein",
584 "orbot_running_alert": "Bitte stellen Sie sicher, dass Orbot läuft, bevor Sie sich mit diesem Knoten verbinden.",
585 "contact_list_contacts": "Kontakte",
586 - "contact_list_wallets": "Meine Geldbörsen",
586 + "contact_list_wallets": "Meine Wallets",
587 "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.",
588 "send_to_this_address": "Senden Sie ${currency} ${tag}an diese Adresse",
589 "arrive_in_this_address": "${currency} ${tag}wird an dieser Adresse ankommen",
@@ -592,8 +592,8 @@
592 "scan_qr_code": "QR-Code scannen",
593 "cold_or_recover_wallet": "Fügen Sie eine Cold Wallet hinzu oder stellen Sie eine Paper Wallet wieder her",
594 "please_wait": "Warten Sie mal",
595 - "sweeping_wallet": "Kehre Geldbörse",
596 - "sweeping_wallet_alert": "Das sollte nicht lange dauern. VERLASSEN SIE DIESEN BILDSCHIRM NICHT, ANDERNFALLS KÖNNEN DIE SWEPT-GELDER VERLOREN GEHEN",
595 + "sweeping_wallet": "Wallet leeren",
596 + "sweeping_wallet_alert": "Das sollte nicht lange dauern. VERLASSEN SIE DIESEN BILDSCHIRM NICHT, ANDERNFALLS KÖNNEN DIE GELDER VERLOREN GEHEN",
597 "decimal_places_error": "Zu viele Nachkommastellen",
598 "edit_node": "Knoten bearbeiten",
599 "frozen_balance": "Gefrorenes Guthaben",
@@ -630,7 +630,7 @@
630 "add_secret_code": "Fügen Sie diesen Geheimcode einem anderen Gerät hinzu",
631 "totp_secret_code": "TOTP-Geheimcode",
632 "important_note": "Wichtiger Hinweis",
633 - "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.",
633 + "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.",
634 "setup_totp_recommended": "TOTP einrichten (empfohlen)",
635 "disable_buy": "Kaufaktion deaktivieren",
636 "disable_sell": "Verkaufsaktion deaktivieren",
@@ -641,7 +641,7 @@
641 "matrix_green_dark_theme": "Matrix Green Dark Theme",
642 "monero_light_theme": "Monero Light-Thema",
643 "auto_generate_subaddresses": "Unteradressen automatisch generieren",
644 - "cake_2fa_preset" : "Cake 2FA-Voreinstellung",
644 + "cake_2fa_preset": "Cake 2FA-Voreinstellung",
645 "narrow": "Eng",
646 "normal": "Normal",
647 "aggressive": "Übereifrig",
@@ -675,7 +675,7 @@
675 "alphabetical": "Alphabetisch",
676 "generate_name": "Namen generieren",
677 "balance_page": "Balance-Seite",
678 - "share": "Aktie",
678 + "share": "Teilen",
679 "slidable": "Verschiebbar",
680 "manage_nodes": "Knoten verwalten",
681 "etherscan_history": "Etherscan-Geschichte",
@@ -687,5 +687,10 @@
687 "support_title_other_links": "Andere Support-Links",
688 "support_description_other_links": "Treten Sie unseren Communities bei oder erreichen Sie uns oder unsere Partner über andere Methoden",
689 "select_destination": "Bitte wählen Sie das Ziel für die Sicherungsdatei aus.",
690 - "save_to_downloads": "Unter „Downloads“ speichern"
691 -}
690 + "save_to_downloads": "Unter „Downloads“ speichern",
691 + "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.",
692 + "onramper_option_description": "Kaufen Sie schnell Krypto mit vielen Zahlungsmethoden. In den meisten Ländern erhältlich. Spreads und Gebühren variieren.",
693 + "default_buy_provider": "Standard-Kaufanbieter",
694 + "ask_each_time": "Jedes Mal fragen",
695 + "buy_provider_unavailable": "Anbieter derzeit nicht verfügbar."
696 +}
\ No newline at end of file
res/values/strings_en.arb
+8 -2
@@ -687,5 +687,11 @@
687 "support_title_other_links": "Other support links",
688 "support_description_other_links": "Join our communities or reach us our our partners through other methods",
689 "select_destination": "Please select destination for the backup file.",
690 - "save_to_downloads": "Save to Downloads"
691 -}
690 + "save_to_downloads": "Save to Downloads",
691 + "select_buy_provider_notice": "Select a buy provider above. You can skip this screen by setting your default buy provider in app settings.",
692 + "onramper_option_description": "Quickly buy crypto with many payment methods. Available in most countries. Spreads and fees vary.",
693 + "default_buy_provider": "Default Buy Provider",
694 + "ask_each_time": "Ask each time",
695 + "robinhood_option_description": "Buy and transfer instantly using your debit card, bank account, or Robinhood balance. USA only.",
696 + "buy_provider_unavailable": "Provider currently unavailable."
697 +}
\ No newline at end of file
res/values/strings_es.arb
+7 -2
@@ -687,5 +687,10 @@
687 "support_title_other_links": "Otros enlaces de soporte",
688 "support_description_other_links": "Únase a nuestras comunidades o comuníquese con nosotros nuestros socios a través de otros métodos",
689 "select_destination": "Seleccione el destino del archivo de copia de seguridad.",
690 - "save_to_downloads": "Guardar en Descargas"
691 -}
690 + "save_to_downloads": "Guardar en Descargas",
691 + "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.",
692 + "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.",
693 + "default_buy_provider": "Proveedor de compra predeterminado",
694 + "ask_each_time": "Pregunta cada vez",
695 + "buy_provider_unavailable": "Proveedor actualmente no disponible."
696 +}
\ No newline at end of file
res/values/strings_fr.arb
+7 -2
@@ -687,5 +687,10 @@
687 "support_title_other_links": "Autres liens d'assistance",
688 "support_description_other_links": "Rejoignez nos communautés ou contactez-nous nos partenaires à travers d'autres méthodes",
689 "select_destination": "Veuillez sélectionner la destination du fichier de sauvegarde.",
690 - "save_to_downloads": "Enregistrer dans les téléchargements"
691 -}
690 + "save_to_downloads": "Enregistrer dans les téléchargements",
691 + "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.",
692 + "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.",
693 + "default_buy_provider": "Fournisseur d'achat par défaut",
694 + "ask_each_time": "Demandez à chaque fois",
695 + "buy_provider_unavailable": "Fournisseur actuellement indisponible."
696 +}
\ No newline at end of file
res/values/strings_ha.arb
+7 -2
@@ -665,5 +665,10 @@
665 "support_title_other_links": "Sauran hanyoyin tallafi",
666 "support_description_other_links": "Kasance tare da al'ummominmu ko kuma ka kai mu abokanmu ta hanyar wasu hanyoyi",
667 "select_destination": "Da fatan za a zaɓi wurin da za a yi wa madadin fayil ɗin.",
668 - "save_to_downloads": "Ajiye zuwa Zazzagewa"
669 -}
668 + "save_to_downloads": "Ajiye zuwa Zazzagewa",
669 + "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.",
670 + "onramper_option_description": "Da sauri sayi Crypto tare da hanyoyin biyan kuɗi da yawa. Akwai a yawancin ƙasashe. Yaduwa da kudade sun bambanta.",
671 + "default_buy_provider": "Tsohuwar Siyarwa",
672 + "ask_each_time": "Tambaya kowane lokaci",
673 + "buy_provider_unavailable": "Mai ba da kyauta a halin yanzu babu."
674 +}
\ No newline at end of file
res/values/strings_hi.arb
+7 -2
@@ -687,5 +687,10 @@
687 "support_title_other_links": "अन्य समर्थन लिंक",
688 "support_description_other_links": "हमारे समुदायों में शामिल हों या अन्य तरीकों के माध्यम से हमारे साथी तक पहुंचें",
689 "select_destination": "कृपया बैकअप फ़ाइल के लिए गंतव्य का चयन करें।",
690 - "save_to_downloads": "डाउनलोड में सहेजें"
691 -}
690 + "save_to_downloads": "डाउनलोड में सहेजें",
691 + "select_buy_provider_notice": "ऊपर एक खरीद प्रदाता का चयन करें। आप इस स्क्रीन को ऐप सेटिंग्स में अपना डिफ़ॉल्ट बाय प्रदाता सेट करके छोड़ सकते हैं।",
692 + "onramper_option_description": "जल्दी से कई भुगतान विधियों के साथ क्रिप्टो खरीदें। अधिकांश देशों में उपलब्ध है। फैलता है और फीस अलग -अलग होती है।",
693 + "default_buy_provider": "डिफ़ॉल्ट खरीद प्रदाता",
694 + "ask_each_time": "हर बार पूछें",
695 + "buy_provider_unavailable": "वर्तमान में प्रदाता अनुपलब्ध है।"
696 +}
\ No newline at end of file
res/values/strings_hr.arb
+7 -2
@@ -687,5 +687,10 @@
687 "support_title_other_links": "Ostale veze za podršku",
688 "support_description_other_links": "Pridružite se našim zajednicama ili nam dosegnu naše partnere drugim metodama",
689 "select_destination": "Odaberite odredište za datoteku sigurnosne kopije.",
690 - "save_to_downloads": "Spremi u Preuzimanja"
691 -}
690 + "save_to_downloads": "Spremi u Preuzimanja",
691 + "select_buy_provider_notice": "Odaberite gornji davatelj kupnje. Ovaj zaslon možete preskočiti postavljanjem zadanog davatelja usluga kupnje u postavkama aplikacija.",
692 + "onramper_option_description": "Brzo kupite kriptovalute s mnogim načinima plaćanja. Dostupno u većini zemalja. Širenja i naknade variraju.",
693 + "default_buy_provider": "Zadani davatelj kupnje",
694 + "ask_each_time": "Pitajte svaki put",
695 + "buy_provider_unavailable": "Davatelj trenutno nije dostupan."
696 +}
\ No newline at end of file
res/values/strings_id.arb
+7 -2
@@ -675,5 +675,10 @@
675 "support_title_other_links": "Tautan dukungan lainnya",
676 "support_description_other_links": "Bergabunglah dengan komunitas kami atau hubungi kami mitra kami melalui metode lain",
677 "select_destination": "Silakan pilih tujuan untuk file cadangan.",
678 - "save_to_downloads": "Simpan ke Unduhan"
679 -}
678 + "save_to_downloads": "Simpan ke Unduhan",
679 + "select_buy_provider_notice": "Pilih penyedia beli di atas. Anda dapat melewatkan layar ini dengan mengatur penyedia pembelian default Anda di pengaturan aplikasi.",
680 + "onramper_option_description": "Beli crypto dengan cepat dengan banyak metode pembayaran. Tersedia di sebagian besar negara. Spread dan biaya bervariasi.",
681 + "default_buy_provider": "Penyedia beli default",
682 + "ask_each_time": "Tanyakan setiap kali",
683 + "buy_provider_unavailable": "Penyedia saat ini tidak tersedia."
684 +}
\ No newline at end of file
res/values/strings_it.arb
+7 -2
@@ -687,5 +687,10 @@
687 "support_title_other_links": "Altri collegamenti di supporto",
688 "support_description_other_links": "Unisciti alle nostre comunità o raggiungici i nostri partner attraverso altri metodi",
689 "select_destination": "Seleziona la destinazione per il file di backup.",
690 - "save_to_downloads": "Salva in Download"
691 -}
690 + "save_to_downloads": "Salva in Download",
691 + "select_buy_provider_notice": "Seleziona un fornitore di acquisto sopra. È possibile saltare questa schermata impostando il provider di acquisto predefinito nelle impostazioni dell'app.",
692 + "onramper_option_description": "Acquista rapidamente la criptovaluta con molti metodi di pagamento. Disponibile nella maggior parte dei paesi. Gli spread e le commissioni variano.",
693 + "default_buy_provider": "Provider di acquisto predefinito",
694 + "ask_each_time": "Chiedi ogni volta",
695 + "buy_provider_unavailable": "Provider attualmente non disponibile."
696 +}
\ No newline at end of file
res/values/strings_ja.arb
+7 -2
@@ -686,6 +686,11 @@
686 "support_title_other_links": "その他のサポートリンク",
687 "support_description_other_links": "私たちのコミュニティに参加するか、他の方法を通して私たちのパートナーに連絡してください",
688 "select_destination": "バックアップファイルの保存先を選択してください。",
689 + "auto_generate_subaddresses": "Autoはサブアドレスを生成します",
690 "save_to_downloads": "ダウンロードに保存",
690 - "auto_generate_subaddresses": "Autoはサブアドレスを生成します"
691 -}
691 + "select_buy_provider_notice": "上記の購入プロバイダーを選択してください。デフォルトの購入プロバイダーをアプリ設定で設定して、この画面をスキップできます。",
692 + "onramper_option_description": "多くの支払い方法で暗号をすばやく購入してください。ほとんどの国で利用可能です。スプレッドと料金は異なります。",
693 + "default_buy_provider": "デフォルトの購入プロバイダー",
694 + "ask_each_time": "毎回尋ねてください",
695 + "buy_provider_unavailable": "現在、プロバイダーは利用できません。"
696 +}
\ No newline at end of file
res/values/strings_ko.arb
+7 -2
@@ -686,6 +686,11 @@
686 "support_title_other_links": "다른 지원 링크",
687 "support_description_other_links": "다른 방법을 통해 커뮤니티에 가입하거나 파트너에게 연락하십시오.",
688 "select_destination": "백업 파일의 대상을 선택하십시오.",
689 + "auto_generate_subaddresses": "자동 생성 서브 아드 드레스",
690 "save_to_downloads": "다운로드에 저장",
690 - "auto_generate_subaddresses": "자동 생성 서브 아드 드레스"
691 -}
691 + "select_buy_provider_notice": "위의 구매 제공자를 선택하십시오. 앱 설정에서 기본 구매 제공자를 설정 하여이 화면을 건너 뛸 수 있습니다.",
692 + "onramper_option_description": "많은 결제 방법으로 암호화를 신속하게 구입하십시오. 대부분의 국가에서 사용할 수 있습니다. 스프레드와 수수료는 다양합니다.",
693 + "default_buy_provider": "기본 구매 제공자",
694 + "ask_each_time": "매번 물어보십시오",
695 + "buy_provider_unavailable": "제공자는 현재 사용할 수 없습니다."
696 +}
\ No newline at end of file
res/values/strings_my.arb
+7 -2
@@ -684,6 +684,11 @@
684 "support_title_other_links": "အခြားအထောက်အပံ့လင့်များ",
685 "support_description_other_links": "ကျွန်ုပ်တို့၏လူမှုအသိုင်းအဝိုင်းများသို့ 0 င်ရောက်ပါ",
686 "select_destination": "အရန်ဖိုင်အတွက် ဦးတည်ရာကို ရွေးပါ။",
687 + "auto_generate_subaddresses": "အော်တို Generate Subaddresses",
688 "save_to_downloads": "ဒေါင်းလုဒ်များထံ သိမ်းဆည်းပါ။",
688 - "auto_generate_subaddresses": "အော်တို Generate Subaddresses"
689 -}
689 + "select_buy_provider_notice": "အပေါ်ကဝယ်သူတစ် ဦး ကိုရွေးချယ်ပါ။ သင်၏ default 0 ယ်သူအား app settings တွင် setting လုပ်ခြင်းဖြင့်ဤ screen ကိုကျော်သွားနိုင်သည်။",
690 + "onramper_option_description": "ငွေပေးချေမှုနည်းလမ်းများစွာဖြင့် Crypto ကိုလျင်မြန်စွာ 0 ယ်ပါ။ နိုင်ငံအများစုတွင်ရရှိနိုင်ပါသည်။ ဖြန့်ဖြူးနှင့်အခကြေးငွေကွဲပြားခြားနားသည်။",
691 + "default_buy_provider": "Default Provider ကိုဝယ်ပါ",
692 + "ask_each_time": "တစ်ခုချင်းစီကိုအချိန်မေးပါ",
693 + "buy_provider_unavailable": "လက်ရှိတွင်လက်ရှိမရနိုင်ပါ။"
694 +}
\ No newline at end of file
res/values/strings_nl.arb
+7 -2
@@ -687,5 +687,10 @@
687 "support_title_other_links": "Andere ondersteuningslinks",
688 "support_description_other_links": "Word lid van onze gemeenschappen of bereik ons ​​onze partners via andere methoden",
689 "select_destination": "Selecteer de bestemming voor het back-upbestand.",
690 - "save_to_downloads": "Opslaan in downloads"
691 -}
690 + "save_to_downloads": "Opslaan in downloads",
691 + "select_buy_provider_notice": "Selecteer hierboven een koopprovider. U kunt dit scherm overslaan door uw standaard kopenprovider in te stellen in app -instellingen.",
692 + "onramper_option_description": "Koop snel crypto met veel betaalmethoden. Beschikbaar in de meeste landen. Spreads en vergoedingen variëren.",
693 + "default_buy_provider": "Standaard Koopprovider",
694 + "ask_each_time": "Vraag het elke keer",
695 + "buy_provider_unavailable": "Provider momenteel niet beschikbaar."
696 +}
\ No newline at end of file
res/values/strings_pl.arb
+7 -2
@@ -687,5 +687,10 @@
687 "support_title_other_links": "Inne linki wsparcia",
688 "support_description_other_links": "Dołącz do naszych społeczności lub skontaktuj się z nami naszymi partnerami za pomocą innych metod",
689 "select_destination": "Wybierz miejsce docelowe dla pliku kopii zapasowej.",
690 - "save_to_downloads": "Zapisz w Pobranych"
691 -}
690 + "save_to_downloads": "Zapisz w Pobranych",
691 + "select_buy_provider_notice": "Wybierz powyższe dostawcę zakupu. Możesz pominąć ten ekran, ustawiając domyślnego dostawcę zakupu w ustawieniach aplikacji.",
692 + "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ę.",
693 + "default_buy_provider": "Domyślny dostawca zakupu",
694 + "ask_each_time": "Zapytaj za każdym razem",
695 + "buy_provider_unavailable": "Dostawca obecnie niedostępny."
696 +}
\ No newline at end of file
res/values/strings_pt.arb
+7 -2
@@ -686,5 +686,10 @@
686 "support_title_other_links": "Outros links de suporte",
687 "support_description_other_links": "Junte -se às nossas comunidades ou chegue a nós nossos parceiros por meio de outros métodos",
688 "select_destination": "Selecione o destino para o arquivo de backup.",
689 - "save_to_downloads": "Salvar em Downloads"
690 -}
689 + "save_to_downloads": "Salvar em Downloads",
690 + "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.",
691 + "onramper_option_description": "Compre rapidamente criptografia com muitos métodos de pagamento. Disponível na maioria dos países. Os spreads e taxas variam.",
692 + "default_buy_provider": "Provedor de compra padrão",
693 + "ask_each_time": "Pergunte cada vez",
694 + "buy_provider_unavailable": "Provedor atualmente indisponível."
695 +}
\ No newline at end of file
res/values/strings_ru.arb
+7 -2
@@ -686,6 +686,11 @@
686 "support_title_other_links": "Другие ссылки на поддержку",
687 "support_description_other_links": "Присоединяйтесь к нашим сообществам или охватите нас наших партнеров с помощью других методов",
688 "select_destination": "Пожалуйста, выберите место для файла резервной копии.",
689 + "auto_generate_subaddresses": "Авто генерируйте Subaddresses",
690 "save_to_downloads": "Сохранить в загрузках",
690 - "auto_generate_subaddresses": "Авто генерируйте Subaddresses"
691 -}
691 + "select_buy_provider_notice": "Выберите поставщика покупки выше. Вы можете пропустить этот экран, установив поставщика покупки по умолчанию в настройках приложения.",
692 + "onramper_option_description": "Быстро купите крипто со многими способами оплаты. Доступно в большинстве стран. Спреды и сборы различаются.",
693 + "default_buy_provider": "По умолчанию поставщик покупки",
694 + "ask_each_time": "Спросите каждый раз",
695 + "buy_provider_unavailable": "Поставщик в настоящее время недоступен."
696 +}
\ No newline at end of file
res/values/strings_th.arb
+7 -2
@@ -684,6 +684,11 @@
684 "support_title_other_links": "ลิงค์สนับสนุนอื่น ๆ",
685 "support_description_other_links": "เข้าร่วมชุมชนของเราหรือเข้าถึงเราพันธมิตรของเราผ่านวิธีการอื่น ๆ",
686 "select_destination": "โปรดเลือกปลายทางสำหรับไฟล์สำรอง",
687 + "auto_generate_subaddresses": "Auto สร้าง subaddresses",
688 "save_to_downloads": "บันทึกลงดาวน์โหลด",
688 - "auto_generate_subaddresses": "Auto สร้าง subaddresses"
689 -}
689 + "select_buy_provider_notice": "เลือกผู้ให้บริการซื้อด้านบน คุณสามารถข้ามหน้าจอนี้ได้โดยการตั้งค่าผู้ให้บริการซื้อเริ่มต้นในการตั้งค่าแอป",
690 + "onramper_option_description": "ซื้อ crypto อย่างรวดเร็วด้วยวิธีการชำระเงินจำนวนมาก มีให้บริการในประเทศส่วนใหญ่ สเปรดและค่าธรรมเนียมแตกต่างกันไป",
691 + "default_buy_provider": "ผู้ให้บริการซื้อเริ่มต้น",
692 + "ask_each_time": "ถามทุกครั้ง",
693 + "buy_provider_unavailable": "ผู้ให้บริการไม่สามารถใช้งานได้ในปัจจุบัน"
694 +}
\ No newline at end of file
res/values/strings_tr.arb
+7 -2
@@ -685,5 +685,10 @@
685 "support_title_other_links": "Diğer destek bağlantıları",
686 "support_description_other_links": "Topluluklarımıza katılın veya ortaklarımıza diğer yöntemlerle bize ulaşın",
687 "select_destination": "Lütfen yedekleme dosyası için hedef seçin.",
688 - "save_to_downloads": "İndirilenlere Kaydet"
689 -}
688 + "save_to_downloads": "İndirilenlere Kaydet",
689 + "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.",
690 + "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.",
691 + "default_buy_provider": "Varsayılan Satın Alma Sağlayıcısı",
692 + "ask_each_time": "Her seferinde sor",
693 + "buy_provider_unavailable": "Sağlayıcı şu anda kullanılamıyor."
694 +}
\ No newline at end of file
res/values/strings_uk.arb
+7 -2
@@ -687,5 +687,10 @@
687 "support_title_other_links": "Інші посилання на підтримку",
688 "support_description_other_links": "Приєднуйтесь до наших спільнот або досягайте нас нашими партнерами іншими методами",
689 "select_destination": "Виберіть місце призначення для файлу резервної копії.",
690 - "save_to_downloads": "Зберегти до завантажень"
691 -}
690 + "save_to_downloads": "Зберегти до завантажень",
691 + "select_buy_provider_notice": "Виберіть постачальника купівлі вище. Ви можете пропустити цей екран, встановивши свого постачальника купівлі за замовчуванням у налаштуваннях додатків.",
692 + "onramper_option_description": "Швидко купуйте криптовалюту з багатьма методами оплати. Доступний у більшості країн. Поширення та збори різняться.",
693 + "default_buy_provider": "Постачальник покупки за замовчуванням",
694 + "ask_each_time": "Запитайте кожен раз",
695 + "buy_provider_unavailable": "В даний час постачальник недоступний."
696 +}
\ No newline at end of file
res/values/strings_ur.arb
+7 -2
@@ -678,6 +678,11 @@
678 "support_title_other_links": "دوسرے سپورٹ لنکس",
679 "support_description_other_links": "ہماری برادریوں میں شامل ہوں یا دوسرے طریقوں سے ہمارے شراکت داروں تک پہنچیں",
680 "select_destination": "۔ﮟﯾﺮﮐ ﺏﺎﺨﺘﻧﺍ ﺎﮐ ﻝﺰﻨﻣ ﮯﯿﻟ ﮯﮐ ﻞﺋﺎﻓ ﭖﺍ ﮏﯿﺑ ﻡﺮﮐ ﮦﺍﺮﺑ",
681 + "auto_generate_subaddresses": "آٹو سب ایڈریس تیار کرتا ہے",
682 "save_to_downloads": "۔ﮟﯾﺮﮐ ﻅﻮﻔﺤﻣ ﮟﯿﻣ ﺯﮈﻮﻟ ﻥﺅﺍﮈ",
682 - "auto_generate_subaddresses": "آٹو سب ایڈریس تیار کرتا ہے"
683 -}
683 + "select_buy_provider_notice": "اوپر خریدنے والا خریدنے والا منتخب کریں۔ آپ ایپ کی ترتیبات میں اپنے پہلے سے طے شدہ خریدنے والے کو ترتیب دے کر اس اسکرین کو چھوڑ سکتے ہیں۔",
684 + "onramper_option_description": "ادائیگی کے بہت سے طریقوں سے جلدی سے کرپٹو خریدیں۔ زیادہ تر ممالک میں دستیاب ہے۔ پھیلاؤ اور فیس مختلف ہوتی ہے۔",
685 + "default_buy_provider": "پہلے سے طے شدہ خریدنے والا",
686 + "ask_each_time": "ہر بار پوچھیں",
687 + "buy_provider_unavailable": "فراہم کنندہ فی الحال دستیاب نہیں ہے۔"
688 +}
\ No newline at end of file
res/values/strings_yo.arb
+7 -2
@@ -680,6 +680,11 @@
680 "matrix_green_dark_theme": "Matrix Green Dark Akori",
681 "monero_light_theme": "Monero Light Akori",
682 "select_destination": "Jọwọ yan ibi ti o nlo fun faili afẹyinti.",
683 + "auto_generate_subaddresses": "Aṣiṣe Ibi-Afọwọkọ",
684 "save_to_downloads": "Fipamọ si Awọn igbasilẹ",
684 - "auto_generate_subaddresses": "Aṣiṣe Ibi-Afọwọkọ"
685 -}
685 + "select_buy_provider_notice": "Yan olupese Ra loke. O le skii iboju yii nipa ṣiṣeto olupese rẹ ni awọn eto App.",
686 + "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ọ.",
687 + "default_buy_provider": "Aiyipada Ra Olupese",
688 + "ask_each_time": "Beere lọwọ kọọkan",
689 + "buy_provider_unavailable": "Olupese lọwọlọwọ ko si."
690 +}
\ No newline at end of file
res/values/strings_zh.arb
+7 -2
@@ -685,6 +685,11 @@
685 "matrix_green_dark_theme": "矩阵绿暗主题",
686 "monero_light_theme": "门罗币浅色主题",
687 "select_destination": "请选择备份文件的目的地。",
688 + "auto_generate_subaddresses": "自动生成子辅助",
689 "save_to_downloads": "保存到下载",
689 - "auto_generate_subaddresses": "自动生成子辅助"
690 -}
690 + "select_buy_provider_notice": "在上面选择买入提供商。您可以通过在应用程序设置中设置默认的购买提供商来跳过此屏幕。",
691 + "onramper_option_description": "快速使用许多付款方式购买加密货币。在大多数国家 /地区可用。利差和费用各不相同。",
692 + "default_buy_provider": "默认购买提供商",
693 + "ask_each_time": "每次问",
694 + "buy_provider_unavailable": "提供者目前不可用。"
695 +}
\ No newline at end of file
tool/utils/secret_key.dart
+2
@@ -32,6 +32,8 @@ class SecretKey {
32 SecretKey('fiatApiKey', () => ''),
33 SecretKey('payfuraApiKey', () => ''),
34 SecretKey('chatwootWebsiteToken', () => ''),
35 + SecretKey('robinhoodApplicationId', () => ''),
36 + SecretKey('robinhoodCIdApiSecret', () => ''),
37 ];
38
39 static final ethereumSecrets = [