V4.26.0 rc (#2198)

* v4.26.0 Release Candidate * Fix Wownero Creation issue * Add KES fiat currency * Adjust backup to default node selection if null Gracefully handle Decred failure Minor fixes

Omar Hatem committed Apr 15, 2025 at 02:50 UTC ca564bc2e66172cffc84eced4d9bea88dabed1ac
8 files changed +78 -51
cw_decred/lib/wallet.dart
+1 -1
@@ -222,7 +222,7 @@ abstract class DecredWalletBase
222
223 Future<bool> checkSync() async {
224 final syncStatusJSON = await _libwallet.syncStatus(walletInfo.name);
225 - final decoded = json.decode(syncStatusJSON);
225 + final decoded = json.decode(syncStatusJSON.isEmpty ? "{}" : syncStatusJSON);
226
227 final syncStatusCode = decoded["syncstatuscode"] ?? 0;
228 // final syncStatusStr = decoded["syncstatus"] ?? "";
lib/core/utilities.dart new
+9
@@ -0,0 +1,9 @@
1 +extension EnhancedList<T> on Iterable<T> {
2 + T? firstWhereOrNull(bool Function(T element) test) {
3 + try {
4 + return firstWhere(test);
5 + } catch (e) {
6 + return null;
7 + }
8 + }
9 +}
\ No newline at end of file
lib/src/screens/backup/backup_page.dart
+4 -3
@@ -3,10 +3,9 @@ import 'package:cake_wallet/core/execution_state.dart';
3 import 'package:cake_wallet/src/screens/base_page.dart';
4 import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart';
5 import 'package:cake_wallet/src/widgets/primary_button.dart';
6 -import 'package:cake_wallet/src/widgets/trail_button.dart';
6 import 'package:cake_wallet/generated/i18n.dart';
8 -import 'package:cake_wallet/palette.dart';
7 import 'package:cake_wallet/routes.dart';
8 +import 'package:cake_wallet/themes/extensions/cake_text_theme.dart';
9 import 'package:cake_wallet/utils/clipboard_util.dart';
10 import 'package:cake_wallet/utils/exception_handler.dart';
11 import 'package:cake_wallet/utils/share_util.dart';
@@ -76,7 +75,9 @@ class BackupPage extends BasePage {
75 Navigator.of(context).pushNamed(Routes.editBackupPassword),
76 text: S.of(context).change_password,
77 color: Theme.of(context).cardColor,
79 - textColor: Colors.white,
78 + textColor: Theme.of(context)
79 + .extension<CakeTextTheme>()!
80 + .buttonTextColor,
81 ),
82 SizedBox(height: 10),
83 Observer(
lib/src/widgets/primary_button.dart
+1 -1
@@ -104,7 +104,7 @@ class LoadingPrimaryButton extends StatelessWidget {
104 ),
105 )),
106 child: isLoading
107 - ? CupertinoActivityIndicator(animating: true)
107 + ? CupertinoActivityIndicator(animating: true, color: textColor)
108 : Text(text,
109 style: TextStyle(
110 fontSize: 15.0,
lib/store/settings_store.dart
+32 -24
@@ -3,6 +3,7 @@ import 'dart:convert';
3 import 'dart:io';
4
5 import 'package:cake_wallet/bitcoin/bitcoin.dart';
6 +import 'package:cake_wallet/core/utilities.dart';
7 import 'package:cake_wallet/decred/decred.dart';
8 import 'package:cake_wallet/bitcoin_cash/bitcoin_cash.dart';
9 import 'package:cake_wallet/core/secure_storage.dart';
@@ -12,13 +13,13 @@ import 'package:cake_wallet/entities/auto_generate_subaddress_status.dart';
13 import 'package:cake_wallet/entities/balance_display_mode.dart';
14 import 'package:cake_wallet/entities/cake_2fa_preset_options.dart';
15 import 'package:cake_wallet/entities/country.dart';
16 +import 'package:cake_wallet/entities/default_settings_migration.dart';
17 import 'package:cake_wallet/entities/exchange_api_mode.dart';
18 import 'package:cake_wallet/entities/fiat_api_mode.dart';
19 import 'package:cake_wallet/entities/fiat_currency.dart';
20 import 'package:cake_wallet/entities/language_service.dart';
21 import 'package:cake_wallet/entities/pin_code_required_duration.dart';
22 import 'package:cake_wallet/entities/preferences_key.dart';
21 -import 'package:cake_wallet/entities/provider_types.dart';
23 import 'package:cake_wallet/entities/secret_store_key.dart';
24 import 'package:cake_wallet/entities/seed_phrase_length.dart';
25 import 'package:cake_wallet/entities/seed_type.dart';
@@ -37,11 +38,8 @@ import 'package:cake_wallet/themes/theme_list.dart';
38 import 'package:cake_wallet/utils/device_info.dart';
39 import 'package:cake_wallet/utils/package_info.dart';
40 import 'package:cake_wallet/view_model/settings/sync_mode.dart';
40 -import 'package:cake_wallet/wallet_type_utils.dart';
41 -import 'package:cake_wallet/wownero/wownero.dart';
41 import 'package:cw_core/node.dart';
42 import 'package:cw_core/set_app_secure_native.dart';
44 -import 'package:cw_core/transaction_priority.dart';
43 import 'package:cw_core/utils/print_verbose.dart';
44 import 'package:cw_core/wallet_type.dart';
45 import 'package:device_info_plus/device_info_plus.dart';
@@ -312,7 +310,7 @@ abstract class SettingsStoreBase with Store {
310
311 reaction((_) => disableTradeOption,
312 (bool disableTradeOption) => sharedPreferences.setBool(PreferencesKey.disableTradeOption, disableTradeOption));
315 -
313 +
314 reaction(
315 (_) => disableBulletin,
316 (bool disableBulletin) =>
@@ -1038,7 +1036,6 @@ abstract class SettingsStoreBase with Store {
1036 sharedPreferences.getInt(PreferencesKey.currentLitecoinElectrumSererIdKey);
1037 final bitcoinCashElectrumServerId =
1038 sharedPreferences.getInt(PreferencesKey.currentBitcoinCashNodeIdKey);
1041 - final havenNodeId = sharedPreferences.getInt(PreferencesKey.currentHavenNodeIdKey);
1039 final ethereumNodeId = sharedPreferences.getInt(PreferencesKey.currentEthereumNodeIdKey);
1040 final polygonNodeId = sharedPreferences.getInt(PreferencesKey.currentPolygonNodeIdKey);
1041 final nanoNodeId = sharedPreferences.getInt(PreferencesKey.currentNanoNodeIdKey);
@@ -1048,20 +1045,35 @@ abstract class SettingsStoreBase with Store {
1045 final wowneroNodeId = sharedPreferences.getInt(PreferencesKey.currentWowneroNodeIdKey);
1046 final zanoNodeId = sharedPreferences.getInt(PreferencesKey.currentZanoNodeIdKey);
1047 final decredNodeId = sharedPreferences.getInt(PreferencesKey.currentDecredNodeIdKey);
1051 - final moneroNode = nodeSource.get(nodeId);
1052 - final bitcoinElectrumServer = nodeSource.get(bitcoinElectrumServerId);
1053 - final litecoinElectrumServer = nodeSource.get(litecoinElectrumServerId);
1054 - final havenNode = nodeSource.get(havenNodeId);
1055 - final ethereumNode = nodeSource.get(ethereumNodeId);
1056 - final polygonNode = nodeSource.get(polygonNodeId);
1057 - final bitcoinCashElectrumServer = nodeSource.get(bitcoinCashElectrumServerId);
1058 - final nanoNode = nodeSource.get(nanoNodeId);
1059 - final decredNode = nodeSource.get(decredNodeId);
1060 - final nanoPowNode = powNodeSource.get(nanoPowNodeId);
1061 - final solanaNode = nodeSource.get(solanaNodeId);
1062 - final tronNode = nodeSource.get(tronNodeId);
1063 - final wowneroNode = nodeSource.get(wowneroNodeId);
1064 - final zanoNode = nodeSource.get(zanoNodeId);
1048 +
1049 + /// get the selected node, if null, then use the default
1050 + final moneroNode = nodeSource.get(nodeId) ??
1051 + nodeSource.values.firstWhereOrNull((e) => e.uriRaw == newCakeWalletMoneroUri);
1052 + final bitcoinElectrumServer = nodeSource.get(bitcoinElectrumServerId) ??
1053 + nodeSource.values.firstWhereOrNull((e) => e.uriRaw == newCakeWalletBitcoinUri);
1054 + final litecoinElectrumServer = nodeSource.get(litecoinElectrumServerId) ??
1055 + nodeSource.values.firstWhereOrNull((e) => e.uriRaw == cakeWalletLitecoinElectrumUri);
1056 + final ethereumNode = nodeSource.get(ethereumNodeId) ??
1057 + nodeSource.values.firstWhereOrNull((e) => e.uriRaw == ethereumDefaultNodeUri);
1058 + final polygonNode = nodeSource.get(polygonNodeId) ??
1059 + nodeSource.values.firstWhereOrNull((e) => e.uriRaw == polygonDefaultNodeUri);
1060 + final bitcoinCashElectrumServer = nodeSource.get(bitcoinCashElectrumServerId) ??
1061 + nodeSource.values.firstWhereOrNull((e) => e.uriRaw == cakeWalletBitcoinCashDefaultNodeUri);
1062 + final nanoNode = nodeSource.get(nanoNodeId) ??
1063 + nodeSource.values.firstWhereOrNull((e) => e.uriRaw == nanoDefaultNodeUri);
1064 + final decredNode = nodeSource.get(decredNodeId) ??
1065 + nodeSource.values.firstWhereOrNull((e) => e.uriRaw == decredDefaultUri);
1066 + final nanoPowNode = powNodeSource.get(nanoPowNodeId) ??
1067 + nodeSource.values.firstWhereOrNull((e) => e.uriRaw == nanoDefaultPowNodeUri);
1068 + final solanaNode = nodeSource.get(solanaNodeId) ??
1069 + nodeSource.values.firstWhereOrNull((e) => e.uriRaw == solanaDefaultNodeUri);
1070 + final tronNode = nodeSource.get(tronNodeId) ??
1071 + nodeSource.values.firstWhereOrNull((e) => e.uriRaw == tronDefaultNodeUri);
1072 + final wowneroNode = nodeSource.get(wowneroNodeId) ??
1073 + nodeSource.values.firstWhereOrNull((e) => e.uriRaw == wowneroDefaultNodeUri);
1074 + final zanoNode = nodeSource.get(zanoNodeId) ??
1075 + nodeSource.values.firstWhereOrNull((e) => e.uriRaw == zanoDefaultNodeUri);
1076 +
1077 final packageInfo = await PackageInfo.fromPlatform();
1078 final deviceName = await _getDeviceName() ?? '';
1079 final shouldShowYatPopup = sharedPreferences.getBool(PreferencesKey.shouldShowYatPopup) ?? true;
@@ -1107,10 +1119,6 @@ abstract class SettingsStoreBase with Store {
1119 nodes[WalletType.litecoin] = litecoinElectrumServer;
1120 }
1121
1110 - if (havenNode != null) {
1111 - nodes[WalletType.haven] = havenNode;
1112 - }
1113 -
1122 if (ethereumNode != null) {
1123 nodes[WalletType.ethereum] = ethereumNode;
1124 }
lib/view_model/anon_invoice_page_view_model.dart
+28 -20
@@ -107,20 +107,24 @@ abstract class AnonInvoicePageViewModelBase with Store {
107 return;
108 }
109 }
110 - final result = await anonPayApi.createInvoice(AnonPayRequest(
111 - cryptoCurrency: cryptoCurrency,
112 - address: address,
113 - amount: amount.isEmpty ? null : amount,
114 - description: description,
115 - email: receipientEmail,
116 - name: receipientName,
117 - fiatEquivalent:
118 - selectedCurrency is FiatCurrency ? (selectedCurrency as FiatCurrency).raw : null,
119 - ));
120 -
121 - _anonpayInvoiceInfoSource.add(result);
122 -
123 - state = ExecutedSuccessfullyState(payload: result);
110 + try {
111 + final result = await anonPayApi.createInvoice(AnonPayRequest(
112 + cryptoCurrency: cryptoCurrency,
113 + address: address,
114 + amount: amount.isEmpty ? null : amount,
115 + description: description,
116 + email: receipientEmail,
117 + name: receipientName,
118 + fiatEquivalent:
119 + selectedCurrency is FiatCurrency ? (selectedCurrency as FiatCurrency).raw : null,
120 + ));
121 +
122 + _anonpayInvoiceInfoSource.add(result);
123 +
124 + state = ExecutedSuccessfullyState(payload: result);
125 + } catch (e) {
126 + state = FailureState(e.toString());
127 + }
128 }
129
130 @action
@@ -156,12 +160,16 @@ abstract class AnonInvoicePageViewModelBase with Store {
160 }
161
162 Future<void> _fetchLimits() async {
159 - final limit = await anonPayApi.fetchLimits(
160 - cryptoCurrency: cryptoCurrency,
161 - fiatCurrency: selectedCurrency is FiatCurrency ? selectedCurrency as FiatCurrency : null,
162 - );
163 - minimum = limit.min;
164 - maximum = limit.max != null ? limit.max! / 4 : null;
163 + try {
164 + final limit = await anonPayApi.fetchLimits(
165 + cryptoCurrency: cryptoCurrency,
166 + fiatCurrency: selectedCurrency is FiatCurrency ? selectedCurrency as FiatCurrency : null,
167 + );
168 + minimum = limit.min;
169 + maximum = limit.max != null ? limit.max! / 4 : null;
170 + } catch (e) {
171 + state = FailureState(e.toString());
172 + }
173 }
174
175 @computed
lib/view_model/hardware_wallet/ledger_view_model.dart
+3 -1
@@ -137,7 +137,9 @@ abstract class LedgerViewModelBase with Store {
137 allowChangeWallet: true,
138 isReconnect: true,
139 onConnectDevice: (context, ledgerVM) async {
140 - Navigator.of(context).pop();
140 + if (context.mounted) {
141 + Navigator.of(context).pop();
142 + }
143 },
144 ),
145 );
lib/view_model/wallet_list/wallet_list_view_model.dart
-1
@@ -216,7 +216,6 @@ abstract class WalletListViewModelBase with Store {
216 await sortGroupByType();
217 break;
218 case FilterListOrderType.Custom:
219 - default:
219 await reorderAccordingToWalletList();
220 break;
221 }