Merge remote-tracking branch 'origin/main'

This commit is contained in:
OmarHatem 2025-04-22 23:56:54 +02:00
commit b4fcec3a01
9 changed files with 277 additions and 335 deletions

View file

@ -11,3 +11,4 @@ Please include a summary of the changes and which issue is fixed / feature is ad
- [ ] Format code
- [ ] Look for code duplication
- [ ] Clear naming for variables and methods
- [ ] Manual tests in accessibility mode (TalkBack on Android) passed

View file

@ -1,6 +1,5 @@
import 'dart:convert';
import 'dart:io';
import 'dart:typed_data';
import 'package:cake_wallet/core/secure_storage.dart';
import 'package:cake_wallet/entities/get_encryption_key.dart';
import 'package:cake_wallet/entities/transaction_description.dart';
@ -164,6 +163,31 @@ class $BackupService {
}
final data = json.decode(preferencesFile.readAsStringSync()) as Map<String, dynamic>;
try { // shouldn't throw an error but just in case, so it doesn't stop the backup restore
for (var entry in data.entries) {
String key = entry.key;
dynamic value = entry.value;
// Check the type of the value and save accordingly
if (value is String) {
await sharedPreferences.setString(key, value);
} else if (value is int) {
await sharedPreferences.setInt(key, value);
} else if (value is double) {
await sharedPreferences.setDouble(key, value);
} else if (value is bool) {
await sharedPreferences.setBool(key, value);
} else if (value is List<String>) {
await sharedPreferences.setStringList(key, value);
} else {
if (kDebugMode) {
printV('Skipping individual save for key "$key": Unsupported type (${value.runtimeType}). Value: $value');
}
}
}
} catch (_) {}
String currentWalletName = data[PreferencesKey.currentWalletName] as String;
int currentWalletType = data[PreferencesKey.currentWalletType] as int;
@ -175,151 +199,10 @@ class $BackupService {
currentWalletType = serializeToInt(correctWallets.first.type);
}
final currentNodeId = data[PreferencesKey.currentNodeIdKey] as int?;
final currentBalanceDisplayMode = data[PreferencesKey.currentBalanceDisplayModeKey] as int?;
final currentFiatCurrency = data[PreferencesKey.currentFiatCurrencyKey] as String?;
final shouldSaveRecipientAddress = data[PreferencesKey.shouldSaveRecipientAddressKey] as bool?;
final isAppSecure = data[PreferencesKey.isAppSecureKey] as bool?;
final disableTradeOption = data[PreferencesKey.disableTradeOption] as bool?;
final currentTransactionPriorityKeyLegacy =
data[PreferencesKey.currentTransactionPriorityKeyLegacy] as int?;
final currentBitcoinElectrumSererId =
data[PreferencesKey.currentBitcoinElectrumSererIdKey] as int?;
final currentLanguageCode = data[PreferencesKey.currentLanguageCode] as String?;
final displayActionListMode = data[PreferencesKey.displayActionListModeKey] as int?;
final fiatApiMode = data[PreferencesKey.currentFiatApiModeKey] as int?;
final currentTheme = data[PreferencesKey.currentTheme] as int?;
final exchangeStatus = data[PreferencesKey.exchangeStatusKey] as int?;
final currentDefaultSettingsMigrationVersion =
data[PreferencesKey.currentDefaultSettingsMigrationVersion] as int?;
final moneroTransactionPriority = data[PreferencesKey.moneroTransactionPriority] as int?;
final bitcoinTransactionPriority = data[PreferencesKey.bitcoinTransactionPriority] as int?;
final sortBalanceTokensBy = data[PreferencesKey.sortBalanceBy] as int?;
final pinNativeTokenAtTop = data[PreferencesKey.pinNativeTokenAtTop] as bool?;
final useEtherscan = data[PreferencesKey.useEtherscan] as bool?;
final defaultNanoRep = data[PreferencesKey.defaultNanoRep] as String?;
final defaultBananoRep = data[PreferencesKey.defaultBananoRep] as String?;
final lookupsTwitter = data[PreferencesKey.lookupsTwitter] as bool?;
final lookupsMastodon = data[PreferencesKey.lookupsMastodon] as bool?;
final lookupsYatService = data[PreferencesKey.lookupsYatService] as bool?;
final lookupsUnstoppableDomains = data[PreferencesKey.lookupsUnstoppableDomains] as bool?;
final lookupsOpenAlias = data[PreferencesKey.lookupsOpenAlias] as bool?;
final lookupsENS = data[PreferencesKey.lookupsENS] as bool?;
final lookupsWellKnown = data[PreferencesKey.lookupsWellKnown] 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);
if (currentNodeId != null)
await sharedPreferences.setInt(PreferencesKey.currentNodeIdKey, currentNodeId);
if (currentBalanceDisplayMode != null)
await sharedPreferences.setInt(
PreferencesKey.currentBalanceDisplayModeKey, currentBalanceDisplayMode);
await sharedPreferences.setInt(PreferencesKey.currentWalletType, currentWalletType);
if (currentFiatCurrency != null)
await sharedPreferences.setString(
PreferencesKey.currentFiatCurrencyKey, currentFiatCurrency);
if (shouldSaveRecipientAddress != null)
await sharedPreferences.setBool(
PreferencesKey.shouldSaveRecipientAddressKey, shouldSaveRecipientAddress);
if (isAppSecure != null)
await sharedPreferences.setBool(PreferencesKey.isAppSecureKey, isAppSecure);
if (disableTradeOption != null)
await sharedPreferences.setBool(PreferencesKey.disableTradeOption, disableTradeOption);
if (currentTransactionPriorityKeyLegacy != null)
await sharedPreferences.setInt(
PreferencesKey.currentTransactionPriorityKeyLegacy, currentTransactionPriorityKeyLegacy);
if (currentBitcoinElectrumSererId != null)
await sharedPreferences.setInt(
PreferencesKey.currentBitcoinElectrumSererIdKey, currentBitcoinElectrumSererId);
if (currentLanguageCode != null)
await sharedPreferences.setString(PreferencesKey.currentLanguageCode, currentLanguageCode);
if (displayActionListMode != null)
await sharedPreferences.setInt(
PreferencesKey.displayActionListModeKey, displayActionListMode);
if (fiatApiMode != null)
await sharedPreferences.setInt(PreferencesKey.currentFiatApiModeKey, fiatApiMode);
if (autoGenerateSubaddressStatus != null)
await sharedPreferences.setInt(
PreferencesKey.autoGenerateSubaddressStatusKey, autoGenerateSubaddressStatus);
if (currentTheme != null && DeviceInfo.instance.isMobile) {
await sharedPreferences.setInt(PreferencesKey.currentTheme, currentTheme);
// enforce dark theme on desktop platforms until the design is ready:
} else if (DeviceInfo.instance.isDesktop) {
if (DeviceInfo.instance.isDesktop) {
await sharedPreferences.setInt(PreferencesKey.currentTheme, ThemeList.darkTheme.raw);
}
if (exchangeStatus != null)
await sharedPreferences.setInt(PreferencesKey.exchangeStatusKey, exchangeStatus);
if (currentDefaultSettingsMigrationVersion != null)
await sharedPreferences.setInt(PreferencesKey.currentDefaultSettingsMigrationVersion,
currentDefaultSettingsMigrationVersion);
if (moneroTransactionPriority != null)
await sharedPreferences.setInt(
PreferencesKey.moneroTransactionPriority, moneroTransactionPriority);
if (bitcoinTransactionPriority != null)
await sharedPreferences.setInt(
PreferencesKey.bitcoinTransactionPriority, bitcoinTransactionPriority);
if (sortBalanceTokensBy != null)
await sharedPreferences.setInt(PreferencesKey.sortBalanceBy, sortBalanceTokensBy);
if (pinNativeTokenAtTop != null)
await sharedPreferences.setBool(PreferencesKey.pinNativeTokenAtTop, pinNativeTokenAtTop);
if (useEtherscan != null)
await sharedPreferences.setBool(PreferencesKey.useEtherscan, useEtherscan);
if (defaultNanoRep != null)
await sharedPreferences.setString(PreferencesKey.defaultNanoRep, defaultNanoRep);
if (defaultBananoRep != null)
await sharedPreferences.setString(PreferencesKey.defaultBananoRep, defaultBananoRep);
if (syncAll != null) await sharedPreferences.setBool(PreferencesKey.syncAllKey, syncAll);
if (lookupsTwitter != null)
await sharedPreferences.setBool(PreferencesKey.lookupsTwitter, lookupsTwitter);
if (lookupsMastodon != null)
await sharedPreferences.setBool(PreferencesKey.lookupsMastodon, lookupsMastodon);
if (lookupsYatService != null)
await sharedPreferences.setBool(PreferencesKey.lookupsYatService, lookupsYatService);
if (lookupsUnstoppableDomains != null)
await sharedPreferences.setBool(
PreferencesKey.lookupsUnstoppableDomains, lookupsUnstoppableDomains);
if (lookupsOpenAlias != null)
await sharedPreferences.setBool(PreferencesKey.lookupsOpenAlias, lookupsOpenAlias);
if (lookupsENS != null) await sharedPreferences.setBool(PreferencesKey.lookupsENS, lookupsENS);
if (lookupsWellKnown != null)
await sharedPreferences.setBool(PreferencesKey.lookupsWellKnown, lookupsWellKnown);
if (syncAll != null) await sharedPreferences.setBool(PreferencesKey.syncAllKey, syncAll);
if (syncMode != null) await sharedPreferences.setInt(PreferencesKey.syncModeKey, syncMode);
await preferencesFile.delete();
}
@ -378,83 +261,45 @@ class $BackupService {
await keyService.saveWalletPassword(walletName: name, password: password);
}
@Deprecated('Use v2 instead')
Future<Uint8List> _exportKeychainDumpV1(String password,
{required String nonce, String keychainSalt = secrets.backupKeychainSalt}) async =>
throw Exception('Deprecated');
Future<Uint8List> exportKeychainDumpV2(String password,
{String keychainSalt = secrets.backupKeychainSalt}) async {
final key = generateStoreKeyFor(key: SecretStoreKey.pinCodePassword);
final wallets = await Future.wait(walletInfoSource.values.map((walletInfo) async {
try {
return {
'name': walletInfo.name,
'type': walletInfo.type.toString(),
'password': await keyService.getWalletPassword(walletName: walletInfo.name)
};
} catch (e) {
return {
'name': walletInfo.name,
'type': walletInfo.type.toString(),
'password': ''
};
}
}));
final backupPasswordKey = generateStoreKeyFor(key: SecretStoreKey.backupPassword);
final backupPassword = await _secureStorage.read(key: backupPasswordKey);
final data = utf8.encode(
json.encode({'wallets': wallets, backupPasswordKey: backupPassword}));
json.encode({'wallets': wallets, backupPasswordKey: backupPassword, '_all': await _secureStorage.readAll()}));
final encrypted = await _encryptV2(Uint8List.fromList(data), '$keychainSalt$password');
return encrypted;
}
static const List<String> _excludedPrefsKeys = [
PreferencesKey.currentPinLength,
PreferencesKey.showCameraConsent,
PreferencesKey.lastSeenAppVersion,
PreferencesKey.failedTotpTokenTrials,
];
Future<String> exportPreferencesJSON() async {
final preferences = <String, dynamic>{
PreferencesKey.currentWalletName:
sharedPreferences.getString(PreferencesKey.currentWalletName),
PreferencesKey.currentNodeIdKey: sharedPreferences.getInt(PreferencesKey.currentNodeIdKey),
PreferencesKey.currentBalanceDisplayModeKey:
sharedPreferences.getInt(PreferencesKey.currentBalanceDisplayModeKey),
PreferencesKey.currentWalletType: sharedPreferences.getInt(PreferencesKey.currentWalletType),
PreferencesKey.currentFiatCurrencyKey:
sharedPreferences.getString(PreferencesKey.currentFiatCurrencyKey),
PreferencesKey.shouldSaveRecipientAddressKey:
sharedPreferences.getBool(PreferencesKey.shouldSaveRecipientAddressKey),
PreferencesKey.disableTradeOption: sharedPreferences.getBool(PreferencesKey.disableTradeOption),
PreferencesKey.currentTransactionPriorityKeyLegacy:
sharedPreferences.getInt(PreferencesKey.currentTransactionPriorityKeyLegacy),
PreferencesKey.currentBitcoinElectrumSererIdKey:
sharedPreferences.getInt(PreferencesKey.currentBitcoinElectrumSererIdKey),
PreferencesKey.currentLanguageCode:
sharedPreferences.getString(PreferencesKey.currentLanguageCode),
PreferencesKey.displayActionListModeKey:
sharedPreferences.getInt(PreferencesKey.displayActionListModeKey),
PreferencesKey.currentTheme: sharedPreferences.getInt(PreferencesKey.currentTheme),
PreferencesKey.exchangeStatusKey: sharedPreferences.getInt(PreferencesKey.exchangeStatusKey),
PreferencesKey.currentDefaultSettingsMigrationVersion:
sharedPreferences.getInt(PreferencesKey.currentDefaultSettingsMigrationVersion),
PreferencesKey.bitcoinTransactionPriority:
sharedPreferences.getInt(PreferencesKey.bitcoinTransactionPriority),
PreferencesKey.moneroTransactionPriority:
sharedPreferences.getInt(PreferencesKey.moneroTransactionPriority),
PreferencesKey.currentFiatApiModeKey:
sharedPreferences.getInt(PreferencesKey.currentFiatApiModeKey),
PreferencesKey.sortBalanceBy: sharedPreferences.getInt(PreferencesKey.sortBalanceBy),
PreferencesKey.pinNativeTokenAtTop:
sharedPreferences.getBool(PreferencesKey.pinNativeTokenAtTop),
PreferencesKey.useEtherscan: sharedPreferences.getBool(PreferencesKey.useEtherscan),
PreferencesKey.defaultNanoRep: sharedPreferences.getString(PreferencesKey.defaultNanoRep),
PreferencesKey.defaultBananoRep:
sharedPreferences.getString(PreferencesKey.defaultBananoRep),
PreferencesKey.lookupsTwitter: sharedPreferences.getBool(PreferencesKey.lookupsTwitter),
PreferencesKey.lookupsMastodon: sharedPreferences.getBool(PreferencesKey.lookupsMastodon),
PreferencesKey.lookupsYatService:
sharedPreferences.getBool(PreferencesKey.lookupsYatService),
PreferencesKey.lookupsUnstoppableDomains:
sharedPreferences.getBool(PreferencesKey.lookupsUnstoppableDomains),
PreferencesKey.lookupsOpenAlias: sharedPreferences.getBool(PreferencesKey.lookupsOpenAlias),
PreferencesKey.lookupsENS: sharedPreferences.getBool(PreferencesKey.lookupsENS),
PreferencesKey.lookupsWellKnown:
sharedPreferences.getBool(PreferencesKey.lookupsWellKnown),
PreferencesKey.syncModeKey: sharedPreferences.getInt(PreferencesKey.syncModeKey),
PreferencesKey.syncAllKey: sharedPreferences.getBool(PreferencesKey.syncAllKey),
PreferencesKey.autoGenerateSubaddressStatusKey:
sharedPreferences.getInt(PreferencesKey.autoGenerateSubaddressStatusKey),
};
final preferences = <String, dynamic>{};
sharedPreferences.getKeys().forEach((key) => preferences[key] = sharedPreferences.get(key));
_excludedPrefsKeys.forEach((key) => preferences.remove(key));
return json.encode(preferences);
}
@ -466,10 +311,6 @@ class $BackupService {
return Uint8List.fromList(bytes);
}
@Deprecated('Use v2 instead')
Future<Uint8List> _encryptV1(Uint8List data, String secretKeySource, String nonceBase64) async =>
throw Exception('Deprecated');
Future<Uint8List> _decryptV1(Uint8List data, String secretKeySource, String nonceBase64,
{int macLength = 16}) async {
final secretKeyHash = await Cryptography.instance.sha256().hash(utf8.encode(secretKeySource));

View file

@ -490,11 +490,19 @@ class BuySellPage extends BasePage {
return DesktopExchangeCardsSection(
firstExchangeCard: fiatExchangeCard,
secondExchangeCard: cryptoExchangeCard,
onBuyTap: () => null,
onSellTap: () =>
buySellViewModel.isBuyAction ? buySellViewModel.changeBuySellAction() : null,
isBuySellOption: true,
);
} else {
return DesktopExchangeCardsSection(
firstExchangeCard: cryptoExchangeCard,
secondExchangeCard: fiatExchangeCard,
onBuyTap: () =>
!buySellViewModel.isBuyAction ? buySellViewModel.changeBuySellAction() : null,
onSellTap: () => null,
isBuySellOption: true,
);
}
},

View file

@ -1,43 +1,92 @@
import 'package:cake_wallet/src/widgets/alert_background.dart';
import 'package:cake_wallet/src/widgets/alert_close_button.dart';
import 'package:cake_wallet/themes/extensions/cake_text_theme.dart';
import 'package:cake_wallet/src/screens/dashboard/widgets/filter_tile.dart';
import 'package:cake_wallet/src/widgets/section_divider.dart';
import 'package:cake_wallet/src/widgets/standard_checkbox.dart';
import 'package:cake_wallet/themes/extensions/menu_theme.dart';
import 'package:cake_wallet/view_model/dashboard/dropdown_filter_item.dart';
import 'package:cake_wallet/view_model/dashboard/dropdown_filter_item_widget.dart';
import 'package:cake_wallet/utils/responsive_layout_util.dart';
import 'package:cake_wallet/view_model/dashboard/filter_item.dart';
import 'package:flutter/material.dart';
import 'package:cake_wallet/src/widgets/picker_wrapper_widget.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
//import 'package:date_range_picker/date_range_picker.dart' as date_rage_picker;
import 'package:cake_wallet/themes/extensions/transaction_trade_theme.dart';
class FilterWidget extends StatelessWidget {
FilterWidget({required this.filterItems});
class FilterWidget extends StatefulWidget {
const FilterWidget({required this.filterItems, this.onClose, Key? key}) : super(key: key);
final Map<String, List<FilterItem>> filterItems;
final Function()? onClose;
@override
_FilterWidgetState createState() => _FilterWidgetState();
}
class _FilterWidgetState extends State<FilterWidget> {
final ScrollController _scrollController = ScrollController();
@override
void dispose() {
_scrollController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
const sectionDivider = const HorizontalSectionDivider();
return PickerWrapperWidget(
return AlertBackground(
child: Column(
children: [
const Expanded(child: SizedBox()),
Expanded(
flex: responsiveLayoutUtil.shouldRenderTabletUI ? 16 : 8,
child: LayoutBuilder(
builder: (context, constraints) {
double availableHeight = constraints.maxHeight;
return _buildFilterContent(context, availableHeight);
},
),
),
Expanded(
child: AlertCloseButton(
key: const ValueKey('filter_wrapper_close_button_key'),
isPositioned: false,
onTap: widget.onClose,
),
),
const SizedBox(height: 24),
],
),
);
}
Widget _buildFilterContent(BuildContext context, double availableHeight) {
const sectionDivider = HorizontalSectionDivider();
const double totalHeaderHeight = 73;
const double filterTileMinHeight = 40;
double availableHeightForItems = availableHeight - totalHeaderHeight;
return Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Padding(
padding: EdgeInsets.only(left: 24, right: 24, top: 24),
padding: const EdgeInsets.only(left: 24, right: 24, top: 24),
child: ClipRRect(
borderRadius: BorderRadius.all(Radius.circular(24)),
borderRadius: BorderRadius.circular(24),
child: Container(
color: Theme.of(context).extension<CakeMenuTheme>()!.backgroundColor,
child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: EdgeInsets.all(24.0),
padding: const EdgeInsets.all(24.0),
child: Text(
S.of(context).filter_by,
style: TextStyle(
color:
Theme.of(context).extension<TransactionTradeTheme>()!.detailsTitlesColor,
color: Theme.of(context)
.extension<TransactionTradeTheme>()!
.detailsTitlesColor,
fontSize: 16,
fontFamily: 'Lato',
decoration: TextDecoration.none,
@ -49,54 +98,28 @@ class FilterWidget extends StatelessWidget {
padding: EdgeInsets.zero,
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
itemCount: filterItems.length,
itemCount: widget.filterItems.length,
separatorBuilder: (context, _) => sectionDivider,
itemBuilder: (_, index1) {
final title = filterItems.keys.elementAt(index1);
final section = filterItems.values.elementAt(index1);
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Padding(
padding: EdgeInsets.only(top: 20, left: 24, right: 24),
child: Text(
title,
style: TextStyle(
color: Theme.of(context).extension<CakeTextTheme>()!.titleColor,
fontSize: 16,
fontFamily: 'Lato',
fontWeight: FontWeight.bold,
decoration: TextDecoration.none),
),
),
ListView.builder(
padding: EdgeInsets.symmetric(horizontal: 28.0),
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
final title = widget.filterItems.keys.elementAt(index1);
final section = widget.filterItems.values.elementAt(index1);
final double itemHeight =
availableHeightForItems / widget.filterItems.length;
final isSectionScrollable =
(itemHeight < (section.length * filterTileMinHeight));
final Widget sectionListView = ListView.builder(
controller: isSectionScrollable ? _scrollController : null,
padding: const EdgeInsets.symmetric(horizontal: 28.0),
shrinkWrap: isSectionScrollable ? false : true,
physics: isSectionScrollable
? const BouncingScrollPhysics()
: const NeverScrollableScrollPhysics(),
itemCount: section.length,
itemBuilder: (_, index2) {
final item = section[index2];
if (item is DropdownFilterItem) {
return Padding(
padding: EdgeInsets.fromLTRB(8, 0, 8, 16),
child: Container(
decoration: BoxDecoration(
border: Border(
bottom: BorderSide(
width: 1.0,
color: Theme.of(context).extension<CakeTextTheme>()!.secondaryTextColor),
),
),
child: DropdownFilterList(
items: item.items,
caption: item.caption,
selectedItem: item.selectedItem,
onItemSelected: item.onItemSelected,
),
),
);
}
final content = Observer(
builder: (_) => StandardCheckbox(
value: item.value(),
@ -105,19 +128,51 @@ class FilterWidget extends StatelessWidget {
borderColor: Theme.of(context).dividerColor,
iconColor: Colors.white,
onChanged: (value) => item.onChanged(),
));
return FilterTile(child: content);
),
);
return FilterTile(
child: content,
);
},
);
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.only(top: 20, left: 24, right: 24),
child: Text(
title,
style: TextStyle(
color: Theme.of(context).extension<CakeTextTheme>()!.titleColor,
fontSize: 16,
fontFamily: 'Lato',
fontWeight: FontWeight.bold,
decoration: TextDecoration.none,
),
),
),
Container(
height: isSectionScrollable ? itemHeight - totalHeaderHeight : null,
child: isSectionScrollable
? Scrollbar(
controller: _scrollController,
thumbVisibility: true,
child: sectionListView,
)
: sectionListView,
),
],
);
},
),
]),
),
),
)
],
),
),
),
),
],
),
);
}
}

View file

@ -1,15 +1,22 @@
import 'package:cake_wallet/src/screens/exchange/widgets/mobile_exchange_cards_section.dart';
import 'package:flutter/material.dart';
class DesktopExchangeCardsSection extends StatelessWidget {
final Widget firstExchangeCard;
final Widget secondExchangeCard;
const DesktopExchangeCardsSection({
Key? key,
required this.firstExchangeCard,
required this.secondExchangeCard,
this.isBuySellOption = false,
this.onBuyTap,
this.onSellTap,
}) : super(key: key);
final Widget firstExchangeCard;
final Widget secondExchangeCard;
final bool isBuySellOption;
final VoidCallback? onBuyTap;
final VoidCallback? onSellTap;
@override
Widget build(BuildContext context) {
return FocusTraversalGroup(
@ -18,7 +25,18 @@ class DesktopExchangeCardsSection extends StatelessWidget {
children: <Widget>[
Padding(
padding: EdgeInsets.only(top: 55, left: 24, right: 24),
child: firstExchangeCard,
child: Column(
children: [
if (isBuySellOption)
Column(
children: [
const SizedBox(height: 16),
BuySellOptionButtons(onBuyTap: onBuyTap, onSellTap: onSellTap),
],
),
firstExchangeCard,
],
),
),
Padding(
padding: EdgeInsets.only(top: 29, left: 24, right: 24),

View file

@ -38,6 +38,7 @@ class PinCodeState<T extends PinCodeWidget> extends State<T> {
static const fourPinLength = 4;
final _gridViewKey = GlobalKey();
final _key = GlobalKey<ScaffoldState>();
late final FocusNode _focusNode;
int pinLength;
String pin;
@ -54,7 +55,17 @@ class PinCodeState<T extends PinCodeWidget> extends State<T> {
pin = '';
title = S.current.enter_your_pin;
_aspectRatio = 0;
WidgetsBinding.instance.addPostFrameCallback(_afterLayout);
_focusNode = FocusNode();
WidgetsBinding.instance.addPostFrameCallback((_) {
_focusNode.requestFocus();
_afterLayout(_);
});
}
@override
void dispose() {
_focusNode.dispose();
super.dispose();
}
void setTitle(String title) => setState(() => this.title = title);
@ -120,8 +131,8 @@ class PinCodeState<T extends PinCodeWidget> extends State<T> {
);
return KeyboardListener(
focusNode: FocusNode(),
autofocus: true,
focusNode: _focusNode,
autofocus: false,
onKeyEvent: (keyEvent) {
if (keyEvent is KeyDownEvent) {
if (keyEvent.logicalKey.keyLabel == "Backspace") {
@ -144,8 +155,7 @@ class PinCodeState<T extends PinCodeWidget> extends State<T> {
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.w500,
color:
Theme.of(context).extension<CakeTextTheme>()!.titleColor)),
color: Theme.of(context).extension<CakeTextTheme>()!.titleColor)),
Spacer(flex: 8),
Container(
width: 180,
@ -162,7 +172,9 @@ class PinCodeState<T extends PinCodeWidget> extends State<T> {
shape: BoxShape.circle,
color: isFilled
? Theme.of(context).extension<CakeTextTheme>()!.titleColor
: Theme.of(context).extension<PinCodeTheme>()!.indicatorsColor
: Theme.of(context)
.extension<PinCodeTheme>()!
.indicatorsColor
.withOpacity(0.25),
));
}),
@ -225,7 +237,8 @@ class PinCodeState<T extends PinCodeWidget> extends State<T> {
child: TextButton(
onPressed: () => _pop(),
style: TextButton.styleFrom(
backgroundColor: Theme.of(context).colorScheme.background,
backgroundColor:
Theme.of(context).colorScheme.background,
shape: CircleBorder(),
),
child: deleteIconImage,
@ -250,7 +263,9 @@ class PinCodeState<T extends PinCodeWidget> extends State<T> {
style: TextStyle(
fontSize: 25.0,
fontWeight: FontWeight.w600,
color: Theme.of(context).extension<CakeTextTheme>()!.titleColor)),
color: Theme.of(context)
.extension<CakeTextTheme>()!
.titleColor)),
),
);
}),

View file

@ -7,6 +7,7 @@ class AlertCloseButton extends StatelessWidget {
this.image,
this.bottom,
this.onTap,
this.isPositioned = true,
super.key,
});
@ -14,6 +15,7 @@ class AlertCloseButton extends StatelessWidget {
final Image? image;
final double? bottom;
final bool isPositioned;
final closeButton = Image.asset(
'assets/images/close.png',
@ -22,9 +24,7 @@ class AlertCloseButton extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Positioned(
bottom: bottom ?? 60,
child: GestureDetector(
final button = GestureDetector(
onTap: onTap ?? () => Navigator.of(context).pop(),
child: Semantics(
label: S.of(context).close,
@ -34,12 +34,8 @@ class AlertCloseButton extends StatelessWidget {
height: 42,
width: 42,
decoration: BoxDecoration(color: Colors.white, shape: BoxShape.circle),
child: Center(
child: image ?? closeButton,
),
),
),
),
);
child: Center(child: image ?? closeButton))));
return isPositioned ? Positioned(bottom: bottom ?? 60, child: button) : button;
}
}

View file

@ -50,7 +50,7 @@ abstract class RestoreFromBackupViewModelBase with Store {
state = FailureState('This is not a valid backup file, please make sure you selected the correct backup file');
} else {
state = FailureState('Failed to restore backup, please try again');
ExceptionHandler.onError(FlutterErrorDetails(exception: e, stack: s, silent: true));
ExceptionHandler.onError(FlutterErrorDetails(exception: e, stack: s));
}
}

View file

@ -1629,6 +1629,7 @@ abstract class SecureStorage {
Future<void> delete({required String key});
// Legacy
Future<String?> readNoIOptions({required String key});
Future<Map<String, String>> readAll();
}""";
const defaultSecureStorage = """
class DefaultSecureStorage extends SecureStorage {
@ -1667,6 +1668,11 @@ class DefaultSecureStorage extends SecureStorage {
iOptions: useNoIOptions ? IOSOptions() : null,
);
}
@override
Future<Map<String, String>> readAll() async {
return await _secureStorage.readAll();
}
}""";
const fakeSecureStorage = """
class FakeSecureStorage extends SecureStorage {
@ -1678,6 +1684,8 @@ class FakeSecureStorage extends SecureStorage {
Future<void> delete({required String key}) async {}
@override
Future<String?> readNoIOptions({required String key}) async => null;
@override
Future<Map<String, String>> readAll() async => {};
}""";
final outputFile = File(secureStoragePath);
final header = hasFlutterSecureStorage