CW-526-Add-Trocador-providers-option-in-privacy-settings (#1177)

* Trocador settings page UI * add trocador providers states * add settings icon * minor fix * Revert "minor fix" This reverts commit e59d9dfdaf9bd1c13a17ae5012db80a8f973a4d4. * fixes * PR fixes * remove trocador menu icon

Serhii committed Nov 17, 2023 at 01:23 UTC 36361ef329fb7ae988d3e4e48b4109b7b98cc836
9 files changed +134 -5
lib/di.dart
+6
@@ -45,6 +45,7 @@ import 'package:cake_wallet/src/screens/ionia/cards/ionia_custom_redeem_page.dar
45 import 'package:cake_wallet/src/screens/ionia/cards/ionia_gift_card_detail_page.dart';
46 import 'package:cake_wallet/src/screens/ionia/cards/ionia_more_options_page.dart';
47 import 'package:cake_wallet/src/screens/settings/connection_sync_page.dart';
48 +import 'package:cake_wallet/src/screens/settings/trocador_providers_page.dart';
49 import 'package:cake_wallet/src/screens/setup_2fa/modify_2fa_page.dart';
50 import 'package:cake_wallet/src/screens/setup_2fa/setup_2fa_qr_page.dart';
51 import 'package:cake_wallet/src/screens/setup_2fa/setup_2fa.dart';
@@ -92,6 +93,7 @@ import 'package:cake_wallet/view_model/settings/other_settings_view_model.dart';
93 import 'package:cake_wallet/view_model/settings/privacy_settings_view_model.dart';
94 import 'package:cake_wallet/view_model/settings/security_settings_view_model.dart';
95 import 'package:cake_wallet/view_model/advanced_privacy_settings_view_model.dart';
96 +import 'package:cake_wallet/view_model/settings/trocador_providers_view_model.dart';
97 import 'package:cake_wallet/view_model/wallet_address_list/wallet_address_list_item.dart';
98 import 'package:cake_wallet/view_model/wallet_list/wallet_edit_view_model.dart';
99 import 'package:cake_wallet/view_model/wallet_list/wallet_list_item.dart';
@@ -699,6 +701,8 @@ Future<void> setup({
701 return PrivacySettingsViewModel(getIt.get<SettingsStore>(), getIt.get<AppStore>().wallet!);
702 });
703
704 + getIt.registerFactory(() => TrocadorProvidersViewModel(getIt.get<SettingsStore>()));
705 +
706 getIt.registerFactory(() {
707 return OtherSettingsViewModel(getIt.get<SettingsStore>(), getIt.get<AppStore>().wallet!);
708 });
@@ -752,6 +756,8 @@ Future<void> setup({
756
757 getIt.registerFactory(() => PrivacyPage(getIt.get<PrivacySettingsViewModel>()));
758
759 + getIt.registerFactory(() => TrocadorProvidersPage(getIt.get<TrocadorProvidersViewModel>()));
760 +
761 getIt.registerFactory(() => DomainLookupsPage(getIt.get<PrivacySettingsViewModel>()));
762
763 getIt.registerFactory(() => DisplaySettingsPage(getIt.get<DisplaySettingsViewModel>()));
lib/exchange/provider/trocador_exchange_provider.dart
+39 -4
@@ -12,11 +12,27 @@ import 'package:cw_core/crypto_currency.dart';
12 import 'package:http/http.dart';
13
14 class TrocadorExchangeProvider extends ExchangeProvider {
15 - TrocadorExchangeProvider({this.useTorOnly = false})
16 - : _lastUsedRateId = '',
15 + TrocadorExchangeProvider({this.useTorOnly = false, this.providerStates = const {}})
16 + : _lastUsedRateId = '', _provider = [],
17 super(pairList: supportedPairs(_notSupported));
18
19 bool useTorOnly;
20 + final Map<String, bool> providerStates;
21 +
22 + static const List<String> availableProviders = [
23 + 'Swapter',
24 + 'StealthEx',
25 + 'Simpleswap',
26 + 'Swapuz'
27 + 'ChangeNow',
28 + 'Changehero',
29 + 'FixedFloat',
30 + 'LetsExchange',
31 + 'Exolix',
32 + 'Godex',
33 + 'Exch',
34 + 'CoinCraddle'
35 + ];
36
37 static const List<CryptoCurrency> _notSupported = [
38 CryptoCurrency.stx,
@@ -33,6 +49,7 @@ class TrocadorExchangeProvider extends ExchangeProvider {
49 static const coinPath = 'api/coin';
50
51 String _lastUsedRateId;
52 + List<dynamic> _provider;
53
54 @override
55 String get title => 'Trocador';
@@ -105,7 +122,6 @@ class TrocadorExchangeProvider extends ExchangeProvider {
122 'payment': isFixedRateMode ? 'True' : 'False',
123 'min_kycrating': 'C',
124 'markup': markup,
108 - 'best_only': 'True',
125 };
126
127 final uri = await _getUri(newRatePath, params);
@@ -115,6 +131,9 @@ class TrocadorExchangeProvider extends ExchangeProvider {
131 final toAmount = double.parse(responseJSON['amount_to'].toString());
132 final rateId = responseJSON['trade_id'] as String? ?? '';
133
134 + var quotes = responseJSON['quotes']['quotes'] as List;
135 + _provider = quotes.map((quote) => quote['provider']).toList();
136 +
137 if (rateId.isNotEmpty) _lastUsedRateId = rateId;
138
139 return isReceiveAmount ? (amount / fromAmount) : (toAmount / amount);
@@ -126,6 +145,7 @@ class TrocadorExchangeProvider extends ExchangeProvider {
145
146 @override
147 Future<Trade> createTrade({required TradeRequest request, required bool isFixedRateMode}) async {
148 +
149 final params = {
150 'api_key': apiKey,
151 'ticker_from': _normalizeCurrency(request.fromCurrency),
@@ -135,7 +155,6 @@ class TrocadorExchangeProvider extends ExchangeProvider {
155 'payment': isFixedRateMode ? 'True' : 'False',
156 'min_kycrating': 'C',
157 'markup': markup,
138 - 'best_only': 'True',
158 if (!isFixedRateMode) 'amount_from': request.fromAmount,
159 if (isFixedRateMode) 'amount_to': request.toAmount,
160 'address': request.toAddress,
@@ -153,6 +172,22 @@ class TrocadorExchangeProvider extends ExchangeProvider {
172 params['id'] = _lastUsedRateId;
173 }
174
175 +
176 + String firstAvailableProvider = '';
177 +
178 + for (var provider in _provider) {
179 + if (providerStates.containsKey(provider) && providerStates[provider] == true) {
180 + firstAvailableProvider = provider as String;
181 + break;
182 + }
183 + }
184 +
185 + if (firstAvailableProvider.isEmpty) {
186 + throw Exception('No available provider is enabled');
187 + }
188 +
189 + params['provider'] = firstAvailableProvider;
190 +
191 final uri = await _getUri(createTradePath, params);
192 final response = await get(uri);
193
lib/router.dart
+5
@@ -42,6 +42,7 @@ import 'package:cake_wallet/src/screens/restore/restore_from_backup_page.dart';
42 import 'package:cake_wallet/src/screens/restore/wallet_restore_page.dart';
43 import 'package:cake_wallet/src/screens/seed/pre_seed_page.dart';
44 import 'package:cake_wallet/src/screens/settings/connection_sync_page.dart';
45 +import 'package:cake_wallet/src/screens/settings/trocador_providers_page.dart';
46 import 'package:cake_wallet/src/screens/setup_2fa/modify_2fa_page.dart';
47 import 'package:cake_wallet/src/screens/setup_2fa/setup_2fa_qr_page.dart';
48 import 'package:cake_wallet/src/screens/setup_2fa/setup_2fa.dart';
@@ -323,6 +324,10 @@ Route<dynamic> createRoute(RouteSettings settings) {
324 return CupertinoPageRoute<void>(
325 fullscreenDialog: true, builder: (_) => getIt.get<PrivacyPage>());
326
327 + case Routes.trocadorProvidersPage:
328 + return CupertinoPageRoute<void>(
329 + fullscreenDialog: true, builder: (_) => getIt.get<TrocadorProvidersPage>());
330 +
331 case Routes.domainLookupsPage:
332 return CupertinoPageRoute<void>(
333 fullscreenDialog: true, builder: (_) => getIt.get<DomainLookupsPage>());
lib/routes.dart
+1
@@ -81,6 +81,7 @@ class Routes {
81 static const connectionSync = '/connection_sync_page';
82 static const securityBackupPage = '/security_and_backup_page';
83 static const privacyPage = '/privacy_page';
84 + static const trocadorProvidersPage = '/trocador_providers_page';
85 static const domainLookupsPage = '/domain_lookups_page';
86 static const displaySettingsPage = '/display_settings_page';
87 static const otherSettingsPage = '/other_settings_page';
lib/src/screens/settings/privacy_page.dart
+4
@@ -90,6 +90,10 @@ class PrivacyPage extends BasePage {
90 title: S.current.domain_looks_up,
91 handler: (context) => Navigator.of(context).pushNamed(Routes.domainLookupsPage),
92 ),
93 + SettingsCellWithArrow(
94 + title: 'Trocador providers',
95 + handler: (context) => Navigator.of(context).pushNamed(Routes.trocadorProvidersPage),
96 + ),
97 ],
98 );
99 }),
lib/src/screens/settings/trocador_providers_page.dart new
+37
@@ -0,0 +1,37 @@
1 +import 'package:cake_wallet/exchange/provider/trocador_exchange_provider.dart';
2 +import 'package:cake_wallet/src/screens/base_page.dart';
3 +import 'package:cake_wallet/src/screens/settings/widgets/settings_switcher_cell.dart';
4 +import 'package:cake_wallet/view_model/settings/trocador_providers_view_model.dart';
5 +import 'package:flutter/material.dart';
6 +import 'package:flutter_mobx/flutter_mobx.dart';
7 +
8 +class TrocadorProvidersPage extends BasePage {
9 + TrocadorProvidersPage(this.trocadorProvidersViewModel);
10 +
11 + @override
12 + String get title => 'Trocador Providers';
13 +
14 + final TrocadorProvidersViewModel trocadorProvidersViewModel;
15 +
16 + @override
17 + Widget body(BuildContext context) {
18 + final availableProviders = TrocadorExchangeProvider.availableProviders;
19 + final providerStates = trocadorProvidersViewModel.providerStates;
20 + return Container(
21 + padding: EdgeInsets.only(top: 10),
22 + child: ListView.builder(
23 + itemCount: availableProviders.length,
24 + itemBuilder: (_, index) {
25 + String provider = availableProviders[index];
26 + return Observer(
27 + builder: (_) => SettingsSwitcherCell(
28 + title: provider,
29 + value: providerStates[provider] ?? false,
30 + onValueChange: (BuildContext _, bool value) {
31 + trocadorProvidersViewModel.toggleProviderState(provider);
32 + }));
33 + },
34 + ),
35 + );
36 + }
37 +}
lib/store/settings_store.dart
+19
@@ -10,6 +10,7 @@ import 'package:cake_wallet/entities/exchange_api_mode.dart';
10 import 'package:cake_wallet/entities/pin_code_required_duration.dart';
11 import 'package:cake_wallet/entities/preferences_key.dart';
12 import 'package:cake_wallet/entities/sort_balance_types.dart';
13 +import 'package:cake_wallet/exchange/provider/trocador_exchange_provider.dart';
14 import 'package:cake_wallet/view_model/settings/sync_mode.dart';
15 import 'package:cake_wallet/utils/device_info.dart';
16 import 'package:cake_wallet/ethereum/ethereum.dart';
@@ -161,6 +162,8 @@ abstract class SettingsStoreBase with Store {
162 priority[WalletType.bitcoinCash] = initialBitcoinCashTransactionPriority;
163 }
164
165 + initializeTrocadorProviderStates();
166 +
167 reaction(
168 (_) => fiatCurrency,
169 (FiatCurrency fiatCurrency) => sharedPreferences.setString(
@@ -515,6 +518,9 @@ abstract class SettingsStoreBase with Store {
518 @observable
519 ObservableMap<WalletType, TransactionPriority> priority;
520
521 + @observable
522 + ObservableMap<String, bool> trocadorProviderStates = ObservableMap<String, bool>();
523 +
524 @observable
525 SortBalanceBy sortBalanceBy;
526
@@ -1059,6 +1065,19 @@ abstract class SettingsStoreBase with Store {
1065 powNodes[walletType] = node;
1066 }
1067
1068 + void initializeTrocadorProviderStates() {
1069 + for (var provider in TrocadorExchangeProvider.availableProviders) {
1070 + final savedState = _sharedPreferences.getBool(provider) ?? true;
1071 + trocadorProviderStates[provider] = savedState;
1072 + }
1073 + }
1074 +
1075 + void saveTrocadorProviderState(String providerName, bool state) {
1076 + _sharedPreferences.setBool(providerName, state);
1077 + trocadorProviderStates[providerName] = state;
1078 + }
1079 +
1080 +
1081 static Future<String?> _getDeviceName() async {
1082 String? deviceName = '';
1083 final deviceInfoPlugin = DeviceInfoPlugin();
lib/view_model/exchange/exchange_view_model.dart
+2 -1
@@ -146,7 +146,8 @@ abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with
146 ChangeNowExchangeProvider(settingsStore: _settingsStore),
147 SideShiftExchangeProvider(),
148 SimpleSwapExchangeProvider(),
149 - TrocadorExchangeProvider(useTorOnly: _useTorOnly),
149 + TrocadorExchangeProvider(useTorOnly: _useTorOnly,
150 + providerStates: _settingsStore.trocadorProviderStates),
151 if (FeatureFlag.isExolixEnabled) ExolixExchangeProvider(),
152 ];
153
lib/view_model/settings/trocador_providers_view_model.dart new
+21
@@ -0,0 +1,21 @@
1 +import 'package:cake_wallet/store/settings_store.dart';
2 +import 'package:mobx/mobx.dart';
3 +
4 +part 'trocador_providers_view_model.g.dart';
5 +
6 +class TrocadorProvidersViewModel = TrocadorProvidersViewModelBase with _$TrocadorProvidersViewModel;
7 +
8 +abstract class TrocadorProvidersViewModelBase with Store {
9 + TrocadorProvidersViewModelBase(this._settingsStore);
10 +
11 + final SettingsStore _settingsStore;
12 +
13 + @computed
14 + Map<String, bool> get providerStates => _settingsStore.trocadorProviderStates;
15 +
16 + @action
17 + void toggleProviderState(String providerName) {
18 + final currentState = providerStates[providerName] ?? false;
19 + _settingsStore.saveTrocadorProviderState(providerName, !currentState);
20 + }
21 +}
\ No newline at end of file