v4.28.0 Release Candidate (#2260)

* v4.28.0 Release Candidate

* Fix Android deeplinking

* minor [skip ci]

* update app versions [skip ci]

* merge main

* - Re-enable SwapTrade
- Hide Keyboard on opening fee selection

* handle old backups import

* - Fix seed type UI
- Temp fix for Deleting Monero wallet

* update build number [skip ci]

* minor nullability handling

* disable payjoin for SP
This commit is contained in:
Omar Hatem 2025-05-16 13:51:36 +03:00 committed by GitHub
parent c12daced40
commit 914561716d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
25 changed files with 113 additions and 109 deletions

View file

@ -83,7 +83,7 @@ class $BackupService {
outer:
for (var file in zip.files) {
final filename = file.name;
for (var ignore in ignoreFiles) {
for (var ignore in ignoreFiles) {
if (filename.endsWith(ignore) && !filename.contains("wallets/")) {
printV("ignoring backup file: $filename");
continue outer;
@ -145,7 +145,7 @@ class $BackupService {
MapEntry(key, TransactionDescription.fromJson(value as Map<String, dynamic>)));
var box = transactionDescriptionBox;
if (!box.isOpen) {
final transactionDescriptionsBoxKey =
final transactionDescriptionsBoxKey =
await getEncryptionKey(secureStorage: _secureStorage, forKey: TransactionDescription.boxKey);
box = await CakeHive.openBox<TransactionDescription>(
TransactionDescription.boxName,
@ -251,19 +251,22 @@ class $BackupService {
await importWalletKeychainInfo(info);
});
for (var key in (keychainJSON['_all'] as Map<String, dynamic>).keys) {
try {
if (!key.startsWith('MONERO_WALLET_')) continue;
final decodedPassword = decodeWalletPassword(password: keychainJSON['_all'][key].toString());
final walletName = key.split('_WALLET_')[1];
final walletType = key.split('_WALLET_')[0].toLowerCase();
await importWalletKeychainInfo({
'name': walletName,
'type': "WalletType.$walletType",
'password': decodedPassword,
});
} catch (e) {
printV('Error importing wallet ($key) password: $e');
if (keychainJSON['_all'] is Map<String, dynamic>) {
for (var key in (keychainJSON['_all'] as Map<String, dynamic>).keys) {
try {
if (!key.startsWith('MONERO_WALLET_')) continue;
final decodedPassword = decodeWalletPassword(
password: keychainJSON['_all'][key].toString());
final walletName = key.split('_WALLET_')[1];
final walletType = key.split('_WALLET_')[0].toLowerCase();
await importWalletKeychainInfo({
'name': walletName,
'type': "WalletType.$walletType",
'password': decodedPassword,
});
} catch (e) {
printV('Error importing wallet ($key) password: $e');
}
}
}

View file

@ -2,16 +2,19 @@ import 'package:cw_core/enumerable_item.dart';
import 'package:cw_core/wallet_info.dart';
class MoneroSeedType extends EnumerableItem<int> with Serializable<int> {
const MoneroSeedType({required String title, required int raw}) : super(title: title, raw: raw);
const MoneroSeedType({required String title, required int raw, this.shortTitle})
: super(title: title, raw: raw);
final String? shortTitle;
static const all = [legacy, polyseed, bip39];
static const defaultSeedType = polyseed;
static const legacy = MoneroSeedType(raw: 0, title: 'Legacy (25 words)');
static const polyseed = MoneroSeedType(raw: 1, title: 'Polyseed (16 words)');
static const legacy = MoneroSeedType(raw: 0, title: 'Legacy (25 words)', shortTitle: "Legacy");
static const polyseed = MoneroSeedType(raw: 1, title: 'Polyseed (16 words)', shortTitle: "Polyseed");
static const wowneroSeed = MoneroSeedType(raw: 2, title: 'Wownero');
static const bip39 = MoneroSeedType(raw: 3, title: 'BIP39 (12 words)');
static const bip39 = MoneroSeedType(raw: 3, title: 'BIP39 (12 words)', shortTitle: "BIP39");
static MoneroSeedType deserialize({required int raw}) {
switch (raw) {

View file

@ -47,10 +47,10 @@ class SwapTradeExchangeProvider extends ExchangeProvider {
String get title => 'SwapTrade';
@override
bool get isAvailable => false;
bool get isAvailable => true;
@override
bool get isEnabled => false;
bool get isEnabled => true;
@override
bool get supportsFixedRate => false;
@ -59,7 +59,7 @@ class SwapTradeExchangeProvider extends ExchangeProvider {
ExchangeProviderDescription get description => ExchangeProviderDescription.swapTrade;
@override
Future<bool> checkIsAvailable() async => false;
Future<bool> checkIsAvailable() async => true;
@override
Future<Limits> fetchLimits({

View file

@ -154,6 +154,7 @@ class _AdvancedPrivacySettingsBodyState extends State<_AdvancedPrivacySettingsBo
items: MoneroSeedType.all,
selectedItem: widget.seedTypeViewModel.moneroSeedType,
onItemSelected: widget.seedTypeViewModel.setMoneroSeedType,
displayItem: (seedType) => seedType.shortTitle ?? seedType.toString(),
),
);
}),

View file

@ -97,8 +97,7 @@ class QRWidget extends StatelessWidget {
padding: EdgeInsets.zero,
decoration: BoxDecoration(
border: Border(top: BorderSide.none),
borderRadius:
BorderRadius.all(Radius.circular(5)),
borderRadius: BorderRadius.all(Radius.circular(5)),
color: Colors.white,
),
child: Column(
@ -112,11 +111,10 @@ class QRWidget extends StatelessWidget {
),
),
),
if (addressListViewModel
.payjoinEndpoint.isNotEmpty) ...[
if (addressListViewModel.payjoinEndpoint.isNotEmpty &&
!addressListViewModel.isSilentPayments) ...[
Row(
mainAxisAlignment:
MainAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Padding(
padding: EdgeInsets.only(
@ -196,10 +194,12 @@ class QRWidget extends StatelessWidget {
walletType: addressListViewModel.type,
textAlign: TextAlign.center,
evenTextStyle: TextStyle(
fontSize: 15,
fontWeight: FontWeight.w500,
color:
Theme.of(context).extension<DashboardPageTheme>()!.textColor))),
fontSize: 15,
fontWeight: FontWeight.w500,
color: Theme.of(context).extension<DashboardPageTheme>()!.textColor,
),
),
),
Padding(
padding: EdgeInsets.only(left: 12),
child: copyImage,
@ -212,13 +212,12 @@ class QRWidget extends StatelessWidget {
),
Observer(
builder: (_) => Offstage(
offstage: addressListViewModel.payjoinEndpoint.isEmpty,
offstage: addressListViewModel.payjoinEndpoint.isEmpty || addressListViewModel.isSilentPayments,
child: Padding(
padding: EdgeInsets.only(top: 12),
child: PrimaryImageButton(
onPressed: () {
Clipboard.setData(
ClipboardData(text: addressUri.toString()));
Clipboard.setData(ClipboardData(text: addressUri.toString()));
showBar<void>(context, S.of(context).copied_to_clipboard);
},
image: Image.asset(
@ -227,9 +226,7 @@ class QRWidget extends StatelessWidget {
),
text: S.of(context).copy_payjoin_url,
color: Theme.of(context).cardColor,
textColor: Theme.of(context)
.extension<CakeTextTheme>()!
.buttonTextColor,
textColor: Theme.of(context).extension<CakeTextTheme>()!.buttonTextColor,
),
),
),

View file

@ -600,6 +600,8 @@ class SendCardState extends State<SendCard> with AutomaticKeepAliveClientMixin<S
final maxCustomFeeRate = sendViewModel.feesViewModel.maxCustomFeeRate?.toDouble();
double? customFeeRate = isBitcoinWallet ? sendViewModel.feesViewModel.customBitcoinFeeRate.toDouble() : null;
FocusManager.instance.primaryFocus?.unfocus();
await showPopUp<void>(
context: context,
builder: (BuildContext context) {

View file

@ -225,7 +225,7 @@ class ExceptionHandler {
// just ignoring until we find a solution to this issue or migrate from flutter secure storage
"core/auth_service.dart:64",
"core/key_service.dart:14",
"core/wallet_loading_service.dart:134",
"core/wallet_loading_service.dart:139",
];
static Future<void> _addDeviceInfo(File file) async {

View file

@ -82,7 +82,7 @@ class BitcoinURI extends PaymentURI {
final qp = <String, String>{};
if (amount.isNotEmpty) qp['amount'] = amount.replaceAll(',', '.');
if (pjUri.isNotEmpty) {
if (pjUri.isNotEmpty && !address.startsWith("sp")) {
qp['pjos'] = '0';
qp['pj'] = pjUri;
}