diff --git a/lib/core/wallet_change_listener_view_model.dart b/lib/core/wallet_change_listener_view_model.dart new file mode 100644 index 000000000..6735afee5 --- /dev/null +++ b/lib/core/wallet_change_listener_view_model.dart @@ -0,0 +1,30 @@ +import 'package:cw_core/balance.dart'; +import 'package:cw_core/transaction_history.dart'; +import 'package:cw_core/transaction_info.dart'; +import 'package:mobx/mobx.dart'; +import 'package:cw_core/wallet_base.dart'; +import 'package:cake_wallet/store/app_store.dart'; + +part 'wallet_change_listener_view_model.g.dart'; + +class WalletChangeListenerViewModel = WalletChangeListenerViewModelBase + with _$WalletChangeListenerViewModel; + +abstract class WalletChangeListenerViewModelBase with Store { + WalletChangeListenerViewModelBase({ + required AppStore appStore, + }) : _wallet = appStore.wallet! { + reaction((_) => appStore.wallet, (WalletBase? wallet) { + _wallet = wallet!; + onWalletChange(wallet); + }); + } + + void onWalletChange(WalletBase wallet) {} + + @observable + WalletBase, TransactionInfo> _wallet; + @computed + WalletBase, TransactionInfo> get wallet => + _wallet; +} diff --git a/lib/di.dart b/lib/di.dart index eb4660424..db7eece6c 100644 --- a/lib/di.dart +++ b/lib/di.dart @@ -527,8 +527,7 @@ Future setup({ getIt.registerFactory( () => SendViewModel( - getIt.get().wallet!, - getIt.get().settingsStore, + getIt.get(), getIt.get(), getIt.get(), getIt.get(), @@ -706,7 +705,7 @@ Future setup({ )); getIt.registerFactory(() => ExchangeViewModel( - getIt.get().wallet!, + getIt.get(), _tradesSource, getIt.get(), getIt.get(), diff --git a/lib/view_model/exchange/exchange_view_model.dart b/lib/view_model/exchange/exchange_view_model.dart index 162a3d723..4ff3cb390 100644 --- a/lib/view_model/exchange/exchange_view_model.dart +++ b/lib/view_model/exchange/exchange_view_model.dart @@ -2,6 +2,7 @@ import 'dart:async'; import 'dart:collection'; import 'dart:convert'; +import 'package:cake_wallet/core/wallet_change_listener_view_model.dart'; import 'package:cake_wallet/entities/exchange_api_mode.dart'; import 'package:cake_wallet/entities/preferences_key.dart'; import 'package:cake_wallet/entities/wallet_contact.dart'; @@ -13,7 +14,7 @@ import 'package:cake_wallet/exchange/trocador/trocador_exchange_provider.dart'; import 'package:cake_wallet/exchange/trocador/trocador_request.dart'; import 'package:cake_wallet/view_model/contact_list/contact_list_view_model.dart'; import 'package:cw_core/transaction_priority.dart'; -import 'package:cw_core/wallet_base.dart'; +import 'package:cake_wallet/store/app_store.dart'; import 'package:cw_core/crypto_currency.dart'; import 'package:cw_core/sync_status.dart'; import 'package:cw_core/wallet_type.dart'; @@ -45,16 +46,22 @@ part 'exchange_view_model.g.dart'; class ExchangeViewModel = ExchangeViewModelBase with _$ExchangeViewModel; -abstract class ExchangeViewModelBase with Store { +abstract class ExchangeViewModelBase extends WalletChangeListenerViewModel with Store { + @override + void onWalletChange(wallet) { + receiveCurrency = wallet.currency; + depositCurrency = wallet.currency; + } + ExchangeViewModelBase( - this.wallet, - this.trades, - this._exchangeTemplateStore, - this.tradesStore, - this._settingsStore, - this.sharedPreferences, - this.contactListViewModel) - : _cryptoNumberFormat = NumberFormat(), + AppStore appStore, + this.trades, + this._exchangeTemplateStore, + this.tradesStore, + this._settingsStore, + this.sharedPreferences, + this.contactListViewModel, + ) : _cryptoNumberFormat = NumberFormat(), isFixedRateMode = false, isReceiveAmountEntered = false, depositAmount = '', @@ -70,10 +77,11 @@ abstract class ExchangeViewModelBase with Store { limits = Limits(min: 0, max: 0), tradeState = ExchangeTradeStateInitial(), limitsState = LimitsInitialState(), - receiveCurrency = wallet.currency, - depositCurrency = wallet.currency, + receiveCurrency = appStore.wallet!.currency, + depositCurrency = appStore.wallet!.currency, providerList = [], - selectedProviders = ObservableList() { + selectedProviders = ObservableList(), + super(appStore: appStore) { _useTorOnly = _settingsStore.exchangeStatus == ExchangeApiMode.torOnly; _setProviders(); const excludeDepositCurrencies = [CryptoCurrency.btt, CryptoCurrency.nano]; @@ -86,10 +94,9 @@ abstract class ExchangeViewModelBase with Store { ]; _initialPairBasedOnWallet(); - final Map exchangeProvidersSelection = json.decode( - sharedPreferences - .getString(PreferencesKey.exchangeProvidersSelection) ?? - "{}") as Map; + final Map exchangeProvidersSelection = + json.decode(sharedPreferences.getString(PreferencesKey.exchangeProvidersSelection) ?? "{}") + as Map; /// if the provider is not in the user settings (user's first time or newly added provider) /// then use its default value decided by us @@ -102,34 +109,28 @@ abstract class ExchangeViewModelBase with Store { _setAvailableProviders(); _calculateBestRate(); - bestRateSync = - Timer.periodic(Duration(seconds: 10), (timer) => _calculateBestRate()); + bestRateSync = Timer.periodic(Duration(seconds: 10), (timer) => _calculateBestRate()); isDepositAddressEnabled = !(depositCurrency == wallet.currency); isReceiveAddressEnabled = !(receiveCurrency == wallet.currency); depositAmount = ''; receiveAmount = ''; receiveAddress = ''; - depositAddress = depositCurrency == wallet.currency - ? wallet.walletAddresses.address - : ''; + depositAddress = depositCurrency == wallet.currency ? wallet.walletAddresses.address : ''; provider = providersForCurrentPair().first; final initialProvider = provider; provider!.checkIsAvailable().then((bool isAvailable) { if (!isAvailable && provider == initialProvider) { - provider = providerList.firstWhere( - (provider) => provider is ChangeNowExchangeProvider, + provider = providerList.firstWhere((provider) => provider is ChangeNowExchangeProvider, orElse: () => providerList.last); _onPairChange(); } }); receiveCurrencies = CryptoCurrency.all - .where((cryptoCurrency) => - !excludeReceiveCurrencies.contains(cryptoCurrency)) + .where((cryptoCurrency) => !excludeReceiveCurrencies.contains(cryptoCurrency)) .toList(); depositCurrencies = CryptoCurrency.all - .where((cryptoCurrency) => - !excludeDepositCurrencies.contains(cryptoCurrency)) + .where((cryptoCurrency) => !excludeDepositCurrencies.contains(cryptoCurrency)) .toList(); _defineIsReceiveAmountEditable(); loadLimits(); @@ -140,7 +141,6 @@ abstract class ExchangeViewModelBase with Store { }); } bool _useTorOnly; - final WalletBase wallet; final Box trades; final ExchangeTemplateStore _exchangeTemplateStore; final TradesStore tradesStore; @@ -165,8 +165,7 @@ abstract class ExchangeViewModelBase with Store { /// initialize with descending comparator /// since we want largest rate first final SplayTreeMap _sortedAvailableProviders = - SplayTreeMap( - (double a, double b) => b.compareTo(a)); + SplayTreeMap((double a, double b) => b.compareTo(a)); final List _tradeAvailableProviders = []; @@ -222,21 +221,17 @@ abstract class ExchangeViewModelBase with Store { SyncStatus get status => wallet.syncStatus; @computed - ObservableList get templates => - _exchangeTemplateStore.templates; + ObservableList get templates => _exchangeTemplateStore.templates; @computed - List get walletContactsToShow => - contactListViewModel.walletContacts - .where((element) => - receiveCurrency == null || element.type == receiveCurrency) - .toList(); + List get walletContactsToShow => contactListViewModel.walletContacts + .where((element) => receiveCurrency == null || element.type == receiveCurrency) + .toList(); @action bool checkIfWalletIsAnInternalWallet(String address) { - final walletContactList = walletContactsToShow - .where((element) => element.address == address) - .toList(); + final walletContactList = + walletContactsToShow.where((element) => element.address == address).toList(); return walletContactList.isNotEmpty; } @@ -256,7 +251,6 @@ abstract class ExchangeViewModelBase with Store { return false; } - @computed TransactionPriority get transactionPriority { final priority = _settingsStore.priority[wallet.type]; @@ -269,7 +263,8 @@ abstract class ExchangeViewModelBase with Store { } bool get hasAllAmount => - (wallet.type == WalletType.bitcoin || wallet.type == WalletType.litecoin) && depositCurrency == wallet.currency; + (wallet.type == WalletType.bitcoin || wallet.type == WalletType.litecoin) && + depositCurrency == wallet.currency; bool get isMoneroWallet => wallet.type == WalletType.monero; @@ -277,14 +272,11 @@ abstract class ExchangeViewModelBase with Store { switch (wallet.type) { case WalletType.monero: case WalletType.haven: - return transactionPriority == - monero!.getMoneroTransactionPrioritySlow(); + return transactionPriority == monero!.getMoneroTransactionPrioritySlow(); case WalletType.bitcoin: - return transactionPriority == - bitcoin!.getBitcoinTransactionPrioritySlow(); + return transactionPriority == bitcoin!.getBitcoinTransactionPrioritySlow(); case WalletType.litecoin: - return transactionPriority == - bitcoin!.getLitecoinTransactionPrioritySlow(); + return transactionPriority == bitcoin!.getLitecoinTransactionPrioritySlow(); default: return false; } @@ -390,20 +382,18 @@ abstract class ExchangeViewModelBase with Store { } Future _calculateBestRate() async { - final amount = - double.tryParse(isFixedRateMode ? receiveAmount : depositAmount) ?? 1; + final amount = double.tryParse(isFixedRateMode ? receiveAmount : depositAmount) ?? 1; final _providers = _tradeAvailableProviders .where((element) => !isFixedRateMode || element.supportsFixedRate) .toList(); - final result = await Future.wait(_providers.map((element) => - element.fetchRate( - from: depositCurrency, - to: receiveCurrency, - amount: amount, - isFixedRateMode: isFixedRateMode, - isReceiveAmount: isFixedRateMode))); + final result = await Future.wait(_providers.map((element) => element.fetchRate( + from: depositCurrency, + to: receiveCurrency, + amount: amount, + isFixedRateMode: isFixedRateMode, + isReceiveAmount: isFixedRateMode))); _sortedAvailableProviders.clear(); @@ -445,14 +435,13 @@ abstract class ExchangeViewModelBase with Store { } try { - final tempLimits = await provider.fetchLimits( - from: from, to: to, isFixedRateMode: isFixedRateMode); + final tempLimits = + await provider.fetchLimits(from: from, to: to, isFixedRateMode: isFixedRateMode); if (lowestMin != null && (tempLimits.min ?? -1) < lowestMin) { lowestMin = tempLimits.min; } - if (highestMax != null && - (tempLimits.max ?? double.maxFinite) > highestMax) { + if (highestMax != null && (tempLimits.max ?? double.maxFinite) > highestMax) { highestMax = tempLimits.max; } } catch (e) { @@ -568,8 +557,8 @@ abstract class ExchangeViewModelBase with Store { } else { try { tradeState = TradeIsCreating(); - final trade = await provider.createTrade( - request: request!, isFixedRateMode: isFixedRateMode); + final trade = + await provider.createTrade(request: request!, isFixedRateMode: isFixedRateMode); trade.walletId = wallet.id; tradesStore.setTrade(trade); await trades.add(trade); @@ -604,12 +593,8 @@ abstract class ExchangeViewModelBase with Store { isReceiveAmountEntered = false; depositAmount = ''; receiveAmount = ''; - depositAddress = depositCurrency == wallet.currency - ? wallet.walletAddresses.address - : ''; - receiveAddress = receiveCurrency == wallet.currency - ? wallet.walletAddresses.address - : ''; + depositAddress = depositCurrency == wallet.currency ? wallet.walletAddresses.address : ''; + receiveAddress = receiveCurrency == wallet.currency ? wallet.walletAddresses.address : ''; isDepositAddressEnabled = !(depositCurrency == wallet.currency); isReceiveAddressEnabled = !(receiveCurrency == wallet.currency); isFixedRateMode = false; @@ -628,8 +613,7 @@ abstract class ExchangeViewModelBase with Store { } final amount = availableBalance - fee; - changeDepositAmount( - amount: bitcoin!.formatterBitcoinAmountToString(amount: amount)); + changeDepositAmount(amount: bitcoin!.formatterBitcoinAmountToString(amount: amount)); } } @@ -664,9 +648,8 @@ abstract class ExchangeViewModelBase with Store { List _providersForPair( {required CryptoCurrency from, required CryptoCurrency to}) { final providers = providerList - .where((provider) => provider.pairList - .where((pair) => pair.from == from && pair.to == to) - .isNotEmpty) + .where((provider) => + provider.pairList.where((pair) => pair.from == from && pair.to == to).isNotEmpty) .toList(); return providers; @@ -746,14 +729,12 @@ abstract class ExchangeViewModelBase with Store { _bestRate = 0; _calculateBestRate(); - final Map exchangeProvidersSelection = json.decode( - sharedPreferences - .getString(PreferencesKey.exchangeProvidersSelection) ?? - "{}") as Map; + final Map exchangeProvidersSelection = + json.decode(sharedPreferences.getString(PreferencesKey.exchangeProvidersSelection) ?? "{}") + as Map; for (var provider in providerList) { - exchangeProvidersSelection[provider.title] = - selectedProviders.contains(provider); + exchangeProvidersSelection[provider.title] = selectedProviders.contains(provider); } sharedPreferences.setString( @@ -764,15 +745,15 @@ abstract class ExchangeViewModelBase with Store { bool get isAvailableInSelected { final providersForPair = providersForCurrentPair(); - return selectedProviders.any( - (element) => element.isAvailable && providersForPair.contains(element)); + return selectedProviders + .any((element) => element.isAvailable && providersForPair.contains(element)); } void _setAvailableProviders() { _tradeAvailableProviders.clear(); - _tradeAvailableProviders.addAll(selectedProviders - .where((provider) => providersForCurrentPair().contains(provider))); + _tradeAvailableProviders.addAll( + selectedProviders.where((provider) => providersForCurrentPair().contains(provider))); } @action @@ -780,16 +761,13 @@ abstract class ExchangeViewModelBase with Store { switch (wallet.type) { case WalletType.monero: case WalletType.haven: - _settingsStore.priority[wallet.type] = - monero!.getMoneroTransactionPriorityAutomatic(); + _settingsStore.priority[wallet.type] = monero!.getMoneroTransactionPriorityAutomatic(); break; case WalletType.bitcoin: - _settingsStore.priority[wallet.type] = - bitcoin!.getBitcoinTransactionPriorityMedium(); + _settingsStore.priority[wallet.type] = bitcoin!.getBitcoinTransactionPriorityMedium(); break; case WalletType.litecoin: - _settingsStore.priority[wallet.type] = - bitcoin!.getLitecoinTransactionPriorityMedium(); + _settingsStore.priority[wallet.type] = bitcoin!.getLitecoinTransactionPriorityMedium(); break; default: break; @@ -798,9 +776,7 @@ abstract class ExchangeViewModelBase with Store { void _setProviders() { if (_settingsStore.exchangeStatus == ExchangeApiMode.torOnly) { - providerList = _allProviders - .where((provider) => provider.supportsOnionAddress) - .toList(); + providerList = _allProviders.where((provider) => provider.supportsOnionAddress).toList(); } else { providerList = _allProviders; } diff --git a/lib/view_model/send/send_view_model.dart b/lib/view_model/send/send_view_model.dart index 9a04b5ea7..329b3c4ad 100644 --- a/lib/view_model/send/send_view_model.dart +++ b/lib/view_model/send/send_view_model.dart @@ -1,3 +1,4 @@ +import 'package:cake_wallet/core/wallet_change_listener_view_model.dart'; import 'package:cake_wallet/entities/contact_record.dart'; import 'package:cake_wallet/entities/priority_for_wallet_type.dart'; import 'package:cake_wallet/entities/transaction_description.dart'; @@ -15,7 +16,7 @@ import 'package:cake_wallet/core/address_validator.dart'; import 'package:cake_wallet/core/amount_validator.dart'; import 'package:cw_core/pending_transaction.dart'; import 'package:cake_wallet/core/validator.dart'; -import 'package:cw_core/wallet_base.dart'; +import 'package:cake_wallet/store/app_store.dart'; import 'package:cake_wallet/core/execution_state.dart'; import 'package:cake_wallet/monero/monero.dart'; import 'package:cw_core/sync_status.dart'; @@ -34,30 +35,38 @@ part 'send_view_model.g.dart'; class SendViewModel = SendViewModelBase with _$SendViewModel; -abstract class SendViewModelBase with Store { - SendViewModelBase( - this._wallet, - this._settingsStore, - this.sendTemplateViewModel, - this._fiatConversationStore, - this.balanceViewModel, - this.contactListViewModel, - this.transactionDescriptionBox) - : state = InitialExecutionState(), - currencies = _wallet.balance.keys.toList(), - selectedCryptoCurrency = _wallet.currency, - hasMultipleTokens = _wallet.type == WalletType.ethereum, - outputs = ObservableList(), - fiatFromSettings = _settingsStore.fiatCurrency { - final priority = _settingsStore.priority[_wallet.type]; - final priorities = priorityForWalletType(_wallet.type); +abstract class SendViewModelBase extends WalletChangeListenerViewModel with Store { + @override + void onWalletChange(wallet) { + currencies = wallet.balance.keys.toList(); + selectedCryptoCurrency = wallet.currency; + hasMultipleTokens = wallet.type == WalletType.ethereum; + } - if (!priorityForWalletType(_wallet.type).contains(priority)) { - _settingsStore.priority[_wallet.type] = priorities.first; + SendViewModelBase( + AppStore appStore, + this.sendTemplateViewModel, + this._fiatConversationStore, + this.balanceViewModel, + this.contactListViewModel, + this.transactionDescriptionBox, + ) : state = InitialExecutionState(), + currencies = appStore.wallet!.balance.keys.toList(), + selectedCryptoCurrency = appStore.wallet!.currency, + hasMultipleTokens = appStore.wallet!.type == WalletType.ethereum, + outputs = ObservableList(), + _settingsStore = appStore.settingsStore, + fiatFromSettings = appStore.settingsStore.fiatCurrency, + super(appStore: appStore) { + final priority = _settingsStore.priority[wallet.type]; + final priorities = priorityForWalletType(wallet.type); + + if (!priorityForWalletType(wallet.type).contains(priority)) { + _settingsStore.priority[wallet.type] = priorities.first; } outputs - .add(Output(_wallet, _settingsStore, _fiatConversationStore, () => selectedCryptoCurrency)); + .add(Output(wallet, _settingsStore, _fiatConversationStore, () => selectedCryptoCurrency)); } @observable @@ -68,7 +77,7 @@ abstract class SendViewModelBase with Store { @action void addOutput() { outputs - .add(Output(_wallet, _settingsStore, _fiatConversationStore, () => selectedCryptoCurrency)); + .add(Output(wallet, _settingsStore, _fiatConversationStore, () => selectedCryptoCurrency)); } @action @@ -107,9 +116,8 @@ abstract class SendViewModelBase with Store { String get pendingTransactionFeeFiatAmount { try { if (pendingTransaction != null) { - final currency = walletType == WalletType.ethereum - ? _wallet.currency - : selectedCryptoCurrency; + final currency = + walletType == WalletType.ethereum ? wallet.currency : selectedCryptoCurrency; final fiat = calculateFiatAmount( price: _fiatConversationStore.prices[currency]!, cryptoAmount: pendingTransaction!.feeFormatted); @@ -125,19 +133,19 @@ abstract class SendViewModelBase with Store { FiatCurrency get fiat => _settingsStore.fiatCurrency; TransactionPriority get transactionPriority { - final priority = _settingsStore.priority[_wallet.type]; + final priority = _settingsStore.priority[wallet.type]; if (priority == null) { - throw Exception('Unexpected type ${_wallet.type}'); + throw Exception('Unexpected type ${wallet.type}'); } return priority; } - CryptoCurrency get currency => _wallet.currency; + CryptoCurrency get currency => wallet.currency; Validator get amountValidator => - AmountValidator(currency: walletTypeToCryptoCurrency(_wallet.type)); + AmountValidator(currency: walletTypeToCryptoCurrency(wallet.type)); Validator get allAmountValidator => AllAmountValidator(); @@ -151,7 +159,7 @@ abstract class SendViewModelBase with Store { PendingTransaction? pendingTransaction; @computed - String get balance => _wallet.balance[selectedCryptoCurrency]!.formattedAvailableBalance; + String get balance => wallet.balance[selectedCryptoCurrency]!.formattedAvailableBalance; @computed bool get isFiatDisabled => balanceViewModel.isFiatDisabled; @@ -165,7 +173,7 @@ abstract class SendViewModelBase with Store { isFiatDisabled ? '' : pendingTransactionFeeFiatAmount + ' ' + fiat.title; @computed - bool get isReadyForSend => _wallet.syncStatus is SyncedSyncStatus; + bool get isReadyForSend => wallet.syncStatus is SyncedSyncStatus; @computed List