From fff5a1c41982ba449132bd20e5f6d71d2318acda Mon Sep 17 00:00:00 2001 From: Godwin Asuquo <41484542+godilite@users.noreply.github.com> Date: Tue, 29 Aug 2023 19:11:51 +0300 Subject: [PATCH 1/9] CW-228 Auto generate monero subaddress (#902) * Add UI and setting logic for subaddresses * Enable auto generate subaddresses * Rename variable * Add comment to unused code * Fix issue with initial state change * Fix observable for isAppSecure * Filter sub account contacts * Fix select account use unused address * Use add address if last address is unused * Fix auto generate wallet issues * Fix button color * Add translation and refactored naming * Fix PR review * Remove unused code * Remove unused overrides in electrum * Fix address info null check * CW-228 Fix ContactListViewModel condition * CW-228 Fix Account Tile; Rework updateAddressesInBox; Fix _getAllUnusedAddresses * CW-228 Fix unintentional address_page.dart regression * CW-228 Fix Merge Conflicts * CW-228 Add more translation Tools * CW-228 More merge conflict fixes * CW-228 Fix Merge Conflicts * CW-228 Auto Translation improvements * CW-228 Resolve requested Changes --------- Co-authored-by: Konstantin Ullrich --- cw_core/lib/address_info.dart | 21 +++++ cw_core/lib/hive_type_ids.dart | 2 +- cw_core/lib/wallet_addresses.dart | 10 ++- cw_core/lib/wallet_base.dart | 4 + cw_core/lib/wallet_info.dart | 7 ++ cw_haven/lib/haven_wallet.dart | 82 +++++++---------- cw_monero/lib/monero_subaddress_list.dart | 88 ++++++++++++++++--- cw_monero/lib/monero_wallet.dart | 35 +++++++- cw_monero/lib/monero_wallet_addresses.dart | 52 ++++++++--- lib/core/backup_service.dart | 6 ++ lib/di.dart | 2 +- .../auto_generate_subaddress_status.dart | 13 +++ lib/entities/preferences_key.dart | 1 + lib/main.dart | 5 ++ lib/reactions/on_current_wallet_change.dart | 81 ++++++++++------- .../dashboard/widgets/address_page.dart | 40 ++++++--- .../monero_accounts/widgets/account_tile.dart | 15 ++-- .../screens/restore/restore_options_page.dart | 2 +- .../restore/widgets/backup_file_button.dart | 0 lib/src/screens/settings/privacy_page.dart | 8 ++ lib/store/settings_store.dart | 27 +++++- .../contact_list/contact_list_view_model.dart | 31 +++++-- .../dashboard/dashboard_view_model.dart | 5 ++ .../settings/privacy_settings_view_model.dart | 22 ++++- res/values/strings_ar.arb | 4 +- res/values/strings_bg.arb | 1 + res/values/strings_cs.arb | 1 + res/values/strings_de.arb | 1 + res/values/strings_en.arb | 1 + res/values/strings_es.arb | 1 + res/values/strings_fr.arb | 1 + res/values/strings_ha.arb | 1 + res/values/strings_hi.arb | 1 + res/values/strings_hr.arb | 1 + res/values/strings_id.arb | 1 + res/values/strings_it.arb | 1 + res/values/strings_ja.arb | 3 +- res/values/strings_ko.arb | 3 +- res/values/strings_my.arb | 3 +- res/values/strings_nl.arb | 1 + res/values/strings_pl.arb | 1 + res/values/strings_pt.arb | 1 + res/values/strings_ru.arb | 3 +- res/values/strings_th.arb | 3 +- res/values/strings_tr.arb | 1 + res/values/strings_uk.arb | 1 + res/values/strings_ur.arb | 3 +- res/values/strings_yo.arb | 3 +- res/values/strings_zh.arb | 3 +- tool/append_translation.dart | 54 ++---------- tool/translation_consistence.dart | 37 ++++++++ tool/utils/translation/arb_file_utils.dart | 66 ++++++++++++++ .../translation/translation_constants.dart | 6 ++ tool/utils/translation/translation_utils.dart | 37 ++++++++ 54 files changed, 603 insertions(+), 200 deletions(-) create mode 100644 cw_core/lib/address_info.dart create mode 100644 lib/entities/auto_generate_subaddress_status.dart create mode 100644 lib/src/screens/restore/widgets/backup_file_button.dart create mode 100644 tool/translation_consistence.dart create mode 100644 tool/utils/translation/arb_file_utils.dart create mode 100644 tool/utils/translation/translation_constants.dart create mode 100644 tool/utils/translation/translation_utils.dart diff --git a/cw_core/lib/address_info.dart b/cw_core/lib/address_info.dart new file mode 100644 index 000000000..63dc023ab --- /dev/null +++ b/cw_core/lib/address_info.dart @@ -0,0 +1,21 @@ +import 'package:cw_core/hive_type_ids.dart'; +import 'package:hive/hive.dart'; + +part 'address_info.g.dart'; + +@HiveType(typeId: ADDRESS_INFO_TYPE_ID) +class AddressInfo extends HiveObject { + AddressInfo({required this.address, this.accountIndex, required this.label}); + + static const typeId = ADDRESS_INFO_TYPE_ID; + static const boxName = 'AddressInfo'; + + @HiveField(0) + int? accountIndex; + + @HiveField(1, defaultValue: '') + String address; + + @HiveField(2, defaultValue: '') + String label; +} diff --git a/cw_core/lib/hive_type_ids.dart b/cw_core/lib/hive_type_ids.dart index 0961182bc..950f39e1f 100644 --- a/cw_core/lib/hive_type_ids.dart +++ b/cw_core/lib/hive_type_ids.dart @@ -9,5 +9,5 @@ const EXCHANGE_TEMPLATE_TYPE_ID = 7; const ORDER_TYPE_ID = 8; const UNSPENT_COINS_INFO_TYPE_ID = 9; const ANONPAY_INVOICE_INFO_TYPE_ID = 10; - +const ADDRESS_INFO_TYPE_ID = 11; const ERC20_TOKEN_TYPE_ID = 12; diff --git a/cw_core/lib/wallet_addresses.dart b/cw_core/lib/wallet_addresses.dart index a34101a88..27b5468c5 100644 --- a/cw_core/lib/wallet_addresses.dart +++ b/cw_core/lib/wallet_addresses.dart @@ -1,8 +1,10 @@ +import 'package:cw_core/address_info.dart'; import 'package:cw_core/wallet_info.dart'; abstract class WalletAddresses { WalletAddresses(this.walletInfo) - : addressesMap = {}; + : addressesMap = {}, + addressInfos = {}; final WalletInfo walletInfo; @@ -12,6 +14,10 @@ abstract class WalletAddresses { Map addressesMap; + Map> addressInfos; + + Set usedAddresses = {}; + Future init(); Future updateAddressesInBox(); @@ -20,6 +26,8 @@ abstract class WalletAddresses { try { walletInfo.address = address; walletInfo.addresses = addressesMap; + walletInfo.addressInfos = addressInfos; + walletInfo.usedAddresses = usedAddresses.toList(); if (walletInfo.isInBox) { await walletInfo.save(); diff --git a/cw_core/lib/wallet_base.dart b/cw_core/lib/wallet_base.dart index 5bc5ef914..e24fdb4b0 100644 --- a/cw_core/lib/wallet_base.dart +++ b/cw_core/lib/wallet_base.dart @@ -52,6 +52,10 @@ abstract class WalletBase< late HistoryType transactionHistory; + set isEnabledAutoGenerateSubaddress(bool value) {} + + bool get isEnabledAutoGenerateSubaddress => false; + Future connectToNode({required Node node}); Future startSync(); diff --git a/cw_core/lib/wallet_info.dart b/cw_core/lib/wallet_info.dart index 6b3fa9e98..210adb9a4 100644 --- a/cw_core/lib/wallet_info.dart +++ b/cw_core/lib/wallet_info.dart @@ -1,4 +1,5 @@ import 'dart:async'; +import 'package:cw_core/address_info.dart'; import 'package:cw_core/hive_type_ids.dart'; import 'package:cw_core/wallet_type.dart'; import 'package:hive/hive.dart'; @@ -72,6 +73,12 @@ class WalletInfo extends HiveObject { @HiveField(13) bool? showIntroCakePayCard; + @HiveField(14) + Map>? addressInfos; + + @HiveField(15) + List? usedAddresses; + String get yatLastUsedAddress => yatLastUsedAddressRaw ?? ''; set yatLastUsedAddress(String address) { diff --git a/cw_haven/lib/haven_wallet.dart b/cw_haven/lib/haven_wallet.dart index 226ace6a1..e639be4b9 100644 --- a/cw_haven/lib/haven_wallet.dart +++ b/cw_haven/lib/haven_wallet.dart @@ -12,8 +12,7 @@ import 'package:cw_core/monero_wallet_utils.dart'; import 'package:cw_haven/api/structs/pending_transaction.dart'; import 'package:flutter/foundation.dart'; import 'package:mobx/mobx.dart'; -import 'package:cw_haven/api/transaction_history.dart' - as haven_transaction_history; +import 'package:cw_haven/api/transaction_history.dart' as haven_transaction_history; //import 'package:cw_haven/wallet.dart'; import 'package:cw_haven/api/wallet.dart' as haven_wallet; import 'package:cw_haven/api/transaction_history.dart' as transaction_history; @@ -37,8 +36,8 @@ const moneroBlockSize = 1000; class HavenWallet = HavenWalletBase with _$HavenWallet; -abstract class HavenWalletBase extends WalletBase with Store { +abstract class HavenWalletBase + extends WalletBase with Store { HavenWalletBase({required WalletInfo walletInfo}) : balance = ObservableMap.of(getHavenBalance(accountIndex: 0)), _isTransactionUpdating = false, @@ -47,8 +46,7 @@ abstract class HavenWalletBase extends WalletBase walletAddresses.account, - (Account? account) { + _onAccountChangeReaction = reaction((_) => walletAddresses.account, (Account? account) { if (account == null) { return; } @@ -96,14 +94,12 @@ abstract class HavenWalletBase extends WalletBase await save()); + _autoSaveTimer = + Timer.periodic(Duration(seconds: _autoSaveInterval), (_) async => await save()); } @override @@ -115,7 +111,7 @@ abstract class HavenWalletBase extends WalletBase connectToNode({required Node node}) async { try { @@ -170,26 +166,25 @@ abstract class HavenWalletBase extends WalletBase item.sendAll - || (item.formattedCryptoAmount ?? 0) <= 0)) { - throw HavenTransactionCreationException('You do not have enough coins to send this amount.'); + if (outputs.any((item) => item.sendAll || (item.formattedCryptoAmount ?? 0) <= 0)) { + throw HavenTransactionCreationException( + 'You do not have enough coins to send this amount.'); } - final int totalAmount = outputs.fold(0, (acc, value) => - acc + (value.formattedCryptoAmount ?? 0)); + final int totalAmount = + outputs.fold(0, (acc, value) => acc + (value.formattedCryptoAmount ?? 0)); if (unlockedBalance < totalAmount) { - throw HavenTransactionCreationException('You do not have enough coins to send this amount.'); + throw HavenTransactionCreationException( + 'You do not have enough coins to send this amount.'); } - final moneroOutputs = outputs.map((output) => - MoneroOutput( - address: output.address, - amount: output.cryptoAmount!.replaceAll(',', '.'))) + final moneroOutputs = outputs + .map((output) => MoneroOutput( + address: output.address, amount: output.cryptoAmount!.replaceAll(',', '.'))) .toList(); - pendingTransactionDescription = - await transaction_history.createTransactionMultDest( + pendingTransactionDescription = await transaction_history.createTransactionMultDest( outputs: moneroOutputs, priorityRaw: _credentials.priority.serialize(), accountIndex: walletAddresses.account!.id); @@ -198,12 +193,8 @@ abstract class HavenWalletBase extends WalletBase - haven_wallet.getAddress( - accountIndex: accountIndex, - addressIndex: addressIndex); + haven_wallet.getAddress(accountIndex: accountIndex, addressIndex: addressIndex); @override Future> fetchTransactions() async { haven_transaction_history.refreshTransactions(); - return _getAllTransactions(null).fold>( - {}, - (Map acc, HavenTransactionInfo tx) { + return _getAllTransactions(null) + .fold>({}, + (Map acc, HavenTransactionInfo tx) { acc[tx.id] = tx; return acc; }); @@ -340,9 +328,9 @@ abstract class HavenWalletBase extends WalletBase _getAllTransactions(dynamic _) => haven_transaction_history - .getAllTransations() - .map((row) => HavenTransactionInfo.fromRow(row)) - .toList(); + .getAllTransations() + .map((row) => HavenTransactionInfo.fromRow(row)) + .toList(); void _setListeners() { _listener?.stop(); @@ -364,8 +352,7 @@ abstract class HavenWalletBase extends WalletBase balance.addAll(getHavenBalance(accountIndex: walletAddresses.account!.id)); - Future _askForUpdateTransactionHistory() async => - await updateTransactions(); + Future _askForUpdateTransactionHistory() async => await updateTransactions(); void _onNewBlock(int height, int blocksLeft, double ptc) async { try { @@ -404,9 +390,9 @@ abstract class HavenWalletBase extends WalletBase(); + : _isRefreshing = false, + _isUpdating = false, + subaddresses = ObservableList(); + + final List _usedAddresses = []; @observable ObservableList subaddresses; @@ -49,20 +50,24 @@ abstract class MoneroSubaddressListBase with Store { subaddresses = [primary] + rest.toList(); } - return subaddresses - .map((subaddressRow) => Subaddress( + return subaddresses.map((subaddressRow) { + final hasDefaultAddressName = + subaddressRow.getLabel().toLowerCase() == 'Primary account'.toLowerCase() || + subaddressRow.getLabel().toLowerCase() == 'Untitled account'.toLowerCase(); + final isPrimaryAddress = subaddressRow.getId() == 0 && hasDefaultAddressName; + return Subaddress( id: subaddressRow.getId(), address: subaddressRow.getAddress(), - label: subaddressRow.getId() == 0 && - subaddressRow.getLabel().toLowerCase() == 'Primary account'.toLowerCase() - ? 'Primary address' - : subaddressRow.getLabel())) - .toList(); + label: isPrimaryAddress + ? 'Primary address' + : hasDefaultAddressName + ? '' + : subaddressRow.getLabel()); + }).toList(); } Future addSubaddress({required int accountIndex, required String label}) async { - await subaddress_list.addSubaddress( - accountIndex: accountIndex, label: label); + await subaddress_list.addSubaddress(accountIndex: accountIndex, label: label); update(accountIndex: accountIndex); } @@ -88,4 +93,59 @@ abstract class MoneroSubaddressListBase with Store { rethrow; } } + + Future updateWithAutoGenerate({ + required int accountIndex, + required String defaultLabel, + required List usedAddresses, + }) async { + _usedAddresses.addAll(usedAddresses); + if (_isUpdating) { + return; + } + + try { + _isUpdating = true; + refresh(accountIndex: accountIndex); + subaddresses.clear(); + final newSubAddresses = + await _getAllUnusedAddresses(accountIndex: accountIndex, label: defaultLabel); + subaddresses.addAll(newSubAddresses); + } catch (e) { + rethrow; + } finally { + _isUpdating = false; + } + } + + Future> _getAllUnusedAddresses( + {required int accountIndex, required String label}) async { + final allAddresses = subaddress_list.getAllSubaddresses(); + + if (allAddresses.isEmpty || _usedAddresses.contains(allAddresses.last.getAddress())) { + final isAddressUnused = await _newSubaddress(accountIndex: accountIndex, label: label); + if (!isAddressUnused) { + return await _getAllUnusedAddresses(accountIndex: accountIndex, label: label); + } + } + + return allAddresses + .map((subaddressRow) => Subaddress( + id: subaddressRow.getId(), + address: subaddressRow.getAddress(), + label: subaddressRow.getId() == 0 && + subaddressRow.getLabel().toLowerCase() == 'Primary account'.toLowerCase() + ? 'Primary address' + : subaddressRow.getLabel())) + .toList(); + } + + Future _newSubaddress({required int accountIndex, required String label}) async { + await subaddress_list.addSubaddress(accountIndex: accountIndex, label: label); + + return subaddress_list + .getAllSubaddresses() + .where((subaddressRow) => !_usedAddresses.contains(subaddressRow.getAddress())) + .isNotEmpty; + } } diff --git a/cw_monero/lib/monero_wallet.dart b/cw_monero/lib/monero_wallet.dart index 76563310e..39c0604a3 100644 --- a/cw_monero/lib/monero_wallet.dart +++ b/cw_monero/lib/monero_wallet.dart @@ -48,12 +48,14 @@ abstract class MoneroWalletBase extends WalletBase walletAddresses.account, (Account? account) { if (account == null) { return; @@ -64,7 +66,11 @@ abstract class MoneroWalletBase extends WalletBase isEnabledAutoGenerateSubaddress, (bool enabled) { + _updateSubAddress(enabled, account: walletAddresses.account); }); } @@ -73,7 +79,11 @@ abstract class MoneroWalletBase extends WalletBase unspentCoinsInfo; @override - MoneroWalletAddresses walletAddresses; + late MoneroWalletAddresses walletAddresses; + + @override + @observable + bool isEnabledAutoGenerateSubaddress; @override @observable @@ -287,6 +297,14 @@ abstract class MoneroWalletBase extends WalletBase save() async { + await walletAddresses.updateUsedSubaddress(); + + if (isEnabledAutoGenerateSubaddress) { + walletAddresses.updateUnusedSubaddress( + accountIndex: walletAddresses.account?.id ?? 0, + defaultLabel: walletAddresses.account?.label ?? ''); + } + await walletAddresses.updateAddressesInBox(); await backupWalletFiles(name); await monero_wallet.store(); @@ -610,4 +628,15 @@ abstract class MoneroWalletBase extends WalletBase init() async { accountList.update(); account = accountList.accounts.first; - updateSubaddressList(accountIndex: account?.id ?? 0); await updateAddressesInBox(); } @@ -46,11 +50,15 @@ abstract class MoneroWalletAddressesBase extends WalletAddresses with Store { final _subaddressList = MoneroSubaddressList(); addressesMap.clear(); + addressInfos.clear(); accountList.accounts.forEach((account) { _subaddressList.update(accountIndex: account.id); _subaddressList.subaddresses.forEach((subaddress) { addressesMap[subaddress.address] = subaddress.label; + addressInfos[account.id] ??= []; + addressInfos[account.id]?.add(AddressInfo( + address: subaddress.address, label: subaddress.label, accountIndex: account.id)); }); }); @@ -62,14 +70,14 @@ abstract class MoneroWalletAddressesBase extends WalletAddresses with Store { bool validate() { accountList.update(); - final accountListLength = accountList.accounts.length ?? 0; + final accountListLength = accountList.accounts.length; if (accountListLength <= 0) { return false; } subaddressList.update(accountIndex: accountList.accounts.first.id); - final subaddressListLength = subaddressList.subaddresses.length ?? 0; + final subaddressListLength = subaddressList.subaddresses.length; if (subaddressListLength <= 0) { return false; @@ -83,4 +91,24 @@ abstract class MoneroWalletAddressesBase extends WalletAddresses with Store { subaddress = subaddressList.subaddresses.first; address = subaddress!.address; } -} \ No newline at end of file + + Future updateUsedSubaddress() async { + final transactions = _moneroTransactionHistory.transactions.values.toList(); + + transactions.forEach((element) { + final accountIndex = element.accountIndex; + final addressIndex = element.addressIndex; + usedAddresses.add(getAddress(accountIndex: accountIndex, addressIndex: addressIndex)); + }); + } + + Future updateUnusedSubaddress( + {required int accountIndex, required String defaultLabel}) async { + await subaddressList.updateWithAutoGenerate( + accountIndex: accountIndex, + defaultLabel: defaultLabel, + usedAddresses: usedAddresses.toList()); + subaddress = subaddressList.subaddresses.last; + address = subaddress!.address; + } +} diff --git a/lib/core/backup_service.dart b/lib/core/backup_service.dart index 6476891ed..3f430b7e9 100644 --- a/lib/core/backup_service.dart +++ b/lib/core/backup_service.dart @@ -246,6 +246,7 @@ class BackupService { final useEtherscan = data[PreferencesKey.useEtherscan] as bool?; final syncAll = data[PreferencesKey.syncAllKey] as bool?; final syncMode = data[PreferencesKey.syncModeKey] as int?; + final autoGenerateSubaddressStatus = data[PreferencesKey.autoGenerateSubaddressStatusKey] as int?; await _sharedPreferences.setString(PreferencesKey.currentWalletName, currentWalletName); @@ -296,6 +297,9 @@ class BackupService { if (fiatApiMode != null) await _sharedPreferences.setInt(PreferencesKey.currentFiatApiModeKey, fiatApiMode); + if (autoGenerateSubaddressStatus != null) + await _sharedPreferences.setInt(PreferencesKey.autoGenerateSubaddressStatusKey, + autoGenerateSubaddressStatus); if (currentPinLength != null) await _sharedPreferences.setInt(PreferencesKey.currentPinLength, currentPinLength); @@ -523,6 +527,8 @@ class BackupService { _sharedPreferences.getInt(PreferencesKey.syncModeKey), PreferencesKey.syncAllKey: _sharedPreferences.getBool(PreferencesKey.syncAllKey), + PreferencesKey.autoGenerateSubaddressStatusKey: + _sharedPreferences.getInt(PreferencesKey.autoGenerateSubaddressStatusKey), }; return json.encode(preferences); diff --git a/lib/di.dart b/lib/di.dart index 80a55e1c6..d32576836 100644 --- a/lib/di.dart +++ b/lib/di.dart @@ -5,6 +5,7 @@ import 'package:cake_wallet/buy/onramper/onramper_buy_provider.dart'; import 'package:cake_wallet/buy/payfura/payfura_buy_provider.dart'; import 'package:cake_wallet/core/yat_service.dart'; import 'package:cake_wallet/entities/background_tasks.dart'; +import 'package:cake_wallet/entities/auto_generate_subaddress_status.dart'; import 'package:cake_wallet/entities/exchange_api_mode.dart'; import 'package:cake_wallet/entities/parse_address_from_domain.dart'; import 'package:cake_wallet/entities/receive_page_option.dart'; @@ -245,7 +246,6 @@ Future setup({ if (!_isSetupFinished) { getIt.registerSingletonAsync(() => SharedPreferences.getInstance()); } - if (!_isSetupFinished) { getIt.registerFactory(() => BackgroundTasks()); } diff --git a/lib/entities/auto_generate_subaddress_status.dart b/lib/entities/auto_generate_subaddress_status.dart new file mode 100644 index 000000000..6d6cc406c --- /dev/null +++ b/lib/entities/auto_generate_subaddress_status.dart @@ -0,0 +1,13 @@ + +enum AutoGenerateSubaddressStatus { + initialized(1), + enabled(2), + disabled(3); + + const AutoGenerateSubaddressStatus(this.value); + final int value; + + static AutoGenerateSubaddressStatus deserialize({required int raw}) => + AutoGenerateSubaddressStatus.values.firstWhere((e) => e.value == raw); + +} \ No newline at end of file diff --git a/lib/entities/preferences_key.dart b/lib/entities/preferences_key.dart index c50629c1b..7b4d3d0dc 100644 --- a/lib/entities/preferences_key.dart +++ b/lib/entities/preferences_key.dart @@ -50,6 +50,7 @@ class PreferencesKey { '${PreferencesKey.moneroWalletPasswordUpdateV1Base}_${name}'; static const exchangeProvidersSelection = 'exchange-providers-selection'; + static const autoGenerateSubaddressStatusKey = 'auto_generate_subaddress_status'; static const clearnetDonationLink = 'clearnet_donation_link'; static const onionDonationLink = 'onion_donation_link'; static const lastSeenAppVersion = 'last_seen_app_version'; diff --git a/lib/main.dart b/lib/main.dart index db5335ac1..62d18708e 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -6,6 +6,7 @@ import 'package:cake_wallet/buy/order.dart'; import 'package:cake_wallet/locales/locale.dart'; import 'package:cake_wallet/store/yat/yat_store.dart'; import 'package:cake_wallet/utils/exception_handler.dart'; +import 'package:cw_core/address_info.dart'; import 'package:cake_wallet/utils/responsive_layout_util.dart'; import 'package:cw_core/hive_type_ids.dart'; import 'package:flutter/foundation.dart'; @@ -89,6 +90,10 @@ Future initializeAppConfigs() async { CakeHive.registerAdapter(TradeAdapter()); } + if (!CakeHive.isAdapterRegistered(AddressInfo.typeId)) { + CakeHive.registerAdapter(AddressInfoAdapter()); + } + if (!CakeHive.isAdapterRegistered(WalletInfo.typeId)) { CakeHive.registerAdapter(WalletInfoAdapter()); } diff --git a/lib/reactions/on_current_wallet_change.dart b/lib/reactions/on_current_wallet_change.dart index 89f39d5f9..95406fb40 100644 --- a/lib/reactions/on_current_wallet_change.dart +++ b/lib/reactions/on_current_wallet_change.dart @@ -1,3 +1,4 @@ +import 'package:cake_wallet/entities/auto_generate_subaddress_status.dart'; import 'package:cake_wallet/entities/fiat_api_mode.dart'; import 'package:cake_wallet/entities/update_haven_rate.dart'; import 'package:cake_wallet/ethereum/ethereum.dart'; @@ -21,36 +22,36 @@ ReactionDisposer? _onCurrentWalletChangeReaction; ReactionDisposer? _onCurrentWalletChangeFiatRateUpdateReaction; //ReactionDisposer _onCurrentWalletAddressChangeReaction; -void startCurrentWalletChangeReaction(AppStore appStore, - SettingsStore settingsStore, FiatConversionStore fiatConversionStore) { +void startCurrentWalletChangeReaction( + AppStore appStore, SettingsStore settingsStore, FiatConversionStore fiatConversionStore) { _onCurrentWalletChangeReaction?.reaction.dispose(); _onCurrentWalletChangeFiatRateUpdateReaction?.reaction.dispose(); //_onCurrentWalletAddressChangeReaction?.reaction?dispose(); //_onCurrentWalletAddressChangeReaction = reaction((_) => appStore.wallet.walletAddresses.address, - //(String address) async { - //if (address == appStore.wallet.walletInfo.yatLastUsedAddress) { - // return; - //} + //(String address) async { + //if (address == appStore.wallet.walletInfo.yatLastUsedAddress) { + // return; + //} - //try { - // final yatStore = getIt.get(); - // await updateEmojiIdAddress( - // appStore.wallet.walletInfo.yatEmojiId, - // appStore.wallet.walletAddresses.address, - // yatStore.apiKey, - // appStore.wallet.type - // ); - // appStore.wallet.walletInfo.yatLastUsedAddress = address; - // await appStore.wallet.walletInfo.save(); - //} catch (e) { - // print(e.toString()); - //} + //try { + // final yatStore = getIt.get(); + // await updateEmojiIdAddress( + // appStore.wallet.walletInfo.yatEmojiId, + // appStore.wallet.walletAddresses.address, + // yatStore.apiKey, + // appStore.wallet.type + // ); + // appStore.wallet.walletInfo.yatLastUsedAddress = address; + // await appStore.wallet.walletInfo.save(); + //} catch (e) { + // print(e.toString()); + //} //}); - _onCurrentWalletChangeReaction = reaction((_) => appStore.wallet, (WalletBase< - Balance, TransactionHistoryBase, TransactionInfo>? - wallet) async { + _onCurrentWalletChangeReaction = reaction((_) => appStore.wallet, + (WalletBase, TransactionInfo>? + wallet) async { try { if (wallet == null) { return; @@ -59,11 +60,13 @@ void startCurrentWalletChangeReaction(AppStore appStore, final node = settingsStore.getCurrentNode(wallet.type); startWalletSyncStatusChangeReaction(wallet, fiatConversionStore); startCheckConnectionReaction(wallet, settingsStore); + await getIt.get().setString(PreferencesKey.currentWalletName, wallet.name); await getIt .get() - .setString(PreferencesKey.currentWalletName, wallet.name); - await getIt.get().setInt( - PreferencesKey.currentWalletType, serializeToInt(wallet.type)); + .setInt(PreferencesKey.currentWalletType, serializeToInt(wallet.type)); + if (wallet.type == WalletType.monero) { + _setAutoGenerateSubaddressStatus(wallet, settingsStore); + } await wallet.connectToNode(node: node); if (wallet.type == WalletType.haven) { @@ -82,9 +85,8 @@ void startCurrentWalletChangeReaction(AppStore appStore, } }); - _onCurrentWalletChangeFiatRateUpdateReaction = - reaction((_) => appStore.wallet, (WalletBase, TransactionInfo>? + _onCurrentWalletChangeFiatRateUpdateReaction = reaction((_) => appStore.wallet, + (WalletBase, TransactionInfo>? wallet) async { try { if (wallet == null || settingsStore.fiatApiMode == FiatApiMode.disabled) { @@ -92,11 +94,10 @@ void startCurrentWalletChangeReaction(AppStore appStore, } fiatConversionStore.prices[wallet.currency] = 0; - fiatConversionStore.prices[wallet.currency] = - await FiatConversionService.fetchPrice( - crypto: wallet.currency, - fiat: settingsStore.fiatCurrency, - torOnly: settingsStore.fiatApiMode == FiatApiMode.torOnly); + fiatConversionStore.prices[wallet.currency] = await FiatConversionService.fetchPrice( + crypto: wallet.currency, + fiat: settingsStore.fiatCurrency, + torOnly: settingsStore.fiatApiMode == FiatApiMode.torOnly); if (wallet.type == WalletType.ethereum) { final currencies = @@ -116,3 +117,17 @@ void startCurrentWalletChangeReaction(AppStore appStore, } }); } + +void _setAutoGenerateSubaddressStatus( + WalletBase, TransactionInfo> wallet, + SettingsStore settingsStore, +) async { + final walletHasAddresses = await wallet.walletAddresses.addressesMap.length > 1; + if (settingsStore.autoGenerateSubaddressStatus == AutoGenerateSubaddressStatus.initialized && + walletHasAddresses) { + settingsStore.autoGenerateSubaddressStatus = AutoGenerateSubaddressStatus.disabled; + } + wallet.isEnabledAutoGenerateSubaddress = + settingsStore.autoGenerateSubaddressStatus == AutoGenerateSubaddressStatus.enabled || + settingsStore.autoGenerateSubaddressStatus == AutoGenerateSubaddressStatus.initialized; +} diff --git a/lib/src/screens/dashboard/widgets/address_page.dart b/lib/src/screens/dashboard/widgets/address_page.dart index 236087595..2f18ef634 100644 --- a/lib/src/screens/dashboard/widgets/address_page.dart +++ b/lib/src/screens/dashboard/widgets/address_page.dart @@ -1,8 +1,10 @@ import 'package:cake_wallet/themes/extensions/keyboard_theme.dart'; +import 'package:cake_wallet/di.dart'; +import 'package:cake_wallet/src/screens/base_page.dart'; +import 'package:cake_wallet/src/screens/monero_accounts/monero_account_list_page.dart'; import 'package:cake_wallet/anonpay/anonpay_donation_link_info.dart'; import 'package:cake_wallet/entities/preferences_key.dart'; import 'package:cake_wallet/entities/receive_page_option.dart'; -import 'package:cake_wallet/src/screens/base_page.dart'; import 'package:cake_wallet/src/screens/dashboard/widgets/present_receive_option_picker.dart'; import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart'; import 'package:cake_wallet/src/widgets/gradient_background.dart'; @@ -24,7 +26,6 @@ import 'package:flutter_mobx/flutter_mobx.dart'; import 'package:keyboard_actions/keyboard_actions.dart'; import 'package:mobx/mobx.dart'; import 'package:shared_preferences/shared_preferences.dart'; -import 'package:cake_wallet/di.dart'; import 'package:cake_wallet/themes/extensions/dashboard_page_theme.dart'; import 'package:cake_wallet/themes/extensions/balance_page_theme.dart'; @@ -174,7 +175,11 @@ class AddressPage extends BasePage { Observer(builder: (_) { if (addressListViewModel.hasAddressList) { return GestureDetector( - onTap: () => Navigator.of(context).pushNamed(Routes.receive), + onTap: () async => dashboardViewModel.isAutoGenerateSubaddressesEnabled + ? await showPopUp( + context: context, + builder: (_) => getIt.get()) + : Navigator.of(context).pushNamed(Routes.receive), child: Container( height: 50, padding: EdgeInsets.only(left: 24, right: 12), @@ -193,17 +198,26 @@ class AddressPage extends BasePage { mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Observer( - builder: (_) => Text( - addressListViewModel.hasAccounts - ? S.of(context).accounts_subaddresses - : S.of(context).addresses, - style: TextStyle( - fontSize: 14, - fontWeight: FontWeight.w500, - color: Theme.of(context) + builder: (_) { + String label = addressListViewModel.hasAccounts + ? S.of(context).accounts_subaddresses + : S.of(context).addresses; + + if (dashboardViewModel.isAutoGenerateSubaddressesEnabled) { + label = addressListViewModel.hasAccounts + ? S.of(context).accounts + : S.of(context).account; + } + return Text( + label, + style: TextStyle( + fontSize: 14, + fontWeight: FontWeight.w500, + color: Theme.of(context) .extension()! .textColor), - )), + ); + },), Icon( Icons.arrow_forward_ios, size: 14, @@ -213,7 +227,7 @@ class AddressPage extends BasePage { ), ), ); - } else if (addressListViewModel.showElectrumAddressDisclaimer) { + } else if (dashboardViewModel.isAutoGenerateSubaddressesEnabled || addressListViewModel.showElectrumAddressDisclaimer) { return Text(S.of(context).electrum_address_disclaimer, textAlign: TextAlign.center, style: TextStyle( diff --git a/lib/src/screens/monero_accounts/widgets/account_tile.dart b/lib/src/screens/monero_accounts/widgets/account_tile.dart index fa8221513..3e428f355 100644 --- a/lib/src/screens/monero_accounts/widgets/account_tile.dart +++ b/lib/src/screens/monero_accounts/widgets/account_tile.dart @@ -5,13 +5,14 @@ import 'package:flutter_slidable/flutter_slidable.dart'; import 'package:cake_wallet/generated/i18n.dart'; class AccountTile extends StatelessWidget { - AccountTile( - {required this.isCurrent, - required this.accountName, - this.accountBalance, - required this.currency, - required this.onTap, - required this.onEdit}); + AccountTile({ + required this.isCurrent, + required this.accountName, + this.accountBalance, + required this.currency, + required this.onTap, + required this.onEdit, + }); final bool isCurrent; final String accountName; diff --git a/lib/src/screens/restore/restore_options_page.dart b/lib/src/screens/restore/restore_options_page.dart index 74e33b87a..3adad4379 100644 --- a/lib/src/screens/restore/restore_options_page.dart +++ b/lib/src/screens/restore/restore_options_page.dart @@ -73,7 +73,7 @@ class RestoreOptionsPage extends BasePage { await restoreFromQRViewModel.create(restoreWallet: restoreWallet); if (restoreFromQRViewModel.state is FailureState) { _onWalletCreateFailure(context, - 'Create wallet state: ${restoreFromQRViewModel.state.runtimeType.toString()}'); + 'Create wallet state: ${(restoreFromQRViewModel.state as FailureState).error}'); } } catch (e) { _onWalletCreateFailure(context, e.toString()); diff --git a/lib/src/screens/restore/widgets/backup_file_button.dart b/lib/src/screens/restore/widgets/backup_file_button.dart new file mode 100644 index 000000000..e69de29bb diff --git a/lib/src/screens/settings/privacy_page.dart b/lib/src/screens/settings/privacy_page.dart index ae6bfe6c8..e953fd4ee 100644 --- a/lib/src/screens/settings/privacy_page.dart +++ b/lib/src/screens/settings/privacy_page.dart @@ -50,6 +50,14 @@ class PrivacyPage extends BasePage { onValueChange: (BuildContext _, bool value) { _privacySettingsViewModel.setShouldSaveRecipientAddress(value); }), + if (_privacySettingsViewModel.isAutoGenerateSubaddressesVisible) + SettingsSwitcherCell( + title: S.current.auto_generate_subaddresses, + value: _privacySettingsViewModel.isAutoGenerateSubaddressesEnabled, + onValueChange: (BuildContext _, bool value) { + _privacySettingsViewModel.setAutoGenerateSubaddresses(value); + }, + ), if (DeviceInfo.instance.isMobile) SettingsSwitcherCell( title: S.current.prevent_screenshots, diff --git a/lib/store/settings_store.dart b/lib/store/settings_store.dart index 703c7d73e..f512063a4 100644 --- a/lib/store/settings_store.dart +++ b/lib/store/settings_store.dart @@ -1,6 +1,7 @@ import 'dart:io'; import 'package:cake_wallet/bitcoin/bitcoin.dart'; +import 'package:cake_wallet/entities/auto_generate_subaddress_status.dart'; import 'package:cake_wallet/entities/cake_2fa_preset_options.dart'; import 'package:cake_wallet/entities/background_tasks.dart'; import 'package:cake_wallet/entities/exchange_api_mode.dart'; @@ -42,6 +43,7 @@ abstract class SettingsStoreBase with Store { required FiatCurrency initialFiatCurrency, required BalanceDisplayMode initialBalanceDisplayMode, required bool initialSaveRecipientAddress, + required AutoGenerateSubaddressStatus initialAutoGenerateSubaddressStatus, required bool initialAppSecure, required bool initialDisableBuy, required bool initialDisableSell, @@ -87,6 +89,7 @@ abstract class SettingsStoreBase with Store { fiatCurrency = initialFiatCurrency, balanceDisplayMode = initialBalanceDisplayMode, shouldSaveRecipientAddress = initialSaveRecipientAddress, + autoGenerateSubaddressStatus = initialAutoGenerateSubaddressStatus, fiatApiMode = initialFiatMode, allowBiometricalAuthentication = initialAllowBiometricalAuthentication, selectedCake2FAPreset = initialCake2FAPresetOptions, @@ -197,6 +200,11 @@ abstract class SettingsStoreBase with Store { (bool disableSell) => sharedPreferences.setBool(PreferencesKey.disableSellKey, disableSell)); + reaction( + (_) => autoGenerateSubaddressStatus, + (AutoGenerateSubaddressStatus autoGenerateSubaddressStatus) => sharedPreferences.setInt( + PreferencesKey.autoGenerateSubaddressStatusKey, autoGenerateSubaddressStatus.value)); + reaction( (_) => fiatApiMode, (FiatApiMode mode) => @@ -337,6 +345,7 @@ abstract class SettingsStoreBase with Store { static const defaultPinLength = 4; static const defaultActionsMode = 11; static const defaultPinCodeTimeOutDuration = PinCodeRequiredDuration.tenminutes; + static const defaultAutoGenerateSubaddressStatus = AutoGenerateSubaddressStatus.initialized; @observable FiatCurrency fiatCurrency; @@ -359,6 +368,9 @@ abstract class SettingsStoreBase with Store { @observable bool shouldSaveRecipientAddress; + @observable + AutoGenerateSubaddressStatus autoGenerateSubaddressStatus; + @observable bool isAppSecure; @@ -602,7 +614,12 @@ abstract class SettingsStoreBase with Store { final packageInfo = await PackageInfo.fromPlatform(); final deviceName = await _getDeviceName() ?? ''; final shouldShowYatPopup = sharedPreferences.getBool(PreferencesKey.shouldShowYatPopup) ?? true; + final generateSubaddresses = + sharedPreferences.getInt(PreferencesKey.autoGenerateSubaddressStatusKey); + final autoGenerateSubaddressStatus = generateSubaddresses != null + ? AutoGenerateSubaddressStatus.deserialize(raw: generateSubaddresses) + : defaultAutoGenerateSubaddressStatus; final nodes = {}; if (moneroNode != null) { @@ -640,6 +657,7 @@ abstract class SettingsStoreBase with Store { initialFiatCurrency: currentFiatCurrency, initialBalanceDisplayMode: currentBalanceDisplayMode, initialSaveRecipientAddress: shouldSaveRecipientAddress, + initialAutoGenerateSubaddressStatus: autoGenerateSubaddressStatus, initialAppSecure: isAppSecure, initialDisableBuy: disableBuy, initialDisableSell: disableSell, @@ -709,6 +727,13 @@ abstract class SettingsStoreBase with Store { priority[WalletType.ethereum]!; } + final generateSubaddresses = + sharedPreferences.getInt(PreferencesKey.autoGenerateSubaddressStatusKey); + + autoGenerateSubaddressStatus = generateSubaddresses != null + ? AutoGenerateSubaddressStatus.deserialize(raw: generateSubaddresses) + : defaultAutoGenerateSubaddressStatus; + balanceDisplayMode = BalanceDisplayMode.deserialize( raw: sharedPreferences.getInt(PreferencesKey.currentBalanceDisplayModeKey)!); shouldSaveRecipientAddress = @@ -719,8 +744,6 @@ abstract class SettingsStoreBase with Store { numberOfFailedTokenTrials = sharedPreferences.getInt(PreferencesKey.failedTotpTokenTrials) ?? numberOfFailedTokenTrials; - sharedPreferences.getBool(PreferencesKey.shouldSaveRecipientAddressKey) ?? - shouldSaveRecipientAddress; isAppSecure = sharedPreferences.getBool(PreferencesKey.isAppSecureKey) ?? isAppSecure; disableBuy = sharedPreferences.getBool(PreferencesKey.disableBuyKey) ?? disableBuy; disableSell = sharedPreferences.getBool(PreferencesKey.disableSellKey) ?? disableSell; diff --git a/lib/view_model/contact_list/contact_list_view_model.dart b/lib/view_model/contact_list/contact_list_view_model.dart index c99984ebc..93b06c378 100644 --- a/lib/view_model/contact_list/contact_list_view_model.dart +++ b/lib/view_model/contact_list/contact_list_view_model.dart @@ -1,4 +1,5 @@ import 'dart:async'; +import 'package:cake_wallet/entities/auto_generate_subaddress_status.dart'; import 'package:cake_wallet/entities/contact_base.dart'; import 'package:cake_wallet/entities/wallet_contact.dart'; import 'package:cake_wallet/store/settings_store.dart'; @@ -10,6 +11,7 @@ import 'package:cake_wallet/entities/contact_record.dart'; import 'package:cake_wallet/entities/contact.dart'; import 'package:cake_wallet/utils/mobx.dart'; import 'package:cw_core/crypto_currency.dart'; +import 'package:collection/collection.dart'; part 'contact_list_view_model.g.dart'; @@ -20,12 +22,26 @@ abstract class ContactListViewModelBase with Store { ContactListViewModelBase(this.contactSource, this.walletInfoSource, this._currency, this.settingsStore) : contacts = ObservableList(), - walletContacts = [] { + walletContacts = [], + isAutoGenerateEnabled = + settingsStore.autoGenerateSubaddressStatus == AutoGenerateSubaddressStatus.enabled { walletInfoSource.values.forEach((info) { - if (info.addresses?.isNotEmpty ?? false) { - info.addresses?.forEach((address, label) { - final name = label.isNotEmpty ? info.name + ' ($label)' : info.name; - + if (isAutoGenerateEnabled && info.type == WalletType.monero && info.addressInfos != null) { + info.addressInfos!.forEach((key, value) { + final nextUnusedAddress = value.firstWhereOrNull( + (addressInfo) => !(info.usedAddresses?.contains(addressInfo.address) ?? false)); + if (nextUnusedAddress != null) { + final name = _createName(info.name, nextUnusedAddress.label); + walletContacts.add(WalletContact( + nextUnusedAddress.address, + name, + walletTypeToCryptoCurrency(info.type), + )); + } + }); + } else if (info.addresses?.isNotEmpty == true) { + info.addresses!.forEach((address, label) { + final name = _createName(info.name, label); walletContacts.add(WalletContact( address, name, @@ -40,6 +56,11 @@ abstract class ContactListViewModelBase with Store { initialFire: true); } + String _createName(String walletName, String label) { + return label.isNotEmpty ? '$walletName ($label)' : walletName; + } + + final bool isAutoGenerateEnabled; final Box contactSource; final Box walletInfoSource; final ObservableList contacts; diff --git a/lib/view_model/dashboard/dashboard_view_model.dart b/lib/view_model/dashboard/dashboard_view_model.dart index 7035130c0..009fe0350 100644 --- a/lib/view_model/dashboard/dashboard_view_model.dart +++ b/lib/view_model/dashboard/dashboard_view_model.dart @@ -1,3 +1,4 @@ +import 'package:cake_wallet/entities/auto_generate_subaddress_status.dart'; import 'package:cake_wallet/entities/exchange_api_mode.dart'; import 'package:cake_wallet/store/anonpay/anonpay_transactions_store.dart'; import 'package:cake_wallet/view_model/dashboard/anonpay_transaction_list_item.dart'; @@ -235,6 +236,10 @@ abstract class DashboardViewModelBase with Store { @computed double get price => balanceViewModel.price; + @computed + bool get isAutoGenerateSubaddressesEnabled => + settingsStore.autoGenerateSubaddressStatus != AutoGenerateSubaddressStatus.disabled; + @computed List get items { final _items = []; diff --git a/lib/view_model/settings/privacy_settings_view_model.dart b/lib/view_model/settings/privacy_settings_view_model.dart index 5dbfd61dd..27ce919df 100644 --- a/lib/view_model/settings/privacy_settings_view_model.dart +++ b/lib/view_model/settings/privacy_settings_view_model.dart @@ -1,6 +1,10 @@ +import 'package:cake_wallet/entities/auto_generate_subaddress_status.dart'; import 'package:cake_wallet/entities/exchange_api_mode.dart'; import 'package:cake_wallet/ethereum/ethereum.dart'; import 'package:cake_wallet/store/settings_store.dart'; +import 'package:cw_core/balance.dart'; +import 'package:cw_core/transaction_history.dart'; +import 'package:cw_core/transaction_info.dart'; import 'package:cw_core/wallet_base.dart'; import 'package:cw_core/wallet_type.dart'; import 'package:mobx/mobx.dart'; @@ -14,11 +18,27 @@ abstract class PrivacySettingsViewModelBase with Store { PrivacySettingsViewModelBase(this._settingsStore, this._wallet); final SettingsStore _settingsStore; - final WalletBase _wallet; + final WalletBase, TransactionInfo> _wallet; @computed ExchangeApiMode get exchangeStatus => _settingsStore.exchangeStatus; + @computed + bool get isAutoGenerateSubaddressesEnabled => + _settingsStore.autoGenerateSubaddressStatus != AutoGenerateSubaddressStatus.disabled; + + @action + void setAutoGenerateSubaddresses(bool value) { + _wallet.isEnabledAutoGenerateSubaddress = value; + if (value) { + _settingsStore.autoGenerateSubaddressStatus = AutoGenerateSubaddressStatus.enabled; + } else { + _settingsStore.autoGenerateSubaddressStatus = AutoGenerateSubaddressStatus.disabled; + } + } + + bool get isAutoGenerateSubaddressesVisible => _wallet.type == WalletType.monero; + @computed bool get shouldSaveRecipientAddress => _settingsStore.shouldSaveRecipientAddress; diff --git a/res/values/strings_ar.arb b/res/values/strings_ar.arb index 5bd8906fe..ae3f669ca 100644 --- a/res/values/strings_ar.arb +++ b/res/values/strings_ar.arb @@ -682,5 +682,7 @@ "support_title_other_links": "روابط دعم أخرى", "support_description_other_links": "انضم إلى مجتمعاتنا أو تصل إلينا شركائنا من خلال أساليب أخرى", "select_destination": ".ﻲﻃﺎﻴﺘﺣﻻﺍ ﺦﺴﻨﻟﺍ ﻒﻠﻣ ﺔﻬﺟﻭ ﺪﻳﺪﺤﺗ ءﺎﺟﺮﻟﺍ", - "save_to_downloads": "ﺕﻼﻳﺰﻨﺘﻟﺍ ﻲﻓ ﻆﻔﺣ" + "save_to_downloads": "ﺕﻼﻳﺰﻨﺘﻟﺍ ﻲﻓ ﻆﻔﺣ", + "support_description_other_links": "انضم إلى مجتمعاتنا أو تصل إلينا شركائنا من خلال أساليب أخرى", + "auto_generate_subaddresses": "تلقائي توليد subddresses" } diff --git a/res/values/strings_bg.arb b/res/values/strings_bg.arb index 325057aa4..ac2a08088 100644 --- a/res/values/strings_bg.arb +++ b/res/values/strings_bg.arb @@ -626,6 +626,7 @@ "setup_totp_recommended": "Настройка на TOTP (препоръчително)", "disable_buy": "Деактивирайте действието за покупка", "disable_sell": "Деактивирайте действието за продажба", + "auto_generate_subaddresses": "Автоматично генериране на подадреси", "cake_2fa_preset": "Торта 2FA Preset", "narrow": "Тесен", "normal": "нормално", diff --git a/res/values/strings_cs.arb b/res/values/strings_cs.arb index a258eaa40..38e01ca6b 100644 --- a/res/values/strings_cs.arb +++ b/res/values/strings_cs.arb @@ -626,6 +626,7 @@ "setup_totp_recommended": "Nastavit TOTP (doporučeno)", "disable_buy": "Zakázat akci nákupu", "disable_sell": "Zakázat akci prodeje", + "auto_generate_subaddresses": "Automaticky generovat podadresy", "cake_2fa_preset": "Předvolba Cake 2FA", "narrow": "Úzký", "normal": "Normální", diff --git a/res/values/strings_de.arb b/res/values/strings_de.arb index b315ddb56..afc5fba82 100644 --- a/res/values/strings_de.arb +++ b/res/values/strings_de.arb @@ -640,6 +640,7 @@ "high_contrast_theme": "Kontrastreiches Thema", "matrix_green_dark_theme": "Matrix Green Dark Theme", "monero_light_theme": "Monero Light-Thema", + "auto_generate_subaddresses": "Unteradressen automatisch generieren", "cake_2fa_preset" : "Cake 2FA-Voreinstellung", "narrow": "Eng", "normal": "Normal", diff --git a/res/values/strings_en.arb b/res/values/strings_en.arb index 9e8e3788c..4698701de 100644 --- a/res/values/strings_en.arb +++ b/res/values/strings_en.arb @@ -568,6 +568,7 @@ "privacy": "Privacy", "display_settings": "Display settings", "other_settings": "Other settings", + "auto_generate_subaddresses": "Auto generate subaddresses", "require_pin_after": "Require PIN after", "always": "Always", "minutes_to_pin_code": "${minute} minutes", diff --git a/res/values/strings_es.arb b/res/values/strings_es.arb index 3c0abce48..8a4519255 100644 --- a/res/values/strings_es.arb +++ b/res/values/strings_es.arb @@ -640,6 +640,7 @@ "high_contrast_theme": "Tema de alto contraste", "matrix_green_dark_theme": "Matrix verde oscuro tema", "monero_light_theme": "Tema ligero de Monero", + "auto_generate_subaddresses": "Generar subdirecciones automáticamente", "cake_2fa_preset": "Pastel 2FA preestablecido", "narrow": "Angosto", "normal": "Normal", diff --git a/res/values/strings_fr.arb b/res/values/strings_fr.arb index 1449d6370..357a73110 100644 --- a/res/values/strings_fr.arb +++ b/res/values/strings_fr.arb @@ -640,6 +640,7 @@ "high_contrast_theme": "Thème à contraste élevé", "matrix_green_dark_theme": "Thème Matrix Green Dark", "monero_light_theme": "Thème de lumière Monero", + "auto_generate_subaddresses": "Générer automatiquement des sous-adresses", "cake_2fa_preset": "Gâteau 2FA prédéfini", "narrow": "Étroit", "normal": "Normal", diff --git a/res/values/strings_ha.arb b/res/values/strings_ha.arb index e3542e9ac..d344bfb15 100644 --- a/res/values/strings_ha.arb +++ b/res/values/strings_ha.arb @@ -618,6 +618,7 @@ "high_contrast_theme": "Babban Jigon Kwatance", "matrix_green_dark_theme": "Matrix Green Dark Jigo", "monero_light_theme": "Jigon Hasken Monero", + "auto_generate_subaddresses": "Saɓaƙa subaddresses ta kai tsaye", "cake_2fa_preset": "Cake 2FA saiti", "narrow": "kunkuntar", "normal": "Na al'ada", diff --git a/res/values/strings_hi.arb b/res/values/strings_hi.arb index ca3e79bd0..e9ee219fe 100644 --- a/res/values/strings_hi.arb +++ b/res/values/strings_hi.arb @@ -640,6 +640,7 @@ "high_contrast_theme": "उच्च कंट्रास्ट थीम", "matrix_green_dark_theme": "मैट्रिक्स ग्रीन डार्क थीम", "monero_light_theme": "मोनेरो लाइट थीम", + "auto_generate_subaddresses": "स्वचालित रूप से उप-पते उत्पन्न करें", "cake_2fa_preset": "केक 2एफए प्रीसेट", "narrow": "सँकरा", "normal": "सामान्य", diff --git a/res/values/strings_hr.arb b/res/values/strings_hr.arb index e19d32ea1..91176248d 100644 --- a/res/values/strings_hr.arb +++ b/res/values/strings_hr.arb @@ -640,6 +640,7 @@ "high_contrast_theme": "Tema visokog kontrasta", "matrix_green_dark_theme": "Matrix Green Dark Theme", "monero_light_theme": "Monero lagana tema", + "auto_generate_subaddresses": "Automatski generirajte podadrese", "cake_2fa_preset": "Cake 2FA Preset", "narrow": "Usko", "normal": "Normalno", diff --git a/res/values/strings_id.arb b/res/values/strings_id.arb index 3f62f968c..daebfd579 100644 --- a/res/values/strings_id.arb +++ b/res/values/strings_id.arb @@ -622,6 +622,7 @@ "setup_totp_recommended": "Siapkan TOTP (Disarankan)", "disable_buy": "Nonaktifkan tindakan beli", "disable_sell": "Nonaktifkan aksi jual", + "auto_generate_subaddresses": "Menghasilkan subalamat secara otomatis", "cake_2fa_preset": "Preset Kue 2FA", "narrow": "Sempit", "normal": "Normal", diff --git a/res/values/strings_it.arb b/res/values/strings_it.arb index 8ad7502fd..691abd15c 100644 --- a/res/values/strings_it.arb +++ b/res/values/strings_it.arb @@ -634,6 +634,7 @@ "setup_totp_recommended": "Imposta TOTP (consigliato)", "disable_buy": "Disabilita l'azione di acquisto", "disable_sell": "Disabilita l'azione di vendita", + "auto_generate_subaddresses": "Genera automaticamente sottindirizzi", "cake_2fa_preset": "Torta 2FA Preset", "narrow": "Stretto", "normal": "Normale", diff --git a/res/values/strings_ja.arb b/res/values/strings_ja.arb index 3fe586279..254d7bbba 100644 --- a/res/values/strings_ja.arb +++ b/res/values/strings_ja.arb @@ -686,5 +686,6 @@ "support_title_other_links": "その他のサポートリンク", "support_description_other_links": "私たちのコミュニティに参加するか、他の方法を通して私たちのパートナーに連絡してください", "select_destination": "バックアップファイルの保存先を選択してください。", - "save_to_downloads": "ダウンロードに保存" + "save_to_downloads": "ダウンロードに保存", + "auto_generate_subaddresses": "Autoはサブアドレスを生成します" } diff --git a/res/values/strings_ko.arb b/res/values/strings_ko.arb index 1b7116e90..63a446fa3 100644 --- a/res/values/strings_ko.arb +++ b/res/values/strings_ko.arb @@ -686,5 +686,6 @@ "support_title_other_links": "다른 지원 링크", "support_description_other_links": "다른 방법을 통해 커뮤니티에 가입하거나 파트너에게 연락하십시오.", "select_destination": "백업 파일의 대상을 선택하십시오.", - "save_to_downloads": "다운로드에 저장" + "save_to_downloads": "다운로드에 저장", + "auto_generate_subaddresses": "자동 생성 서브 아드 드레스" } diff --git a/res/values/strings_my.arb b/res/values/strings_my.arb index c12adb5ac..b131e66d8 100644 --- a/res/values/strings_my.arb +++ b/res/values/strings_my.arb @@ -684,5 +684,6 @@ "support_title_other_links": "အခြားအထောက်အပံ့လင့်များ", "support_description_other_links": "ကျွန်ုပ်တို့၏လူမှုအသိုင်းအဝိုင်းများသို့ 0 င်ရောက်ပါ", "select_destination": "အရန်ဖိုင်အတွက် ဦးတည်ရာကို ရွေးပါ။", - "save_to_downloads": "ဒေါင်းလုဒ်များထံ သိမ်းဆည်းပါ။" + "save_to_downloads": "ဒေါင်းလုဒ်များထံ သိမ်းဆည်းပါ။", + "auto_generate_subaddresses": "အော်တို Generate Subaddresses" } diff --git a/res/values/strings_nl.arb b/res/values/strings_nl.arb index 18b3b5e5b..fcd683fd1 100644 --- a/res/values/strings_nl.arb +++ b/res/values/strings_nl.arb @@ -634,6 +634,7 @@ "setup_totp_recommended": "TOTP instellen (aanbevolen)", "disable_buy": "Koopactie uitschakelen", "disable_sell": "Verkoopactie uitschakelen", + "auto_generate_subaddresses": "Automatisch subadressen genereren", "cake_2fa_preset": "Taart 2FA Voorinstelling", "narrow": "Smal", "normal": "Normaal", diff --git a/res/values/strings_pl.arb b/res/values/strings_pl.arb index e458339ba..17745ccb8 100644 --- a/res/values/strings_pl.arb +++ b/res/values/strings_pl.arb @@ -634,6 +634,7 @@ "setup_totp_recommended": "Skonfiguruj TOTP (zalecane)", "disable_buy": "Wyłącz akcję kupna", "disable_sell": "Wyłącz akcję sprzedaży", + "auto_generate_subaddresses": "Automatycznie generuj podadresy", "cake_2fa_preset": "Ciasto 2FA Preset", "narrow": "Wąski", "normal": "Normalna", diff --git a/res/values/strings_pt.arb b/res/values/strings_pt.arb index aa06cd095..2148e6905 100644 --- a/res/values/strings_pt.arb +++ b/res/values/strings_pt.arb @@ -633,6 +633,7 @@ "setup_totp_recommended": "Configurar TOTP (recomendado)", "disable_buy": "Desativar ação de compra", "disable_sell": "Desativar ação de venda", + "auto_generate_subaddresses": "Gerar subendereços automaticamente", "cake_2fa_preset": "Predefinição de bolo 2FA", "narrow": "Estreito", "normal": "Normal", diff --git a/res/values/strings_ru.arb b/res/values/strings_ru.arb index 533ca431d..e229ddee9 100644 --- a/res/values/strings_ru.arb +++ b/res/values/strings_ru.arb @@ -686,5 +686,6 @@ "support_title_other_links": "Другие ссылки на поддержку", "support_description_other_links": "Присоединяйтесь к нашим сообществам или охватите нас наших партнеров с помощью других методов", "select_destination": "Пожалуйста, выберите место для файла резервной копии.", - "save_to_downloads": "Сохранить в загрузках" + "save_to_downloads": "Сохранить в загрузках", + "auto_generate_subaddresses": "Авто генерируйте Subaddresses" } diff --git a/res/values/strings_th.arb b/res/values/strings_th.arb index f54194617..100929da2 100644 --- a/res/values/strings_th.arb +++ b/res/values/strings_th.arb @@ -684,5 +684,6 @@ "support_title_other_links": "ลิงค์สนับสนุนอื่น ๆ", "support_description_other_links": "เข้าร่วมชุมชนของเราหรือเข้าถึงเราพันธมิตรของเราผ่านวิธีการอื่น ๆ", "select_destination": "โปรดเลือกปลายทางสำหรับไฟล์สำรอง", - "save_to_downloads": "บันทึกลงดาวน์โหลด" + "save_to_downloads": "บันทึกลงดาวน์โหลด", + "auto_generate_subaddresses": "Auto สร้าง subaddresses" } diff --git a/res/values/strings_tr.arb b/res/values/strings_tr.arb index fa835912a..e74d1a281 100644 --- a/res/values/strings_tr.arb +++ b/res/values/strings_tr.arb @@ -632,6 +632,7 @@ "setup_totp_recommended": "TOTP'yi kurun (Önerilir)", "disable_buy": "Satın alma işlemini devre dışı bırak", "disable_sell": "Satış işlemini devre dışı bırak", + "auto_generate_subaddresses": "Alt adresleri otomatik olarak oluştur", "cake_2fa_preset": "Kek 2FA Ön Ayarı", "narrow": "Dar", "normal": "Normal", diff --git a/res/values/strings_uk.arb b/res/values/strings_uk.arb index 6d9a648fa..313f6aeb0 100644 --- a/res/values/strings_uk.arb +++ b/res/values/strings_uk.arb @@ -634,6 +634,7 @@ "setup_totp_recommended": "Налаштувати TOTP (рекомендовано)", "disable_buy": "Вимкнути дію покупки", "disable_sell": "Вимкнути дію продажу", + "auto_generate_subaddresses": "Автоматично генерувати підадреси", "cake_2fa_preset": "Торт 2FA Preset", "narrow": "вузькі", "normal": "нормальний", diff --git a/res/values/strings_ur.arb b/res/values/strings_ur.arb index 39cae33d0..b8e0c0a00 100644 --- a/res/values/strings_ur.arb +++ b/res/values/strings_ur.arb @@ -678,5 +678,6 @@ "support_title_other_links": "دوسرے سپورٹ لنکس", "support_description_other_links": "ہماری برادریوں میں شامل ہوں یا دوسرے طریقوں سے ہمارے شراکت داروں تک پہنچیں", "select_destination": "۔ﮟﯾﺮﮐ ﺏﺎﺨﺘﻧﺍ ﺎﮐ ﻝﺰﻨﻣ ﮯﯿﻟ ﮯﮐ ﻞﺋﺎﻓ ﭖﺍ ﮏﯿﺑ ﻡﺮﮐ ﮦﺍﺮﺑ", - "save_to_downloads": "۔ﮟﯾﺮﮐ ﻅﻮﻔﺤﻣ ﮟﯿﻣ ﺯﮈﻮﻟ ﻥﺅﺍﮈ" + "save_to_downloads": "۔ﮟﯾﺮﮐ ﻅﻮﻔﺤﻣ ﮟﯿﻣ ﺯﮈﻮﻟ ﻥﺅﺍﮈ", + "auto_generate_subaddresses": "آٹو سب ایڈریس تیار کرتا ہے" } diff --git a/res/values/strings_yo.arb b/res/values/strings_yo.arb index 75df8de6b..143fd0ccd 100644 --- a/res/values/strings_yo.arb +++ b/res/values/strings_yo.arb @@ -680,5 +680,6 @@ "matrix_green_dark_theme": "Matrix Green Dark Akori", "monero_light_theme": "Monero Light Akori", "select_destination": "Jọwọ yan ibi ti o nlo fun faili afẹyinti.", - "save_to_downloads": "Fipamọ si Awọn igbasilẹ" + "save_to_downloads": "Fipamọ si Awọn igbasilẹ", + "auto_generate_subaddresses": "Aṣiṣe Ibi-Afọwọkọ" } diff --git a/res/values/strings_zh.arb b/res/values/strings_zh.arb index 3fa31bb10..7e3ceae31 100644 --- a/res/values/strings_zh.arb +++ b/res/values/strings_zh.arb @@ -685,5 +685,6 @@ "matrix_green_dark_theme": "矩阵绿暗主题", "monero_light_theme": "门罗币浅色主题", "select_destination": "请选择备份文件的目的地。", - "save_to_downloads": "保存到下载" + "save_to_downloads": "保存到下载", + "auto_generate_subaddresses": "自动生成子辅助" } diff --git a/tool/append_translation.dart b/tool/append_translation.dart index e56ad89d6..5c48aceab 100644 --- a/tool/append_translation.dart +++ b/tool/append_translation.dart @@ -1,15 +1,6 @@ -import 'dart:convert'; -import 'dart:io'; - -import 'package:translator/translator.dart'; - -const defaultLang = "en"; -const langs = [ - "ar", "bg", "cs", "de", "en", "es", "fr", "ha", "hi", "hr", "id", "it", - "ja", "ko", "my", "nl", "pl", "pt", "ru", "th", "tr", "uk", "ur", "yo", - "zh-cn" // zh, but Google Translate uses zh-cn for Chinese (Simplified) -]; -final translator = GoogleTranslator(); +import 'utils/translation/arb_file_utils.dart'; +import 'utils/translation/translation_constants.dart'; +import 'utils/translation/translation_utils.dart'; void main(List args) async { if (args.length != 2) { @@ -23,44 +14,9 @@ void main(List args) async { print('Appending "$name": "$text"'); for (var lang in langs) { - final fileName = getFileName(lang); + final fileName = getArbFileName(lang); final translation = await getTranslation(text, lang); - appendArbFile(fileName, name, translation); + appendStringToArbFile(fileName, name, translation); } } - -void appendArbFile(String fileName, String name, String text) { - final file = File(fileName); - final inputContent = file.readAsStringSync(); - final arbObj = json.decode(inputContent) as Map; - - if (arbObj.containsKey(name)) { - print("String $name already exists in $fileName!"); - return; - } - - arbObj.addAll({name: text}); - - final outputContent = json - .encode(arbObj) - .replaceAll('","', '",\n "') - .replaceAll('{"', '{\n "') - .replaceAll('"}', '"\n}') - .replaceAll('":"', '": "'); - - file.writeAsStringSync(outputContent); -} - - -Future getTranslation(String text, String lang) async { - if (lang == defaultLang) return text; - return (await translator.translate(text, from: defaultLang, to: lang)).text; -} - -String getFileName(String lang) { - final shortLang = lang - .split("-") - .first; - return "./res/values/strings_$shortLang.arb"; -} diff --git a/tool/translation_consistence.dart b/tool/translation_consistence.dart new file mode 100644 index 000000000..04f64dfc8 --- /dev/null +++ b/tool/translation_consistence.dart @@ -0,0 +1,37 @@ +import 'dart:io'; + +import 'utils/translation/arb_file_utils.dart'; +import 'utils/translation/translation_constants.dart'; +import 'utils/translation/translation_utils.dart'; + +void main(List args) async { + print('Checking Consistency of all arb-files. Default: $defaultLang'); + + final doFix = args.contains("--fix"); + + if (doFix) + print('Auto fixing enabled!\n'); + else + print('Auto fixing disabled!\nRun with arg "--fix" to enable autofix\n'); + + final fileName = getArbFileName(defaultLang); + final file = File(fileName); + final arbObj = readArbFile(file); + + for (var lang in langs) { + final fileName = getArbFileName(lang); + final missingKeys = getMissingKeysInArbFile(fileName, arbObj.keys); + if (missingKeys.isNotEmpty) { + final missingDefaults = {}; + + missingKeys.forEach((key) { + print('Missing in "$lang": "$key"'); + if (doFix) + missingDefaults[key] = arbObj[key] as String; + }); + + if (missingDefaults.isNotEmpty) + await appendTranslations(lang, missingDefaults); + } + } +} diff --git a/tool/utils/translation/arb_file_utils.dart b/tool/utils/translation/arb_file_utils.dart new file mode 100644 index 000000000..693c5b93e --- /dev/null +++ b/tool/utils/translation/arb_file_utils.dart @@ -0,0 +1,66 @@ +import 'dart:convert'; +import 'dart:io'; + +void appendStringToArbFile(String fileName, String name, String text) { + final file = File(fileName); + final arbObj = readArbFile(file); + + if (arbObj.containsKey(name)) { + print("String $name already exists in $fileName!"); + return; + } + + arbObj.addAll({name: text}); + + final outputContent = json + .encode(arbObj) + .replaceAll('","', '",\n "') + .replaceAll('{"', '{\n "') + .replaceAll('"}', '"\n}') + .replaceAll('":"', '": "'); + + file.writeAsStringSync(outputContent); +} + +void appendStringsToArbFile(String fileName, Map strings) { + final file = File(fileName); + final arbObj = readArbFile(file); + + arbObj.addAll(strings); + + final outputContent = json + .encode(arbObj) + .replaceAll('","', '",\n "') + .replaceAll('{"', '{\n "') + .replaceAll('"}', '"\n}') + .replaceAll('":"', '": "'); + + file.writeAsStringSync(outputContent); +} + +Map readArbFile(File file) { + final inputContent = file.readAsStringSync(); + + return json.decode(inputContent) as Map; +} + +String getArbFileName(String lang) { + final shortLang = lang + .split("-") + .first; + return "./res/values/strings_$shortLang.arb"; +} + +List getMissingKeysInArbFile(String fileName, Iterable langKeys) { + final file = File(fileName); + final arbObj = readArbFile(file); + final results = []; + + for (var langKey in langKeys) { + if (!arbObj.containsKey(langKey)) { + results.add(langKey); + } + } + + return results; +} diff --git a/tool/utils/translation/translation_constants.dart b/tool/utils/translation/translation_constants.dart new file mode 100644 index 000000000..6563feb32 --- /dev/null +++ b/tool/utils/translation/translation_constants.dart @@ -0,0 +1,6 @@ +const defaultLang = "en"; +const langs = [ + "ar", "bg", "cs", "de", "en", "es", "fr", "ha", "hi", "hr", "id", "it", + "ja", "ko", "my", "nl", "pl", "pt", "ru", "th", "tr", "uk", "ur", "yo", + "zh-cn" // zh, but Google Translate uses zh-cn for Chinese (Simplified) +]; diff --git a/tool/utils/translation/translation_utils.dart b/tool/utils/translation/translation_utils.dart new file mode 100644 index 000000000..a37838b91 --- /dev/null +++ b/tool/utils/translation/translation_utils.dart @@ -0,0 +1,37 @@ +import 'package:translator/translator.dart'; + +import 'arb_file_utils.dart'; +import 'translation_constants.dart'; + +final translator = GoogleTranslator(); + +Future appendTranslation(String lang, String key, String text) async { + final fileName = getArbFileName(lang); + final translation = await getTranslation(text, lang); + + appendStringToArbFile(fileName, key, translation); +} + +Future appendTranslations(String lang, Map defaults) async { + final fileName = getArbFileName(lang); + final translations = {}; + + for (var key in defaults.keys) { + final value = defaults[key]!; + + if (value.contains("{")) continue; + final translation = await getTranslation(value, lang); + + translations[key] = translation; + } + + print(translations); + + appendStringsToArbFile(fileName, translations); +} + +Future getTranslation(String text, String lang) async { + if (lang == defaultLang) return text; + return (await translator.translate(text, from: defaultLang, to: lang)).text; +} + From 1cc2c645fa1e6fc338c62b96eab830c13012d186 Mon Sep 17 00:00:00 2001 From: Omar Hatem Date: Wed, 30 Aug 2023 18:11:56 +0300 Subject: [PATCH 2/9] Fix Wallet Loading issues (basic_string & input_stream) (#1059) * Recover from wallet loading exceptions (basic_string & input_stream) Recover from removed cached wallets * Fix restoring as Monero wallets Fix restoring wallets with invalid files * Add coin control missing changes for macos monero files * Add same key for cached dependencies [skip ci] --- .github/workflows/cache_dependencies.yml | 2 +- cw_monero/lib/monero_wallet_service.dart | 21 +- cw_monero/macos/Classes/monero_api.cpp | 203 +++++++++++++++++-- lib/entities/default_settings_migration.dart | 77 ++++++- macos/Podfile.lock | 8 +- 5 files changed, 279 insertions(+), 32 deletions(-) diff --git a/.github/workflows/cache_dependencies.yml b/.github/workflows/cache_dependencies.yml index d57281447..ef4175641 100644 --- a/.github/workflows/cache_dependencies.yml +++ b/.github/workflows/cache_dependencies.yml @@ -45,7 +45,7 @@ jobs: /opt/android/cake_wallet/cw_monero/android/.cxx /opt/android/cake_wallet/cw_monero/ios/External /opt/android/cake_wallet/cw_shared_external/ios/External - key: ${{ hashFiles('**/build_monero.sh', '**/build_haven.sh') }} + key: ${{ hashFiles('**/build_monero.sh', '**/build_haven.sh', '**/monero_api.cpp') }} - if: ${{ steps.cache-externals.outputs.cache-hit != 'true' }} name: Generate Externals diff --git a/cw_monero/lib/monero_wallet_service.dart b/cw_monero/lib/monero_wallet_service.dart index 90c63fb72..9d1a0b3e4 100644 --- a/cw_monero/lib/monero_wallet_service.dart +++ b/cw_monero/lib/monero_wallet_service.dart @@ -57,7 +57,7 @@ class MoneroWalletService extends WalletService< final Box walletInfoSource; final Box unspentCoinsInfoSource; - + static bool walletFilesExist(String path) => !File(path).existsSync() && !File('$path.keys').existsSync(); @@ -124,13 +124,20 @@ class MoneroWalletService extends WalletService< } catch (e) { // TODO: Implement Exception for wallet list service. - if ((e.toString().contains('bad_alloc') || + final bool isBadAlloc = e.toString().contains('bad_alloc') || (e is WalletOpeningException && - (e.message == 'std::bad_alloc' || - e.message.contains('bad_alloc')))) || - (e.toString().contains('does not correspond') || - (e is WalletOpeningException && - e.message.contains('does not correspond')))) { + (e.message == 'std::bad_alloc' || e.message.contains('bad_alloc'))); + + final bool doesNotCorrespond = e.toString().contains('does not correspond') || + (e is WalletOpeningException && e.message.contains('does not correspond')); + + final bool isMissingCacheFilesIOS = e.toString().contains('basic_string') || + (e is WalletOpeningException && e.message.contains('basic_string')); + + final bool isMissingCacheFilesAndroid = e.toString().contains('input_stream') || + (e is WalletOpeningException && e.message.contains('input_stream')); + + if (isBadAlloc || doesNotCorrespond || isMissingCacheFilesIOS || isMissingCacheFilesAndroid) { await restoreOrResetWalletFiles(name); return openWallet(name, password); } diff --git a/cw_monero/macos/Classes/monero_api.cpp b/cw_monero/macos/Classes/monero_api.cpp index 56548e79e..ac8a64861 100644 --- a/cw_monero/macos/Classes/monero_api.cpp +++ b/cw_monero/macos/Classes/monero_api.cpp @@ -3,8 +3,10 @@ #include #include #include +#include #include #include +#include #include "thread" #include "CwWalletListener.h" #if __APPLE__ @@ -137,7 +139,7 @@ extern "C" int8_t direction; int8_t isPending; uint32_t subaddrIndex; - + char *hash; char *paymentId; @@ -152,7 +154,7 @@ extern "C" std::set::iterator it = transaction->subaddrIndex().begin(); subaddrIndex = *it; confirmations = transaction->confirmations(); - datetime = static_cast(transaction->timestamp()); + datetime = static_cast(transaction->timestamp()); direction = transaction->direction(); isPending = static_cast(transaction->isPending()); std::string *hash_str = new std::string(transaction->hash()); @@ -181,6 +183,62 @@ extern "C" } }; + struct CoinsInfoRow + { + uint64_t blockHeight; + char *hash; + uint64_t internalOutputIndex; + uint64_t globalOutputIndex; + bool spent; + bool frozen; + uint64_t spentHeight; + uint64_t amount; + bool rct; + bool keyImageKnown; + uint64_t pkIndex; + uint32_t subaddrIndex; + uint32_t subaddrAccount; + char *address; + char *addressLabel; + char *keyImage; + uint64_t unlockTime; + bool unlocked; + char *pubKey; + bool coinbase; + char *description; + + CoinsInfoRow(Monero::CoinsInfo *coinsInfo) + { + blockHeight = coinsInfo->blockHeight(); + std::string *hash_str = new std::string(coinsInfo->hash()); + hash = strdup(hash_str->c_str()); + internalOutputIndex = coinsInfo->internalOutputIndex(); + globalOutputIndex = coinsInfo->globalOutputIndex(); + spent = coinsInfo->spent(); + frozen = coinsInfo->frozen(); + spentHeight = coinsInfo->spentHeight(); + amount = coinsInfo->amount(); + rct = coinsInfo->rct(); + keyImageKnown = coinsInfo->keyImageKnown(); + pkIndex = coinsInfo->pkIndex(); + subaddrIndex = coinsInfo->subaddrIndex(); + subaddrAccount = coinsInfo->subaddrAccount(); + address = strdup(coinsInfo->address().c_str()) ; + addressLabel = strdup(coinsInfo->addressLabel().c_str()); + keyImage = strdup(coinsInfo->keyImage().c_str()); + unlockTime = coinsInfo->unlockTime(); + unlocked = coinsInfo->unlocked(); + pubKey = strdup(coinsInfo->pubKey().c_str()); + coinbase = coinsInfo->coinbase(); + description = strdup(coinsInfo->description().c_str()); + } + + void setUnlocked(bool unlocked); + + }; + + Monero::Coins *m_coins; + Monero::Wallet *m_wallet; Monero::TransactionHistory *m_transaction_history; MoneroWalletListener *m_listener; @@ -188,6 +246,7 @@ extern "C" Monero::SubaddressAccount *m_account; uint64_t m_last_known_wallet_height; uint64_t m_cached_syncing_blockchain_height = 0; + std::list m_coins_info; std::mutex store_lock; bool is_storing = false; @@ -195,7 +254,7 @@ extern "C" { m_wallet = wallet; m_listener = nullptr; - + if (wallet != nullptr) { @@ -223,6 +282,17 @@ extern "C" { m_subaddress = nullptr; } + + m_coins_info = std::list(); + + if (wallet != nullptr) + { + m_coins = wallet->coins(); + } + else + { + m_coins = nullptr; + } } Monero::Wallet *get_current_wallet() @@ -405,13 +475,14 @@ extern "C" return is_connected; } - bool setup_node(char *address, char *login, char *password, bool use_ssl, bool is_light_wallet, char *error) + bool setup_node(char *address, char *login, char *password, bool use_ssl, bool is_light_wallet, char *socksProxyAddress, char *error) { nice(19); Monero::Wallet *wallet = get_current_wallet(); - + std::string _login = ""; std::string _password = ""; + std::string _socksProxyAddress = ""; if (login != nullptr) { @@ -423,7 +494,12 @@ extern "C" _password = std::string(password); } - bool inited = wallet->init(std::string(address), 0, _login, _password, use_ssl, is_light_wallet); + if (socksProxyAddress != nullptr) + { + _socksProxyAddress = std::string(socksProxyAddress); + } + + bool inited = wallet->init(std::string(address), 0, _login, _password, use_ssl, is_light_wallet, _socksProxyAddress); if (!inited) { @@ -480,10 +556,19 @@ extern "C" } bool transaction_create(char *address, char *payment_id, char *amount, - uint8_t priority_raw, uint32_t subaddr_account, Utf8Box &error, PendingTransactionRaw &pendingTransaction) + uint8_t priority_raw, uint32_t subaddr_account, + char **preferred_inputs, uint32_t preferred_inputs_size, + Utf8Box &error, PendingTransactionRaw &pendingTransaction) { nice(19); - + + std::set _preferred_inputs; + + for (int i = 0; i < preferred_inputs_size; i++) { + _preferred_inputs.insert(std::string(*preferred_inputs)); + preferred_inputs++; + } + auto priority = static_cast(priority_raw); std::string _payment_id; Monero::PendingTransaction *transaction; @@ -496,13 +581,13 @@ extern "C" if (amount != nullptr) { uint64_t _amount = Monero::Wallet::amountFromString(std::string(amount)); - transaction = m_wallet->createTransaction(std::string(address), _payment_id, _amount, m_wallet->defaultMixin(), priority, subaddr_account); + transaction = m_wallet->createTransaction(std::string(address), _payment_id, _amount, m_wallet->defaultMixin(), priority, subaddr_account, {}, _preferred_inputs); } else { - transaction = m_wallet->createTransaction(std::string(address), _payment_id, Monero::optional(), m_wallet->defaultMixin(), priority, subaddr_account); + transaction = m_wallet->createTransaction(std::string(address), _payment_id, Monero::optional(), m_wallet->defaultMixin(), priority, subaddr_account, {}, _preferred_inputs); } - + int status = transaction->status(); if (status == Monero::PendingTransaction::Status::Status_Error || status == Monero::PendingTransaction::Status::Status_Critical) @@ -520,7 +605,9 @@ extern "C" } bool transaction_create_mult_dest(char **addresses, char *payment_id, char **amounts, uint32_t size, - uint8_t priority_raw, uint32_t subaddr_account, Utf8Box &error, PendingTransactionRaw &pendingTransaction) + uint8_t priority_raw, uint32_t subaddr_account, + char **preferred_inputs, uint32_t preferred_inputs_size, + Utf8Box &error, PendingTransactionRaw &pendingTransaction) { nice(19); @@ -534,6 +621,13 @@ extern "C" amounts++; } + std::set _preferred_inputs; + + for (int i = 0; i < preferred_inputs_size; i++) { + _preferred_inputs.insert(std::string(*preferred_inputs)); + preferred_inputs++; + } + auto priority = static_cast(priority_raw); std::string _payment_id; Monero::PendingTransaction *transaction; @@ -793,6 +887,91 @@ extern "C" return m_wallet->trustedDaemon(); } + CoinsInfoRow* coin(int index) + { + if (index >= 0 && index < m_coins_info.size()) { + std::list::iterator it = m_coins_info.begin(); + std::advance(it, index); + Monero::CoinsInfo* element = *it; + std::cout << "Element at index " << index << ": " << element << std::endl; + return new CoinsInfoRow(element); + } else { + std::cout << "Invalid index." << std::endl; + return nullptr; // Return a default value (nullptr) for invalid index + } + } + + void refresh_coins(uint32_t accountIndex) + { + m_coins_info.clear(); + + m_coins->refresh(); + for (const auto i : m_coins->getAll()) { + if (i->subaddrAccount() == accountIndex && !(i->spent())) { + m_coins_info.push_back(i); + } + } + } + + uint64_t coins_count() + { + return m_coins_info.size(); + } + + CoinsInfoRow** coins_from_account(uint32_t accountIndex) + { + std::vector matchingCoins; + + for (int i = 0; i < coins_count(); i++) { + CoinsInfoRow* coinInfo = coin(i); + if (coinInfo->subaddrAccount == accountIndex) { + matchingCoins.push_back(coinInfo); + } + } + + CoinsInfoRow** result = new CoinsInfoRow*[matchingCoins.size()]; + std::copy(matchingCoins.begin(), matchingCoins.end(), result); + return result; + } + + CoinsInfoRow** coins_from_txid(const char* txid, size_t* count) + { + std::vector matchingCoins; + + for (int i = 0; i < coins_count(); i++) { + CoinsInfoRow* coinInfo = coin(i); + if (std::string(coinInfo->hash) == txid) { + matchingCoins.push_back(coinInfo); + } + } + + *count = matchingCoins.size(); + CoinsInfoRow** result = new CoinsInfoRow*[*count]; + std::copy(matchingCoins.begin(), matchingCoins.end(), result); + return result; + } + + CoinsInfoRow** coins_from_key_image(const char** keyimages, size_t keyimageCount, size_t* count) + { + std::vector matchingCoins; + + for (int i = 0; i < coins_count(); i++) { + CoinsInfoRow* coinsInfoRow = coin(i); + for (size_t j = 0; j < keyimageCount; j++) { + if (coinsInfoRow->keyImageKnown && std::string(coinsInfoRow->keyImage) == keyimages[j]) { + matchingCoins.push_back(coinsInfoRow); + break; + } + } + } + + *count = matchingCoins.size(); + CoinsInfoRow** result = new CoinsInfoRow*[*count]; + std::copy(matchingCoins.begin(), matchingCoins.end(), result); + return result; + } + + #ifdef __cplusplus } #endif diff --git a/lib/entities/default_settings_migration.dart b/lib/entities/default_settings_migration.dart index b4cb23131..a783eec43 100644 --- a/lib/entities/default_settings_migration.dart +++ b/lib/entities/default_settings_migration.dart @@ -1,12 +1,11 @@ -import 'dart:io' show File, Platform; +import 'dart:io' show Directory, File, Platform; import 'package:cake_wallet/bitcoin/bitcoin.dart'; import 'package:cake_wallet/entities/exchange_api_mode.dart'; import 'package:cw_core/pathForWallet.dart'; import 'package:cake_wallet/entities/secret_store_key.dart'; -import 'package:flutter/foundation.dart'; import 'package:flutter_secure_storage/flutter_secure_storage.dart'; import 'package:hive/hive.dart'; -import 'package:share_plus/share_plus.dart'; +import 'package:path_provider/path_provider.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:cake_wallet/entities/preferences_key.dart'; import 'package:cw_core/wallet_type.dart'; @@ -28,7 +27,7 @@ const cakeWalletLitecoinElectrumUri = 'ltc-electrum.cakewallet.com:50002'; const havenDefaultNodeUri = 'nodes.havenprotocol.org:443'; const ethereumDefaultNodeUri = 'ethereum.publicnode.com'; -Future defaultSettingsMigration( +Future defaultSettingsMigration( {required int version, required SharedPreferences sharedPreferences, required FlutterSecureStorage secureStorage, @@ -43,6 +42,8 @@ Future defaultSettingsMigration( // check current nodes for nullability regardless of the version await checkCurrentNodes(nodes, sharedPreferences); + await _validateWalletInfoBoxData(walletInfoSource); + final isNewInstall = sharedPreferences .getInt(PreferencesKey.currentDefaultSettingsMigrationVersion) == null; @@ -179,6 +180,66 @@ Future defaultSettingsMigration( PreferencesKey.currentDefaultSettingsMigrationVersion, version); } +Future _validateWalletInfoBoxData(Box walletInfoSource) async { + final root = await getApplicationDocumentsDirectory(); + + for (var type in WalletType.values) { + if (type == WalletType.none) { + continue; + } + + String prefix = walletTypeToString(type).toLowerCase(); + Directory walletsDir = Directory('${root.path}/wallets/$prefix/'); + + if (!walletsDir.existsSync()) { + continue; + } + + List walletNames = walletsDir.listSync().map((e) => e.path.split("/").last).toList(); + + for (var name in walletNames) { + final dir = Directory(await pathForWalletDir(name: name, type: type)); + + final walletFiles = dir.listSync(); + final hasCacheFile = walletFiles.any((element) => element.path.contains("$name/$name")); + + if (!hasCacheFile) { + continue; + } + + if (type == WalletType.monero || type == WalletType.haven) { + final hasKeysFile = walletFiles.any((element) => element.path.contains(".keys")); + + if (!hasKeysFile) { + continue; + } + } + + final id = prefix + '_' + name; + final exist = walletInfoSource.values.any((el) => el.id == id); + + if (exist) { + continue; + } + + final walletInfo = WalletInfo.external( + id: id, + type: type, + name: name, + isRecovery: true, + restoreHeight: 0, + date: DateTime.now(), + dirPath: dir.path, + path: '${dir.path}/$name', + address: '', + showIntroCakePayCard: false, + ); + + walletInfoSource.add(walletInfo); + } + } +} + Future validateBitcoinSavedTransactionPriority(SharedPreferences sharedPreferences) async { if (bitcoin == null) { return; @@ -226,7 +287,7 @@ Future changeMoneroCurrentNodeToDefault( {required SharedPreferences sharedPreferences, required Box nodes}) async { final node = getMoneroDefaultNode(nodes: nodes); - final nodeId = node?.key as int ?? 0; // 0 - England + final nodeId = node.key as int? ?? 0; // 0 - England await sharedPreferences.setInt(PreferencesKey.currentNodeIdKey, nodeId); } @@ -279,7 +340,7 @@ Future changeBitcoinCurrentElectrumServerToDefault( {required SharedPreferences sharedPreferences, required Box nodes}) async { final server = getBitcoinDefaultElectrumServer(nodes: nodes); - final serverId = server?.key as int ?? 0; + final serverId = server?.key as int? ?? 0; await sharedPreferences.setInt(PreferencesKey.currentBitcoinElectrumSererIdKey, serverId); } @@ -288,7 +349,7 @@ Future changeLitecoinCurrentElectrumServerToDefault( {required SharedPreferences sharedPreferences, required Box nodes}) async { final server = getLitecoinDefaultElectrumServer(nodes: nodes); - final serverId = server?.key as int ?? 0; + final serverId = server?.key as int? ?? 0; await sharedPreferences.setInt(PreferencesKey.currentLitecoinElectrumSererIdKey, serverId); } @@ -297,7 +358,7 @@ Future changeHavenCurrentNodeToDefault( {required SharedPreferences sharedPreferences, required Box nodes}) async { final node = getHavenDefaultNode(nodes: nodes); - final nodeId = node?.key as int ?? 0; + final nodeId = node?.key as int? ?? 0; await sharedPreferences.setInt(PreferencesKey.currentHavenNodeIdKey, nodeId); } diff --git a/macos/Podfile.lock b/macos/Podfile.lock index 6657ec4dc..664a5231b 100644 --- a/macos/Podfile.lock +++ b/macos/Podfile.lock @@ -57,11 +57,11 @@ DEPENDENCIES: - FlutterMacOS (from `Flutter/ephemeral`) - in_app_review (from `Flutter/ephemeral/.symlinks/plugins/in_app_review/macos`) - package_info (from `Flutter/ephemeral/.symlinks/plugins/package_info/macos`) - - path_provider_foundation (from `Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/macos`) + - path_provider_foundation (from `Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/darwin`) - platform_device_id (from `Flutter/ephemeral/.symlinks/plugins/platform_device_id/macos`) - platform_device_id_macos (from `Flutter/ephemeral/.symlinks/plugins/platform_device_id_macos/macos`) - share_plus_macos (from `Flutter/ephemeral/.symlinks/plugins/share_plus_macos/macos`) - - shared_preferences_foundation (from `Flutter/ephemeral/.symlinks/plugins/shared_preferences_foundation/macos`) + - shared_preferences_foundation (from `Flutter/ephemeral/.symlinks/plugins/shared_preferences_foundation/darwin`) - url_launcher_macos (from `Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos`) - wakelock_macos (from `Flutter/ephemeral/.symlinks/plugins/wakelock_macos/macos`) @@ -87,7 +87,7 @@ EXTERNAL SOURCES: package_info: :path: Flutter/ephemeral/.symlinks/plugins/package_info/macos path_provider_foundation: - :path: Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/macos + :path: Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/darwin platform_device_id: :path: Flutter/ephemeral/.symlinks/plugins/platform_device_id/macos platform_device_id_macos: @@ -95,7 +95,7 @@ EXTERNAL SOURCES: share_plus_macos: :path: Flutter/ephemeral/.symlinks/plugins/share_plus_macos/macos shared_preferences_foundation: - :path: Flutter/ephemeral/.symlinks/plugins/shared_preferences_foundation/macos + :path: Flutter/ephemeral/.symlinks/plugins/shared_preferences_foundation/darwin url_launcher_macos: :path: Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos wakelock_macos: From 6b9c47563e6410772f080bc3f6c875814285b786 Mon Sep 17 00:00:00 2001 From: OmarHatem Date: Wed, 30 Aug 2023 19:15:07 +0300 Subject: [PATCH 3/9] Allow triggering cache workflow manually Fix haven build script --- .github/workflows/cache_dependencies.yml | 1 + scripts/android/build_haven.sh | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/cache_dependencies.yml b/.github/workflows/cache_dependencies.yml index ef4175641..5373bbc87 100644 --- a/.github/workflows/cache_dependencies.yml +++ b/.github/workflows/cache_dependencies.yml @@ -1,6 +1,7 @@ name: Cache Dependencies on: + workflow_dispatch: push: branches: [ main ] diff --git a/scripts/android/build_haven.sh b/scripts/android/build_haven.sh index f745e942e..16cf05039 100755 --- a/scripts/android/build_haven.sh +++ b/scripts/android/build_haven.sh @@ -5,8 +5,8 @@ HAVEN_VERSION=tags/v3.0.7 HAVEN_SRC_DIR=${WORKDIR}/haven git clone https://github.com/haven-protocol-org/haven-main.git ${HAVEN_SRC_DIR} -git checkout ${HAVEN_VERSION} cd $HAVEN_SRC_DIR +git checkout ${HAVEN_VERSION} git submodule init git submodule update From d51b9bd684d2bb888bc1b77ea970f7f183100b10 Mon Sep 17 00:00:00 2001 From: Adegoke David <64401859+Blazebrain@users.noreply.github.com> Date: Thu, 31 Aug 2023 18:09:10 +0100 Subject: [PATCH 4/9] feat: Replace transfer.sh (#1066) * feat: Replace transfer.sh * Update build_haven.sh * Update pr_test_build.yml * Update pr_test_build.yml * Update pr_test_build.yml * feat: Change apk name in PR workflow * feat: Change apk name in PR workflow * feat: Change apk name in PR workflow * fix: Remove test-apk prefix in app name --------- Co-authored-by: Omar Hatem --- .github/workflows/pr_test_build.yml | 12 ++++++--- cw_core/pubspec.lock | 42 ++++++++++++++--------------- 2 files changed, 29 insertions(+), 25 deletions(-) diff --git a/.github/workflows/pr_test_build.yml b/.github/workflows/pr_test_build.yml index 80d5d74aa..30bf3058e 100644 --- a/.github/workflows/pr_test_build.yml +++ b/.github/workflows/pr_test_build.yml @@ -163,7 +163,11 @@ jobs: - name: Send Test APK continue-on-error: true - run: | - cd /opt/android/cake_wallet - var=$(curl --upload-file build/app/outputs/apk/release/app-release.apk https://transfer.sh/$GITHUB_HEAD_REF.apk -H "Max-Days: 10") - curl ${{ secrets.SLACK_WEB_HOOK }} -H "Content-Type: application/json" -d '{"apk_link": "'"$var"'","ticket": "'"$GITHUB_HEAD_REF"'"}' + uses: adrey/slack-file-upload-action@1.0.5 + with: + token: ${{ secrets.SLACK_APP_TOKEN }} + path: /opt/android/cake_wallet/build/app/outputs/apk/release/app-release.apk + channel: ${{ secrets.SLACK_APK_CHANNEL }} + title: '${{github.head_ref}}.apk' + filename: ${{github.head_ref}}.apk + initial_comment: ${{ github.event.head_commit.message }} diff --git a/cw_core/pubspec.lock b/cw_core/pubspec.lock index 01e19dda4..e399526fd 100644 --- a/cw_core/pubspec.lock +++ b/cw_core/pubspec.lock @@ -37,10 +37,10 @@ packages: dependency: transitive description: name: async - sha256: bfe67ef28df125b7dddcea62755991f807aa39a2492a23e1550161692950bbe0 + sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" url: "https://pub.dev" source: hosted - version: "2.10.0" + version: "2.11.0" boolean_selector: dependency: transitive description: @@ -117,10 +117,10 @@ packages: dependency: transitive description: name: characters - sha256: e6a326c8af69605aec75ed6c187d06b349707a27fbff8222ca9cc2cff167975c + sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" url: "https://pub.dev" source: hosted - version: "1.2.1" + version: "1.3.0" checked_yaml: dependency: transitive description: @@ -149,10 +149,10 @@ packages: dependency: transitive description: name: collection - sha256: cfc915e6923fe5ce6e153b0723c753045de46de1b4d63771530504004a45fae0 + sha256: "4a07be6cb69c84d677a6c3096fcf960cc3285a8330b4603e0d463d15d9bd934c" url: "https://pub.dev" source: hosted - version: "1.17.0" + version: "1.17.1" convert: dependency: transitive description: @@ -279,10 +279,10 @@ packages: dependency: "direct main" description: name: http - sha256: "6aa2946395183537c8b880962d935877325d6a09a2867c3970c05c0fed6ac482" + sha256: "759d1a329847dd0f39226c688d3e06a6b8679668e350e2891a6474f8b4bb8525" url: "https://pub.dev" source: hosted - version: "0.13.5" + version: "1.1.0" http_multi_server: dependency: transitive description: @@ -303,10 +303,10 @@ packages: dependency: "direct main" description: name: intl - sha256: "910f85bce16fb5c6f614e117efa303e85a1731bb0081edf3604a2ae6e9a3cc91" + sha256: "3bc132a9dbce73a7e4a21a17d06e1878839ffbf975568bc875c60537824b0c4d" url: "https://pub.dev" source: hosted - version: "0.17.0" + version: "0.18.1" io: dependency: transitive description: @@ -319,10 +319,10 @@ packages: dependency: transitive description: name: js - sha256: "5528c2f391ededb7775ec1daa69e65a2d61276f7552de2b5f7b8d34ee9fd4ab7" + sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3 url: "https://pub.dev" source: hosted - version: "0.6.5" + version: "0.6.7" json_annotation: dependency: transitive description: @@ -343,10 +343,10 @@ packages: dependency: transitive description: name: matcher - sha256: "16db949ceee371e9b99d22f88fa3a73c4e59fd0afed0bd25fc336eb76c198b72" + sha256: "6501fbd55da300384b768785b83e5ce66991266cec21af89ab9ae7f5ce1c4cbb" url: "https://pub.dev" source: hosted - version: "0.12.13" + version: "0.12.15" material_color_utilities: dependency: transitive description: @@ -359,10 +359,10 @@ packages: dependency: transitive description: name: meta - sha256: "6c268b42ed578a53088d834796959e4a1814b5e9e164f147f580a386e5decf42" + sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3" url: "https://pub.dev" source: hosted - version: "1.8.0" + version: "1.9.1" mime: dependency: transitive description: @@ -399,10 +399,10 @@ packages: dependency: transitive description: name: path - sha256: db9d4f58c908a4ba5953fcee2ae317c94889433e5024c27ce74a37f94267945b + sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917" url: "https://pub.dev" source: hosted - version: "1.8.2" + version: "1.8.3" path_provider: dependency: "direct main" description: @@ -596,10 +596,10 @@ packages: dependency: transitive description: name: test_api - sha256: ad540f65f92caa91bf21dfc8ffb8c589d6e4dc0c2267818b4cc2792857706206 + sha256: eb6ac1540b26de412b3403a163d919ba86f6a973fe6cc50ae3541b80092fdcfb url: "https://pub.dev" source: hosted - version: "0.4.16" + version: "0.5.1" timing: dependency: transitive description: @@ -665,5 +665,5 @@ packages: source: hosted version: "3.1.1" sdks: - dart: ">=2.19.0 <3.0.0" + dart: ">=3.0.0 <4.0.0" flutter: ">=3.0.0" From 8757390c8f1da5500e750c5592796c0bc2b04c2f Mon Sep 17 00:00:00 2001 From: Konstantin Ullrich Date: Fri, 1 Sep 2023 13:04:35 +0200 Subject: [PATCH 5/9] CW-481 Fix empty Address (#1072) --- cw_monero/lib/monero_wallet_addresses.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/cw_monero/lib/monero_wallet_addresses.dart b/cw_monero/lib/monero_wallet_addresses.dart index fcc3576f4..a9da3d5d4 100644 --- a/cw_monero/lib/monero_wallet_addresses.dart +++ b/cw_monero/lib/monero_wallet_addresses.dart @@ -41,6 +41,7 @@ abstract class MoneroWalletAddressesBase extends WalletAddresses with Store { Future init() async { accountList.update(); account = accountList.accounts.first; + updateSubaddressList(accountIndex: account?.id ?? 0); await updateAddressesInBox(); } From e634562cf468776bea7c5e887f063fc1dfb0ef68 Mon Sep 17 00:00:00 2001 From: Adegoke David <64401859+Blazebrain@users.noreply.github.com> Date: Fri, 1 Sep 2023 14:59:18 +0100 Subject: [PATCH 6/9] Cw-465-Remove CakePay Button on Mobile/Desktop (#1057) * feat: Remove CakePayButton Mobile * feat: Remove CakePayButton Desktop * feat: Remove CakePay Mobile Button * feat: Remove CakePay Mobile Button * feat: Remove CakePay Mobile Button * fix: Action breaking --- cw_bitcoin/pubspec.lock | 56 +++++++++---------- cw_haven/pubspec.lock | 42 +++++++------- cw_monero/example/pubspec.lock | 42 +++++++------- cw_monero/pubspec.lock | 42 +++++++------- .../desktop_dashboard_actions.dart | 10 ++-- .../dashboard/widgets/market_place_page.dart | 18 +++--- 6 files changed, 105 insertions(+), 105 deletions(-) diff --git a/cw_bitcoin/pubspec.lock b/cw_bitcoin/pubspec.lock index bfcd9e5a6..843daa771 100644 --- a/cw_bitcoin/pubspec.lock +++ b/cw_bitcoin/pubspec.lock @@ -37,19 +37,19 @@ packages: dependency: transitive description: name: async - sha256: bfe67ef28df125b7dddcea62755991f807aa39a2492a23e1550161692950bbe0 + sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" url: "https://pub.dev" source: hosted - version: "2.10.0" + version: "2.11.0" bech32: dependency: transitive description: path: "." - ref: "cake-0.2.1" - resolved-ref: cafd1c270641e95017d57d69f55cca9831d4db56 + ref: "cake-0.2.2" + resolved-ref: "05755063b593aa6cca0a4820a318e0ce17de6192" url: "https://github.com/cake-tech/bech32.git" source: git - version: "0.2.1" + version: "0.2.2" bip32: dependency: transitive description: @@ -70,8 +70,8 @@ packages: dependency: "direct main" description: path: "." - ref: cake-update-v2 - resolved-ref: "8f86453761c0c26e368392d0ff2c6f12f3b7397b" + ref: cake-update-v3 + resolved-ref: df9204144011ed9419eff7d9ef3143102a40252d url: "https://github.com/cake-tech/bitcoin_flutter.git" source: git version: "2.0.2" @@ -159,10 +159,10 @@ packages: dependency: transitive description: name: characters - sha256: e6a326c8af69605aec75ed6c187d06b349707a27fbff8222ca9cc2cff167975c + sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" url: "https://pub.dev" source: hosted - version: "1.2.1" + version: "1.3.0" checked_yaml: dependency: transitive description: @@ -191,18 +191,18 @@ packages: dependency: transitive description: name: collection - sha256: cfc915e6923fe5ce6e153b0723c753045de46de1b4d63771530504004a45fae0 + sha256: "4a07be6cb69c84d677a6c3096fcf960cc3285a8330b4603e0d463d15d9bd934c" url: "https://pub.dev" source: hosted - version: "1.17.0" + version: "1.17.1" convert: dependency: transitive description: name: convert - sha256: "196284f26f69444b7f5c50692b55ec25da86d9e500451dc09333bf2e3ad69259" + sha256: "0f08b14755d163f6e2134cb58222dd25ea2a2ee8a195e53983d57c075324d592" url: "https://pub.dev" source: hosted - version: "3.0.2" + version: "3.1.1" crypto: dependency: transitive description: @@ -344,10 +344,10 @@ packages: dependency: "direct main" description: name: http - sha256: "6aa2946395183537c8b880962d935877325d6a09a2867c3970c05c0fed6ac482" + sha256: "759d1a329847dd0f39226c688d3e06a6b8679668e350e2891a6474f8b4bb8525" url: "https://pub.dev" source: hosted - version: "0.13.5" + version: "1.1.0" http_multi_server: dependency: transitive description: @@ -368,10 +368,10 @@ packages: dependency: "direct main" description: name: intl - sha256: "910f85bce16fb5c6f614e117efa303e85a1731bb0081edf3604a2ae6e9a3cc91" + sha256: "3bc132a9dbce73a7e4a21a17d06e1878839ffbf975568bc875c60537824b0c4d" url: "https://pub.dev" source: hosted - version: "0.17.0" + version: "0.18.1" io: dependency: transitive description: @@ -384,10 +384,10 @@ packages: dependency: transitive description: name: js - sha256: "5528c2f391ededb7775ec1daa69e65a2d61276f7552de2b5f7b8d34ee9fd4ab7" + sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3 url: "https://pub.dev" source: hosted - version: "0.6.5" + version: "0.6.7" json_annotation: dependency: transitive description: @@ -408,10 +408,10 @@ packages: dependency: transitive description: name: matcher - sha256: "16db949ceee371e9b99d22f88fa3a73c4e59fd0afed0bd25fc336eb76c198b72" + sha256: "6501fbd55da300384b768785b83e5ce66991266cec21af89ab9ae7f5ce1c4cbb" url: "https://pub.dev" source: hosted - version: "0.12.13" + version: "0.12.15" material_color_utilities: dependency: transitive description: @@ -424,10 +424,10 @@ packages: dependency: transitive description: name: meta - sha256: "6c268b42ed578a53088d834796959e4a1814b5e9e164f147f580a386e5decf42" + sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3" url: "https://pub.dev" source: hosted - version: "1.8.0" + version: "1.9.1" mime: dependency: transitive description: @@ -464,10 +464,10 @@ packages: dependency: transitive description: name: path - sha256: db9d4f58c908a4ba5953fcee2ae317c94889433e5024c27ce74a37f94267945b + sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917" url: "https://pub.dev" source: hosted - version: "1.8.2" + version: "1.8.3" path_provider: dependency: "direct main" description: @@ -669,10 +669,10 @@ packages: dependency: transitive description: name: test_api - sha256: ad540f65f92caa91bf21dfc8ffb8c589d6e4dc0c2267818b4cc2792857706206 + sha256: eb6ac1540b26de412b3403a163d919ba86f6a973fe6cc50ae3541b80092fdcfb url: "https://pub.dev" source: hosted - version: "0.4.16" + version: "0.5.1" timing: dependency: transitive description: @@ -746,5 +746,5 @@ packages: source: hosted version: "3.1.1" sdks: - dart: ">=2.19.0 <3.0.0" + dart: ">=3.0.0 <4.0.0" flutter: ">=3.0.0" diff --git a/cw_haven/pubspec.lock b/cw_haven/pubspec.lock index a79d2d3cf..a63aa3237 100644 --- a/cw_haven/pubspec.lock +++ b/cw_haven/pubspec.lock @@ -37,10 +37,10 @@ packages: dependency: transitive description: name: async - sha256: bfe67ef28df125b7dddcea62755991f807aa39a2492a23e1550161692950bbe0 + sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" url: "https://pub.dev" source: hosted - version: "2.10.0" + version: "2.11.0" boolean_selector: dependency: transitive description: @@ -117,10 +117,10 @@ packages: dependency: transitive description: name: characters - sha256: e6a326c8af69605aec75ed6c187d06b349707a27fbff8222ca9cc2cff167975c + sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" url: "https://pub.dev" source: hosted - version: "1.2.1" + version: "1.3.0" checked_yaml: dependency: transitive description: @@ -149,10 +149,10 @@ packages: dependency: transitive description: name: collection - sha256: cfc915e6923fe5ce6e153b0723c753045de46de1b4d63771530504004a45fae0 + sha256: "4a07be6cb69c84d677a6c3096fcf960cc3285a8330b4603e0d463d15d9bd934c" url: "https://pub.dev" source: hosted - version: "1.17.0" + version: "1.17.1" convert: dependency: transitive description: @@ -286,10 +286,10 @@ packages: dependency: "direct main" description: name: http - sha256: "6aa2946395183537c8b880962d935877325d6a09a2867c3970c05c0fed6ac482" + sha256: "759d1a329847dd0f39226c688d3e06a6b8679668e350e2891a6474f8b4bb8525" url: "https://pub.dev" source: hosted - version: "0.13.5" + version: "1.1.0" http_multi_server: dependency: transitive description: @@ -310,10 +310,10 @@ packages: dependency: "direct main" description: name: intl - sha256: "910f85bce16fb5c6f614e117efa303e85a1731bb0081edf3604a2ae6e9a3cc91" + sha256: "3bc132a9dbce73a7e4a21a17d06e1878839ffbf975568bc875c60537824b0c4d" url: "https://pub.dev" source: hosted - version: "0.17.0" + version: "0.18.1" io: dependency: transitive description: @@ -326,10 +326,10 @@ packages: dependency: transitive description: name: js - sha256: "5528c2f391ededb7775ec1daa69e65a2d61276f7552de2b5f7b8d34ee9fd4ab7" + sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3 url: "https://pub.dev" source: hosted - version: "0.6.5" + version: "0.6.7" json_annotation: dependency: transitive description: @@ -350,10 +350,10 @@ packages: dependency: transitive description: name: matcher - sha256: "16db949ceee371e9b99d22f88fa3a73c4e59fd0afed0bd25fc336eb76c198b72" + sha256: "6501fbd55da300384b768785b83e5ce66991266cec21af89ab9ae7f5ce1c4cbb" url: "https://pub.dev" source: hosted - version: "0.12.13" + version: "0.12.15" material_color_utilities: dependency: transitive description: @@ -366,10 +366,10 @@ packages: dependency: transitive description: name: meta - sha256: "6c268b42ed578a53088d834796959e4a1814b5e9e164f147f580a386e5decf42" + sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3" url: "https://pub.dev" source: hosted - version: "1.8.0" + version: "1.9.1" mime: dependency: transitive description: @@ -406,10 +406,10 @@ packages: dependency: transitive description: name: path - sha256: db9d4f58c908a4ba5953fcee2ae317c94889433e5024c27ce74a37f94267945b + sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917" url: "https://pub.dev" source: hosted - version: "1.8.2" + version: "1.8.3" path_provider: dependency: "direct main" description: @@ -603,10 +603,10 @@ packages: dependency: transitive description: name: test_api - sha256: ad540f65f92caa91bf21dfc8ffb8c589d6e4dc0c2267818b4cc2792857706206 + sha256: eb6ac1540b26de412b3403a163d919ba86f6a973fe6cc50ae3541b80092fdcfb url: "https://pub.dev" source: hosted - version: "0.4.16" + version: "0.5.1" timing: dependency: transitive description: @@ -672,5 +672,5 @@ packages: source: hosted version: "3.1.1" sdks: - dart: ">=2.19.0 <3.0.0" + dart: ">=3.0.0 <4.0.0" flutter: ">=3.0.0" diff --git a/cw_monero/example/pubspec.lock b/cw_monero/example/pubspec.lock index f4c36e69c..1aae6b0c9 100644 --- a/cw_monero/example/pubspec.lock +++ b/cw_monero/example/pubspec.lock @@ -21,10 +21,10 @@ packages: dependency: transitive description: name: async - sha256: bfe67ef28df125b7dddcea62755991f807aa39a2492a23e1550161692950bbe0 + sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" url: "https://pub.dev" source: hosted - version: "2.10.0" + version: "2.11.0" boolean_selector: dependency: transitive description: @@ -37,10 +37,10 @@ packages: dependency: transitive description: name: characters - sha256: e6a326c8af69605aec75ed6c187d06b349707a27fbff8222ca9cc2cff167975c + sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" url: "https://pub.dev" source: hosted - version: "1.2.1" + version: "1.3.0" clock: dependency: transitive description: @@ -53,10 +53,10 @@ packages: dependency: transitive description: name: collection - sha256: cfc915e6923fe5ce6e153b0723c753045de46de1b4d63771530504004a45fae0 + sha256: "4a07be6cb69c84d677a6c3096fcf960cc3285a8330b4603e0d463d15d9bd934c" url: "https://pub.dev" source: hosted - version: "1.17.0" + version: "1.17.1" convert: dependency: transitive description: @@ -157,10 +157,10 @@ packages: dependency: transitive description: name: http - sha256: "6aa2946395183537c8b880962d935877325d6a09a2867c3970c05c0fed6ac482" + sha256: "759d1a329847dd0f39226c688d3e06a6b8679668e350e2891a6474f8b4bb8525" url: "https://pub.dev" source: hosted - version: "0.13.5" + version: "1.1.0" http_parser: dependency: transitive description: @@ -173,18 +173,18 @@ packages: dependency: transitive description: name: intl - sha256: "910f85bce16fb5c6f614e117efa303e85a1731bb0081edf3604a2ae6e9a3cc91" + sha256: "3bc132a9dbce73a7e4a21a17d06e1878839ffbf975568bc875c60537824b0c4d" url: "https://pub.dev" source: hosted - version: "0.17.0" + version: "0.18.1" js: dependency: transitive description: name: js - sha256: "5528c2f391ededb7775ec1daa69e65a2d61276f7552de2b5f7b8d34ee9fd4ab7" + sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3 url: "https://pub.dev" source: hosted - version: "0.6.5" + version: "0.6.7" lints: dependency: transitive description: @@ -197,10 +197,10 @@ packages: dependency: transitive description: name: matcher - sha256: "16db949ceee371e9b99d22f88fa3a73c4e59fd0afed0bd25fc336eb76c198b72" + sha256: "6501fbd55da300384b768785b83e5ce66991266cec21af89ab9ae7f5ce1c4cbb" url: "https://pub.dev" source: hosted - version: "0.12.13" + version: "0.12.15" material_color_utilities: dependency: transitive description: @@ -213,10 +213,10 @@ packages: dependency: transitive description: name: meta - sha256: "6c268b42ed578a53088d834796959e4a1814b5e9e164f147f580a386e5decf42" + sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3" url: "https://pub.dev" source: hosted - version: "1.8.0" + version: "1.9.1" mobx: dependency: transitive description: @@ -229,10 +229,10 @@ packages: dependency: transitive description: name: path - sha256: db9d4f58c908a4ba5953fcee2ae317c94889433e5024c27ce74a37f94267945b + sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917" url: "https://pub.dev" source: hosted - version: "1.8.2" + version: "1.8.3" path_provider: dependency: transitive description: @@ -362,10 +362,10 @@ packages: dependency: transitive description: name: test_api - sha256: ad540f65f92caa91bf21dfc8ffb8c589d6e4dc0c2267818b4cc2792857706206 + sha256: eb6ac1540b26de412b3403a163d919ba86f6a973fe6cc50ae3541b80092fdcfb url: "https://pub.dev" source: hosted - version: "0.4.16" + version: "0.5.1" typed_data: dependency: transitive description: @@ -399,5 +399,5 @@ packages: source: hosted version: "0.2.0+3" sdks: - dart: ">=2.18.1 <3.0.0" + dart: ">=3.0.0 <4.0.0" flutter: ">=3.0.0" diff --git a/cw_monero/pubspec.lock b/cw_monero/pubspec.lock index 437184a7d..37e08e7ca 100644 --- a/cw_monero/pubspec.lock +++ b/cw_monero/pubspec.lock @@ -37,10 +37,10 @@ packages: dependency: transitive description: name: async - sha256: bfe67ef28df125b7dddcea62755991f807aa39a2492a23e1550161692950bbe0 + sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" url: "https://pub.dev" source: hosted - version: "2.10.0" + version: "2.11.0" boolean_selector: dependency: transitive description: @@ -117,10 +117,10 @@ packages: dependency: transitive description: name: characters - sha256: e6a326c8af69605aec75ed6c187d06b349707a27fbff8222ca9cc2cff167975c + sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" url: "https://pub.dev" source: hosted - version: "1.2.1" + version: "1.3.0" checked_yaml: dependency: transitive description: @@ -149,10 +149,10 @@ packages: dependency: transitive description: name: collection - sha256: cfc915e6923fe5ce6e153b0723c753045de46de1b4d63771530504004a45fae0 + sha256: "4a07be6cb69c84d677a6c3096fcf960cc3285a8330b4603e0d463d15d9bd934c" url: "https://pub.dev" source: hosted - version: "1.17.0" + version: "1.17.1" convert: dependency: transitive description: @@ -286,10 +286,10 @@ packages: dependency: "direct main" description: name: http - sha256: "6aa2946395183537c8b880962d935877325d6a09a2867c3970c05c0fed6ac482" + sha256: "759d1a329847dd0f39226c688d3e06a6b8679668e350e2891a6474f8b4bb8525" url: "https://pub.dev" source: hosted - version: "0.13.5" + version: "1.1.0" http_multi_server: dependency: transitive description: @@ -310,10 +310,10 @@ packages: dependency: "direct main" description: name: intl - sha256: "910f85bce16fb5c6f614e117efa303e85a1731bb0081edf3604a2ae6e9a3cc91" + sha256: "3bc132a9dbce73a7e4a21a17d06e1878839ffbf975568bc875c60537824b0c4d" url: "https://pub.dev" source: hosted - version: "0.17.0" + version: "0.18.1" io: dependency: transitive description: @@ -326,10 +326,10 @@ packages: dependency: transitive description: name: js - sha256: "5528c2f391ededb7775ec1daa69e65a2d61276f7552de2b5f7b8d34ee9fd4ab7" + sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3 url: "https://pub.dev" source: hosted - version: "0.6.5" + version: "0.6.7" json_annotation: dependency: transitive description: @@ -350,10 +350,10 @@ packages: dependency: transitive description: name: matcher - sha256: "16db949ceee371e9b99d22f88fa3a73c4e59fd0afed0bd25fc336eb76c198b72" + sha256: "6501fbd55da300384b768785b83e5ce66991266cec21af89ab9ae7f5ce1c4cbb" url: "https://pub.dev" source: hosted - version: "0.12.13" + version: "0.12.15" material_color_utilities: dependency: transitive description: @@ -366,10 +366,10 @@ packages: dependency: transitive description: name: meta - sha256: "6c268b42ed578a53088d834796959e4a1814b5e9e164f147f580a386e5decf42" + sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3" url: "https://pub.dev" source: hosted - version: "1.8.0" + version: "1.9.1" mime: dependency: transitive description: @@ -406,10 +406,10 @@ packages: dependency: transitive description: name: path - sha256: db9d4f58c908a4ba5953fcee2ae317c94889433e5024c27ce74a37f94267945b + sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917" url: "https://pub.dev" source: hosted - version: "1.8.2" + version: "1.8.3" path_provider: dependency: "direct main" description: @@ -603,10 +603,10 @@ packages: dependency: transitive description: name: test_api - sha256: ad540f65f92caa91bf21dfc8ffb8c589d6e4dc0c2267818b4cc2792857706206 + sha256: eb6ac1540b26de412b3403a163d919ba86f6a973fe6cc50ae3541b80092fdcfb url: "https://pub.dev" source: hosted - version: "0.4.16" + version: "0.5.1" timing: dependency: transitive description: @@ -672,5 +672,5 @@ packages: source: hosted version: "3.1.1" sdks: - dart: ">=2.19.0 <3.0.0" + dart: ">=3.0.0 <4.0.0" flutter: ">=3.0.0" diff --git a/lib/src/screens/dashboard/desktop_widgets/desktop_dashboard_actions.dart b/lib/src/screens/dashboard/desktop_widgets/desktop_dashboard_actions.dart index ad2291ba5..5e1f8d16a 100644 --- a/lib/src/screens/dashboard/desktop_widgets/desktop_dashboard_actions.dart +++ b/lib/src/screens/dashboard/desktop_widgets/desktop_dashboard_actions.dart @@ -73,12 +73,12 @@ class DesktopDashboardActions extends StatelessWidget { ), ], ), - Expanded( - child: MarketPlacePage( - dashboardViewModel: dashboardViewModel, - marketPlaceViewModel: getIt.get(), - ), + Expanded( + child: MarketPlacePage( + dashboardViewModel: dashboardViewModel, + marketPlaceViewModel: getIt.get(), ), + ), ], ); } diff --git a/lib/src/screens/dashboard/widgets/market_place_page.dart b/lib/src/screens/dashboard/widgets/market_place_page.dart index 2d6539370..1bdcb61b4 100644 --- a/lib/src/screens/dashboard/widgets/market_place_page.dart +++ b/lib/src/screens/dashboard/widgets/market_place_page.dart @@ -48,15 +48,15 @@ class MarketPlacePage extends StatelessWidget { child: ListView( controller: _scrollController, children: [ - SizedBox(height: 20), - DashBoardRoundedCardWidget( - onTap: () => launchUrl( - Uri.parse("https://cakelabs.com/news/cake-pay-mobile-to-shut-down/"), - mode: LaunchMode.externalApplication, - ), - title: S.of(context).cake_pay_title, - subTitle: S.of(context).cake_pay_subtitle, - ), + // SizedBox(height: 20), + // DashBoardRoundedCardWidget( + // onTap: () => launchUrl( + // Uri.parse("https://cakelabs.com/news/cake-pay-mobile-to-shut-down/"), + // mode: LaunchMode.externalApplication, + // ), + // title: S.of(context).cake_pay_title, + // subTitle: S.of(context).cake_pay_subtitle, + // ), SizedBox(height: 20), DashBoardRoundedCardWidget( onTap: () => launchUrl( From 710fe82d7a7ba327b0b146400b0fbdfac0a32d24 Mon Sep 17 00:00:00 2001 From: Adegoke David <64401859+Blazebrain@users.noreply.github.com> Date: Fri, 1 Sep 2023 15:05:45 +0100 Subject: [PATCH 7/9] CW-471-Changing-nodes-bug (#1062) * chore: Cleanup * fix: Node selection not updating * fix: Node selection not updating --------- Co-authored-by: Omar Hatem --- .../screens/settings/manage_nodes_page.dart | 58 ++++++++++--------- .../node_list/node_list_view_model.dart | 1 + 2 files changed, 32 insertions(+), 27 deletions(-) diff --git a/lib/src/screens/settings/manage_nodes_page.dart b/lib/src/screens/settings/manage_nodes_page.dart index 0a67f0502..4b1034f2b 100644 --- a/lib/src/screens/settings/manage_nodes_page.dart +++ b/lib/src/screens/settings/manage_nodes_page.dart @@ -42,36 +42,40 @@ class ManageNodesPage extends BasePage { return nodeListViewModel.nodes.length; }, itemBuilder: (_, index) { - final node = nodeListViewModel.nodes[index]; - final isSelected = node.keyIndex == nodeListViewModel.currentNode.keyIndex; - final nodeListRow = NodeListRow( - title: node.uriRaw, - node: node, - isSelected: isSelected, - onTap: (_) async { - if (isSelected) { - return; - } + return Observer( + builder: (context) { + final node = nodeListViewModel.nodes[index]; + final isSelected = node.keyIndex == nodeListViewModel.currentNode.keyIndex; + final nodeListRow = NodeListRow( + title: node.uriRaw, + node: node, + isSelected: isSelected, + onTap: (_) async { + if (isSelected) { + return; + } - await showPopUp( - context: context, - builder: (BuildContext context) { - return AlertWithTwoActions( - alertTitle: S.of(context).change_current_node_title, - alertContent: nodeListViewModel.getAlertContent(node.uriRaw), - leftButtonText: S.of(context).cancel, - rightButtonText: S.of(context).change, - actionLeftButton: () => Navigator.of(context).pop(), - actionRightButton: () async { - await nodeListViewModel.setAsCurrent(node); - Navigator.of(context).pop(); - }, - ); - }); + await showPopUp( + context: context, + builder: (BuildContext context) { + return AlertWithTwoActions( + alertTitle: S.of(context).change_current_node_title, + alertContent: nodeListViewModel.getAlertContent(node.uriRaw), + leftButtonText: S.of(context).cancel, + rightButtonText: S.of(context).change, + actionLeftButton: () => Navigator.of(context).pop(), + actionRightButton: () async { + await nodeListViewModel.setAsCurrent(node); + Navigator.of(context).pop(); + }, + ); + }, + ); + }, + ); + return nodeListRow; }, ); - - return nodeListRow; }, ), ); diff --git a/lib/view_model/node_list/node_list_view_model.dart b/lib/view_model/node_list/node_list_view_model.dart index 1c81c255b..ea612c63c 100644 --- a/lib/view_model/node_list/node_list_view_model.dart +++ b/lib/view_model/node_list/node_list_view_model.dart @@ -76,6 +76,7 @@ abstract class NodeListViewModelBase with Store { @action Future delete(Node node) async => node.delete(); + @action Future setAsCurrent(Node node) async => settingsStore.nodes[_appStore.wallet!.type] = node; @action From 68a821cc0e293e38dccdc6cc0e015ed86f087489 Mon Sep 17 00:00:00 2001 From: Omar Hatem Date: Fri, 1 Sep 2023 18:06:18 +0300 Subject: [PATCH 8/9] Fiat api fix (#1070) * Cw 474 linux swapping wallets btc then xmr still shows btc receive qr code (#1068) * feat: improve address page txt field * fix: switching wallets from the desktop dropdown updates dashboard pages * Revert "feat: improve address page txt field" This reverts commit 0a30e6d9e1d13795219b66a6fdf2c1d218479089. * refactor: rename to WalletChangeListener * fix: _init also behaves on wallet change * Fix Fiat conversion API parsing Cherry pick fix for Desktop switching wallets not updating dashboard screens * Minor indentations fix [skip ci] --------- Co-authored-by: Rafael Saes <76502841+saltrafael@users.noreply.github.com> --- cw_monero/ios/Classes/monero_api.cpp | 14 +- lib/core/fiat_conversion_service.dart | 8 +- .../wallet_change_listener_view_model.dart | 30 ++++ lib/di.dart | 5 +- .../exchange/exchange_view_model.dart | 164 ++++++++---------- lib/view_model/send/send_view_model.dart | 133 +++++++------- .../wallet_address_list_view_model.dart | 56 +++--- 7 files changed, 209 insertions(+), 201 deletions(-) create mode 100644 lib/core/wallet_change_listener_view_model.dart diff --git a/cw_monero/ios/Classes/monero_api.cpp b/cw_monero/ios/Classes/monero_api.cpp index 66b8605c6..1cb01fac7 100644 --- a/cw_monero/ios/Classes/monero_api.cpp +++ b/cw_monero/ios/Classes/monero_api.cpp @@ -139,7 +139,7 @@ extern "C" int8_t direction; int8_t isPending; uint32_t subaddrIndex; - + char *hash; char *paymentId; @@ -154,7 +154,7 @@ extern "C" std::set::iterator it = transaction->subaddrIndex().begin(); subaddrIndex = *it; confirmations = transaction->confirmations(); - datetime = static_cast(transaction->timestamp()); + datetime = static_cast(transaction->timestamp()); direction = transaction->direction(); isPending = static_cast(transaction->isPending()); std::string *hash_str = new std::string(transaction->hash()); @@ -234,7 +234,7 @@ extern "C" } void setUnlocked(bool unlocked); - + }; Monero::Coins *m_coins; @@ -254,7 +254,7 @@ extern "C" { m_wallet = wallet; m_listener = nullptr; - + if (wallet != nullptr) { @@ -479,7 +479,7 @@ extern "C" { nice(19); Monero::Wallet *wallet = get_current_wallet(); - + std::string _login = ""; std::string _password = ""; std::string _socksProxyAddress = ""; @@ -568,7 +568,7 @@ extern "C" _preferred_inputs.insert(std::string(*preferred_inputs)); preferred_inputs++; } - + auto priority = static_cast(priority_raw); std::string _payment_id; Monero::PendingTransaction *transaction; @@ -587,7 +587,7 @@ extern "C" { transaction = m_wallet->createTransaction(std::string(address), _payment_id, Monero::optional(), m_wallet->defaultMixin(), priority, subaddr_account, {}, _preferred_inputs); } - + int status = transaction->status(); if (status == Monero::PendingTransaction::Status::Status_Error || status == Monero::PendingTransaction::Status::Status_Critical) diff --git a/lib/core/fiat_conversion_service.dart b/lib/core/fiat_conversion_service.dart index 9690c430a..479aa3b82 100644 --- a/lib/core/fiat_conversion_service.dart +++ b/lib/core/fiat_conversion_service.dart @@ -21,7 +21,7 @@ Future _fetchPrice(Map args) async { 'key': secrets.fiatApiKey, }; - double price = 0.0; + num price = 0.0; try { late final Uri uri; @@ -41,12 +41,12 @@ Future _fetchPrice(Map args) async { final results = responseJSON['results'] as Map; if (results.isNotEmpty) { - price = results.values.first as double; + price = results.values.first as num; } - return price; + return price.toDouble(); } catch (e) { - return price; + return price.toDouble(); } } 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 d32576836..dd3e6c495 100644 --- a/lib/di.dart +++ b/lib/di.dart @@ -523,8 +523,7 @@ Future setup({ getIt.registerFactory( () => SendViewModel( - getIt.get().wallet!, - getIt.get().settingsStore, + getIt.get(), getIt.get(), getIt.get(), getIt.get(), @@ -702,7 +701,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