diff --git a/assets/new-ui/black_accent.svg.vec b/assets/new-ui/black_accent.svg.vec new file mode 100644 index 000000000..5d087f000 Binary files /dev/null and b/assets/new-ui/black_accent.svg.vec differ diff --git a/assets/new-ui/cake-setting.svg.vec b/assets/new-ui/cake-setting.svg.vec new file mode 100644 index 000000000..33efbf6b1 Binary files /dev/null and b/assets/new-ui/cake-setting.svg.vec differ diff --git a/assets/new-ui/chooser.svg.vec b/assets/new-ui/chooser.svg.vec index 69befbc78..4f8ed1364 100644 Binary files a/assets/new-ui/chooser.svg.vec and b/assets/new-ui/chooser.svg.vec differ diff --git a/assets/new-ui/dark.svg.vec b/assets/new-ui/dark.svg.vec new file mode 100644 index 000000000..d270f2152 Binary files /dev/null and b/assets/new-ui/dark.svg.vec differ diff --git a/assets/new-ui/light.svg.vec b/assets/new-ui/light.svg.vec new file mode 100644 index 000000000..7971583f3 Binary files /dev/null and b/assets/new-ui/light.svg.vec differ diff --git a/assets/new-ui/lightning-icon.svg.vec b/assets/new-ui/lightning-icon.svg.vec new file mode 100644 index 000000000..09d9daa76 Binary files /dev/null and b/assets/new-ui/lightning-icon.svg.vec differ diff --git a/assets/new-ui/privacy.svg.vec b/assets/new-ui/privacy.svg.vec index f8d6e3e47..e50f9641b 100644 Binary files a/assets/new-ui/privacy.svg.vec and b/assets/new-ui/privacy.svg.vec differ diff --git a/assets/new-ui/security.svg.vec b/assets/new-ui/security.svg.vec index 4ed940fcd..97a589e92 100644 Binary files a/assets/new-ui/security.svg.vec and b/assets/new-ui/security.svg.vec differ diff --git a/assets/new-ui/settings_row_icons/privacy.svg.vec b/assets/new-ui/settings_row_icons/privacy.svg.vec index f8d6e3e47..e50f9641b 100644 Binary files a/assets/new-ui/settings_row_icons/privacy.svg.vec and b/assets/new-ui/settings_row_icons/privacy.svg.vec differ diff --git a/assets/new-ui/settings_row_icons/security.svg.vec b/assets/new-ui/settings_row_icons/security.svg.vec index 4ed940fcd..97a589e92 100644 Binary files a/assets/new-ui/settings_row_icons/security.svg.vec and b/assets/new-ui/settings_row_icons/security.svg.vec differ diff --git a/assets/new-ui/settings_row_icons/wc.svg.vec b/assets/new-ui/settings_row_icons/wc.svg.vec new file mode 100644 index 000000000..a071b14ee Binary files /dev/null and b/assets/new-ui/settings_row_icons/wc.svg.vec differ diff --git a/assets/new-ui/wallet-setting.svg.vec b/assets/new-ui/wallet-setting.svg.vec new file mode 100644 index 000000000..22d32c474 Binary files /dev/null and b/assets/new-ui/wallet-setting.svg.vec differ diff --git a/assets/new-ui/wc.svg.vec b/assets/new-ui/wc.svg.vec new file mode 100644 index 000000000..a071b14ee Binary files /dev/null and b/assets/new-ui/wc.svg.vec differ diff --git a/lib/di.dart b/lib/di.dart index a6b5c740a..156a36247 100644 --- a/lib/di.dart +++ b/lib/di.dart @@ -252,6 +252,7 @@ import 'package:cake_wallet/view_model/send/fees_view_model.dart'; import 'package:cake_wallet/view_model/send/send_template_view_model.dart'; import 'package:cake_wallet/view_model/send/send_view_model.dart'; import 'package:cake_wallet/view_model/set_up_2fa_viewmodel.dart'; +import 'package:cake_wallet/view_model/settings/connection_sync_view_model.dart'; import 'package:cake_wallet/view_model/settings/display_settings_view_model.dart'; import 'package:cake_wallet/view_model/settings/mweb_settings_view_model.dart'; import 'package:cake_wallet/view_model/settings/other_settings_view_model.dart'; @@ -1131,7 +1132,9 @@ Future setup({ return PowNodeListViewModel(_powNodeSource, appStore); }); - getIt.registerFactory(() => ConnectionSyncPage(getIt.get())); + getIt.registerFactory(() => ConnectionSyncViewModel(getIt.get(), getIt.get().wallet!)); + + getIt.registerFactory(() => ConnectionSyncPage(getIt.get())); getIt.registerFactory(() => SecurityBackupPage(getIt.get(), getIt.get(), getIt.get().wallet!.isHardwareWallet)); @@ -1140,7 +1143,7 @@ Future setup({ getIt.registerFactory(() => TrocadorProvidersPage(getIt.get())); - getIt.registerFactory(() => DomainLookupsPage(getIt.get())); + getIt.registerFactory(() => DomainLookupsPage(getIt.get())); getIt.registerFactory(() => DisplaySettingsPage(getIt.get())); @@ -1691,9 +1694,9 @@ Future setup({ getIt.registerFactoryParam((bool isPow, _) { if (isPow) { - return ManageNodesPage(isPow, powNodeListViewModel: getIt.get()); + return ManageNodesPage(isPow, powNodeListViewModel: getIt.get(), dashboardViewModel: getIt.get()); } - return ManageNodesPage(isPow, nodeListViewModel: getIt.get()); + return ManageNodesPage(isPow, nodeListViewModel: getIt.get(), dashboardViewModel: getIt.get()); }); getIt.registerFactory( diff --git a/lib/new-ui/pages/about_page.dart b/lib/new-ui/pages/about_page.dart index 7a9a15acc..a869ba1a3 100644 --- a/lib/new-ui/pages/about_page.dart +++ b/lib/new-ui/pages/about_page.dart @@ -2,11 +2,11 @@ import 'dart:math'; import 'package:cake_wallet/entities/new_ui_entities/list_item/list_item_regular_row.dart'; import 'package:cake_wallet/generated/i18n.dart'; +import 'package:cake_wallet/new-ui/widgets/modal_page_wrapper.dart'; import 'package:cake_wallet/new-ui/widgets/receive_page/receive_top_bar.dart'; import 'package:cake_wallet/src/widgets/cake_image_widget.dart'; import 'package:cake_wallet/src/widgets/new_list_row/new_list_section.dart'; import 'package:flutter/material.dart'; -import 'package:flutter_svg/svg.dart'; import 'package:url_launcher/url_launcher.dart'; // written by people who happened to read my slack message in 2025 @@ -52,61 +52,60 @@ class _AboutPageState extends State { @override Widget build(BuildContext context) { - return Container( - color: Theme.of(context).colorScheme.surface, - child: Column(children: [ - ModalTopBar( - title: S.of(context).about, - leadingIcon: Icon(Icons.arrow_back_ios_new), - onLeadingPressed: Navigator.of(context).pop, - ), - Column( - children: [ - Column( - spacing: 16, - children: [ - SizedBox(), - GestureDetector( - onTap: _easterEgg, - child: CakeImageWidget( - imageUrl: "assets/new-ui/cake_squircle_icon.svg", - width: 128, - height: 128, - ), - ), - Row( - mainAxisAlignment: MainAxisAlignment.center, - spacing: 8, - children: [ - Text( - "Cake Wallet", - style: TextStyle(fontSize: 32, fontWeight: FontWeight.w500), + return ModalPageWrapper( + topBar: ModalTopBar( + title: S.of(context).about, + leadingIcon: Icon(Icons.arrow_back_ios_new), + onLeadingPressed: Navigator.of(context).pop, + ), + content: Container( + color: Theme.of(context).colorScheme.surface, + child: Column(children: [ + Column( + children: [ + Column( + spacing: 16, + children: [ + SizedBox(), + GestureDetector( + onTap: _easterEgg, + child: CakeImageWidget( + imageUrl: "assets/new-ui/cake_squircle_icon.svg", + width: 128, + height: 128, ), - Text(widget.appVersion, - style: TextStyle( - fontSize: 32, - fontWeight: FontWeight.w500, - color: Theme.of(context).colorScheme.onSurfaceVariant)) - ], - ), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 12.0), - child: Wrap( + ), + Row( + mainAxisAlignment: MainAxisAlignment.center, + spacing: 8, children: [ Text( - _bottomText, - textAlign: TextAlign.center, - style: TextStyle(color: Theme.of(context).colorScheme.onSurfaceVariant), + "Cake Wallet", + style: TextStyle(fontSize: 32, fontWeight: FontWeight.w500), ), + Text(widget.appVersion, + style: TextStyle( + fontSize: 32, + fontWeight: FontWeight.w500, + color: Theme.of(context).colorScheme.onSurfaceVariant)) ], ), - ), - ], - ), - SizedBox(height: 32), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 16.0), - child: NewListSections(sections: { + Padding( + padding: const EdgeInsets.symmetric(horizontal: 12.0), + child: Wrap( + children: [ + Text( + _bottomText, + textAlign: TextAlign.center, + style: TextStyle(color: Theme.of(context).colorScheme.onSurfaceVariant), + ), + ], + ), + ), + ], + ), + SizedBox(height: 32), + NewListSections(sections: { "": [ ListItemRegularRow( keyValue: "official website", @@ -146,10 +145,10 @@ class _AboutPageState extends State { foregroundColor: Theme.of(context).colorScheme.primary, trailingIconSize: 10) ] - }), - ) - ], - ) - ])); + }) + ], + ) + ])), + ); } } diff --git a/lib/new-ui/pages/home_page.dart b/lib/new-ui/pages/home_page.dart index 8da84dc50..c4fedb630 100644 --- a/lib/new-ui/pages/home_page.dart +++ b/lib/new-ui/pages/home_page.dart @@ -144,18 +144,21 @@ class _NewHomePageState extends State { ); }, ), + + ], + ), + ), Observer( builder: (_) => AssetsHistorySection( nftViewModel: widget.nftViewModel, dashboardViewModel: widget.dashboardViewModel, ), ), - SizedBox(height: 80.0) - ], - ), - ),] - ), - Container( + SliverToBoxAdapter( + child: SizedBox(height: 80.0), + ) + ]), + Container( height: (MediaQuery.of(context).padding.top), decoration: BoxDecoration( gradient: LinearGradient( diff --git a/lib/new-ui/pages/send_page.dart b/lib/new-ui/pages/send_page.dart index ad924f44c..ca6ba4142 100644 --- a/lib/new-ui/pages/send_page.dart +++ b/lib/new-ui/pages/send_page.dart @@ -449,6 +449,11 @@ class _NewSendPageState extends State { fiatInputMode: _fiatInputMode, onSwitchButtonPressed: () { setState(() { + if(!_fiatInputMode) { + widget.sendViewModel.outputs[_selectedOutput] + .cryptoAmount = + _amountControllers[_selectedOutput].text; + } _fiatInputMode = !_fiatInputMode; _amountControllers[_selectedOutput].text = _fiatInputMode diff --git a/lib/new-ui/pages/settings_page.dart b/lib/new-ui/pages/settings_page.dart index 470d745b2..76446f0f9 100644 --- a/lib/new-ui/pages/settings_page.dart +++ b/lib/new-ui/pages/settings_page.dart @@ -9,6 +9,7 @@ import 'package:cake_wallet/src/widgets/new_list_row/new_list_section.dart'; import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart'; import 'package:cw_core/wallet_info.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_svg/svg.dart'; import 'package:modal_bottom_sheet/modal_bottom_sheet.dart'; import "package:cw_core/wallet_type.dart"; @@ -18,8 +19,12 @@ bool _falseFunc(DashboardViewModel _) => false; bool _isBtc(DashboardViewModel vm) => vm.wallet.type == WalletType.bitcoin; +bool _hasLightning(DashboardViewModel vm) => vm.hasLightning; + bool _hasMweb(DashboardViewModel vm) => vm.hasMweb; +bool _hasWalletConnect(DashboardViewModel vm) => vm.hasWalletConnect; + bool _isCupcake(DashboardViewModel vm) => vm.wallet.hardwareWalletType == HardwareWalletType.cupcake; class SettingsListItem { @@ -42,21 +47,21 @@ class SettingsSectionData { const SettingsSectionData(this.title, this.titleIconPath, this.items); static SettingsSectionData walletSettings = - SettingsSectionData(S.current.wallet_settings, "assets/new-ui/wallet_settings.svg", [ + SettingsSectionData(S.current.wallet_settings, "assets/new-ui/wallet-setting.svg", [ SettingsListItem("assets/new-ui/settings_row_icons/nodes.svg", S.current.nodes, Routes.manageNodes), SettingsListItem("assets/new-ui/settings_row_icons/privacy.svg", S.current.privacy, Routes.privacyPage), SettingsListItem("assets/new-ui/settings_row_icons/seed.svg", S.current.seed_and_keys, Routes.showKeys, routeArgs: true, requireAuth: true, use2fa: (vm)=>vm.settingsStore.shouldRequireTOTP2FAForAllSecurityAndBackupSettings), - SettingsListItem("assets/new-ui/settings_row_icons/lightning_username.svg", - "Lightning ${S.current.username}", Routes.lightningUsernamePage, condition: _isBtc), - SettingsListItem("assets/new-ui/settings_row_icons/silent-payments.svg", S.current.silent_payments_settings, Routes.silentPaymentsSettings, condition: _isBtc), - SettingsListItem("assets/new-ui/settings_row_icons/mweb.svg", S.current.litecoin_mweb_settings, Routes.mwebSettings, condition: _hasMweb), + SettingsListItem("assets/new-ui/settings_row_icons/lightning_username.svg", "Lightning ${S.current.username}", Routes.lightningUsernamePage, condition: _hasLightning), + SettingsListItem("assets/new-ui/settings_row_icons/wc.svg", S.current.walletConnect, Routes.walletConnectConnectionsListing, condition: _hasWalletConnect), + //SettingsListItem("assets/new-ui/settings_row_icons/silent-payments.svg", S.current.silent_payments_settings, Routes.silentPaymentsSettings, condition: _isBtc), + //SettingsListItem("assets/new-ui/settings_row_icons/mweb.svg", S.current.litecoin_mweb_settings, Routes.mwebSettings, condition: _hasMweb), SettingsListItem("assets/new-ui/settings_row_icons/cupcake.svg", S.current.export_outputs, Routes.urqrAnimatedPage, routeArgs: {'export-outputs': 'export-outputs'}, condition: _isCupcake), SettingsListItem("assets/new-ui/settings_row_icons/other.svg", S.current.other, Routes.otherSettingsPage), ]); static SettingsSectionData appSettings = - SettingsSectionData(S.current.app_settings, "assets/new-ui/app_settings.svg", [ + SettingsSectionData(S.current.app_settings, "assets/new-ui/cake-setting.svg", [ SettingsListItem("assets/new-ui/settings_row_icons/connections.svg", S.current.connections, Routes.connectionSync), // SettingsListItem("assets/new-ui/settings_row_icons/defaults.svg", "Defaults", ""), SettingsListItem("assets/new-ui/settings_row_icons/display.svg", S.current.display, Routes.displaySettingsPage), @@ -97,29 +102,26 @@ class SettingsMainPage extends StatelessWidget { @override Widget build(BuildContext context) { - Map> sections = - Map.fromEntries(SettingsSectionData.all.map((section) => MapEntry( - section.title, - section.items - .map((item) => item.condition(dashboardViewModel) - ? ListItemRegularRow( - keyValue: item.title, - label: item.title, - iconPath: item.iconPath, - onTap: () { - if (item.route.isNotEmpty) { - if(item.requireAuth) { - authService.authenticateAction(context, - conditionToDetermineIfToUse2FA: item.use2fa(dashboardViewModel), - route: item.route); - } else { - Navigator.of(context).pushNamed(item.route, arguments: item.routeArgs); - } - } - }) - : null) - .whereType() - .toList()))); + List buildItems(SettingsSectionData section) => section.items + .map((item) => item.condition(dashboardViewModel) + ? ListItemRegularRow( + keyValue: item.title, + label: item.title, + iconPath: item.iconPath, + onTap: () { + if (item.route.isNotEmpty) { + if (item.requireAuth) { + authService.authenticateAction(context, + conditionToDetermineIfToUse2FA: item.use2fa(dashboardViewModel), + route: item.route); + } else { + Navigator.of(context).pushNamed(item.route, arguments: item.routeArgs); + } + } + }) + : null) + .whereType() + .toList(); return Container( color: Theme.of(context).colorScheme.surface, @@ -131,12 +133,31 @@ class SettingsMainPage extends StatelessWidget { onTrailingPressed: () {}, ), Expanded( - child: ListView( - controller: ModalScrollController.of(context), - children: [Padding( - padding: const EdgeInsets.symmetric(vertical: 20.0, horizontal: 20.0), - child: NewListSections( - sections: sections, + child: ListView(controller: ModalScrollController.of(context), children: [ + Padding( + padding: const EdgeInsets.symmetric(horizontal: 20.0), + child: Column( + spacing: 16, + children: SettingsSectionData.all.expand((section) { + return [ + if (section.titleIconPath.isNotEmpty) + Padding( + padding: const EdgeInsets.only(top: 8.0), + child: Row( + spacing: 8, + children: [ + SvgPicture.asset(section.titleIconPath, + colorFilter: ColorFilter.mode( + Theme.of(context).colorScheme.onSurfaceVariant, + BlendMode.srcIn, + )), + Text(section.title, style: Theme.of(context).textTheme.titleMedium), + ], + ), + ), + NewListSections(sections: {section.title: buildItems(section)}), + ]; + }).toList(), ), ),] ), diff --git a/lib/new-ui/pages/swap_page.dart b/lib/new-ui/pages/swap_page.dart index 0c533b358..0cd331351 100644 --- a/lib/new-ui/pages/swap_page.dart +++ b/lib/new-ui/pages/swap_page.dart @@ -480,133 +480,135 @@ class _NewSwapPageState extends State { mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisSize: MainAxisSize.max, children: [ - Form( - key: formKey, - child: Column( - children: [ - Observer( - builder: (_) => SwapAmountBox( - isReceiverCard: false, - walletSwitcherViewModel: widget.walletSwitcherViewModel, - exchangeViewModel: widget.exchangeViewModel, - onDispose: disposeBestRateSync, - hasAllAmount: widget.exchangeViewModel.hasAllAmount, - allAmount: widget.exchangeViewModel.hasAllAmount - ? () => widget.exchangeViewModel.enableSendAllAmount() - : null, - key: depositKey, - title: S.of(context).send, - initialCurrency: widget.exchangeViewModel.depositCurrency, - hasRefundAddress: true, - currencies: widget.exchangeViewModel.depositCurrencies, - onCurrencySelected: (currency) { - if (currency is CryptoCurrency) { - widget.exchangeViewModel - .changeDepositCurrency(currency: currency); - } - }, - currencyValueValidator: (value) { - return !widget.exchangeViewModel.isFixedRateMode && - value != S.of(context).all - ? AmountValidator( - isAutovalidate: true, - currency: widget.exchangeViewModel.isFixedRateMode - ? widget.exchangeViewModel.receiveCurrency - : widget.exchangeViewModel.depositCurrency, - minValue: widget.exchangeViewModel.limits.min.toString(), - maxValue: widget.exchangeViewModel.limits.max.toString(), - amountParsingProxy: - widget.exchangeViewModel.amountParsingProxy, - ).call(value) - : null; - }, - addressTextFieldValidator: AddressValidator( - type: widget.exchangeViewModel.depositCurrency), - onPushPasteButton: (context) async { - final clipboard = await Clipboard.getData('text/plain'); - widget.exchangeViewModel.depositAddress = clipboard?.text ?? ''; + RepaintBoundary( + child: Form( + key: formKey, + child: Column( + children: [ + Observer( + builder: (_) => SwapAmountBox( + isReceiverCard: false, + walletSwitcherViewModel: widget.walletSwitcherViewModel, + exchangeViewModel: widget.exchangeViewModel, + onDispose: disposeBestRateSync, + hasAllAmount: widget.exchangeViewModel.hasAllAmount, + allAmount: widget.exchangeViewModel.hasAllAmount + ? () => widget.exchangeViewModel.enableSendAllAmount() + : null, + key: depositKey, + title: S.of(context).send, + initialCurrency: widget.exchangeViewModel.depositCurrency, + hasRefundAddress: true, + currencies: widget.exchangeViewModel.depositCurrencies, + onCurrencySelected: (currency) { + if (currency is CryptoCurrency) { + widget.exchangeViewModel + .changeDepositCurrency(currency: currency); + } + }, + currencyValueValidator: (value) { + return !widget.exchangeViewModel.isFixedRateMode && + value != S.of(context).all + ? AmountValidator( + isAutovalidate: true, + currency: widget.exchangeViewModel.isFixedRateMode + ? widget.exchangeViewModel.receiveCurrency + : widget.exchangeViewModel.depositCurrency, + minValue: widget.exchangeViewModel.limits.min.toString(), + maxValue: widget.exchangeViewModel.limits.max.toString(), + amountParsingProxy: + widget.exchangeViewModel.amountParsingProxy, + ).call(value) + : null; + }, + addressTextFieldValidator: AddressValidator( + type: widget.exchangeViewModel.depositCurrency), + onPushPasteButton: (context) async { + final clipboard = await Clipboard.getData('text/plain'); + widget.exchangeViewModel.depositAddress = clipboard?.text ?? ''; - final domain = widget.exchangeViewModel.depositAddress; - widget.exchangeViewModel.depositAddress = - await fetchParsedAddress(context, domain, - widget.exchangeViewModel.depositCurrency); - }, - onPushAddressBookButton: (context) async { - final domain = widget.exchangeViewModel.depositAddress; - widget.exchangeViewModel.depositAddress = - await fetchParsedAddress(context, domain, - widget.exchangeViewModel.depositCurrency); - }, + final domain = widget.exchangeViewModel.depositAddress; + widget.exchangeViewModel.depositAddress = + await fetchParsedAddress(context, domain, + widget.exchangeViewModel.depositCurrency); + }, + onPushAddressBookButton: (context) async { + final domain = widget.exchangeViewModel.depositAddress; + widget.exchangeViewModel.depositAddress = + await fetchParsedAddress(context, domain, + widget.exchangeViewModel.depositCurrency); + }, + ), ), - ), - SwapLimitPopup(exchangeViewModel: widget.exchangeViewModel), - Padding( - padding: const EdgeInsets.symmetric(vertical: 12.0), - child: Stack( - alignment: Alignment.center, - children: [ - Container( - height: 1, - width: double.infinity, - color: Theme.of(context).colorScheme.surfaceContainerHigh, - ), - ModernButton.svg( - size: 36, - iconSize: 24, - svgPath: "assets/new-ui/swap_amounts.svg", - onPressed: widget.exchangeViewModel.reverseSwapDirection, - ), - ], + SwapLimitPopup(exchangeViewModel: widget.exchangeViewModel), + Padding( + padding: const EdgeInsets.symmetric(vertical: 12.0), + child: Stack( + alignment: Alignment.center, + children: [ + Container( + height: 1, + width: double.infinity, + color: Theme.of(context).colorScheme.surfaceContainerHigh, + ), + ModernButton.svg( + size: 36, + iconSize: 24, + svgPath: "assets/new-ui/swap_amounts.svg", + onPressed: widget.exchangeViewModel.reverseSwapDirection, + ), + ], + ), ), - ), - Observer( - builder: (_) => SwapAmountBox( - isReceiverCard: true, - walletSwitcherViewModel: widget.walletSwitcherViewModel, - exchangeViewModel: widget.exchangeViewModel, - onDispose: disposeBestRateSync, - key: receiveKey, - title: S.of(context).receive, - initialCurrency: widget.exchangeViewModel.receiveCurrency, - currencies: widget.exchangeViewModel.receiveCurrencies, - onCurrencySelected: (currency) { - if (currency is CryptoCurrency) { - widget.exchangeViewModel - .changeReceiveCurrency(currency: currency); - } - }, - currencyValueValidator: (value) { - return widget.exchangeViewModel.isFixedRateMode - ? AmountValidator( - isAutovalidate: true, - currency: widget.exchangeViewModel.receiveCurrency, - minValue: widget.exchangeViewModel.limits.min.toString(), - maxValue: widget.exchangeViewModel.limits.max.toString(), - amountParsingProxy: - widget.exchangeViewModel.amountParsingProxy, - ).call(value) - : null; - }, - addressTextFieldValidator: AddressValidator( - type: widget.exchangeViewModel.receiveCurrency), - onPushPasteButton: (context) async { - final clipboard = await Clipboard.getData('text/plain'); - widget.exchangeViewModel.receiveAddress = clipboard?.text ?? ''; + Observer( + builder: (_) => SwapAmountBox( + isReceiverCard: true, + walletSwitcherViewModel: widget.walletSwitcherViewModel, + exchangeViewModel: widget.exchangeViewModel, + onDispose: disposeBestRateSync, + key: receiveKey, + title: S.of(context).receive, + initialCurrency: widget.exchangeViewModel.receiveCurrency, + currencies: widget.exchangeViewModel.receiveCurrencies, + onCurrencySelected: (currency) { + if (currency is CryptoCurrency) { + widget.exchangeViewModel + .changeReceiveCurrency(currency: currency); + } + }, + currencyValueValidator: (value) { + return widget.exchangeViewModel.isFixedRateMode + ? AmountValidator( + isAutovalidate: true, + currency: widget.exchangeViewModel.receiveCurrency, + minValue: widget.exchangeViewModel.limits.min.toString(), + maxValue: widget.exchangeViewModel.limits.max.toString(), + amountParsingProxy: + widget.exchangeViewModel.amountParsingProxy, + ).call(value) + : null; + }, + addressTextFieldValidator: AddressValidator( + type: widget.exchangeViewModel.receiveCurrency), + onPushPasteButton: (context) async { + final clipboard = await Clipboard.getData('text/plain'); + widget.exchangeViewModel.receiveAddress = clipboard?.text ?? ''; - final domain = widget.exchangeViewModel.receiveAddress; - widget.exchangeViewModel.receiveAddress = - await fetchParsedAddress(context, domain, - widget.exchangeViewModel.receiveCurrency); - }, - onPushAddressBookButton: (context) async { - final domain = widget.exchangeViewModel.receiveAddress; - widget.exchangeViewModel.receiveAddress = - await fetchParsedAddress(context, domain, - widget.exchangeViewModel.receiveCurrency); - }, - ), - ) - ], + final domain = widget.exchangeViewModel.receiveAddress; + widget.exchangeViewModel.receiveAddress = + await fetchParsedAddress(context, domain, + widget.exchangeViewModel.receiveCurrency); + }, + onPushAddressBookButton: (context) async { + final domain = widget.exchangeViewModel.receiveAddress; + widget.exchangeViewModel.receiveAddress = + await fetchParsedAddress(context, domain, + widget.exchangeViewModel.receiveCurrency); + }, + ), + ) + ], + ), ), ), Observer( diff --git a/lib/new-ui/widgets/coins_page/assets_history/assets_history_section.dart b/lib/new-ui/widgets/coins_page/assets_history/assets_history_section.dart index df4e8253f..998ebc137 100644 --- a/lib/new-ui/widgets/coins_page/assets_history/assets_history_section.dart +++ b/lib/new-ui/widgets/coins_page/assets_history/assets_history_section.dart @@ -68,9 +68,9 @@ class _AssetsHistorySectionState extends State { @override Widget build(BuildContext context) { - return Column( - children: [ - if(tabs.length>1) + return SliverMainAxisGroup( + slivers: [ + // if(tabs.length>1) AssetsTopBar( dashboardViewModel: widget.dashboardViewModel, tabs: tabs.map((item) => item.title).toList(), diff --git a/lib/new-ui/widgets/coins_page/assets_history/assets_section.dart b/lib/new-ui/widgets/coins_page/assets_history/assets_section.dart index e2d15eea8..74990e294 100644 --- a/lib/new-ui/widgets/coins_page/assets_history/assets_section.dart +++ b/lib/new-ui/widgets/coins_page/assets_history/assets_section.dart @@ -15,45 +15,47 @@ class AssetsSection extends StatelessWidget { @override Widget build(BuildContext context) { - return Padding( - padding: const EdgeInsets.only(bottom: 64.0), - child: Observer( - builder: (context) { - final hasMweb = dashboardViewModel.hasMweb && dashboardViewModel.mwebEnabled; - return ListView.builder( - shrinkWrap: true, - padding: EdgeInsets.zero, - physics: NeverScrollableScrollPhysics(), - itemCount: dashboardViewModel.balanceViewModel.formattedBalances.length + (hasMweb ? 1 : 0), - itemBuilder: (context, index) { - return Observer(builder: (context) { - if (hasMweb) { + return SliverToBoxAdapter( + child: Padding( + padding: const EdgeInsets.only(bottom: 64.0), + child: Observer( + builder: (context) { + final hasMweb = dashboardViewModel.hasMweb && dashboardViewModel.mwebEnabled; + return ListView.builder( + shrinkWrap: true, + padding: EdgeInsets.zero, + physics: NeverScrollableScrollPhysics(), + itemCount: dashboardViewModel.balanceViewModel.formattedBalances.length + (hasMweb ? 1 : 0), + itemBuilder: (context, index) { + return Observer(builder: (context) { + if (hasMweb) { + return AssetTile( + showSwap: dashboardViewModel.isEnabledSwapAction, + balance: dashboardViewModel.balanceViewModel.formattedBalances.first, + showSecondary: index > 0 ? true : false, + wallet: dashboardViewModel.wallet, + chainIconPath: index > 0 ? dashboardViewModel.wallet.currency.chainIconPath! : "", + trailingText: index > 0 ? "MWEB" : null, + modalMode: index > 0 + ? AssetDetailsModalModes.ltcPrivate + : AssetDetailsModalModes.ltcTransparent, + title: index > 0 ? "Litecoin Private" : null, + ); + } + + final balance = dashboardViewModel.balanceViewModel.formattedBalances.elementAt(index); return AssetTile( showSwap: dashboardViewModel.isEnabledSwapAction, - balance: dashboardViewModel.balanceViewModel.formattedBalances.first, - showSecondary: index > 0 ? true : false, + balance: balance, wallet: dashboardViewModel.wallet, - chainIconPath: index > 0 ? dashboardViewModel.wallet.currency.chainIconPath! : "", - trailingText: index > 0 ? "MWEB" : null, - modalMode: index > 0 - ? AssetDetailsModalModes.ltcPrivate - : AssetDetailsModalModes.ltcTransparent, - title: index > 0 ? "Litecoin Private" : null, + chainIconPath: _getChainIconPath(), ); - } - - final balance = dashboardViewModel.balanceViewModel.formattedBalances.elementAt(index); - return AssetTile( - showSwap: dashboardViewModel.isEnabledSwapAction, - balance: balance, - wallet: dashboardViewModel.wallet, - chainIconPath: _getChainIconPath(), - ); - }); - - }, - ); - } + }); + + }, + ); + } + ), ), ); } diff --git a/lib/new-ui/widgets/coins_page/assets_history/assets_top_bar.dart b/lib/new-ui/widgets/coins_page/assets_history/assets_top_bar.dart index 0ca0aff5b..c2a19f5fb 100644 --- a/lib/new-ui/widgets/coins_page/assets_history/assets_top_bar.dart +++ b/lib/new-ui/widgets/coins_page/assets_history/assets_top_bar.dart @@ -1,6 +1,8 @@ import 'package:cake_wallet/generated/i18n.dart'; import 'package:cake_wallet/new-ui/widgets/line_tab_switcher.dart'; import 'package:cake_wallet/routes.dart'; +import 'package:cake_wallet/src/screens/dashboard/widgets/filter_widget.dart'; +import 'package:cake_wallet/utils/show_pop_up.dart'; import 'package:cake_wallet/src/widgets/cake_image_widget.dart'; import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart'; import 'package:flutter/material.dart'; @@ -21,28 +23,37 @@ class AssetsTopBar extends StatelessWidget { @override Widget build(BuildContext context) { - final hasTokenSettingsButton = tabs[selectedTab] == S.of(context).assets && dashboardViewModel.balanceViewModel.isHomeScreenSettingsEnabled; + final settingsButtonText = _getSettingsButtonText(); + final hasTokenSettingsButton = settingsButtonText != null; - return Padding( - padding: const EdgeInsets.only(top: 12.0, bottom: 12.0, left: 12.0, right: 18.0), - child: Row( + return SliverToBoxAdapter( + child: Padding( + padding: const EdgeInsets.only(top: 12.0, bottom: 12.0, left: 12.0, right: 18.0), + child: Row( crossAxisAlignment: CrossAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ + if(tabs.length > 1) LineTabSwitcher( tabs: tabs, onTabChange: onTabChange, selectedTab: selectedTab, - ), + ) + else SizedBox.shrink(), Opacity( opacity: hasTokenSettingsButton ? 1 : 0, child: ElevatedButton( onPressed: () { - if (hasTokenSettingsButton) { + if (tabs[selectedTab] == S.of(context).assets) { Navigator.of(context).pushNamed( Routes.homeSettings, arguments: dashboardViewModel.balanceViewModel, ); + } else if(tabs[selectedTab] == S.of(context).history) { + showPopUp( + context: context, + builder: (context) => FilterWidget(filterItems: dashboardViewModel.filterItems), + ); } }, style: ElevatedButton.styleFrom( @@ -64,17 +75,28 @@ class AssetsTopBar extends StatelessWidget { colorFilter: ColorFilter.mode( Theme.of(context).colorScheme.primary, BlendMode.srcIn)), Text( - S.of(context).tokens, + settingsButtonText??"", style: TextStyle(color: Theme.of(context).colorScheme.primary), ) ], ), ), ), - ), - - ], + ), + ], + ), ), ); } + + String? _getSettingsButtonText() { + if (tabs[selectedTab] == S.current.assets && + dashboardViewModel.balanceViewModel.isHomeScreenSettingsEnabled) { + return S.current.tokens; + } + + if (tabs[selectedTab] == S.current.history) { + return S.current.filters; + } + } } diff --git a/lib/new-ui/widgets/coins_page/assets_history/history_section.dart b/lib/new-ui/widgets/coins_page/assets_history/history_section.dart index fd1617fec..8ee960838 100644 --- a/lib/new-ui/widgets/coins_page/assets_history/history_section.dart +++ b/lib/new-ui/widgets/coins_page/assets_history/history_section.dart @@ -27,26 +27,29 @@ class HistorySection extends StatelessWidget { @override Widget build(BuildContext context) { - return Padding( - padding: const EdgeInsets.symmetric(horizontal: 24.0), - child: Observer( - builder: (_) => (dashboardViewModel.items.isEmpty && - dashboardViewModel.status is! SyncingSyncStatus) - ? Padding( - padding: EdgeInsets.only(top: 24), - child: Text(S.of(context).transactions_will_appear_here, - style: TextStyle( - fontSize: 14, - fontWeight: FontWeight.w400, - color: Theme.of(context).colorScheme.onSurfaceVariant))) - : ListView.builder( - physics: const NeverScrollableScrollPhysics(), - padding: EdgeInsets.zero, - shrinkWrap: true, - itemCount: dashboardViewModel.items.length, - itemBuilder: (context, index) => Observer(builder: (_) { - final prevItem = index == 0 ? null : dashboardViewModel.items[index - 1]; - final item = dashboardViewModel.items[index]; + return SliverPadding( + padding: const EdgeInsets.symmetric(horizontal: 24.0), + sliver: Observer( + builder: (_) => (dashboardViewModel.items.isEmpty && + dashboardViewModel.status is! SyncingSyncStatus) + ? SliverPadding( + padding: EdgeInsets.only(top: 24), + sliver: SliverToBoxAdapter( + child: Center( + child: Text(S.of(context).transactions_will_appear_here, + style: TextStyle( + fontSize: 14, + fontWeight: FontWeight.w400, + color: Theme.of(context).colorScheme.onSurfaceVariant)), + ), + ), + ) + : SliverList( + delegate: SliverChildBuilderDelegate( + childCount: dashboardViewModel.items.length, + (context, index) => Observer(builder: (_) { + final prevItem = index == 0 ? null : dashboardViewModel.items[index - 1]; + final item = dashboardViewModel.items[index]; final nextItem = index == dashboardViewModel.items.length - 1 ? null : dashboardViewModel.items[index + 1]; @@ -163,6 +166,6 @@ class HistorySection extends StatelessWidget { }), ), ), - ); + )); } } diff --git a/lib/new-ui/widgets/coins_page/assets_history/history_tile.dart b/lib/new-ui/widgets/coins_page/assets_history/history_tile.dart index ef4ed615a..3fcbc8dd8 100644 --- a/lib/new-ui/widgets/coins_page/assets_history/history_tile.dart +++ b/lib/new-ui/widgets/coins_page/assets_history/history_tile.dart @@ -63,7 +63,7 @@ class HistoryTile extends StatelessWidget { return Stack( children: [ CakeImageWidget(imageUrl: - "assets/images/lightning-icon.svg", + "assets/new-ui/lightning-icon.svg", width: 34, height: 34, ), diff --git a/lib/new-ui/widgets/coins_page/top_bar_widget/sync_bar.dart b/lib/new-ui/widgets/coins_page/top_bar_widget/sync_bar.dart index 2c55d97fc..45e28bbbd 100644 --- a/lib/new-ui/widgets/coins_page/top_bar_widget/sync_bar.dart +++ b/lib/new-ui/widgets/coins_page/top_bar_widget/sync_bar.dart @@ -1,12 +1,14 @@ import 'package:cake_wallet/core/sync_status_title.dart'; +import 'package:cake_wallet/di.dart'; import 'package:cake_wallet/new-ui/widgets/coins_page/top_bar_widget/pulsing_dot.dart'; -import 'package:cake_wallet/routes.dart'; +import 'package:cake_wallet/src/screens/settings/manage_nodes_page.dart'; import 'package:cake_wallet/src/widgets/cake_image_widget.dart'; import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart'; import 'package:cw_core/sync_status.dart'; import 'package:flutter/material.dart'; import 'package:flutter_mobx/flutter_mobx.dart'; import 'package:flutter_svg/flutter_svg.dart'; +import 'package:modal_bottom_sheet/modal_bottom_sheet.dart'; class SyncBar extends StatelessWidget { SyncBar({super.key, required this.dashboardViewModel, required this.isSyncHeavy}); @@ -62,7 +64,13 @@ class SyncBar extends StatelessWidget { if (_showFullBar()) GestureDetector( onTap: () { - Navigator.of(context, rootNavigator: true).pushNamed(Routes.connectionSync); + CupertinoScaffold.showCupertinoModalBottomSheet( + context: context, + barrierColor: Colors.black.withAlpha(85), + builder: (context) => FractionallySizedBox( + child: Material( + child: getIt.get(param1: false), + ))); }, child: AnimatedSwitcher( duration: Duration(milliseconds: 100), @@ -162,7 +170,7 @@ class SyncBar extends StatelessWidget { children.add(CakeImageWidget(imageUrl: "assets/new-ui/mweb_sync.svg", colorFilter: - ColorFilter.mode(Theme.of(context).colorScheme.onSurfaceVariant, BlendMode.srcIn), + ColorFilter.mode(Theme.of(context).colorScheme.onSurfaceVariant, BlendMode.srcIn), )); } if (dashboardViewModel.hasSilentPayments) { @@ -186,4 +194,4 @@ class SyncBar extends StatelessWidget { bool _showDot() { return !isSyncHeavy && progressStatuses.contains(dashboardViewModel.status.runtimeType); } -} +} \ No newline at end of file diff --git a/lib/new-ui/widgets/modal_page_wrapper.dart b/lib/new-ui/widgets/modal_page_wrapper.dart new file mode 100644 index 000000000..717b2078c --- /dev/null +++ b/lib/new-ui/widgets/modal_page_wrapper.dart @@ -0,0 +1,88 @@ +import 'package:cake_wallet/new-ui/widgets/modal_header.dart'; +import 'package:cake_wallet/new-ui/widgets/receive_page/receive_top_bar.dart'; +import 'package:flutter/material.dart'; +import 'package:modal_bottom_sheet/modal_bottom_sheet.dart'; + +void nothing(){} + +class ModalPageWrapper extends StatelessWidget { + ModalPageWrapper( + {super.key, + required this.content, + required this.topBar, + this.header, + this.bottomContent, + this.horizontalPadding = 18.0, + this.verticalPadding = 72.0}) { + } + + final Widget content; + final Widget? bottomContent; + final ModalTopBar topBar; + final ModalHeader? header; + final double horizontalPadding; + final double verticalPadding; + + @override + Widget build(BuildContext context) { + const gradientSize = 84; + + return Container( + color: Theme.of(context).colorScheme.surface, + child: Scaffold( + resizeToAvoidBottomInset: true, + body: Stack( + alignment: Alignment.topCenter, + children: [ + Positioned.fill( + child: Padding( + padding: EdgeInsets.symmetric(horizontal: horizontalPadding), + child: CustomScrollView( + controller: ModalScrollController.of(context), + physics: ClampingScrollPhysics(), + slivers: [ + SliverToBoxAdapter(child: SizedBox(height: verticalPadding)), + if (header != null) ...[ + SliverToBoxAdapter(child: header), + SliverToBoxAdapter(child: SizedBox(height: 20)), + ], + SliverSafeArea(sliver: SliverToBoxAdapter(child: content)), + SliverToBoxAdapter(child: SizedBox(height: verticalPadding)), + ], + ) + )), + if (bottomContent != null) + Positioned( + bottom: 12, + left: horizontalPadding, + right: horizontalPadding, + child: SafeArea(child: bottomContent!), + ), + Container( + height: (MediaQuery.of(context).padding.top + gradientSize), + decoration: BoxDecoration( + gradient: LinearGradient( + begin: Alignment.bottomCenter, + end: Alignment.topCenter, + colors: [ + Theme.of(context).colorScheme.surface.withAlpha(5), + Theme.of(context).colorScheme.surface.withAlpha(25), + Theme.of(context).colorScheme.surface.withAlpha(50), + Theme.of(context).colorScheme.surface.withAlpha(100), + Theme.of(context).colorScheme.surface.withAlpha(150), + Theme.of(context).colorScheme.surface.withAlpha(200), + Theme.of(context).colorScheme.surface.withAlpha(235), + Theme.of(context).colorScheme.surface.withAlpha(255), + Theme.of(context).colorScheme.surface.withAlpha(255), + Theme.of(context).colorScheme.surface.withAlpha(255), + ], + ), + ), + ), + topBar, + ], + ), + ), + ); + } +} diff --git a/lib/new-ui/widgets/receive_page/receive_qr_code.dart b/lib/new-ui/widgets/receive_page/receive_qr_code.dart index b5428421c..e6f4ba5d1 100644 --- a/lib/new-ui/widgets/receive_page/receive_qr_code.dart +++ b/lib/new-ui/widgets/receive_page/receive_qr_code.dart @@ -6,7 +6,6 @@ import 'package:cake_wallet/view_model/wallet_address_list/wallet_address_list_v import 'package:cw_core/crypto_currency.dart'; import 'package:flutter/material.dart'; import 'package:flutter_mobx/flutter_mobx.dart'; -import 'package:flutter_svg/flutter_svg.dart'; import '../../../src/screens/receive/widgets/qr_image.dart'; @@ -22,40 +21,57 @@ class ReceiveQrCode extends StatelessWidget { final bool largeQrMode; final WalletAddressListViewModel addressListViewModel; - static const double largeQrModeBottomPadding = 70; + static const double largeQrModeBottomPadding = 140; + static const Duration animDuration = Duration(milliseconds: 500); final bool isLightMode = !(getIt.get().currentTheme.isDark); + @override Widget build(BuildContext context) { - final double targetY = largeQrMode ? 60 : 0; - final double resolvedSize = MediaQuery.of(context).size.width * (largeQrMode ? 0.85 : 0.5); + final double targetY = largeQrMode ? largeQrModeBottomPadding+50 : 0; + final double resolvedSize = MediaQuery.of(context).size.width * 0.5; + final double resolvedScale = largeQrMode ? 1.7 : 1; return Stack( alignment: Alignment.topCenter, children: [ - AnimatedOpacity( - duration: Duration(milliseconds: 300), - opacity: largeQrMode ? 1 : 0, - child: CakeImageWidget(imageUrl: - isLightMode - ? "assets/new-ui/cakewallet-wordmark-light.svg" - : "assets/new-ui/cakewallet-wordmark.svg", - height: 45, - )), + AnimatedSlide( + curve: Curves.easeOutCubic, + duration: animDuration, + offset: largeQrMode ? Offset(0, -1) : Offset.zero, + child: AnimatedOpacity( + duration: animDuration, + opacity: largeQrMode ? 1 : 0, + child: CakeImageWidget(imageUrl: + isLightMode + ? "assets/new-ui/cakewallet-wordmark-light.svg" + : "assets/new-ui/cakewallet-wordmark.svg", + height: 45, + )), + ), GestureDetector( onTap: onTap, + behavior: HitTestBehavior.opaque, child: TweenAnimationBuilder( - tween: Tween(begin: 0, end: targetY), - duration: const Duration(milliseconds: 400), - curve: Curves.easeOut, - builder: (context, value, child) { - return Transform.translate( - offset: Offset(0, value), - child: Observer( - builder: (_)=>Column( - children: [ - AnimatedContainer( - duration: const Duration(milliseconds: 400), + tween: Tween(begin: 0, end: targetY), + duration: animDuration, + curve: Curves.easeOutCubic, + builder: (context, value, child) { + return Transform.translate( + offset: Offset(0, value), + child: child, + ); + }, + child: Observer( + builder: (_) => Column( + children: [ + AnimatedScale( + curve: Curves.easeOutCubic, + alignment: Alignment.bottomCenter, + scale: resolvedScale, + duration: animDuration, + child: AnimatedContainer( + duration: animDuration, curve: Curves.easeOutCubic, width: resolvedSize, height: resolvedSize, @@ -71,49 +87,47 @@ class ReceiveQrCode extends StatelessWidget { : Colors.transparent), child: ClipRRect( borderRadius: BorderRadius.circular(20), - child: LayoutBuilder( - builder: (context, constraints) => Observer( - builder: (_) => QrImage( - data: addressListViewModel.uri.toString(), - embeddedImagePath: - addressListViewModel.tokenCurrency != null - ? addressListViewModel.tokenCurrency == CryptoCurrency.btcln - ? addressListViewModel.qrImage - : addressListViewModel.tokenCurrency!.iconPath - : addressListViewModel.qrImage, - size: constraints.maxWidth)))), + child: Observer( + builder: (_) => QrImage( + data: addressListViewModel.uri.toString(), + size: resolvedSize, + embeddedImagePath: + addressListViewModel.tokenCurrency != null + ? addressListViewModel.tokenCurrency == + CryptoCurrency.btcln + ? addressListViewModel.qrImage + : addressListViewModel.tokenCurrency!.iconPath + : addressListViewModel.qrImage, + ))), ), ), - if (addressListViewModel.hasPayjoin) - Opacity( - opacity: largeQrMode ? 0 : 1, - child: Container( - width:resolvedSize, - decoration: BoxDecoration( - borderRadius: - BorderRadius.vertical(bottom: Radius.circular(16)), - color: Theme.of(context).colorScheme.surfaceContainer), - child: Padding( - padding: const EdgeInsets.symmetric(vertical: 2.0), - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - spacing: 4, - children: [ - CakeImageWidget(imageUrl: "assets/new-ui/payjoin.svg"), - Text(S.of(context).payjoin_enabled) - ], - ), - ))), - AnimatedSize( - duration: Duration(milliseconds: 300), - // curve: Curves.easeOutCubic, - child: SizedBox(height: largeQrMode ? largeQrModeBottomPadding : 0)) - ], - ), + ), + if (addressListViewModel.hasPayjoin) + Opacity( + opacity: largeQrMode ? 0 : 1, + child: Container( + width: resolvedSize, + decoration: BoxDecoration( + borderRadius: BorderRadius.vertical(bottom: Radius.circular(16)), + color: Theme.of(context).colorScheme.surfaceContainer), + child: Padding( + padding: const EdgeInsets.symmetric(vertical: 2.0), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + spacing: 4, + children: [ + CakeImageWidget(imageUrl:"assets/new-ui/payjoin.svg"), + Text(S.of(context).payjoin_enabled) + ], + ), + ))), + AnimatedSize( + duration: animDuration, + curve: Curves.easeOutCubic, + child: SizedBox(height: largeQrMode ? largeQrModeBottomPadding+40 : 0)) + ], ), - ); - }, - ), + )), ), ], ); diff --git a/lib/new-ui/widgets/receive_page/receive_top_bar.dart b/lib/new-ui/widgets/receive_page/receive_top_bar.dart index f92f00e7e..9de76d6e1 100644 --- a/lib/new-ui/widgets/receive_page/receive_top_bar.dart +++ b/lib/new-ui/widgets/receive_page/receive_top_bar.dart @@ -6,14 +6,14 @@ void nothing(){} class ModalTopBar extends StatelessWidget { ModalTopBar( {super.key, - required this.title, + required this.title, this.subtitle, - this.onLeadingPressed=nothing, - this.onTrailingPressed=nothing, - this.leadingIcon, - this.trailingIcon, - this.leadingWidget, - this.trailingWidget}) { + this.onLeadingPressed=nothing, + this.onTrailingPressed=nothing, + this.leadingIcon, + this.trailingIcon, + this.leadingWidget, + this.trailingWidget}) { if(leadingIcon != null && leadingWidget != null) { throw Exception("Cannot have both leadingIcon and leadingWidget"); } @@ -38,10 +38,10 @@ class ModalTopBar extends StatelessWidget { return Padding( padding: const EdgeInsets.all(18), child: Stack( - alignment: Alignment.center, + alignment: Alignment.topCenter, children: [ - Align( - alignment: Alignment.center, + Positioned( + top: 6, child: Row( mainAxisSize: MainAxisSize.max, mainAxisAlignment: MainAxisAlignment.center, @@ -72,11 +72,11 @@ class ModalTopBar extends StatelessWidget { if (leadingIcon != null || leadingWidget != null) leadingIcon != null ? ModernButton( - key: ValueKey(leadingIcon.hashCode), - size: buttonSize, - onPressed: onLeadingPressed, - icon: leadingIcon!, - iconColor: Theme.of(context).colorScheme.onSurfaceVariant) + key: ValueKey(leadingIcon.hashCode), + size: buttonSize, + onPressed: onLeadingPressed, + icon: leadingIcon!, + iconColor: Theme.of(context).colorScheme.onSurfaceVariant) : leadingWidget! else Container(width: buttonSize), @@ -85,10 +85,10 @@ class ModalTopBar extends StatelessWidget { duration: const Duration(milliseconds: 200), child: trailingIcon != null ? ModernButton( - key: ValueKey(trailingIcon.hashCode), - size: buttonSize, - onPressed: onTrailingPressed, - icon: trailingIcon!,) + key: ValueKey(trailingIcon.hashCode), + size: buttonSize, + onPressed: onTrailingPressed, + icon: trailingIcon!,) : trailingWidget!, ) else @@ -99,4 +99,4 @@ class ModalTopBar extends StatelessWidget { ), ); } -} +} \ No newline at end of file diff --git a/lib/src/screens/base_page.dart b/lib/src/screens/base_page.dart index e5f3781e1..4602c92b6 100644 --- a/lib/src/screens/base_page.dart +++ b/lib/src/screens/base_page.dart @@ -47,6 +47,8 @@ abstract class BasePage extends StatelessWidget { AppBarStyle get appBarStyle => AppBarStyle.regular; + bool get hideAppBar => false; + Widget Function(BuildContext, Widget)? get rootWrapper => null; MaterialThemeBase get currentTheme => getIt.get().currentTheme; @@ -191,24 +193,21 @@ abstract class BasePage extends StatelessWidget { : null, // color: Colors.grey[200], ), - child: Padding( - padding: const EdgeInsets.only(top:6.0), - child: Scaffold( - key: _scaffoldKey, - backgroundColor: pageBackgroundColor(context), - resizeToAvoidBottomInset: resizeToAvoidBottomInset, - extendBodyBehindAppBar: extendBodyBehindAppBar, - endDrawer: endDrawer, - appBar: appBar(context), - body: SafeArea( - left: false, - right: false, - top: false, - bottom: Platform.isAndroid, - child: body(context), - ), - floatingActionButton: floatingActionButton(context), + child: Scaffold( + key: _scaffoldKey, + backgroundColor: pageBackgroundColor(context), + resizeToAvoidBottomInset: resizeToAvoidBottomInset, + extendBodyBehindAppBar: extendBodyBehindAppBar, + endDrawer: endDrawer, + appBar: hideAppBar ? null : appBar(context), + body: SafeArea( + left: false, + right: false, + top: false, + bottom: Platform.isAndroid, + child: body(context), ), + floatingActionButton: floatingActionButton(context), ), ); }, diff --git a/lib/src/screens/dashboard/pages/nft_listing_page.dart b/lib/src/screens/dashboard/pages/nft_listing_page.dart index c59300860..616c7bdfc 100644 --- a/lib/src/screens/dashboard/pages/nft_listing_page.dart +++ b/lib/src/screens/dashboard/pages/nft_listing_page.dart @@ -31,38 +31,40 @@ class _NFTListingPageState extends State { @override Widget build(BuildContext context) { - return Observer( - builder: (context) { - return Column( - children: [ - const SizedBox(height: 16), - Padding( - padding: const EdgeInsets.only(left: 16, right: 16, bottom: 16), - child: PrimaryButton( - text: S.current.import, - color: Theme.of(context).colorScheme.surfaceContainer, - textColor: Theme.of(context).colorScheme.onSecondaryContainer, - onPressed: () => Navigator.pushNamed( - context, - Routes.importNFTPage, - arguments: widget.nftViewModel, - ), - ), - ), - if (widget.nftViewModel.isLoading) - Center( - child: CircularProgressIndicator( - backgroundColor: Theme.of(context).colorScheme.surfaceContainerHighest, - valueColor: AlwaysStoppedAnimation( - Theme.of(context).colorScheme.primary, + return SliverToBoxAdapter( + child: Observer( + builder: (context) { + return Column( + children: [ + const SizedBox(height: 16), + Padding( + padding: const EdgeInsets.only(left: 16, right: 16, bottom: 16), + child: PrimaryButton( + text: S.current.import, + color: Theme.of(context).colorScheme.surfaceContainer, + textColor: Theme.of(context).colorScheme.onSecondaryContainer, + onPressed: () => Navigator.pushNamed( + context, + Routes.importNFTPage, + arguments: widget.nftViewModel, ), ), - ) - else - NFTListWidget(nftViewModel: widget.nftViewModel) - ], - ); - }, + ), + if (widget.nftViewModel.isLoading) + Center( + child: CircularProgressIndicator( + backgroundColor: Theme.of(context).colorScheme.surfaceContainerHighest, + valueColor: AlwaysStoppedAnimation( + Theme.of(context).colorScheme.primary, + ), + ), + ) + else + NFTListWidget(nftViewModel: widget.nftViewModel) + ], + ); + }, + ), ); } } diff --git a/lib/src/screens/nodes/widgets/node_form.dart b/lib/src/screens/nodes/widgets/node_form.dart index 96ef7d6df..525252d89 100644 --- a/lib/src/screens/nodes/widgets/node_form.dart +++ b/lib/src/screens/nodes/widgets/node_form.dart @@ -5,6 +5,7 @@ import 'package:cake_wallet/entities/new_ui_entities/list_item/list_Item_checkbo import 'package:cake_wallet/entities/new_ui_entities/list_item/list_item_text_field.dart'; import 'package:cake_wallet/generated/i18n.dart'; import 'package:cake_wallet/src/widgets/new_list_row/new_list_section.dart'; +import 'package:cake_wallet/utils/feature_flag.dart'; import 'package:cake_wallet/view_model/node_list/node_create_or_edit_view_model.dart'; import 'package:flutter/material.dart'; import 'package:flutter_mobx/flutter_mobx.dart'; @@ -140,6 +141,7 @@ class NodeFormState extends State { initialValue: vm.socksProxyAddress, validator: SocksProxyNodeAddressValidator(), ), + if(FeatureFlag.isAutomaticNodeSwitchingEnabled) ListItemCheckbox( keyValue: vm.autoSwitchingUIKey, label: S.current.enable_for_auto_switching, diff --git a/lib/src/screens/nodes/widgets/node_list_row.dart b/lib/src/screens/nodes/widgets/node_list_row.dart index 25d47775d..2e43ad3ff 100644 --- a/lib/src/screens/nodes/widgets/node_list_row.dart +++ b/lib/src/screens/nodes/widgets/node_list_row.dart @@ -7,11 +7,12 @@ import 'package:flutter/material.dart'; class NodeListRow extends StandardListRow { NodeListRow( {required String title, + String? subtitle, required this.node, required void Function(BuildContext context) onTap, required bool isSelected, required this.isPow}) - : super(title: title, onTap: onTap, isSelected: isSelected); + : super(title: title, onTap: onTap, isSelected: isSelected, subtitle: subtitle); final Node node; final bool isPow; diff --git a/lib/src/screens/send/send_page.dart b/lib/src/screens/send/send_page.dart index fc98e9eca..7e44e413d 100644 --- a/lib/src/screens/send/send_page.dart +++ b/lib/src/screens/send/send_page.dart @@ -227,262 +227,264 @@ class SendPage extends BasePage { !sendViewModel.balanceViewModel.isReversing, onLongPressUp: () => sendViewModel.balanceViewModel.isReversing = !sendViewModel.balanceViewModel.isReversing, - child: Form( - key: _formKey, - child: ScrollableWithBottomSection( - contentPadding: EdgeInsets.only(bottom: 24), - content: FocusTraversalGroup( - policy: OrderedTraversalPolicy(), - child: Column( - children: [ - PageViewHeightAdaptable( - controller: controller, - children: sendCards, - ), - SizedBox(height: 10), - Padding( - padding: EdgeInsets.only(left: 24, right: 24, bottom: 10), - child: Container( - height: 10, - child: Observer( - builder: (_) { - final count = sendViewModel.outputs.length; - - return count > 1 - ? Semantics( - label: 'Page Indicator', - hint: 'Swipe to change receiver', - excludeSemantics: true, - child: SmoothPageIndicator( - controller: controller, - count: count, - effect: ScrollingDotsEffect( - spacing: 6.0, - radius: 6.0, - dotWidth: 6.0, - dotHeight: 6.0, - dotColor: Theme.of(context) - .colorScheme - .primary - .withAlpha(100), - activeDotColor: Theme.of(context).colorScheme.primary, + child: RepaintBoundary( + child: Form( + key: _formKey, + child: ScrollableWithBottomSection( + contentPadding: EdgeInsets.only(bottom: 24), + content: FocusTraversalGroup( + policy: OrderedTraversalPolicy(), + child: Column( + children: [ + PageViewHeightAdaptable( + controller: controller, + children: sendCards, + ), + SizedBox(height: 10), + Padding( + padding: EdgeInsets.only(left: 24, right: 24, bottom: 10), + child: Container( + height: 10, + child: Observer( + builder: (_) { + final count = sendViewModel.outputs.length; + + return count > 1 + ? Semantics( + label: 'Page Indicator', + hint: 'Swipe to change receiver', + excludeSemantics: true, + child: SmoothPageIndicator( + controller: controller, + count: count, + effect: ScrollingDotsEffect( + spacing: 6.0, + radius: 6.0, + dotWidth: 6.0, + dotHeight: 6.0, + dotColor: Theme.of(context) + .colorScheme + .primary + .withAlpha(100), + activeDotColor: Theme.of(context).colorScheme.primary, + ), ), - ), - ) - : Offstage(); - }, + ) + : Offstage(); + }, + ), ), ), - ), - Container( - height: 40, - width: double.infinity, - padding: EdgeInsets.only(left: 24), - child: SingleChildScrollView( - scrollDirection: Axis.horizontal, - child: Observer( - builder: (_) { - final templates = sendViewModel.templates; - final itemCount = templates.length; - - return Row( - children: [ - AddTemplateButton( - key: ValueKey('send_page_add_template_button_key'), - onTap: () => - Navigator.of(context).pushNamed(Routes.sendTemplate), - currentTemplatesLength: templates.length, - ), - ListView.builder( - scrollDirection: Axis.horizontal, - shrinkWrap: true, - physics: NeverScrollableScrollPhysics(), - itemCount: itemCount, - itemBuilder: (context, index) { - final template = templates[index]; - return TemplateTile( - key: UniqueKey(), - to: template.name, - hasMultipleRecipients: - template.additionalRecipients != null && - template.additionalRecipients!.length > 1, - amount: template.isCurrencySelected - ? template.amount - : template.amountFiat, - from: template.isCurrencySelected - ? template.cryptoCurrency - : template.fiatCurrency, - onTap: () async { - sendViewModel.state = LoadingTemplateExecutingState(); - if (template.additionalRecipients?.isNotEmpty ?? - false) { - sendViewModel.clearOutputs(); - - for (int i = 0; - i < template.additionalRecipients!.length; - i++) { - Output output; - try { - output = sendViewModel.outputs[i]; - } catch (e) { - sendViewModel.addOutput(); - output = sendViewModel.outputs[i]; + Container( + height: 40, + width: double.infinity, + padding: EdgeInsets.only(left: 24), + child: SingleChildScrollView( + scrollDirection: Axis.horizontal, + child: Observer( + builder: (_) { + final templates = sendViewModel.templates; + final itemCount = templates.length; + + return Row( + children: [ + AddTemplateButton( + key: ValueKey('send_page_add_template_button_key'), + onTap: () => + Navigator.of(context).pushNamed(Routes.sendTemplate), + currentTemplatesLength: templates.length, + ), + ListView.builder( + scrollDirection: Axis.horizontal, + shrinkWrap: true, + physics: NeverScrollableScrollPhysics(), + itemCount: itemCount, + itemBuilder: (context, index) { + final template = templates[index]; + return TemplateTile( + key: UniqueKey(), + to: template.name, + hasMultipleRecipients: + template.additionalRecipients != null && + template.additionalRecipients!.length > 1, + amount: template.isCurrencySelected + ? template.amount + : template.amountFiat, + from: template.isCurrencySelected + ? template.cryptoCurrency + : template.fiatCurrency, + onTap: () async { + sendViewModel.state = LoadingTemplateExecutingState(); + if (template.additionalRecipients?.isNotEmpty ?? + false) { + sendViewModel.clearOutputs(); + + for (int i = 0; + i < template.additionalRecipients!.length; + i++) { + Output output; + try { + output = sendViewModel.outputs[i]; + } catch (e) { + sendViewModel.addOutput(); + output = sendViewModel.outputs[i]; + } + + await _setInputsFromTemplate( + context, + output: output, + template: template.additionalRecipients![i], + ); } - + } else { + final output = _defineCurrentOutput(); await _setInputsFromTemplate( context, output: output, - template: template.additionalRecipients![i], + template: template, ); } - } else { - final output = _defineCurrentOutput(); - await _setInputsFromTemplate( - context, - output: output, - template: template, + sendViewModel.state = InitialExecutionState(); + }, + onRemove: () { + showPopUp( + context: context, + builder: (dialogContext) { + return AlertWithTwoActions( + alertTitle: S.of(context).template, + alertContent: + S.of(context).confirm_delete_template, + rightButtonText: S.of(context).delete, + leftButtonText: S.of(context).cancel, + actionRightButton: () { + Navigator.of(dialogContext).pop(); + sendViewModel.sendTemplateViewModel + .removeTemplate(template: template); + }, + actionLeftButton: () => + Navigator.of(dialogContext).pop()); + }, ); - } - sendViewModel.state = InitialExecutionState(); - }, - onRemove: () { - showPopUp( - context: context, - builder: (dialogContext) { - return AlertWithTwoActions( - alertTitle: S.of(context).template, - alertContent: - S.of(context).confirm_delete_template, - rightButtonText: S.of(context).delete, - leftButtonText: S.of(context).cancel, - actionRightButton: () { - Navigator.of(dialogContext).pop(); - sendViewModel.sendTemplateViewModel - .removeTemplate(template: template); - }, - actionLeftButton: () => - Navigator.of(dialogContext).pop()); - }, - ); - }, - ); - }, - ), - ], - ); - }, + }, + ); + }, + ), + ], + ); + }, + ), ), ), - ), - ], + ], + ), ), - ), - bottomSectionPadding: EdgeInsets.only(left: 24, right: 24, bottom: 24), - bottomSection: Column( - children: [ - if (sendViewModel.sendTemplateViewModel.hasMultiRecipient) - Padding( - padding: EdgeInsets.only(bottom: 12), - child: PrimaryButton( - key: ValueKey('send_page_add_receiver_button_key'), - onPressed: () { - sendViewModel.addOutput(); - Future.delayed(const Duration(milliseconds: 250), () { - controller.jumpToPage(sendViewModel.outputs.length - 1); - }); - }, - text: S.of(context).add_receiver, - color: Colors.transparent, - textColor: Theme.of(context).colorScheme.onSurfaceVariant, - isDottedBorder: true, - borderColor: Theme.of(context).colorScheme.outline, - )), - Observer( - builder: (_) { - return LoadingPrimaryButton( - key: ValueKey('send_page_send_button_key'), - onPressed: () async { - //Request dummy node to get the focus out of the text fields - FocusScope.of(context).requestFocus(FocusNode()); - - if (sendViewModel.state is IsExecutingState) return; - if (_formKey.currentState != null && - !_formKey.currentState!.validate()) { - if (sendViewModel.outputs.length > 1) { - showErrorValidationAlert(context); - } - - return; - } - - final hasInvalidItems = sendViewModel.outputs.any((item) => - item.address.isEmpty || - (item.cryptoAmount.isEmpty && !item.sendAll)); - - if (hasInvalidItems) { - showErrorValidationAlert(context); - return; - } - - if (sendViewModel.wallet.isHardwareWallet) { - if (!sendViewModel.hardwareWalletViewModel!.isConnected) { - await Navigator.of(context).pushNamed(Routes.connectDevices, - arguments: ConnectDevicePageParams( - walletType: sendViewModel.walletType, - hardwareWalletType: - sendViewModel.wallet.walletInfo.hardwareWalletType!, - onConnectDevice: (BuildContext context, _) { - sendViewModel.hardwareWalletViewModel! - .initWallet(sendViewModel.wallet); - Navigator.of(context).pop(); - }, - )); - } else { - sendViewModel.hardwareWalletViewModel! - .initWallet(sendViewModel.wallet); - } - } - - if (sendViewModel.wallet.type == WalletType.monero) { - int amount = 0; - for (var item in sendViewModel.outputs) { - amount += item.formattedCryptoAmount; - } - if (monero!.needExportOutputs(sendViewModel.wallet, amount)) { - await Navigator.of(context).pushNamed(Routes.urqrAnimatedPage, - arguments: monero!.exportOutputsUR(sendViewModel.wallet)); - await Future.delayed( - Duration(seconds: 1)); // wait for monero to refresh the state - } - if (monero!.needExportOutputs(sendViewModel.wallet, amount)) { + bottomSectionPadding: EdgeInsets.only(left: 24, right: 24, bottom: 24), + bottomSection: Column( + children: [ + if (sendViewModel.sendTemplateViewModel.hasMultiRecipient) + Padding( + padding: EdgeInsets.only(bottom: 12), + child: PrimaryButton( + key: ValueKey('send_page_add_receiver_button_key'), + onPressed: () { + sendViewModel.addOutput(); + Future.delayed(const Duration(milliseconds: 250), () { + controller.jumpToPage(sendViewModel.outputs.length - 1); + }); + }, + text: S.of(context).add_receiver, + color: Colors.transparent, + textColor: Theme.of(context).colorScheme.onSurfaceVariant, + isDottedBorder: true, + borderColor: Theme.of(context).colorScheme.outline, + )), + Observer( + builder: (_) { + return LoadingPrimaryButton( + key: ValueKey('send_page_send_button_key'), + onPressed: () async { + //Request dummy node to get the focus out of the text fields + FocusScope.of(context).requestFocus(FocusNode()); + + if (sendViewModel.state is IsExecutingState) return; + if (_formKey.currentState != null && + !_formKey.currentState!.validate()) { + if (sendViewModel.outputs.length > 1) { + showErrorValidationAlert(context); + } + return; } - } - - final check = sendViewModel.shouldDisplayTotp(); - authService.authenticateAction( - context, - conditionToDetermineIfToUse2FA: check, - onAuthSuccess: (value) async { - if (value) { - await sendViewModel.createTransaction(); + + final hasInvalidItems = sendViewModel.outputs.any((item) => + item.address.isEmpty || + (item.cryptoAmount.isEmpty && !item.sendAll)); + + if (hasInvalidItems) { + showErrorValidationAlert(context); + return; + } + + if (sendViewModel.wallet.isHardwareWallet) { + if (!sendViewModel.hardwareWalletViewModel!.isConnected) { + await Navigator.of(context).pushNamed(Routes.connectDevices, + arguments: ConnectDevicePageParams( + walletType: sendViewModel.walletType, + hardwareWalletType: + sendViewModel.wallet.walletInfo.hardwareWalletType!, + onConnectDevice: (BuildContext context, _) { + sendViewModel.hardwareWalletViewModel! + .initWallet(sendViewModel.wallet); + Navigator.of(context).pop(); + }, + )); + } else { + sendViewModel.hardwareWalletViewModel! + .initWallet(sendViewModel.wallet); } - }, - ); - }, - text: _sendButtonText(context), - color: Theme.of(context).colorScheme.primary, - textColor: Theme.of(context).colorScheme.onPrimary, - isLoading: sendViewModel.state is IsExecutingState || - sendViewModel.state is TransactionCommitting || - sendViewModel.state is IsAwaitingDeviceResponseState || - sendViewModel.state is LoadingTemplateExecutingState, - isDisabled: !sendViewModel.isReadyForSend || sendViewModel.state is ExecutedSuccessfullyState, - ); - }, - ) - ], - )), + } + + if (sendViewModel.wallet.type == WalletType.monero) { + int amount = 0; + for (var item in sendViewModel.outputs) { + amount += item.formattedCryptoAmount; + } + if (monero!.needExportOutputs(sendViewModel.wallet, amount)) { + await Navigator.of(context).pushNamed(Routes.urqrAnimatedPage, + arguments: monero!.exportOutputsUR(sendViewModel.wallet)); + await Future.delayed( + Duration(seconds: 1)); // wait for monero to refresh the state + } + if (monero!.needExportOutputs(sendViewModel.wallet, amount)) { + return; + } + } + + final check = sendViewModel.shouldDisplayTotp(); + authService.authenticateAction( + context, + conditionToDetermineIfToUse2FA: check, + onAuthSuccess: (value) async { + if (value) { + await sendViewModel.createTransaction(); + } + }, + ); + }, + text: _sendButtonText(context), + color: Theme.of(context).colorScheme.primary, + textColor: Theme.of(context).colorScheme.onPrimary, + isLoading: sendViewModel.state is IsExecutingState || + sendViewModel.state is TransactionCommitting || + sendViewModel.state is IsAwaitingDeviceResponseState || + sendViewModel.state is LoadingTemplateExecutingState, + isDisabled: !sendViewModel.isReadyForSend || sendViewModel.state is ExecutedSuccessfullyState, + ); + }, + ) + ], + )), + ), ), ), ], diff --git a/lib/src/screens/settings/connection_sync_page.dart b/lib/src/screens/settings/connection_sync_page.dart index 61a88cf12..e609810f6 100644 --- a/lib/src/screens/settings/connection_sync_page.dart +++ b/lib/src/screens/settings/connection_sync_page.dart @@ -1,119 +1,219 @@ import 'dart:io'; +import 'package:cake_wallet/entities/exchange_api_mode.dart'; +import 'package:cake_wallet/entities/fiat_api_mode.dart'; +import 'package:cake_wallet/entities/new_ui_entities/list_item/list_item_regular_row.dart'; +import 'package:cake_wallet/entities/new_ui_entities/list_item/list_item_selector.dart'; +import 'package:cake_wallet/entities/new_ui_entities/list_item/list_item_toggle.dart'; import 'package:cake_wallet/generated/i18n.dart'; -import 'package:cake_wallet/reactions/wallet_connect.dart'; +import 'package:cake_wallet/new-ui/widgets/modal_header.dart'; +import 'package:cake_wallet/new-ui/widgets/modal_page_wrapper.dart'; +import 'package:cake_wallet/new-ui/widgets/receive_page/receive_top_bar.dart'; import 'package:cake_wallet/routes.dart'; import 'package:cake_wallet/src/screens/base_page.dart'; -import 'package:cake_wallet/src/screens/settings/widgets/settings_cell_with_arrow.dart'; -import 'package:cake_wallet/src/screens/settings/widgets/settings_switcher_cell.dart'; -import 'package:cake_wallet/src/screens/settings/widgets/wallet_connect_button.dart'; import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart'; +import 'package:cake_wallet/src/widgets/new_list_row/new_list_section.dart'; +import 'package:cake_wallet/src/widgets/picker.dart'; import 'package:cake_wallet/utils/feature_flag.dart'; import 'package:cake_wallet/utils/show_pop_up.dart'; -import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart'; +import 'package:cake_wallet/view_model/settings/connection_sync_view_model.dart'; import 'package:flutter/material.dart'; import 'package:flutter_mobx/flutter_mobx.dart'; class ConnectionSyncPage extends BasePage { - ConnectionSyncPage(this.dashboardViewModel); + ConnectionSyncPage(this._connectionSyncViewModel); @override - String get title => S.current.connection_sync; + bool get hideAppBar => true; - final DashboardViewModel dashboardViewModel; + final ConnectionSyncViewModel _connectionSyncViewModel; @override Widget body(BuildContext context) { - return Container( - padding: EdgeInsets.only(top: 10), - child: Column( + return ModalPageWrapper( + topBar: ModalTopBar( + title: "", + leadingIcon: Icon(Icons.arrow_back_ios_new), + onLeadingPressed: () => Navigator.of(context).pop()), + header: ModalHeader( + iconPath: "assets/new-ui/settings_row_icons/connections.svg", + message: S.of(context).connections_desc, + title: S.of(context).connections), + content: Column( mainAxisSize: MainAxisSize.min, children: [ - SettingsCellWithArrow( - title: S.current.reconnect, - handler: (context) => _presentReconnectAlert(context), - ), - if (dashboardViewModel.hasRescan) ...[ - SettingsCellWithArrow( - title: dashboardViewModel.hasSilentPayments - ? S.current.silent_payments_scanning - : S.current.rescan, - handler: (context) => Navigator.of(context).pushNamed(Routes.rescan), - ), - ], - SettingsCellWithArrow( - title: S.current.manage_nodes, - handler: (context) => Navigator.of(context).pushNamed(Routes.manageNodes), - ), - if (Platform.isAndroid && FeatureFlag.isBackgroundSyncEnabled) ...[ - SettingsCellWithArrow( - title: S.current.background_sync, - handler: (context) => Navigator.of(context).pushNamed(Routes.backgroundSync), - ), - ], Observer( - builder: (context) { - if (!dashboardViewModel.hasPowNodes) return const SizedBox(); + builder: (context) => + NewListSections( + sections: { + "": [ + if (FeatureFlag.isInAppTorEnabled) + ListItemToggle( + keyValue: "enable_builtin_tor", + label: S.of(context).enable_builtin_tor, + value: _connectionSyncViewModel.builtinTor, + onChanged: (val) { + _connectionSyncViewModel.setBuiltinTor(val, context); + }), + ListItemToggle( + keyValue: "disable_automatic_exchange_status_updates", + label: S.of(context).disable_automatic_exchange_status_updates, + value: _connectionSyncViewModel.disableAutomaticExchangeStatusUpdates, + onChanged: (val) { + _connectionSyncViewModel.setDisableAutomaticExchangeStatusUpdates(val); + }), + if (_connectionSyncViewModel.canUseBlinkProtection) + ListItemToggle( + keyValue: "can_use_blink_protection", + label: S.of(context).use_blink_protection, + value: _connectionSyncViewModel.useBlinkProtection, + onChanged: (val) { + _connectionSyncViewModel.setUseBlinkProtection(val); + }), + if (_connectionSyncViewModel.canUseEtherscan) + ListItemToggle( + keyValue: "can_use_etherscan", + label: S.of(context).etherscan_history, + value: _connectionSyncViewModel.useEtherscan, + onChanged: (val) { + _connectionSyncViewModel.setUseEtherscan(val); + }), + if (_connectionSyncViewModel.canUsePolygonScan) + ListItemToggle( + keyValue: "can_use_polygonscan", + label: S.of(context).polygonscan_history, + value: _connectionSyncViewModel.usePolygonScan, + onChanged: (val) { + _connectionSyncViewModel.setUsePolygonScan(val); + }), + if (_connectionSyncViewModel.canUseBaseScan) + ListItemToggle( + keyValue: "can_use_basescan", + label: S.of(context).basescan_history, + value: _connectionSyncViewModel.canUseBaseScan, + onChanged: (val) { + _connectionSyncViewModel.setUseBaseScan(val); + }), + if (_connectionSyncViewModel.canUseArbiScan) + ListItemToggle( + keyValue: "can_use_arbiscan", + label: S.of(context).arbiscan_history, + value: _connectionSyncViewModel.useArbiScan, + onChanged: (val) { + _connectionSyncViewModel.setUsePolygonScan(val); + }), + if (_connectionSyncViewModel.canUseBscScan) + ListItemToggle( + keyValue: "can_use_bscscan", + label: S.of(context).bscscan_history, + value: _connectionSyncViewModel.useBscScan, + onChanged: (val) { + _connectionSyncViewModel.setUseBscScan(val); + }), + if (_connectionSyncViewModel.canUseTronGrid) + ListItemToggle( + keyValue: "can_use_trongrid", + label: S.of(context).trongrid_history, + value: _connectionSyncViewModel.useTronGrid, + onChanged: (val) { + _connectionSyncViewModel.setUsePolygonScan(val); + }), + if (_connectionSyncViewModel.canUseMempoolFeeAPI) + ListItemToggle( + keyValue: "enable_mempool_api", + label: S.of(context).enable_mempool_api, + value: _connectionSyncViewModel.useMempoolFeeAPI, + onChanged: (bool isEnabled) async { + if (!isEnabled) { + final bool confirmation = await showPopUp( + context: context, + builder: (BuildContext context) { + return AlertWithTwoActions( + alertTitle: S.of(context).warning, + alertContent: S.of(context).disable_fee_api_warning, + rightButtonText: S.of(context).confirm, + leftButtonText: S.of(context).cancel, + actionRightButton: () => Navigator.of(context).pop(true), + actionLeftButton: () => Navigator.of(context).pop(false)); + }) ?? + false; + if (confirmation) { + _connectionSyncViewModel.setUseMempoolFeeAPI(isEnabled); + } + return; + } - return Column( - children: [ - SettingsCellWithArrow( - title: S.current.manage_pow_nodes, - handler: (context) => Navigator.of(context).pushNamed(Routes.managePowNodes), - ), - ], - ); - }, + _connectionSyncViewModel.setUseMempoolFeeAPI(isEnabled); + }), + if (Platform.isAndroid && FeatureFlag.isBackgroundSyncEnabled) + ListItemRegularRow( + keyValue: "background_sync", + label: S.of(context).background_sync, + onTap: () => Navigator.of(context).pushNamed(Routes.backgroundSync) + ), + if (_connectionSyncViewModel.hasPowNodes) + ListItemRegularRow( + keyValue: "manage_pow_nodes", + label: S.of(context).manage_pow_nodes, + onTap: () => Navigator.of(context).pushNamed(Routes.managePowNodes), + ), + ListItemSelector( + keyValue: "fiat_api", + label: S.of(context).fiat_api, + options: [_connectionSyncViewModel.fiatApiMode.title], + onTap: () async { + final items = FiatApiMode.all; + + final selectedAtIndex = + items.indexOf(_connectionSyncViewModel.fiatApiMode); + + await showPopUp( + context: context, + builder: (_) => Picker( + items: items, + selectedAtIndex: selectedAtIndex, + mainAxisAlignment: MainAxisAlignment.start, + onItemSelected: (FiatApiMode fiatApiMode) { + _connectionSyncViewModel.setFiatMode(fiatApiMode); + }, + isSeparated: false, + ), + ); + }), + ListItemSelector( + keyValue: "swap", + label: S.of(context).swap, + options: [_connectionSyncViewModel.exchangeStatus.title], + onTap: () async { + final items = ExchangeApiMode.all; + + final selectedAtIndex = + items.indexOf(_connectionSyncViewModel.exchangeStatus); + + await showPopUp( + context: context, + builder: (_) => Picker( + items: items, + selectedAtIndex: selectedAtIndex, + mainAxisAlignment: MainAxisAlignment.start, + onItemSelected: (ExchangeApiMode mode) { + _connectionSyncViewModel.setExchangeApiMode(mode); + }, + isSeparated: false, + ), + ); + }), + ListItemRegularRow( + keyValue: "domain_lookups", + label: S.of(context).domain_looks_up, + onTap: () => Navigator.of(context).pushNamed(Routes.domainLookupsPage) + ), + ], + } + ) ), - if (isWalletConnectCompatibleChain(dashboardViewModel.wallet.type) && - !dashboardViewModel.wallet.isHardwareWallet) ...[ // ToDo: Remove this line once WalletConnect is implemented - WalletConnectTile( - onTap: () => Navigator.of(context).pushNamed(Routes.walletConnectConnectionsListing), - ), - ], - if (FeatureFlag.isInAppTorEnabled) - Observer(builder: (context) { - return SettingsSwitcherCell( - leading: Container( - padding: EdgeInsets.symmetric(horizontal: 4, vertical: 1), - decoration: BoxDecoration( - color: Colors.red.shade100, - borderRadius: BorderRadius.circular(3), - ), - child: Text( - 'Alpha', - style: TextStyle( - color: Colors.red.shade700, - fontSize: 8, - fontWeight: FontWeight.bold, - ), - ), - ), - title: S.current.enable_builtin_tor, - value: dashboardViewModel.builtinTor, - onValueChange: (_, bool value) => dashboardViewModel.setBuiltinTor(value, context), - ); - }), ], ), ); } - - Future _presentReconnectAlert(BuildContext context) async { - await showPopUp( - context: context, - builder: (BuildContext context) { - return AlertWithTwoActions( - alertTitle: S.of(context).reconnection, - alertContent: S.of(context).reconnect_alert_text, - rightButtonText: S.of(context).ok, - leftButtonText: S.of(context).cancel, - actionRightButton: () async { - Navigator.of(context).pop(); - await dashboardViewModel.reconnect(); - }, - actionLeftButton: () => Navigator.of(context).pop()); - }, - ); - } } diff --git a/lib/src/screens/settings/display_settings_page.dart b/lib/src/screens/settings/display_settings_page.dart index 05a32262a..c10efc9d2 100644 --- a/lib/src/screens/settings/display_settings_page.dart +++ b/lib/src/screens/settings/display_settings_page.dart @@ -1,14 +1,18 @@ import 'package:cake_wallet/entities/bitcoin_amount_display_mode.dart'; import 'package:cake_wallet/entities/fiat_currency.dart'; import 'package:cake_wallet/entities/language_service.dart'; +import 'package:cake_wallet/entities/new_ui_entities/list_item/list_item_regular_row.dart'; +import 'package:cake_wallet/entities/new_ui_entities/list_item/list_item_selector.dart'; +import 'package:cake_wallet/entities/new_ui_entities/list_item/list_item_toggle.dart'; import 'package:cake_wallet/entities/sync_status_display_mode.dart'; import 'package:cake_wallet/generated/i18n.dart'; import 'package:cake_wallet/new-ui/widgets/receive_page/receive_top_bar.dart'; import 'package:cake_wallet/src/screens/settings/widgets/settings_choices_cell.dart'; -import 'package:cake_wallet/src/screens/settings/widgets/settings_picker_cell.dart'; -import 'package:cake_wallet/src/screens/settings/widgets/settings_switcher_cell.dart'; import 'package:cake_wallet/src/screens/settings/widgets/settings_theme_choice.dart'; import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart'; +import 'package:cake_wallet/src/widgets/new_list_row/new_list_section.dart'; +import 'package:cake_wallet/new-ui/widgets/modal_page_wrapper.dart'; +import 'package:cake_wallet/src/widgets/picker.dart'; import 'package:cake_wallet/src/widgets/standard_list.dart'; import 'package:cake_wallet/utils/device_info.dart'; import 'package:cake_wallet/utils/feature_flag.dart'; @@ -19,7 +23,6 @@ import 'package:cake_wallet/view_model/settings/display_settings_view_model.dart import 'package:flutter/material.dart'; import 'package:flutter_mobx/flutter_mobx.dart'; import 'package:image_picker/image_picker.dart'; -import 'package:modal_bottom_sheet/modal_bottom_sheet.dart'; class DisplaySettingsPage extends StatelessWidget { DisplaySettingsPage(this._displaySettingsViewModel); @@ -29,172 +32,228 @@ class DisplaySettingsPage extends StatelessWidget { @override Widget build(BuildContext context) { - return Container( - color: Theme.of(context).colorScheme.surface, - child: SafeArea( - top: false, - child: Column( - children: [ - ModalTopBar(title: S.of(context).display_settings,leadingIcon: Icon(Icons.arrow_back_ios_new),onLeadingPressed: () => Navigator.of(context).pop()), - Expanded( - child: SingleChildScrollView( - controller: ModalScrollController.of(context), - physics: ClampingScrollPhysics(), - child: Observer(builder: (_) { - return Container( - padding: EdgeInsets.only(top: 10), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - SettingsSwitcherCell( - title: S.of(context).apps, - value: _displaySettingsViewModel.shouldShowMarketPlaceInDashboard, - onValueChange: (_, bool value) { - _displaySettingsViewModel.setShouldShowMarketPlaceInDashbaord(value); - }, - ), - SettingsSwitcherCell( - title: S.of(context).show_address_book_popup, - value: _displaySettingsViewModel.showAddressBookPopup, - onValueChange: (_, bool value) { - _displaySettingsViewModel.setShowAddressBookPopup(value); - }, - ), - if (_displaySettingsViewModel.showZcashCardSetting) - SettingsSwitcherCell( - title: S.of(context).show_zcash_card, - value: _displaySettingsViewModel.showZcashCard, - onValueChange: (_, bool value) { - _displaySettingsViewModel.setShowZcashCard(value); - }, - ), - SettingsPickerCell( - title: S.current.sync_status_display_mode, - items: SyncStatusDisplayMode.values.toList(), - selectedItem: _displaySettingsViewModel.syncStatusDisplayMode, - onItemSelected: (SyncStatusDisplayMode mode) => - _displaySettingsViewModel.setSyncStatusDisplayMode(mode), - displayItem: (SyncStatusDisplayMode mode) => mode.title, - isGridView: false, - ), - if (_displaySettingsViewModel.showDisplayAmountsInSatoshiSetting) - SettingsPickerCell( - title: S.of(context).bitcoin_amount_display, - items: BitcoinAmountDisplayMode.all, - selectedItem: _displaySettingsViewModel.displayAmountsInSatoshi, - onItemSelected: _displaySettingsViewModel.setDisplayAmountsInSatoshi, - displayItem: (mode) => mode.title, - isGridView: false, - ), - //if (!isHaven) it does not work correctly - if (!_displaySettingsViewModel.disabledFiatApiMode) - SettingsPickerCell( - title: S.of(context).settings_currency, - searchHintText: S.of(context).search_currency, - items: FiatCurrency.all, - selectedItem: _displaySettingsViewModel.fiatCurrency, - onItemSelected: (FiatCurrency currency) => - _displaySettingsViewModel.setFiatCurrency(currency), - images: FiatCurrency.all - .map((e) => Image.asset("assets/images/flags/${e.countryCode}.png")) - .toList(), - isGridView: true, - matchingCriteria: (FiatCurrency currency, String searchText) { - return currency.title.toLowerCase().contains(searchText) || - currency.fullName.toLowerCase().contains(searchText); - }, - ), - SettingsPickerCell( - title: S.of(context).settings_change_language, - searchHintText: S.of(context).search_language, - items: LanguageService.list.keys.toList(), - displayItem: (dynamic code) { - return LanguageService.list[code] ?? ''; - }, - selectedItem: _displaySettingsViewModel.languageCode, - onItemSelected: _displaySettingsViewModel.onLanguageSelected, - images: LanguageService.list.keys - .map((e) => Image.asset( - "assets/images/flags/${LanguageService.localeCountryCode[e]}.png")) - .toList(), - matchingCriteria: (String code, String searchText) { - return LanguageService.list[code]?.toLowerCase().contains(searchText) ?? false; - }, - ), - - if (FeatureFlag.customBackgroundEnabled) - StandardListRow( - title: "Custom background", - isSelected: false, - onTap: (_) => _pickImage(context), - ), - - if (responsiveLayoutUtil.shouldRenderMobileUI && DeviceInfo.instance.isMobile) ...[ - SizedBox(height: 24), - Padding( - padding: const EdgeInsets.only(left: 24), - child: Text( - S.current.appearance, - style: Theme.of(context).textTheme.bodyMedium?.copyWith( - fontWeight: FontWeight.w400, - fontSize: 14, - height: 22 / 14, - color: Theme.of(context).colorScheme.onSurface, - ), - ), - ), - SizedBox(height: 12), - Container( - margin: EdgeInsets.symmetric(horizontal: 20), - decoration: BoxDecoration( - color: Theme.of(context).colorScheme.surfaceContainer, - borderRadius: BorderRadius.circular(16), - ), - child: Column( - children: [ - SettingsChoicesCell( - ChoicesListItem( - title: "", - items: ThemeMode.values, - selectedItem: _displaySettingsViewModel.themeMode, - onItemSelected: (ThemeMode themeMode) => - _displaySettingsViewModel.setThemeMode(themeMode), - displayItem: (ThemeMode themeMode) { - return themeMode.name[0].toUpperCase() + - themeMode.name.substring(1).toLowerCase(); - }, - ), - useGenericColor: false, - padding: EdgeInsets.all(12), - ), - Container( - decoration: BoxDecoration( - color: Theme.of(context).colorScheme.surfaceContainerHigh, - borderRadius: BorderRadius.circular(16), - ), - child: Column( - children: [ - Semantics( - label: S.of(context).color_theme, - child: SettingsThemeChoicesCell(_displaySettingsViewModel), - ), - ], - ), - ), - ], - ), - ), - ], - ], - ), - ); - }), - ), - ), - ], - ), + return ModalPageWrapper( + topBar: ModalTopBar( + title: S.of(context).display, + leadingIcon: Icon(Icons.arrow_back_ios_new), + onLeadingPressed: () => Navigator.of(context).pop(), ), - ); + content: Column( + spacing: 16, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + if (responsiveLayoutUtil.shouldRenderMobileUI && + DeviceInfo.instance.isMobile) ...[ + Padding( + padding: const EdgeInsets.only(left: 14, top: 14), + child: Text( + S.of(context).appearance, + style: Theme.of(context).textTheme.labelLarge?.copyWith( + height: 0.2, + color: Theme.of(context).colorScheme.onSurfaceVariant, + ), + ), + ), + Container( + decoration: ShapeDecoration( + color: Theme.of(context).colorScheme.surfaceContainerHigh, + shape: RoundedSuperellipseBorder( + borderRadius: BorderRadius.circular(18))), + child: Column( + children: [ + SettingsChoicesCell( + ChoicesListItem( + title: "", + items: ThemeMode.values, + selectedItem: _displaySettingsViewModel.themeMode, + onItemSelected: (ThemeMode themeMode) => + _displaySettingsViewModel.setThemeMode(themeMode), + displayItem: (ThemeMode themeMode) { + return themeMode.name[0].toUpperCase() + + themeMode.name.substring(1).toLowerCase(); + }, + ), + useGenericColor: false, + padding: EdgeInsets.all(14), + ), + Container( + decoration: ShapeDecoration( + color: Theme.of(context).colorScheme.surfaceContainer, + shape: RoundedSuperellipseBorder( + borderRadius: BorderRadius.circular(18))), + child: Column( + children: [ + Semantics( + label: S.of(context).color_theme, + child: SettingsThemeChoicesCell(_displaySettingsViewModel), + ), + ], + ), + ), + ], + ), + ), + ], + Observer( + builder: (_) => NewListSections( + sections: { + "": [ + ListItemToggle( + keyValue: "apps", + label: S.of(context).apps, + value: _displaySettingsViewModel.shouldShowMarketPlaceInDashboard, + onChanged: (val) { + _displaySettingsViewModel.setShouldShowMarketPlaceInDashbaord(val); + }), + ListItemToggle( + keyValue: "display_settings_show_address_book_popup", + label: S.of(context).show_address_book_popup, + value: _displaySettingsViewModel.showAddressBookPopup, + onChanged: (val) { + _displaySettingsViewModel.setShowAddressBookPopup(val); + }), + ListItemToggle( + keyValue: "display_settings_disable_buy_button", + label: S.of(context).disable_buy, + value: _displaySettingsViewModel.disableTradeOption, + onChanged: (val) { + _displaySettingsViewModel.setDisableTradeOption(val); + }), + if (_displaySettingsViewModel.showZcashCardSetting) + ListItemToggle( + keyValue: "display_settings_show_zcashcard", + label: S.of(context).show_zcash_card, + value: _displaySettingsViewModel.showZcashCard, + onChanged: (val) { + _displaySettingsViewModel.setShowZcashCard(val); + }), + ListItemSelector( + keyValue: "display_settings_sync_status_display", + label: S.of(context).sync_status_display_mode, + options: [_displaySettingsViewModel.syncStatusDisplayMode.title], + onTap: () async { + final items = SyncStatusDisplayMode.values.toList(); + + final selectedAtIndex = + items.indexOf(_displaySettingsViewModel.syncStatusDisplayMode); + + await showPopUp( + context: context, + builder: (_) => Picker( + items: items, + selectedAtIndex: selectedAtIndex, + mainAxisAlignment: MainAxisAlignment.start, + onItemSelected: (SyncStatusDisplayMode mode) { + _displaySettingsViewModel.setSyncStatusDisplayMode(mode); + }, + displayItem: (SyncStatusDisplayMode mode) => mode.title, + isSeparated: false, + ), + ); + }), + if (_displaySettingsViewModel.showDisplayAmountsInSatoshiSetting) + ListItemRegularRow( + keyValue: "display_settings_bitcoin_amount_display", + label: S.of(context).bitcoin_amount_display, + trailingText: _displaySettingsViewModel.displayAmountsInSatoshi.title, + onTap: () async { + final items = BitcoinAmountDisplayMode.all; + + final selectedAtIndex = + items.indexOf(_displaySettingsViewModel.displayAmountsInSatoshi); + + await showPopUp( + context: context, + builder: (_) => Picker( + items: items, + selectedAtIndex: selectedAtIndex, + mainAxisAlignment: MainAxisAlignment.start, + onItemSelected: _displaySettingsViewModel.setDisplayAmountsInSatoshi, + displayItem: (BitcoinAmountDisplayMode mode) => mode.title, + isSeparated: false, + ), + ); + }), + if (!_displaySettingsViewModel.disabledFiatApiMode) + ListItemSelector( + keyValue: "display_settings_fiat_currency", + label: S.of(context).settings_currency, + options: [_displaySettingsViewModel.fiatCurrency.title], + onTap: () async { + final items = FiatCurrency.all; + + final selectedAtIndex = + items.indexOf(_displaySettingsViewModel.fiatCurrency); + + await showPopUp( + context: context, + builder: (_) => Picker( + items: items, + selectedAtIndex: selectedAtIndex, + mainAxisAlignment: MainAxisAlignment.start, + onItemSelected: (FiatCurrency currency) { + _displaySettingsViewModel.setFiatCurrency(currency); + }, + images: FiatCurrency.all + .map((e) => Image.asset("assets/images/flags/${e.countryCode}.png")) + .toList(), + hintText: S.of(context).search_currency, + isGridView: true, + matchingCriteria: (FiatCurrency currency, String searchText) { + return currency.title.toLowerCase().contains(searchText) || + currency.fullName.toLowerCase().contains(searchText); + }, + isSeparated: false, + + ), + ); + }), + ListItemSelector( + keyValue: "display_settings_language", + label: S.of(context).settings_change_language, + options: [LanguageService.list[_displaySettingsViewModel.languageCode] ?? ''], + onTap: () async { + final items = LanguageService.list.keys.toList(); + + final selectedAtIndex = + items.indexOf(_displaySettingsViewModel.languageCode); + + await showPopUp( + context: context, + builder: (_) => Picker( + displayItem: (dynamic code) { + return LanguageService.list[code] ?? ''; + }, + items: items, + selectedAtIndex: selectedAtIndex, + mainAxisAlignment: MainAxisAlignment.start, + onItemSelected: _displaySettingsViewModel.onLanguageSelected, + images: LanguageService.list.keys + .map((e) => Image.asset( + "assets/images/flags/${LanguageService.localeCountryCode[e]}.png")) + .toList(), + hintText: S.of(context).search_language, + matchingCriteria: (String code, String searchText) { + return LanguageService.list[code]?.toLowerCase().contains(searchText) ?? false; + }, + isSeparated: true, + + ), + ); + }), + ], + }, + ), + ), + if (FeatureFlag.customBackgroundEnabled) + StandardListRow( + title: "Custom background", + isSelected: false, + onTap: (_) => _pickImage(context), + ), + ], + ), + ); } // Function to pick an image from the gallery diff --git a/lib/src/screens/settings/domain_lookups_page.dart b/lib/src/screens/settings/domain_lookups_page.dart index ed5916437..b566892aa 100644 --- a/lib/src/screens/settings/domain_lookups_page.dart +++ b/lib/src/screens/settings/domain_lookups_page.dart @@ -1,17 +1,17 @@ import 'package:cake_wallet/generated/i18n.dart'; import 'package:cake_wallet/src/screens/base_page.dart'; import 'package:cake_wallet/src/screens/settings/widgets/settings_switcher_cell.dart'; -import 'package:cake_wallet/view_model/settings/privacy_settings_view_model.dart'; +import 'package:cake_wallet/view_model/settings/connection_sync_view_model.dart'; import 'package:flutter/material.dart'; import 'package:flutter_mobx/flutter_mobx.dart'; class DomainLookupsPage extends BasePage { - DomainLookupsPage(this._privacySettingsViewModel); + DomainLookupsPage(this._connectionsSyncViewModel); @override String get title => S.current.domain_looks_up; - final PrivacySettingsViewModel _privacySettingsViewModel; + final ConnectionSyncViewModel _connectionsSyncViewModel; @override Widget body(BuildContext context) { @@ -23,36 +23,36 @@ class DomainLookupsPage extends BasePage { children: [ SettingsSwitcherCell( title: 'Twitter', - value: _privacySettingsViewModel.lookupTwitter, - onValueChange: (_, bool value) => _privacySettingsViewModel.setLookupsTwitter(value)), + value: _connectionsSyncViewModel.lookupTwitter, + onValueChange: (_, bool value) => _connectionsSyncViewModel.setLookupsTwitter(value)), SettingsSwitcherCell( title: 'Mastodon', - value: _privacySettingsViewModel.looksUpMastodon, - onValueChange: (_, bool value) => _privacySettingsViewModel.setLookupsMastodon(value)), + value: _connectionsSyncViewModel.looksUpMastodon, + onValueChange: (_, bool value) => _connectionsSyncViewModel.setLookupsMastodon(value)), SettingsSwitcherCell( title: 'Yat service', - value: _privacySettingsViewModel.looksUpYatService, - onValueChange: (_, bool value) => _privacySettingsViewModel.setLookupsYatService(value)), + value: _connectionsSyncViewModel.looksUpYatService, + onValueChange: (_, bool value) => _connectionsSyncViewModel.setLookupsYatService(value)), SettingsSwitcherCell( title: 'Unstoppable Domains', - value: _privacySettingsViewModel.looksUpUnstoppableDomains, - onValueChange: (_, bool value) => _privacySettingsViewModel.setLookupsUnstoppableDomains(value)), + value: _connectionsSyncViewModel.looksUpUnstoppableDomains, + onValueChange: (_, bool value) => _connectionsSyncViewModel.setLookupsUnstoppableDomains(value)), SettingsSwitcherCell( title: 'OpenAlias', - value: _privacySettingsViewModel.looksUpOpenAlias, - onValueChange: (_, bool value) => _privacySettingsViewModel.setLookupsOpenAlias(value)), + value: _connectionsSyncViewModel.looksUpOpenAlias, + onValueChange: (_, bool value) => _connectionsSyncViewModel.setLookupsOpenAlias(value)), SettingsSwitcherCell( title: 'Ethereum Name Service', - value: _privacySettingsViewModel.looksUpENS, - onValueChange: (_, bool value) => _privacySettingsViewModel.setLookupsENS(value)), + value: _connectionsSyncViewModel.looksUpENS, + onValueChange: (_, bool value) => _connectionsSyncViewModel.setLookupsENS(value)), SettingsSwitcherCell( title: '.well-known', - value: _privacySettingsViewModel.looksUpWellKnown, - onValueChange: (_, bool value) => _privacySettingsViewModel.setLookupsWellKnown(value)), + value: _connectionsSyncViewModel.looksUpWellKnown, + onValueChange: (_, bool value) => _connectionsSyncViewModel.setLookupsWellKnown(value)), SettingsSwitcherCell( title: 'Zano Aliases', - value: _privacySettingsViewModel.lookupsZanoAlias, - onValueChange: (_, bool value) => _privacySettingsViewModel.setLookupsZanoAlias(value)), + value: _connectionsSyncViewModel.lookupsZanoAlias, + onValueChange: (_, bool value) => _connectionsSyncViewModel.setLookupsZanoAlias(value)), //if (!isHaven) it does not work correctly ], diff --git a/lib/src/screens/settings/manage_nodes_page.dart b/lib/src/screens/settings/manage_nodes_page.dart index b96ea8756..c1cd92526 100644 --- a/lib/src/screens/settings/manage_nodes_page.dart +++ b/lib/src/screens/settings/manage_nodes_page.dart @@ -1,4 +1,8 @@ import 'package:cake_wallet/generated/i18n.dart'; +import 'package:cake_wallet/new-ui/widgets/modal_header.dart'; +import 'package:cake_wallet/new-ui/widgets/modal_page_wrapper.dart'; +import 'package:cake_wallet/new-ui/widgets/modern_button.dart'; +import 'package:cake_wallet/new-ui/widgets/receive_page/receive_top_bar.dart'; import 'package:cake_wallet/routes.dart'; import 'package:cake_wallet/src/screens/base_page.dart'; import 'package:cake_wallet/src/screens/nodes/widgets/node_list_row.dart'; @@ -7,6 +11,7 @@ import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart'; import 'package:cake_wallet/src/widgets/standard_list.dart'; import 'package:cake_wallet/utils/feature_flag.dart'; import 'package:cake_wallet/utils/show_pop_up.dart'; +import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart'; import 'package:cake_wallet/view_model/node_list/node_list_view_model.dart'; import 'package:cake_wallet/view_model/node_list/pow_node_list_view_model.dart'; import 'package:flutter/material.dart'; @@ -14,30 +19,46 @@ import 'package:flutter_mobx/flutter_mobx.dart'; import 'package:modal_bottom_sheet/modal_bottom_sheet.dart'; class ManageNodesPage extends BasePage { - ManageNodesPage(this.isPow, {this.nodeListViewModel, this.powNodeListViewModel}) + ManageNodesPage(this.isPow, {this.nodeListViewModel, this.powNodeListViewModel, this.dashboardViewModel}) : assert((isPow && powNodeListViewModel != null) || (!isPow && nodeListViewModel != null)), assert(powNodeListViewModel == null || nodeListViewModel == null); + final DashboardViewModel? dashboardViewModel; final NodeListViewModel? nodeListViewModel; final PowNodeListViewModel? powNodeListViewModel; final bool isPow; + @override + bool get hideAppBar => true; + @override String get title => S.current.manage_nodes; @override Widget body(BuildContext context) { - return Padding( - padding: EdgeInsets.only(top: 10), - child: Column( + return ModalPageWrapper( + horizontalPadding: 0, + topBar: ModalTopBar( + title: S.of(context).manage_nodes, + leadingIcon: Icon(Icons.arrow_back_ios_new), + onLeadingPressed: () => Navigator.of(context).pop(), + trailingWidget: Row( + spacing: 8, + children: [ + if (dashboardViewModel?.hasRescan ?? true) + ModernButton( + size: 36, + icon: Icon(Icons.history), + onPressed: () => Navigator.of(context).pushNamed(Routes.rescan)), + ModernButton( + size: 36, + icon: Icon(Icons.add), + onPressed: () => Navigator.of(context).pushNamed(isPow ? Routes.newPowNode : Routes.newNode)) + ], + ), + ), + content: Column( children: [ - Semantics( - button: true, - child: NodeHeaderListRow( - title: S.of(context).add_new_node, - onTap: (_) async => await Navigator.of(context).pushNamed(isPow ? Routes.newPowNode : Routes.newNode), - ), - ), if (FeatureFlag.isAutomaticNodeSwitchingEnabled) Observer( builder: (_) => SettingsSwitcherCell( @@ -60,32 +81,34 @@ class ManageNodesPage extends BasePage { builder: (BuildContext context) { int itemsCount = nodeListViewModel?.nodes.length ?? powNodeListViewModel!.nodes.length; - return Flexible( - child: SectionStandardList( - scrollController: ModalScrollController.of(context), - sectionCount: 1, - dividerPadding: EdgeInsets.symmetric(horizontal: 24), - itemCounter: (int sectionIndex) => itemsCount, - itemBuilder: (_, index) { - return Observer( - builder: (context) { - final node = - nodeListViewModel?.nodes[index] ?? powNodeListViewModel!.nodes[index]; - late bool isSelected; - if (isPow) { - isSelected = node.keyIndex == powNodeListViewModel!.currentNode.keyIndex; - } else { - isSelected = node.keyIndex == nodeListViewModel!.currentNode.keyIndex; - } - final nodeListRow = NodeListRow( - title: node.uriRaw, - node: node, - isSelected: isSelected, - isPow: isPow, - onTap: (_) async { - if (isSelected) { - return; - } + return SectionStandardList( + scrollController: ModalScrollController.of(context), + sectionCount: 1, + shrinkWrap: true, + physics: NeverScrollableScrollPhysics(), + dividerPadding: EdgeInsets.symmetric(horizontal: 24), + itemCounter: (int sectionIndex) => itemsCount, + itemBuilder: (_, index) { + return Observer( + builder: (context) { + final node = + nodeListViewModel?.nodes[index] ?? powNodeListViewModel!.nodes[index]; + late bool isSelected; + if (isPow) { + isSelected = node.keyIndex == powNodeListViewModel!.currentNode.keyIndex; + } else { + isSelected = node.keyIndex == nodeListViewModel!.currentNode.keyIndex; + } + final nodeListRow = NodeListRow( + title: node.uriRaw, + subtitle: node.label!, + node: node, + isSelected: isSelected, + isPow: isPow, + onTap: (_) async { + if (isSelected) { + return; + } await showPopUp( context: context, @@ -110,11 +133,10 @@ class ManageNodesPage extends BasePage { ); }, ); - return nodeListRow; - }, - ); - }, - ), + return nodeListRow; + }, + ); + }, ); }, ), @@ -122,4 +144,21 @@ class ManageNodesPage extends BasePage { ), ); } + Future _presentReconnectAlert(BuildContext context) async { + await showPopUp( + context: context, + builder: (BuildContext context) { + return AlertWithTwoActions( + alertTitle: S.of(context).reconnection, + alertContent: S.of(context).reconnect_alert_text, + rightButtonText: S.of(context).ok, + leftButtonText: S.of(context).cancel, + actionRightButton: () async { + Navigator.of(context).pop(); + await dashboardViewModel!.reconnect(); + }, + actionLeftButton: () => Navigator.of(context).pop()); + }, + ); + } } diff --git a/lib/src/screens/settings/mweb_node_page.dart b/lib/src/screens/settings/mweb_node_page.dart index 3f153f42b..32ba6886c 100644 --- a/lib/src/screens/settings/mweb_node_page.dart +++ b/lib/src/screens/settings/mweb_node_page.dart @@ -1,56 +1,64 @@ -import 'package:cake_wallet/src/screens/base_page.dart'; -import 'package:cake_wallet/src/widgets/base_text_form_field.dart'; +import 'package:cake_wallet/core/node_address_validator.dart'; +import 'package:cake_wallet/entities/new_ui_entities/list_item/list_item_text_field.dart'; +import 'package:cake_wallet/new-ui/widgets/modal_page_wrapper.dart'; +import 'package:cake_wallet/new-ui/widgets/receive_page/receive_top_bar.dart'; +import 'package:cake_wallet/src/widgets/new_list_row/new_list_section.dart'; import 'package:cake_wallet/src/widgets/primary_button.dart'; import 'package:cake_wallet/generated/i18n.dart'; import 'package:cake_wallet/view_model/settings/mweb_settings_view_model.dart'; import 'package:flutter/material.dart'; -import 'package:flutter_mobx/flutter_mobx.dart'; - -class MwebNodePage extends BasePage { - MwebNodePage(this.mwebSettingsViewModelBase) - : _nodeUriController = TextEditingController(text: mwebSettingsViewModelBase.mwebNodeUri), - super(); +class MwebNodePage extends StatefulWidget { + const MwebNodePage(this.mwebSettingsViewModelBase, {super.key}); final MwebSettingsViewModelBase mwebSettingsViewModelBase; - final TextEditingController _nodeUriController; @override - String get title => S.current.litecoin_mweb_node; + State createState() => _MwebNodePageState(); +} + +class _MwebNodePageState extends State { + late final TextEditingController _nodeUriController = + TextEditingController(text: widget.mwebSettingsViewModelBase.mwebNodeUri); @override - Widget body(BuildContext context) { - return Stack( - fit: StackFit.expand, - children: [ - Container( - padding: EdgeInsets.symmetric(horizontal: 24), - child: Row( - children: [ - Expanded( - child: BaseTextFormField(controller: _nodeUriController), - ) - ], - ), + void dispose() { + _nodeUriController.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return ModalPageWrapper( + topBar: ModalTopBar( + title: S.current.litecoin_mweb_settings, + onLeadingPressed: Navigator.of(context).pop, + leadingIcon: Icon(Icons.arrow_back_ios_new)), + content: Container( + child: NewListSections( + controllers: { + widget.mwebSettingsViewModelBase.mwebNodeUri: _nodeUriController, + }, + sections: { + 'main': [ + ListItemTextField( + keyValue: widget.mwebSettingsViewModelBase.mwebNodeUri, + label: S.current.node_address, + validator: NodePathValidator(), + ), + ] + }), ), - Positioned( - child: Observer( - builder: (_) => LoadingPrimaryButton( - onPressed: () => save(context), - text: S.of(context).save, - color: Theme.of(context).colorScheme.primary, - textColor: Theme.of(context).colorScheme.onPrimary, - ), - ), - bottom: 24, - left: 24, - right: 24, - ) - ], + bottomContent: LoadingPrimaryButton( + onPressed: () => save(context), + text: S.of(context).save, + color: Theme.of(context).colorScheme.primary, + textColor: Theme.of(context).colorScheme.onPrimary, + ), ); } void save(BuildContext context) { - mwebSettingsViewModelBase.setMwebNodeUri(_nodeUriController.text); + widget.mwebSettingsViewModelBase.setMwebNodeUri(_nodeUriController.text); Navigator.pop(context); } } diff --git a/lib/src/screens/settings/other_settings_page.dart b/lib/src/screens/settings/other_settings_page.dart index fb1059ecc..78456f699 100644 --- a/lib/src/screens/settings/other_settings_page.dart +++ b/lib/src/screens/settings/other_settings_page.dart @@ -1,21 +1,24 @@ import 'package:cake_wallet/bitcoin/bitcoin.dart'; +import 'package:cake_wallet/entities/new_ui_entities/list_item/list_item_regular_row.dart'; +import 'package:cake_wallet/entities/new_ui_entities/list_item/list_item_selector.dart'; import 'package:cake_wallet/entities/priority_for_wallet_type.dart'; import 'package:cake_wallet/generated/i18n.dart'; +import 'package:cake_wallet/new-ui/widgets/modal_page_wrapper.dart'; +import 'package:cake_wallet/new-ui/widgets/receive_page/receive_top_bar.dart'; import 'package:cake_wallet/routes.dart'; import 'package:cake_wallet/src/screens/base_page.dart'; import 'package:cake_wallet/src/screens/dev/moneroc_cache_debug.dart'; -import 'package:cake_wallet/src/screens/settings/widgets/setting_priority_picker_cell.dart'; -import 'package:cake_wallet/src/screens/settings/widgets/settings_cell_with_arrow.dart'; -import 'package:cake_wallet/src/screens/settings/widgets/settings_picker_cell.dart'; -import 'package:cake_wallet/src/screens/settings/widgets/settings_version_cell.dart'; +import 'package:cake_wallet/src/widgets/new_list_row/new_list_section.dart'; +import 'package:cake_wallet/src/widgets/picker.dart'; import 'package:cake_wallet/utils/feature_flag.dart'; import 'package:cake_wallet/utils/share_util.dart'; +import 'package:cake_wallet/utils/show_pop_up.dart'; import 'package:cake_wallet/view_model/settings/other_settings_view_model.dart'; import 'package:cw_core/wallet_info.dart'; +import 'package:cw_core/transaction_priority.dart'; import 'package:cw_core/wallet_type.dart'; import 'package:cw_core/db/sqlite.dart'; import 'package:flutter/material.dart'; -import 'package:flutter_mobx/flutter_mobx.dart'; class OtherSettingsPage extends BasePage { OtherSettingsPage(this._otherSettingsViewModel) { @@ -25,164 +28,206 @@ class OtherSettingsPage extends BasePage { } @override - String get title => S.current.other_settings; + bool get hideAppBar => true; final OtherSettingsViewModel _otherSettingsViewModel; + @override - Widget body(BuildContext context) => Observer( - builder: (_) => Container( - padding: EdgeInsets.only(top: 10), - child: Column( - children: [ - Expanded( - child: SingleChildScrollView( - child: Column( - children: [ - if (_otherSettingsViewModel.displayTransactionPriority) - _otherSettingsViewModel.walletType == WalletType.bitcoin - ? SettingsPriorityPickerCell( - title: S.current.settings_fee_priority, - items: priorityForWalletType(_otherSettingsViewModel.walletType), - displayItem: _otherSettingsViewModel.getDisplayBitcoinPriority, - selectedItem: _otherSettingsViewModel.transactionPriority, - customItemIndex: _otherSettingsViewModel.customPriorityItemIndex, - onItemSelected: _otherSettingsViewModel.onDisplayBitcoinPrioritySelected, - customValue: _otherSettingsViewModel.customBitcoinFeeRate, - maxValue: _otherSettingsViewModel.maxCustomFeeRate?.toDouble(), - ) - : SettingsPickerCell( - title: S.current.settings_fee_priority, - items: priorityForWalletType(_otherSettingsViewModel.walletType), - displayItem: _otherSettingsViewModel.getDisplayPriority, - selectedItem: _otherSettingsViewModel.transactionPriority, - onItemSelected: _otherSettingsViewModel.onDisplayPrioritySelected, - ), - if (_otherSettingsViewModel.changeRepresentativeEnabled) - SettingsCellWithArrow( - title: S.current.change_rep, - handler: (context) => Navigator.of(context).pushNamed(Routes.changeRep), - ), - if (_otherSettingsViewModel.changeHardwareWalletTypeEnabled) - SettingsCellWithArrow( - title: "Hardware wallet manufacturer", - handler: (context) => Navigator.of(context) - .pushNamed(Routes.restoreWalletFromHardwareWallet, arguments: { - "showUnavailable": false, - "availableHardwareWalletTypes": [ - HardwareWalletType.cupcake, - HardwareWalletType.coldcard, - HardwareWalletType.seedsigner, - ], - "onSelect": (BuildContext context, HardwareWalletType hwType) async { - await _otherSettingsViewModel.onHardwareWalletTypeChanged(hwType); - Navigator.pop(context); + Widget body(BuildContext context) { + return ModalPageWrapper( + topBar: ModalTopBar( + title: S.of(context).other, + leadingIcon: Icon(Icons.arrow_back_ios_new), + onLeadingPressed: () => Navigator.of(context).pop(), + ), + // header: ModalHeader( + // iconPath: "assets/new-ui/settings_row_icons/other.svg", + // message: "Other settings", + // title: S.of(context).other_settings), + content: NewListSections( + sections: { + "": [ + if (_otherSettingsViewModel.displayTransactionPriority) + _otherSettingsViewModel.walletType == WalletType.bitcoin + ? ListItemSelector( + keyValue: "fee_priority", + label: S.of(context).settings_fee_priority, + options: [_otherSettingsViewModel.transactionPriority.title], + onTap: () async { + + final items = priorityForWalletType(_otherSettingsViewModel.walletType); + + var selectedAtIndex = items.indexOf(_otherSettingsViewModel.transactionPriority); + double sliderValue = _otherSettingsViewModel.customBitcoinFeeRate ?? 0.0; + + await showPopUp( + context: context, + builder: (BuildContext context) { + return StatefulBuilder( + builder: (BuildContext context, StateSetter setState) { + return Picker( + items: items, + displayItem: (TransactionPriority item) => _otherSettingsViewModel.getDisplayBitcoinPriority(item, sliderValue.round()), + selectedAtIndex: selectedAtIndex, + customItemIndex: _otherSettingsViewModel.customPriorityItemIndex, + maxValue: _otherSettingsViewModel.maxCustomFeeRate?.toDouble(), + headerEnabled: false, + closeOnItemSelected: false, + mainAxisAlignment: MainAxisAlignment.center, + sliderValue: sliderValue, + onSliderChanged: (double newValue) => + setState(() => sliderValue = newValue), + onItemSelected: (TransactionPriority priority) { + setState(() => selectedAtIndex = items.indexOf(priority)); + _otherSettingsViewModel.onDisplayBitcoinPrioritySelected.call(priority, sliderValue); }, - }), - ), - if (_otherSettingsViewModel.walletType == WalletType.bitcoin) ...[ - SettingsCellWithArrow( - title: S.of(context).export_lightning_logs, - handler: onExportLNLog, - ), - SettingsCellWithArrow( - title: S.of(context).export_payjoin_logs, - handler: onExportPJLog, - ), - ], - SettingsCellWithArrow( - title: S.current.settings_terms_and_conditions, - handler: (context) => Navigator.of(context).pushNamed(Routes.readDisclaimer), - ), - if (FeatureFlag.hasDevOptions && - _otherSettingsViewModel.walletType == WalletType.monero) - SettingsCellWithArrow( - title: '[dev] monero background sync', - handler: (context) => - Navigator.of(context).pushNamed(Routes.devMoneroBackgroundSync), - ), - if (FeatureFlag.hasDevOptions && - [WalletType.monero, WalletType.wownero, WalletType.zano] - .contains(_otherSettingsViewModel.walletType)) - SettingsCellWithArrow( - title: '[dev] xmr call profiler', - handler: (context) => - Navigator.of(context).pushNamed(Routes.devMoneroCallProfiler), - ), - if (FeatureFlag.hasDevOptions && - [WalletType.monero].contains(_otherSettingsViewModel.walletType)) - SettingsCellWithArrow( - title: '[dev] xmr wallet cache debug', - handler: (context) => - Navigator.of(context).pushNamed(Routes.devMoneroWalletCacheDebug), - ), - if (FeatureFlag.hasDevOptions) - SettingsCellWithArrow( - title: '[dev] shared preferences', - handler: (context) => - Navigator.of(context).pushNamed(Routes.devSharedPreferences), - ), - if (FeatureFlag.hasDevOptions) - SettingsCellWithArrow( - title: '[dev] secure storage preferences', - handler: (context) => - Navigator.of(context).pushNamed(Routes.devSecurePreferences), - ), - if (FeatureFlag.hasDevOptions) - SettingsCellWithArrow( - title: '[dev] background sync logs', - handler: (context) => - Navigator.of(context).pushNamed(Routes.devBackgroundSyncLogs), - ), - if (FeatureFlag.hasDevOptions) - SettingsCellWithArrow( - title: '[dev] socket health logs', - handler: (context) => Navigator.of(context).pushNamed(Routes.devSocketHealthLogs), - ), - if (FeatureFlag.hasDevOptions) - SettingsCellWithArrow( - title: '[dev] network requests logs', - handler: (context) => Navigator.of(context).pushNamed(Routes.devNetworkRequests), - ), - if (FeatureFlag.hasDevOptions) - SettingsCellWithArrow( - title: '[dev] exchange provider logs', - handler: (context) => - Navigator.of(context).pushNamed(Routes.devExchangeProviderLogs), - ), - if (FeatureFlag.hasDevOptions) - SettingsCellWithArrow( - title: '[dev] *QR tools', - handler: (context) => Navigator.of(context).pushNamed(Routes.devQRTools), - ), - if (FeatureFlag.hasDevOptions) - SettingsCellWithArrow( - title: '[dev] exchange provider logs', - handler: (BuildContext context) => - Navigator.of(context).pushNamed(Routes.devExchangeProviderLogs), - ), - if (FeatureFlag.hasDevOptions) - SettingsCellWithArrow( - title: '[dev] browse sqlite db', - handler: (BuildContext context) async { - final data = await dumpDb(); - Navigator.of(context).push( - MaterialPageRoute(builder: (context) => JsonExplorerPage(data: data, title: 'sqlite db')), - ); - } - ), - ], + ); + }, + ); + }, + ); + _otherSettingsViewModel.onDisplayBitcoinPrioritySelected.call(items[selectedAtIndex], sliderValue); + } + ) + : ListItemSelector( + keyValue: "fee_priority", + label: S.of(context).settings_fee_priority, + options: [_otherSettingsViewModel.transactionPriority.title], + onTap: () async { + final selectedAtIndex = priorityForWalletType(_otherSettingsViewModel.walletType).indexOf(_otherSettingsViewModel.transactionPriority,); + + await showPopUp( + context: context, + builder: (_) => Picker( + items: priorityForWalletType(_otherSettingsViewModel.walletType), + displayItem: _otherSettingsViewModel.getDisplayPriority, + selectedAtIndex: selectedAtIndex, + mainAxisAlignment: MainAxisAlignment.start, + onItemSelected: (TransactionPriority item) => _otherSettingsViewModel.onDisplayBitcoinPrioritySelected.call(item, 0), + isSeparated: false, ), - ), + ); + }, + ), + if(_otherSettingsViewModel.changeRepresentativeEnabled) + ListItemRegularRow( + keyValue: "change_rep", + label: S.of(context).change_rep, + onTap: () => Navigator.of(context).pushNamed(Routes.changeRep) + ), + if (_otherSettingsViewModel.changeHardwareWalletTypeEnabled) + ListItemRegularRow( + keyValue: "hardware_wallet_manufacturer", + label: "Hardware wallet manufacturer", + onTap: () => Navigator.of(context) + .pushNamed(Routes.restoreWalletFromHardwareWallet, arguments: { + "showUnavailable": false, + "availableHardwareWalletTypes": [ + HardwareWalletType.cupcake, + HardwareWalletType.coldcard, + HardwareWalletType.seedsigner, + ], + "onSelect": (BuildContext context, HardwareWalletType hwType) async { + await _otherSettingsViewModel.onHardwareWalletTypeChanged(hwType); + Navigator.pop(context); + }, + }), + ), + ListItemRegularRow( + keyValue: "security_backup_page_sign_and_verify", + label: S.of(context).sign_verify_title, + onTap: () { + Navigator.of(context).pushNamed(Routes.signPage); + }), + ], + if(FeatureFlag.hasDevOptions) + "dev": [ + if(_otherSettingsViewModel.walletType == WalletType.bitcoin) ...[ + ListItemRegularRow( + keyValue: "export_lightning_logs", + label: S.of(context).export_lightning_logs, + onTap: () => onExportLNLog(context) ), - SettingsVersionCell( - title: S.of(context).version(_otherSettingsViewModel.currentVersion), + ListItemRegularRow( + keyValue: "export_payjoin_logs", + label: S.of(context).export_payjoin_logs, + onTap: () => onExportPJLog(context) ), ], - ), - ), - ); - + if (FeatureFlag.hasDevOptions && + _otherSettingsViewModel.walletType == WalletType.monero) + ListItemRegularRow( + keyValue: "[dev] monero background sync", + label: "[dev] monero background sync", + onTap: () => Navigator.of(context).pushNamed(Routes.devMoneroBackgroundSync) + ), + if (FeatureFlag.hasDevOptions && + [WalletType.monero, WalletType.wownero, WalletType.zano] + .contains(_otherSettingsViewModel.walletType)) + ListItemRegularRow( + keyValue: "[dev] xmr call profiler", + label: "[dev] xmr call profiler", + onTap: () => Navigator.of(context).pushNamed(Routes.devMoneroCallProfiler) + ), + if (FeatureFlag.hasDevOptions && + [WalletType.monero].contains(_otherSettingsViewModel.walletType)) + ListItemRegularRow( + keyValue: '[dev] xmr wallet cache debug', + label: '[dev] xmr wallet cache debug', + onTap: () => Navigator.of(context).pushNamed(Routes.devMoneroWalletCacheDebug) + ), + ListItemRegularRow( + keyValue: '[dev] shared preferences', + label: '[dev] shared preferences', + onTap: () => Navigator.of(context).pushNamed(Routes.devSharedPreferences) + ), + ListItemRegularRow( + keyValue: '[dev] secure storage preferences', + label: '[dev] secure storage preferences', + onTap: () => Navigator.of(context).pushNamed(Routes.devSecurePreferences) + ), + ListItemRegularRow( + keyValue: '[dev] background sync logs', + label: '[dev] background sync logs', + onTap: () => Navigator.of(context).pushNamed(Routes.devBackgroundSyncLogs) + ), + ListItemRegularRow( + keyValue: '[dev] socket health logs', + label: '[dev] socket health logs', + onTap: () => Navigator.of(context).pushNamed(Routes.devSocketHealthLogs) + ), + ListItemRegularRow( + keyValue: '[dev] network requests logs', + label: '[dev] network requests logs', + onTap: () => Navigator.of(context).pushNamed(Routes.devNetworkRequests) + ), + ListItemRegularRow( + keyValue: '[dev] exchange provider logs', + label: '[dev] exchange provider logs', + onTap: () => Navigator.of(context).pushNamed(Routes.devExchangeProviderLogs) + ), + ListItemRegularRow( + keyValue: '[dev] *QR tools', + label: '[dev] *QR tools', + onTap: () => Navigator.of(context).pushNamed(Routes.devExchangeProviderLogs) + ), + ListItemRegularRow( + keyValue: '[dev] browse sqlite db', + label: '[dev] browse sqlite db', + onTap: () async { + final data = await dumpDb(); + Navigator.of(context).push( + MaterialPageRoute(builder: (context) => + JsonExplorerPage(data: data, title: 'sqlite db')), + ); + } + ), + ] + } + ), + ); + } Future onExportLNLog(BuildContext context) async { final file = await _otherSettingsViewModel.getLightningLog(); diff --git a/lib/src/screens/settings/privacy_page.dart b/lib/src/screens/settings/privacy_page.dart index 8eb1f7851..c16223132 100644 --- a/lib/src/screens/settings/privacy_page.dart +++ b/lib/src/screens/settings/privacy_page.dart @@ -1,15 +1,12 @@ -import 'package:cake_wallet/entities/exchange_api_mode.dart'; -import 'package:cake_wallet/entities/fiat_api_mode.dart'; +import 'package:cake_wallet/entities/new_ui_entities/list_item/list_item_regular_row.dart'; +import 'package:cake_wallet/entities/new_ui_entities/list_item/list_item_toggle.dart'; import 'package:cake_wallet/generated/i18n.dart'; +import 'package:cake_wallet/new-ui/widgets/modal_header.dart'; +import 'package:cake_wallet/new-ui/widgets/modal_page_wrapper.dart'; +import 'package:cake_wallet/new-ui/widgets/receive_page/receive_top_bar.dart'; import 'package:cake_wallet/routes.dart'; import 'package:cake_wallet/src/screens/base_page.dart'; -import 'package:cake_wallet/src/screens/settings/widgets/settings_cell_with_arrow.dart'; -import 'package:cake_wallet/src/screens/settings/widgets/settings_choices_cell.dart'; -import 'package:cake_wallet/src/screens/settings/widgets/settings_switcher_cell.dart'; -import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart'; -import 'package:cake_wallet/utils/device_info.dart'; -import 'package:cake_wallet/utils/show_pop_up.dart'; -import 'package:cake_wallet/view_model/settings/choices_list_item.dart'; +import 'package:cake_wallet/src/widgets/new_list_row/new_list_section.dart'; import 'package:cake_wallet/view_model/settings/privacy_settings_view_model.dart'; import 'package:flutter/material.dart'; import 'package:flutter_mobx/flutter_mobx.dart'; @@ -18,195 +15,94 @@ class PrivacyPage extends BasePage { PrivacyPage(this._privacySettingsViewModel); @override - String get title => S.current.privacy_settings; + bool get hideAppBar => true; final PrivacySettingsViewModel _privacySettingsViewModel; @override Widget body(BuildContext context) { - return SingleChildScrollView( - child: Container( - padding: EdgeInsets.only(top: 10), - child: Observer(builder: (_) { - return Column( - mainAxisSize: MainAxisSize.min, - children: [ - SettingsChoicesCell( - ChoicesListItem( - title: S.current.fiat_api, - items: FiatApiMode.all, - selectedItem: _privacySettingsViewModel.fiatApiMode, - onItemSelected: (FiatApiMode fiatApiMode) => - _privacySettingsViewModel.setFiatMode(fiatApiMode), + return ModalPageWrapper( + topBar: ModalTopBar( + title: "", + leadingIcon: Icon(Icons.arrow_back_ios_new), + onLeadingPressed: () => Navigator.of(context).pop(), + ), + header: ModalHeader( + iconPath: "assets/new-ui/settings_row_icons/privacy.svg", + message: S.of(context).privacy_desc, + title: S.of(context).privacy), + content: SingleChildScrollView( + child: Container( + child: Observer(builder: (_) { + return Column( + mainAxisSize: MainAxisSize.min, + children: [ + NewListSections( + sections: { + "1": [ + if (_privacySettingsViewModel.isAutoGenerateSubaddressesVisible) + ListItemToggle( + keyValue: "auto_generate_subaddresses", + label: _privacySettingsViewModel.isMoneroWallet + ? S.of(context).auto_generate_subaddresses + : S.of(context).auto_generate_addresses, + value: _privacySettingsViewModel.isAutoGenerateSubaddressesEnabled, + onChanged: (val) { + _privacySettingsViewModel.setAutoGenerateSubaddresses(val); + }), + ListItemToggle( + keyValue: "save_recipient_address", + label: S.of(context).settings_save_recipient_address, + value: _privacySettingsViewModel.shouldSaveRecipientAddress, + onChanged: (val) { + _privacySettingsViewModel.setShouldSaveRecipientAddress(val); + }), + if (_privacySettingsViewModel.canUsePayjoin) + ListItemToggle( + keyValue: "use_payjoin", + label: S.of(context).use_payjoin, + value: _privacySettingsViewModel.usePayjoin, + onChanged: (val) { + _privacySettingsViewModel.setUsePayjoin(val); + }), + if (_privacySettingsViewModel.canUseLightning) + ListItemToggle( + keyValue: "enable_lightning", + label: S.of(context).enable_lightning, + value: _privacySettingsViewModel.useLightning, + onChanged: (val) { + _privacySettingsViewModel.setUseLightning(val); + }), + ], + "": [ + if (_privacySettingsViewModel.isBitcoin) + ListItemRegularRow( + iconPath: "assets/new-ui/settings_row_icons/silent-payments.svg", + keyValue: "silent_payments", + label: S.of(context).silent_payments, + onTap: () => + Navigator.of(context).pushNamed(Routes.silentPaymentsSettings)), + if (_privacySettingsViewModel.isLitecoin) + ListItemRegularRow( + iconPath: "assets/new-ui/settings_row_icons/mweb.svg", + keyValue: "mweb", + label: "MWEB", + onTap: () => + Navigator.of(context).pushNamed(Routes.mwebSettings)), + if (_privacySettingsViewModel.hasCoinControl) + ListItemRegularRow( + iconPath: "assets/new-ui/settings_row_icons/coin-control.svg", + keyValue: "coin_control", + label: "Coin Control", + onTap: () => + Navigator.of(context).pushNamed(Routes.unspentCoinsList)), + ], + } ), - ), - SettingsChoicesCell( - ChoicesListItem( - title: S.current.swap, - items: ExchangeApiMode.all, - selectedItem: _privacySettingsViewModel.exchangeStatus, - onItemSelected: (ExchangeApiMode mode) => - _privacySettingsViewModel.setExchangeApiMode(mode), - ), - ), - if (_privacySettingsViewModel.canUseLightning) - SettingsSwitcherCell( - title: S.of(context).enable_lightning, - value: _privacySettingsViewModel.useLightning, - onValueChange: (_, value) => _privacySettingsViewModel.setUseLightning(value), - ), - if (_privacySettingsViewModel.canUsePayjoin) - SettingsSwitcherCell( - title: S.of(context).use_payjoin, - value: _privacySettingsViewModel.usePayjoin, - onValueChange: (BuildContext _, bool value) { - _privacySettingsViewModel.setUsePayjoin(value); - }, - ), - SettingsSwitcherCell( - title: S.current.settings_save_recipient_address, - value: _privacySettingsViewModel.shouldSaveRecipientAddress, - onValueChange: (BuildContext _, bool value) { - _privacySettingsViewModel.setShouldSaveRecipientAddress(value); - }), - if (_privacySettingsViewModel.isAutoGenerateSubaddressesVisible) - SettingsSwitcherCell( - title: _privacySettingsViewModel.isMoneroWallet - ? S.current.auto_generate_subaddresses - : S.current.auto_generate_addresses, - value: _privacySettingsViewModel.isAutoGenerateSubaddressesEnabled, - onValueChange: (BuildContext _, bool value) { - _privacySettingsViewModel.setAutoGenerateSubaddresses(value); - }, - ), - if (DeviceInfo.instance.isMobile) - SettingsSwitcherCell( - title: S.current.prevent_screenshots, - value: _privacySettingsViewModel.isAppSecure, - onValueChange: (BuildContext _, bool value) { - _privacySettingsViewModel.setIsAppSecure(value); - }, - ), - SettingsSwitcherCell( - title: S.current.disable_buy, - value: _privacySettingsViewModel.disableTradeOption, - onValueChange: (BuildContext _, bool value) { - _privacySettingsViewModel.setDisableTradeOption(value); - }, - ), - SettingsSwitcherCell( - title: S.current.disable_automatic_exchange_status_updates, - value: _privacySettingsViewModel.disableAutomaticExchangeStatusUpdates, - onValueChange: (BuildContext _, bool value) { - _privacySettingsViewModel.setDisableAutomaticExchangeStatusUpdates(value); - }, - ), - // SettingsSwitcherCell( - // title: S.current.disable_bulletin, - // value: _privacySettingsViewModel.disableBulletin, - // onValueChange: (BuildContext _, bool value) { - // _privacySettingsViewModel.setDisableBulletin(value); - // }, - // ), - if (_privacySettingsViewModel.canUseBlinkProtection) - SettingsSwitcherCell( - title: S.current.use_blink_protection, - value: _privacySettingsViewModel.useBlinkProtection, - onValueChange: (BuildContext _, bool value) { - _privacySettingsViewModel.setUseBlinkProtection(value); - }, - ), - if (_privacySettingsViewModel.canUseEtherscan) - SettingsSwitcherCell( - title: S.current.etherscan_history, - value: _privacySettingsViewModel.useEtherscan, - onValueChange: (BuildContext _, bool value) { - _privacySettingsViewModel.setUseEtherscan(value); - }, - ), - if (_privacySettingsViewModel.canUsePolygonScan) - SettingsSwitcherCell( - title: S.current.polygonscan_history, - value: _privacySettingsViewModel.usePolygonScan, - onValueChange: (BuildContext _, bool value) { - _privacySettingsViewModel.setUsePolygonScan(value); - }, - ), - if (_privacySettingsViewModel.canUseBaseScan) - SettingsSwitcherCell( - title: S.current.basescan_history, - value: _privacySettingsViewModel.useBaseScan, - onValueChange: (BuildContext _, bool value) { - _privacySettingsViewModel.setUseBaseScan(value); - }, - ), - if (_privacySettingsViewModel.canUseArbiScan) - SettingsSwitcherCell( - title: S.current.arbiscan_history, - value: _privacySettingsViewModel.useArbiScan, - onValueChange: (BuildContext _, bool value) { - _privacySettingsViewModel.setUseArbiScan(value); - }, - ), - if (_privacySettingsViewModel.canUseBscScan) - SettingsSwitcherCell( - title: S.current.bscscan_history, - value: _privacySettingsViewModel.useBscScan, - onValueChange: (BuildContext _, bool value) { - _privacySettingsViewModel.setUseBscScan(value); - }, - ), - if (_privacySettingsViewModel.canUseTronGrid) - SettingsSwitcherCell( - title: S.current.trongrid_history, - value: _privacySettingsViewModel.useTronGrid, - onValueChange: (BuildContext _, bool value) { - _privacySettingsViewModel.setUseTronGrid(value); - }, - ), - if (_privacySettingsViewModel.canUseMempoolFeeAPI) - SettingsSwitcherCell( - title: S.current.enable_mempool_api, - value: _privacySettingsViewModel.useMempoolFeeAPI, - onValueChange: (BuildContext _, bool isEnabled) async { - if (!isEnabled) { - final bool confirmation = await showPopUp( - context: context, - builder: (BuildContext context) { - return AlertWithTwoActions( - alertTitle: S.of(context).warning, - alertContent: S.of(context).disable_fee_api_warning, - rightButtonText: S.of(context).confirm, - leftButtonText: S.of(context).cancel, - actionRightButton: () => Navigator.of(context).pop(true), - actionLeftButton: () => Navigator.of(context).pop(false)); - }) ?? - false; - if (confirmation) { - _privacySettingsViewModel.setUseMempoolFeeAPI(isEnabled); - } - return; - } - - _privacySettingsViewModel.setUseMempoolFeeAPI(isEnabled); - }, - ), - SettingsCellWithArrow( - title: S.current.domain_looks_up, - handler: (context) => Navigator.of(context).pushNamed(Routes.domainLookupsPage), - ), - if(_privacySettingsViewModel.hasCoinControl) - SettingsCellWithArrow( - title: "Coin Control", - handler: (context) => Navigator.of(context).pushNamed(Routes.unspentCoinsList), - ), - // SettingsCellWithArrow( - // title: 'Trocador providers', - // handler: (context) => Navigator.of(context).pushNamed(Routes.trocadorProvidersPage), - // ), - ], - ); - }), + ], + ); + }), + ), ), ); } diff --git a/lib/src/screens/settings/security_backup_page.dart b/lib/src/screens/settings/security_backup_page.dart index 714b69535..37fb7c710 100644 --- a/lib/src/screens/settings/security_backup_page.dart +++ b/lib/src/screens/settings/security_backup_page.dart @@ -2,9 +2,12 @@ import 'dart:io'; import 'package:cake_wallet/core/auth_service.dart'; import 'package:cake_wallet/entities/new_ui_entities/list_item/list_item_regular_row.dart'; +import 'package:cake_wallet/entities/new_ui_entities/list_item/list_item_selector.dart'; import 'package:cake_wallet/entities/new_ui_entities/list_item/list_item_toggle.dart'; import 'package:cake_wallet/entities/pin_code_required_duration.dart'; import 'package:cake_wallet/new-ui/widgets/modal_header.dart'; +import 'package:cake_wallet/new-ui/widgets/modal_page_wrapper.dart'; +import 'package:cake_wallet/new-ui/widgets/receive_page/receive_top_bar.dart'; import 'package:cake_wallet/routes.dart'; import 'package:cake_wallet/src/screens/base_page.dart'; import 'package:cake_wallet/generated/i18n.dart'; @@ -14,7 +17,6 @@ import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart'; import 'package:cake_wallet/src/widgets/new_list_row/new_list_section.dart'; import 'package:cake_wallet/src/widgets/picker.dart'; import 'package:cake_wallet/utils/device_info.dart'; -import 'package:cake_wallet/store/settings_store.dart'; import 'package:cake_wallet/utils/feature_flag.dart'; import 'package:cake_wallet/utils/show_pop_up.dart'; import 'package:cake_wallet/view_model/settings/security_settings_view_model.dart'; @@ -28,7 +30,10 @@ class SecurityBackupPage extends BasePage { final AuthService _authService; @override - String get title => S.current.security; + bool get hideAppBar => true; + + @override + String get title => S.current.security_and_backup; final SecuritySettingsViewModel _securitySettingsViewModel; @@ -36,170 +41,160 @@ class SecurityBackupPage extends BasePage { @override Widget body(BuildContext context) { - return Container( - padding: EdgeInsets.only(top: 10), - child: Padding( - padding: const EdgeInsets.symmetric(horizontal: 18.0), - child: Column( - spacing: 16, - mainAxisSize: MainAxisSize.min, - children: [ - ModalHeader( - iconPath: "assets/new-ui/settings_row_icons/security.svg", - message: S.of(context).privacy_and_security_desc, - title: S.of(context).security), - Observer( - builder: (_) => NewListSections(sections: { - "": [ - if (DeviceInfo.instance.isMobile || Platform.isMacOS || Platform.isLinux) - ListItemToggle( - keyValue: "security_backup_page_allow_biometrics_button_key", - label: S.current.settings_allow_biometrical_authentication, - value: _securitySettingsViewModel.allowBiometricalAuthentication, - onChanged: (bool value) { - if (value) { - _authService.authenticateAction( - context, - onAuthSuccess: (isAuthenticatedSuccessfully) async { - if (isAuthenticatedSuccessfully) { - if (await _securitySettingsViewModel.biometricAuthenticated()) { - _securitySettingsViewModel.setAllowBiometricalAuthentication( - isAuthenticatedSuccessfully); - } - } else { + return ModalPageWrapper( + topBar: ModalTopBar( + title: "", + leadingIcon: Icon(Icons.arrow_back_ios_new), + onLeadingPressed: () => Navigator.of(context).pop()), + header: ModalHeader( + iconPath: "assets/new-ui/settings_row_icons/security.svg", + message: S.of(context).privacy_and_security_desc, + title: S.of(context).security), + content: Column( + spacing: 16, + mainAxisSize: MainAxisSize.min, + children: [ + Observer( + builder: (_) => NewListSections(sections: { + "": [ + if (DeviceInfo.instance.isMobile || Platform.isMacOS || Platform.isLinux) + ListItemToggle( + keyValue: "security_backup_page_allow_biometrics_button_key", + label: S.current.settings_allow_biometrical_authentication, + value: _securitySettingsViewModel.allowBiometricalAuthentication, + onChanged: (bool value) { + if (value) { + _authService.authenticateAction( + context, + onAuthSuccess: (isAuthenticatedSuccessfully) async { + if (isAuthenticatedSuccessfully) { + if (await _securitySettingsViewModel.biometricAuthenticated()) { _securitySettingsViewModel.setAllowBiometricalAuthentication( isAuthenticatedSuccessfully); } - }, - conditionToDetermineIfToUse2FA: _securitySettingsViewModel - .shouldRequireTOTP2FAForAllSecurityAndBackupSettings, - ); - } else { - _securitySettingsViewModel.setAllowBiometricalAuthentication(value); - } - }), - if (FeatureFlag.duressPinEnabled) - ListItemToggle( - keyValue: "security_backup_page_duress_pin_button_key", - label: "Duress PIN", - value: _securitySettingsViewModel.enableDuressPin, - onChanged: (bool value) { - _authService - .authenticateAction(context, route: Routes.securityBackupDuressPin, - onAuthSuccess: (isAuthenticatedSuccessfully) async { - if (isAuthenticatedSuccessfully) { - if (!value) { - _securitySettingsViewModel.setEnableDuressPin(value); - _securitySettingsViewModel.clearDuressPin(); - return; + } else { + _securitySettingsViewModel.setAllowBiometricalAuthentication( + isAuthenticatedSuccessfully); } - final res = await _showDuressPinDescription(context); - if (res) { - final confirmation = await _showDuressPinConfirmation(context); + }, + conditionToDetermineIfToUse2FA: _securitySettingsViewModel + .shouldRequireTOTP2FAForAllSecurityAndBackupSettings, + ); + } else { + _securitySettingsViewModel.setAllowBiometricalAuthentication(value); + } + }), + if (DeviceInfo.instance.isMobile) + ListItemToggle( + keyValue: "display_settings_prevent_screen_capture", + label: S.of(context).prevent_screenshots, + value: _securitySettingsViewModel.isAppSecure, + onChanged: (val) { + _securitySettingsViewModel.setIsAppSecure(val); + }), + if (FeatureFlag.duressPinEnabled) + ListItemToggle( + keyValue: "security_backup_page_duress_pin_button_key", + label: "Duress PIN", + value: _securitySettingsViewModel.enableDuressPin, + onChanged: (bool value) { + _authService + .authenticateAction(context, route: Routes.securityBackupDuressPin, + onAuthSuccess: (isAuthenticatedSuccessfully) async { + if (isAuthenticatedSuccessfully) { + if (!value) { + _securitySettingsViewModel.setEnableDuressPin(value); + _securitySettingsViewModel.clearDuressPin(); + return; + } + final res = await _showDuressPinDescription(context); + if (res) { + final confirmation = await _showDuressPinConfirmation(context); - if (confirmation) { - Navigator.of(context).pushNamed( - Routes.setupDuressPin, - arguments: - (PinCodeState pinCtx, String _) async { - pinCtx.close(); - _securitySettingsViewModel.setEnableDuressPin(true); - }, - ); - } + if (confirmation) { + Navigator.of(context).pushNamed( + Routes.setupDuressPin, + arguments: + (PinCodeState pinCtx, String _) async { + pinCtx.close(); + _securitySettingsViewModel.setEnableDuressPin(true); + }, + ); } } - }, - conditionToDetermineIfToUse2FA: _securitySettingsViewModel - .shouldRequireTOTP2FAForAllSecurityAndBackupSettings); - }), - ListItemRegularRow( - keyValue: "security_backup_page_require_pin_after_button_key", - label: S.current.require_pin_after, - trailingText: _securitySettingsViewModel.pinCodeRequiredDuration.toString(), - onTap: () async { - final items = PinCodeRequiredDuration.values; + } + }, + conditionToDetermineIfToUse2FA: _securitySettingsViewModel + .shouldRequireTOTP2FAForAllSecurityAndBackupSettings); + }), + ListItemSelector( + keyValue: "security_backup_page_require_pin_after_button_key", + label: S.current.require_pin_after, + options: [_securitySettingsViewModel.pinCodeRequiredDuration.toString()], + onTap: () async { + final items = PinCodeRequiredDuration.values; - final selectedAtIndex = - items.indexOf(_securitySettingsViewModel.pinCodeRequiredDuration); + final selectedAtIndex = + items.indexOf(_securitySettingsViewModel.pinCodeRequiredDuration); - await showPopUp( - context: context, - builder: (_) => Picker( - items: items, - selectedAtIndex: selectedAtIndex, - mainAxisAlignment: MainAxisAlignment.start, - onItemSelected: (PinCodeRequiredDuration item) { - _securitySettingsViewModel.setPinCodeRequiredDuration(item); - }, - isSeparated: false, - ), - ); - }), - // if (!_isHardwareWallet) - // ListItemRegularRow( - // keyValue: "security_backup_page_show_keys_button_key", - // label: S.current.show_keys, - // onTap: () { - // _authService.authenticateAction( - // context, - // route: Routes.showKeys, - // conditionToDetermineIfToUse2FA: _securitySettingsViewModel - // .shouldRequireTOTP2FAForAllSecurityAndBackupSettings, - // ); - // }), - // if (!SettingsStoreBase.walletPasswordDirectInput) - // ListItemRegularRow( - // keyValue: "security_backup_page_change_password_button_key", - // label: S.current.create_backup, - // onTap: () { - // _authService.authenticateAction( - // context, - // route: Routes.backup, - // conditionToDetermineIfToUse2FA: _securitySettingsViewModel - // .shouldRequireTOTP2FAForAllSecurityAndBackupSettings, - // ); - // }), - ListItemRegularRow( - keyValue: "security_backup_page_change_pin_button_key", - label: S.current.settings_change_pin, - onTap: () { - _authService.authenticateAction( - context, - route: Routes.setupPin, - arguments: (PinCodeState setupPinContext, String _) { - setupPinContext.close(); + await showPopUp( + context: context, + builder: (_) => Picker( + items: items, + selectedAtIndex: selectedAtIndex, + mainAxisAlignment: MainAxisAlignment.start, + onItemSelected: (PinCodeRequiredDuration item) { + _securitySettingsViewModel.setPinCodeRequiredDuration(item); }, - conditionToDetermineIfToUse2FA: _securitySettingsViewModel - .shouldRequireTOTP2FAForAllSecurityAndBackupSettings, - ); - }), - ListItemRegularRow( - keyValue: "security_backup_page_sign_and_verify", - label: S.current.sign_verify_title, - onTap: () { - Navigator.of(context).pushNamed(Routes.signPage); - }), - ListItemRegularRow( - keyValue: "security_backup_page_totp_2fa_button_key", - label: _securitySettingsViewModel.useTotp2FA - ? S.current.modify_2fa - : S.current.setup_2fa, - onTap: () { - _authService.authenticateAction( - context, - route: _securitySettingsViewModel.useTotp2FA - ? Routes.modify2FAPage - : Routes.setup2faInfoPage, - conditionToDetermineIfToUse2FA: _securitySettingsViewModel - .shouldRequireTOTP2FAForAllSecurityAndBackupSettings, - ); - }) - ] - }), - ), - ], - ), + isSeparated: false, + ), + ); + }), + // if (!_isHardwareWallet) + // ListItemRegularRow( + // keyValue: "security_backup_page_show_keys_button_key", + // label: S.current.show_keys, + // onTap: () { + // _authService.authenticateAction( + // context, + // route: Routes.showKeys, + // conditionToDetermineIfToUse2FA: _securitySettingsViewModel + // .shouldRequireTOTP2FAForAllSecurityAndBackupSettings, + // ); + // }), + ListItemRegularRow( + keyValue: "security_backup_page_change_pin_button_key", + label: S.current.settings_change_pin, + onTap: () { + _authService.authenticateAction( + context, + route: Routes.setupPin, + arguments: (PinCodeState setupPinContext, String _) { + setupPinContext.close(); + }, + conditionToDetermineIfToUse2FA: _securitySettingsViewModel + .shouldRequireTOTP2FAForAllSecurityAndBackupSettings, + ); + }), + ListItemRegularRow( + keyValue: "security_backup_page_totp_2fa_button_key", + label: _securitySettingsViewModel.useTotp2FA + ? S.current.modify_2fa + : S.current.setup_2fa, + onTap: () { + _authService.authenticateAction( + context, + route: _securitySettingsViewModel.useTotp2FA + ? Routes.modify2FAPage + : Routes.setup2faInfoPage, + conditionToDetermineIfToUse2FA: _securitySettingsViewModel + .shouldRequireTOTP2FAForAllSecurityAndBackupSettings, + ); + }) + ] + }), + ), + ], ), ); } diff --git a/lib/src/screens/settings/widgets/settings_choices_cell.dart b/lib/src/screens/settings/widgets/settings_choices_cell.dart index 798093f8d..fdf81d569 100644 --- a/lib/src/screens/settings/widgets/settings_choices_cell.dart +++ b/lib/src/screens/settings/widgets/settings_choices_cell.dart @@ -12,9 +12,13 @@ class SettingsChoicesCell extends StatelessWidget { @override Widget build(BuildContext context) { + final items = choicesListItem.items; + final selectedIndex = items.indexOf(choicesListItem.selectedItem); + final itemCount = items.length; + return Container( color: useGenericColor ? Theme.of(context).colorScheme.surface : null, - padding: padding ?? EdgeInsets.only(left: 24, right: 24, top: 16, bottom: 16), + padding: padding ?? const EdgeInsets.only(left: 24, right: 24, top: 16, bottom: 16), child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, @@ -33,46 +37,68 @@ class SettingsChoicesCell extends StatelessWidget { Center( child: Container( decoration: BoxDecoration( - borderRadius: BorderRadius.circular(10), + borderRadius: BorderRadius.circular(12), border: useGenericColor ? null : Border.all( color: Theme.of(context).colorScheme.surfaceContainerHighest, width: 1.5, ), - color: - useGenericColor ? Theme.of(context).colorScheme.surfaceContainerHighest : null, + color: useGenericColor + ? Theme.of(context).colorScheme.surfaceContainerHighest + : null, ), - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: choicesListItem.items.map((dynamic e) { - final isSelected = choicesListItem.selectedItem == e; - return Expanded( - child: GestureDetector( - onTap: () { - choicesListItem.onItemSelected.call(e); - }, - child: Container( - padding: EdgeInsets.symmetric(vertical: 8), - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(10), - color: isSelected ? Theme.of(context).colorScheme.primary : null, - ), - child: Center( - child: Text( - choicesListItem.displayItem.call(e), - style: Theme.of(context).textTheme.bodyMedium?.copyWith( - color: isSelected - ? Theme.of(context).colorScheme.onPrimary - : Theme.of(context).colorScheme.onSurfaceVariant, - fontWeight: isSelected ? FontWeight.w600 : FontWeight.normal, - ), + child: LayoutBuilder( + builder: (context, constraints) { + final itemWidth = constraints.maxWidth / itemCount; + return Stack( + children: [ + if (selectedIndex >= 0) + AnimatedPositioned( + duration: const Duration(milliseconds: 450), + curve: Curves.easeInOut, + left: selectedIndex * itemWidth, + top: 0, + bottom: 0, + width: itemWidth, + child: Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(12), + color: Theme.of(context).colorScheme.primary, + ), ), ), + Row( + children: items.map((dynamic e) { + final isSelected = choicesListItem.selectedItem == e; + return Expanded( + child: GestureDetector( + onTap: () => choicesListItem.onItemSelected.call(e), + child: Container( + padding: const EdgeInsets.symmetric(vertical: 8), + color: Colors.transparent, + child: Center( + child: AnimatedDefaultTextStyle( + duration: const Duration(milliseconds: 220), + curve: Curves.easeInOut, + style: Theme.of(context).textTheme.bodyMedium!.copyWith( + color: isSelected + ? Theme.of(context).colorScheme.onPrimary + : Theme.of(context).colorScheme.onSurfaceVariant, + fontWeight: + isSelected ? FontWeight.w600 : FontWeight.normal, + ), + child: Text(choicesListItem.displayItem.call(e)), + ), + ), + ), + ), + ); + }).toList(), ), - ), + ], ); - }).toList(), + }, ), ), ), @@ -80,4 +106,4 @@ class SettingsChoicesCell extends StatelessWidget { ), ); } -} +} \ No newline at end of file diff --git a/lib/src/screens/settings/widgets/settings_switcher_cell.dart b/lib/src/screens/settings/widgets/settings_switcher_cell.dart index 34019c2c8..1c18036a7 100644 --- a/lib/src/screens/settings/widgets/settings_switcher_cell.dart +++ b/lib/src/screens/settings/widgets/settings_switcher_cell.dart @@ -13,6 +13,7 @@ class SettingsSwitcherCell extends StandardListRow { Key? key, this.padding, this.switchBackgroundColor, + this.height = 56, }) : super(title: title, isSelected: false, decoration: decoration, onTap: onTap, key: key); final bool value; @@ -20,6 +21,7 @@ class SettingsSwitcherCell extends StandardListRow { final void Function(BuildContext context, bool value)? onValueChange; final Widget? leading; final EdgeInsets? padding; + final double? height; @override Widget buildTrailing(BuildContext context) => StandardSwitch( @@ -33,7 +35,7 @@ class SettingsSwitcherCell extends StandardListRow { final leading = buildLeading(context); final trailing = buildTrailing(context); return Container( - height: 56, + height: height, padding: padding ?? EdgeInsets.only(left: 12, right: 12), child: TextButton( onPressed: () { diff --git a/lib/src/screens/settings/widgets/settings_theme_choice.dart b/lib/src/screens/settings/widgets/settings_theme_choice.dart index ed6d8ca66..c96743eb3 100644 --- a/lib/src/screens/settings/widgets/settings_theme_choice.dart +++ b/lib/src/screens/settings/widgets/settings_theme_choice.dart @@ -12,9 +12,9 @@ class SettingsThemeChoicesCell extends StatelessWidget { final DisplaySettingsViewModel _displaySettingsViewModel; - final double cellHeight = 25; + final double cellHeight = 12; final double cellWidth = 12; - final double cellRadius = 8; + final double cellRadius = 18; @override Widget build(BuildContext context) { @@ -28,14 +28,17 @@ class SettingsThemeChoicesCell extends StatelessWidget { final previewHeight = _getResponsivePreviewHeight(screenHeight); final previewWidth = previewHeight * 0.6; - return Container( + return AnimatedContainer( + duration: const Duration(milliseconds: 350), + curve: Curves.easeInOut, height: getHeight(context, currentTheme, currentTheme.hasAccentColors), - padding: EdgeInsets.all(12), + padding: EdgeInsets.all(14), child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ - Expanded( + SizedBox( + height: previewHeight, child: ListView.builder( itemCount: availableThemes.length, scrollDirection: Axis.horizontal, @@ -50,16 +53,16 @@ class SettingsThemeChoicesCell extends StatelessWidget { onTap: () { _displaySettingsViewModel.onThemeSelected(theme); }, - child: Container( + child: AnimatedContainer( + duration: const Duration(milliseconds: 350), + curve: Curves.easeInOut, margin: EdgeInsets.only(right: 24), - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(cellRadius), - border: isSelected - ? Border.all( - color: Theme.of(context).colorScheme.primary, + decoration: ShapeDecoration( + shape: RoundedSuperellipseBorder(borderRadius: BorderRadius.circular(cellRadius), + side: BorderSide( + color: isSelected ? Theme.of(context).colorScheme.primary : Colors.transparent, strokeAlign: BorderSide.strokeAlignOutside) - : null, - ), + )), child: ClipRRect( borderRadius: BorderRadius.circular(cellRadius), child: CakeImageWidget( @@ -75,56 +78,94 @@ class SettingsThemeChoicesCell extends StatelessWidget { }, ), ), - if (_displaySettingsViewModel.currentTheme.hasAccentColors) ...[ - SizedBox(height: cellHeight), - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text( - S.of(context).accent_color, - style: Theme.of(context).textTheme.bodyMedium, - ), - Container( - child: Row( - children: availableAccentColors.map((accentColor) { - final isSelected = _displaySettingsViewModel - .isAccentColorSelected(accentColor.name.toLowerCase()); - return GestureDetector( - onTap: () { - _displaySettingsViewModel - .onAccentColorSelected(accentColor.name.toLowerCase()); - }, - child: Container( - width: 26, - height: 26, - margin: EdgeInsets.only(right: 11), - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(16), - border: isSelected - ? Border.all( - color: Theme.of(context).colorScheme.onSurfaceVariant, - width: 3) - : null, - color: accentColor.color, + AnimatedSwitcher( + duration: const Duration(milliseconds: 350), + switchInCurve: Curves.easeOut, + switchOutCurve: Curves.easeIn, + transitionBuilder: (child, animation) => FadeTransition( + opacity: animation, + child: SizeTransition( + sizeFactor: animation, + axisAlignment: -1, + child: child, + ), + ), + child: _displaySettingsViewModel.currentTheme.hasAccentColors + ? Column( + key: const ValueKey('accent'), + mainAxisSize: MainAxisSize.min, + children: [ + Padding( + padding: EdgeInsets.only(top: 14), + child: Container(height: 1, color: Theme.of(context).colorScheme.outlineVariant) + ), + SizedBox(height: cellHeight), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + S.of(context).accent_color, + style: Theme.of(context).textTheme.bodyMedium, ), - ), - ); - }).toList(), - ), - ), - ], - ), - ], + Row( + spacing: 4, + children: availableAccentColors.map((accentColor) { + final isSelected = _displaySettingsViewModel + .isAccentColorSelected(accentColor.name.toLowerCase()); + return GestureDetector( + onTap: () { + _displaySettingsViewModel + .onAccentColorSelected(accentColor.name.toLowerCase()); + }, + child: Stack( + children: [ + AnimatedOpacity( + duration: Duration(milliseconds: 350), + opacity: isSelected ? 1 : 0, + child: Container( + width:28,height:28,decoration: BoxDecoration(borderRadius: BorderRadius.circular(99999999),border: Border.all(color:Theme.of(context) + .colorScheme + .onSurface)) + ), + ), + AnimatedScale( + duration: Duration(milliseconds: 350), + scale: isSelected ? 0.8 : 1, + child: Container( + width: 28, + height: 28, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(99999999), + color: accentColor.color), + ), + ), + ], + ), + ); + }).toList(), + ), + ], + ), + ], + ) + : const SizedBox.shrink(key: ValueKey('no-accent')), + ), if (_displaySettingsViewModel.currentTheme is BlackTheme) - SettingsSwitcherCell( - title: S.current.oled_mode, - value: _displaySettingsViewModel.isBlackThemeOledEnabled, - onValueChange: (_, bool value) { - _displaySettingsViewModel.setBlackThemeOled(value); - }, - padding: EdgeInsets.zero, - switchBackgroundColor: currentTheme.colorScheme.secondaryContainer, + Padding( + padding: EdgeInsets.only(top: 12, bottom: 4), + child: Container(height: 1, color: Theme.of(context).colorScheme.outlineVariant) ), + if (_displaySettingsViewModel.currentTheme is BlackTheme) + SettingsSwitcherCell( + height: 40, + title: S.current.oled_mode, + value: _displaySettingsViewModel.isBlackThemeOledEnabled, + onValueChange: (_, bool value) { + _displaySettingsViewModel.setBlackThemeOled(value); + }, + padding: EdgeInsets.zero, + switchBackgroundColor: currentTheme.colorScheme.secondaryContainer, + ), ], ), ); @@ -135,14 +176,14 @@ class SettingsThemeChoicesCell extends StatelessWidget { double getHeight(BuildContext context, MaterialThemeBase theme, bool hasAccentColors) { final screenHeight = MediaQuery.of(context).size.height; - double baseHeight = (screenHeight * 0.25).clamp(150.0, screenHeight * 0.5); + double baseHeight = (screenHeight * 0.253).clamp(150.0, screenHeight * 0.5); if (hasAccentColors) { - baseHeight += (screenHeight * 0.05).clamp(35.0, 60.0); + baseHeight += (screenHeight * 0.60).clamp(35.0, 60.0); } if (theme is BlackTheme) { - baseHeight += (screenHeight * 0.08).clamp(48.0, 96.0); + baseHeight += (screenHeight * 0.059).clamp(48.0, 96.0); } return baseHeight; diff --git a/lib/src/widgets/new_list_row/list_Item_style_wrapper.dart b/lib/src/widgets/new_list_row/list_Item_style_wrapper.dart index a56137568..1e4d5e70f 100644 --- a/lib/src/widgets/new_list_row/list_Item_style_wrapper.dart +++ b/lib/src/widgets/new_list_row/list_Item_style_wrapper.dart @@ -6,20 +6,21 @@ class ListItemStyleWrapper extends StatelessWidget { required this.isFirstInSection, required this.isLastInSection, required this.builder, - this.hasImage, this.onTap, this.height = 50, + this.iconPath, }); + final String? iconPath; final bool isFirstInSection; final bool isLastInSection; - final bool ?hasImage; final double height; final VoidCallback? onTap; final Widget Function(BuildContext context, TextStyle textStyle, TextStyle labelStyle) builder; @override Widget build(BuildContext context) { + final theme = Theme.of(context); final textStyle = TextStyle( @@ -60,17 +61,17 @@ class ListItemStyleWrapper extends StatelessWidget { child: Padding( padding: const EdgeInsets.symmetric(horizontal: 12), child: builder(context, textStyle, labelStyle))))), - if(hasImage == true && isLastInSection == false) Container( + if(iconPath != null && isLastInSection == false) Container( color: theme.colorScheme.surfaceContainer, child: Padding( - padding: const EdgeInsets.only(left: 48, right: 13), + padding: const EdgeInsets.only(left: 50, right: 13), child: Container(height: 1, color: theme.colorScheme.outlineVariant), ), ) else if(!isLastInSection) Container( color: theme.colorScheme.surfaceContainer, child: Padding( - padding: const EdgeInsets.symmetric(horizontal: 13), + padding: const EdgeInsets.symmetric(horizontal: 12), child: Container(height: 1, color: theme.colorScheme.outlineVariant), ), ) diff --git a/lib/src/widgets/new_list_row/list_item_checkbox_widget.dart b/lib/src/widgets/new_list_row/list_item_checkbox_widget.dart index 0d2422182..823328723 100644 --- a/lib/src/widgets/new_list_row/list_item_checkbox_widget.dart +++ b/lib/src/widgets/new_list_row/list_item_checkbox_widget.dart @@ -37,6 +37,7 @@ class _ListItemCheckboxWidgetState extends State { @override Widget build(BuildContext context) { return ListItemStyleWrapper( + iconPath: widget.iconPath, onTap: widget.onTap ?? () { widget.onChanged(!widget.value); }, @@ -53,11 +54,11 @@ class _ListItemCheckboxWidgetState extends State { children: [ if (widget.iconPath != null) widget.iconPath!.toLowerCase().endsWith("svg") - ? CakeImageWidget(imageUrl:widget.iconPath!, height: 24, width: 24) + ? CakeImageWidget(imageUrl:widget.iconPath!, height: 26, width: 26) : Image.asset( widget.iconPath!, - width: 24, - height: 24, + width: 26, + height: 26, ), Expanded( child: Column( diff --git a/lib/src/widgets/new_list_row/list_item_regular_row_widget.dart b/lib/src/widgets/new_list_row/list_item_regular_row_widget.dart index 36e509229..7705cdcac 100644 --- a/lib/src/widgets/new_list_row/list_item_regular_row_widget.dart +++ b/lib/src/widgets/new_list_row/list_item_regular_row_widget.dart @@ -12,7 +12,6 @@ class ListItemRegularRowWidget extends StatelessWidget { this.trailingText, this.iconPath, this.onTap, - this.hasImage, this.isFirstInSection = false, this.isLastInSection = false, this.showArrow = true, @@ -28,7 +27,6 @@ class ListItemRegularRowWidget extends StatelessWidget { final String? trailingText; final String? iconPath; final VoidCallback? onTap; - final bool? hasImage; final bool isFirstInSection; final bool isLastInSection; final bool showArrow; @@ -46,7 +44,7 @@ class ListItemRegularRowWidget extends StatelessWidget { return ListItemStyleWrapper( onTap: onTap, - hasImage: iconPath != null ? true : false, + iconPath: iconPath, isFirstInSection: isFirstInSection, isLastInSection: isLastInSection, height: subtitle != null ? 64 : 50, diff --git a/lib/src/widgets/new_list_row/list_item_selector_widget.dart b/lib/src/widgets/new_list_row/list_item_selector_widget.dart index b3e0790cf..cb138906f 100644 --- a/lib/src/widgets/new_list_row/list_item_selector_widget.dart +++ b/lib/src/widgets/new_list_row/list_item_selector_widget.dart @@ -33,25 +33,25 @@ class ListItemSelectorWidget extends StatelessWidget { isLastInSection: isLastInSection, builder: (context, textStyle, labelStyle) { return Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Flexible(child: Text(label, style: textStyle)), - Row( - children: [ - Text( - options[selectedIndex], - style: labelStyle, - ), - CakeImageWidget(imageUrl: - "assets/new-ui/chooser.svg", - colorFilter: - ColorFilter.mode(theme.colorScheme.onSurfaceVariant, BlendMode.srcIn), - ), - SizedBox(width: 8) - ], - ), - ], + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Flexible(child: Text(label, style: textStyle)), + Row( + spacing: 8, + children: [ + Text( + options[selectedIndex], + style: labelStyle, + ), + CakeImageWidget(imageUrl: + "assets/new-ui/chooser.svg", + colorFilter: + ColorFilter.mode(theme.colorScheme.onSurfaceVariant, BlendMode.srcIn), + ), + ], + ), + ], ); }); } -} +} \ No newline at end of file diff --git a/lib/src/widgets/standard_list.dart b/lib/src/widgets/standard_list.dart index 3e5fd1bd9..f0afdcde5 100644 --- a/lib/src/widgets/standard_list.dart +++ b/lib/src/widgets/standard_list.dart @@ -6,12 +6,14 @@ class StandardListRow extends StatelessWidget { StandardListRow({ required this.title, required this.isSelected, + this.subtitle, this.onTap, this.decoration, super.key, }); final String title; + final String? subtitle; final bool isSelected; final void Function(BuildContext context)? onTap; final Decoration? decoration; @@ -48,18 +50,52 @@ class StandardListRow extends StatelessWidget { Widget? buildLeading(BuildContext context) => null; Widget buildCenter(BuildContext context, {required bool hasLeftOffset}) { + return Expanded( child: Row( mainAxisAlignment: MainAxisAlignment.start, children: [ if (hasLeftOffset) SizedBox(width: 10), Expanded( - child: Text( - title, - style: Theme.of(context).textTheme.bodyMedium?.copyWith( - color: titleColor(context), - fontWeight: isSelected ? FontWeight.w800 : FontWeight.w400, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + if (subtitle != null && subtitle!.isNotEmpty) + Align( + alignment: Alignment.centerLeft, + child: Text( + textAlign: TextAlign.left, + subtitle!, + style: Theme.of(context).textTheme.labelMedium?.copyWith( + color: titleColor(context), + fontWeight: isSelected ? FontWeight.w800 : FontWeight.w400, + ), + ), ), + if (subtitle != null && subtitle!.isNotEmpty) + Align( + alignment: Alignment.centerLeft, + child: Text( + textAlign: TextAlign.left, + title, + style: Theme.of(context).textTheme.labelSmall?.copyWith( + color: Theme.of(context).colorScheme.onSurfaceVariant, + fontWeight: isSelected ? FontWeight.w800 : FontWeight.w400, + ), + ), + ) + else + Align( + alignment: Alignment.centerLeft, + child: Text( + title, + style: Theme.of(context).textTheme.bodyMedium?.copyWith( + color: titleColor(context), + fontWeight: isSelected ? FontWeight.w800 : FontWeight.w400, + ), + ), + ), + ], ), ) ], @@ -138,6 +174,7 @@ class SectionStandardList extends StatelessWidget { this.hasTopSeparator = false, this.scrollController, this.physics, + this.shrinkWrap = false, }) : totalRows = []; final int sectionCount; @@ -150,6 +187,7 @@ class SectionStandardList extends StatelessWidget { final EdgeInsets dividerPadding; final ScrollController? scrollController; final ScrollPhysics? physics; + final bool shrinkWrap; List transform( bool hasTopSeparator, @@ -208,6 +246,7 @@ class SectionStandardList extends StatelessWidget { return ListView.separated( controller: scrollController, physics: physics, + shrinkWrap: shrinkWrap, separatorBuilder: (_, index) { final row = totalRows[index]; diff --git a/lib/view_model/dashboard/dashboard_view_model.dart b/lib/view_model/dashboard/dashboard_view_model.dart index 4fd628bb0..2a699e384 100644 --- a/lib/view_model/dashboard/dashboard_view_model.dart +++ b/lib/view_model/dashboard/dashboard_view_model.dart @@ -327,7 +327,7 @@ abstract class DashboardViewModelBase with Store { } catch (_) {} } return length * confirmations; - }, _transactionDisposerCallback); + }, _transactionDisposerCallback, delay: 300); if (hasSilentPayments) { silentPaymentsScanningActive = bitcoin!.getScanningActive(wallet); @@ -481,13 +481,23 @@ abstract class DashboardViewModelBase with Store { } // printV("Transaction disposer callback (relevantTxs: ${relevantTxs.length} current: ${transactions.length})"); - transactions.clear(); - transactions.addAll(relevantTxs.map((tx) => TransactionListItem( - transaction: tx, - balanceViewModel: balanceViewModel, - appStore: appStore, - key: ValueKey('${wallet.type.name}_transaction_history_item_${tx.id}_key'), - ))); + final newTransactions = relevantTxs + .map((tx) => TransactionListItem( + transaction: tx, + balanceViewModel: balanceViewModel, + appStore: appStore, + key: ValueKey('${wallet.type.name}_transaction_history_item_${tx.id}_key'), + )) + .where((item) => !transactions.contains(item)); + + transactions.addAll(newTransactions); + // transactions.clear(); + // transactions.addAll(relevantTxs.map((tx) => TransactionListItem( + // transaction: tx, + // balanceViewModel: balanceViewModel, + // appStore: appStore, + // key: ValueKey('${wallet.type.name}_transaction_history_item_${tx.id}_key'), + // ))); } finally { _isTransactionDisposerCallbackRunning = false; } @@ -671,6 +681,10 @@ abstract class DashboardViewModelBase with Store { bool get hasLightning => wallet.type == WalletType.bitcoin && wallet.isSoftwareWallet && bitcoin!.useLightning(wallet); + @computed + bool get hasWalletConnect => + isWalletConnectCompatibleChain(wallet.type) && !wallet.isHardwareWallet; + @computed bool get isTestnet => wallet.type == WalletType.bitcoin && bitcoin!.isTestnet(wallet); @@ -1248,7 +1262,7 @@ abstract class DashboardViewModelBase with Store { } catch (_) {} } return length * confirmations; - }, _transactionDisposerCallback); + }, _transactionDisposerCallback, delay: 300); } @action diff --git a/lib/view_model/dashboard/transaction_list_item.dart b/lib/view_model/dashboard/transaction_list_item.dart index e765e88c5..de025e114 100644 --- a/lib/view_model/dashboard/transaction_list_item.dart +++ b/lib/view_model/dashboard/transaction_list_item.dart @@ -289,4 +289,12 @@ class TransactionListItem extends ActionListItem with Keyable { @override DateTime get date => transaction.date; + + @override + bool operator ==(Object other) { + if (other is TransactionListItem) { + return other.transaction.txHash == transaction.txHash; + } + return false; + } } diff --git a/lib/view_model/settings/connection_sync_view_model.dart b/lib/view_model/settings/connection_sync_view_model.dart new file mode 100644 index 000000000..b48ae3230 --- /dev/null +++ b/lib/view_model/settings/connection_sync_view_model.dart @@ -0,0 +1,236 @@ +import 'dart:async'; + +import 'package:cake_wallet/entities/exchange_api_mode.dart'; +import 'package:cake_wallet/entities/fiat_api_mode.dart'; +import 'package:cake_wallet/evm/evm.dart'; +import 'package:cake_wallet/generated/i18n.dart'; +import 'package:cake_wallet/reactions/wallet_connect.dart'; +import 'package:cake_wallet/src/widgets/alert_with_one_action.dart'; +import 'package:cake_wallet/store/app_store.dart'; +import 'package:cake_wallet/store/settings_store.dart'; +import 'package:cake_wallet/tron/tron.dart'; +import 'package:cake_wallet/utils/show_pop_up.dart'; +import 'package:cake_wallet/utils/tor.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:flutter/material.dart'; +import 'package:mobx/mobx.dart'; + +part 'connection_sync_view_model.g.dart'; + +class ConnectionSyncViewModel = ConnectionSyncViewModelBase with _$ConnectionSyncViewModel; + +abstract class ConnectionSyncViewModelBase with Store { + ConnectionSyncViewModelBase(this._settingsStore, this._wallet); + + final SettingsStore _settingsStore; + final WalletBase, TransactionInfo> _wallet; + + @computed + bool get lookupTwitter => _settingsStore.lookupsTwitter; + + @computed + bool get lookupsZanoAlias => _settingsStore.lookupsZanoAlias; + + @computed + bool get looksUpMastodon => _settingsStore.lookupsMastodon; + + @computed + bool get looksUpYatService => _settingsStore.lookupsYatService; + + @computed + bool get looksUpUnstoppableDomains => _settingsStore.lookupsUnstoppableDomains; + + @computed + bool get looksUpOpenAlias => _settingsStore.lookupsOpenAlias; + + @computed + bool get looksUpENS => _settingsStore.lookupsENS; + + @computed + bool get looksUpWellKnown => _settingsStore.lookupsWellKnown; + + @computed + ExchangeApiMode get exchangeStatus => _settingsStore.exchangeStatus; + + @computed + FiatApiMode get fiatApiMode => _settingsStore.fiatApiMode; + + @computed + bool get disableAutomaticExchangeStatusUpdates => + _settingsStore.disableAutomaticExchangeStatusUpdates; + + @computed + bool get builtinTor => _settingsStore.currentBuiltinTor; + + + @computed + bool get hasPowNodes => [WalletType.nano, WalletType.banano].contains(_wallet.type); + + @computed + bool get useMempoolFeeAPI => _settingsStore.useMempoolFeeAPI; + + @computed + bool get canUseMempoolFeeAPI => _wallet.type == WalletType.bitcoin; + + @computed + bool get useBlinkProtection => _settingsStore.useBlinkProtection; + + bool get canUseBlinkProtection => canSupportBlinkProtection(_wallet.chainId); + + @computed + bool get useEtherscan => _settingsStore.useEtherscan; + + @computed + bool get usePolygonScan => _settingsStore.usePolygonScan; + + @computed + bool get useBaseScan => _settingsStore.useBaseScan; + + @computed + bool get useArbiScan => _settingsStore.useArbiScan; + + @computed + bool get useBscScan => _settingsStore.useBscScan; + + @computed + bool get useTronGrid => _settingsStore.useTronGrid; + + @computed + bool get canUseEtherscan => _wallet.chainId == 1; + + @computed + bool get canUsePolygonScan => _wallet.chainId == 137; + + @computed + bool get canUseBaseScan => _wallet.chainId == 8453; + + @computed + bool get canUseArbiScan => _wallet.chainId == 42161; + + @computed + bool get canUseBscScan => _wallet.chainId == 56; + + @computed + bool get canUseTronGrid => _wallet.type == WalletType.tron; + + @action + void setLookupsTwitter(bool value) => _settingsStore.lookupsTwitter = value; + + @action + void setLookupsZanoAlias(bool value) => _settingsStore.lookupsZanoAlias = value; + + @action + void setLookupsMastodon(bool value) => _settingsStore.lookupsMastodon = value; + + @action + void setLookupsENS(bool value) => _settingsStore.lookupsENS = value; + + @action + void setLookupsWellKnown(bool value) => _settingsStore.lookupsWellKnown = value; + + @action + void setLookupsYatService(bool value) => _settingsStore.lookupsYatService = value; + + @action + void setLookupsUnstoppableDomains(bool value) => _settingsStore.lookupsUnstoppableDomains = value; + + @action + void setLookupsOpenAlias(bool value) => _settingsStore.lookupsOpenAlias = value; + + @action + void setUseMempoolFeeAPI(bool value) => _settingsStore.useMempoolFeeAPI = value; + + @action + void setDisableAutomaticExchangeStatusUpdates(bool value) => + _settingsStore.disableAutomaticExchangeStatusUpdates = value; + + @action + void setFiatMode(FiatApiMode fiatApiMode) => _settingsStore.fiatApiMode = fiatApiMode; + + @action + void setExchangeApiMode(ExchangeApiMode value) => _settingsStore.exchangeStatus = value; + + @action + void setUseBlinkProtection(bool value) => _settingsStore.useBlinkProtection = value; + + @action + void setUseEtherscan(bool value) { + _settingsStore.useEtherscan = value; + evm!.updateScanProviderUsageState(_wallet, value); + } + + @action + void setUsePolygonScan(bool value) { + _settingsStore.usePolygonScan = value; + evm!.updateScanProviderUsageState(_wallet, value); + } + + @action + void setUseBaseScan(bool value) { + _settingsStore.useBaseScan = value; + evm!.updateScanProviderUsageState(_wallet, value); + } + + @action + void setUseTronGrid(bool value) { + _settingsStore.useTronGrid = value; + tron!.updateTronGridUsageState(_wallet, value); + } + + @action + void setUseArbiScan(bool value) { + _settingsStore.useArbiScan = value; + evm!.updateScanProviderUsageState(_wallet, value); + } + + @action + void setUseBscScan(bool value) { + _settingsStore.useBscScan = value; + evm!.updateScanProviderUsageState(_wallet, value); + } + + @action + void setBuiltinTor(bool value, BuildContext context) { + if (value) { + unawaited( + showPopUp( + context: context, + builder: (BuildContext context) { + return AlertWithOneAction( + alertTitle: S.of(context).tor_connection, + alertContent: S.of(context).tor_experimental, + buttonText: S.of(context).ok, + buttonAction: () => Navigator.of(context).pop(true), + ); + }, + ), + ); + } + _settingsStore.currentBuiltinTor = value; + if (value) { + unawaited(ensureTorStarted(context: context).then((_) async { + if (_settingsStore.currentBuiltinTor == false) return; + int? chainId; + if (isEVMCompatibleChain(_wallet.type)) { + chainId = evm!.getSelectedChainId(_wallet); + } + await _wallet.connectToNode( + node: _settingsStore.getCurrentNode(_wallet.type, chainId: chainId)); + })); + } else { + unawaited(ensureTorStopped(context: context).then((_) async { + if (_settingsStore.currentBuiltinTor == true) return; + int? chainId; + if (isEVMCompatibleChain(_wallet.type)) { + chainId = evm!.getSelectedChainId(_wallet); + } + await _wallet.connectToNode( + node: _settingsStore.getCurrentNode(_wallet.type, chainId: chainId)); + })); + } + } +} diff --git a/lib/view_model/settings/display_settings_view_model.dart b/lib/view_model/settings/display_settings_view_model.dart index 7bc2cc4f0..ab6c8f9c9 100644 --- a/lib/view_model/settings/display_settings_view_model.dart +++ b/lib/view_model/settings/display_settings_view_model.dart @@ -24,6 +24,9 @@ abstract class DisplaySettingsViewModelBase with Store { SettingsStore get _settingsStore => _appStore.settingsStore; final ThemeStore _themeStore; + @computed + bool get disableTradeOption => _settingsStore.disableTradeOption; + @computed FiatCurrency get fiatCurrency => _settingsStore.fiatCurrency; @@ -110,6 +113,9 @@ abstract class DisplaySettingsViewModelBase with Store { return []; } + @action + void setDisableTradeOption(bool value) => _settingsStore.disableTradeOption = value; + @action void setBalanceDisplayMode(BalanceDisplayMode value) => _settingsStore.balanceDisplayMode = value; @@ -246,18 +252,18 @@ abstract class DisplaySettingsViewModelBase with Store { String getImageForTheme(MaterialThemeBase theme) { switch (theme.title) { case 'Dark Theme': - return 'assets/images/dark.svg'; + return 'assets/new-ui/dark.svg'; case 'Light Theme': - return 'assets/images/light.svg'; + return 'assets/new-ui/light.svg'; case 'Black Theme (Cake Primary)': case 'Black Theme (BCH Green)': case 'Black Theme (Bitcoin Yellow)': case 'Black Theme (Monero Orange)': case 'Black Theme (Tron Red)': case 'Black Theme (Frosting Purple)': - return 'assets/images/black_accent.svg'; + return 'assets/new-ui/black_accent.svg'; default: - return 'assets/images/dark.svg'; + return 'assets/new-ui/dark.svg'; } } } diff --git a/lib/view_model/settings/other_settings_view_model.dart b/lib/view_model/settings/other_settings_view_model.dart index b94e7f2c8..8741c265d 100644 --- a/lib/view_model/settings/other_settings_view_model.dart +++ b/lib/view_model/settings/other_settings_view_model.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/priority_for_wallet_type.dart'; import 'package:cake_wallet/store/settings_store.dart'; import 'package:cake_wallet/utils/package_info.dart'; @@ -57,6 +58,33 @@ abstract class OtherSettingsViewModelBase with Store { return priority; } + @computed + bool get isAutoGenerateSubaddressesEnabled => + _settingsStore.autoGenerateSubaddressStatus != AutoGenerateSubaddressStatus.disabled; + + @action + void setAutoGenerateSubaddresses(bool value) { + _wallet.isEnabledAutoGenerateSubaddress = value; + _settingsStore.autoGenerateSubaddressStatus = + value ? AutoGenerateSubaddressStatus.enabled : AutoGenerateSubaddressStatus.disabled; + } + + bool get isAutoGenerateSubaddressesVisible => [ + WalletType.monero, + WalletType.wownero, + WalletType.bitcoin, + WalletType.litecoin, + WalletType.bitcoinCash, + WalletType.dogecoin, + WalletType.decred + ].contains(_wallet.type); + + @computed + bool get isMoneroWallet => _wallet.type == WalletType.monero; + + @computed + bool get shouldSaveRecipientAddress => _settingsStore.shouldSaveRecipientAddress; + @computed bool get changeRepresentativeEnabled => [WalletType.nano, WalletType.banano].contains(_wallet.type); @@ -127,6 +155,10 @@ abstract class OtherSettingsViewModelBase with Store { @computed double get customBitcoinFeeRate => _settingsStore.customBitcoinFeeRate.toDouble(); + @action + void setShouldSaveRecipientAddress(bool value) => + _settingsStore.shouldSaveRecipientAddress = value; + int? get customPriorityItemIndex { final priorities = priorityForWalletType(walletType); final customItem = priorities diff --git a/lib/view_model/settings/privacy_settings_view_model.dart b/lib/view_model/settings/privacy_settings_view_model.dart index 1b99d6644..d77eb6682 100644 --- a/lib/view_model/settings/privacy_settings_view_model.dart +++ b/lib/view_model/settings/privacy_settings_view_model.dart @@ -1,10 +1,6 @@ import 'package:cake_wallet/bitcoin/bitcoin.dart'; import 'package:cake_wallet/entities/auto_generate_subaddress_status.dart'; -import 'package:cake_wallet/entities/exchange_api_mode.dart'; -import 'package:cake_wallet/evm/evm.dart'; import 'package:cake_wallet/store/settings_store.dart'; -import 'package:cake_wallet/tron/tron.dart'; -import 'package:cake_wallet/reactions/wallet_connect.dart'; import 'package:cake_wallet/utils/device_info.dart'; import 'package:cw_core/balance.dart'; import 'package:cw_core/transaction_history.dart'; @@ -12,7 +8,6 @@ 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'; -import 'package:cake_wallet/entities/fiat_api_mode.dart'; part 'privacy_settings_view_model.g.dart'; @@ -25,7 +20,10 @@ abstract class PrivacySettingsViewModelBase with Store { final WalletBase, TransactionInfo> _wallet; @computed - ExchangeApiMode get exchangeStatus => _settingsStore.exchangeStatus; + bool get isBitcoin => _wallet.type == WalletType.bitcoin; + + @computed + bool get isLitecoin => _wallet.type == WalletType.litecoin; @computed bool get isAutoGenerateSubaddressesEnabled => @@ -65,9 +63,6 @@ abstract class PrivacySettingsViewModelBase with Store { @computed bool get shouldSaveRecipientAddress => _settingsStore.shouldSaveRecipientAddress; - @computed - FiatApiMode get fiatApiMode => _settingsStore.fiatApiMode; - @computed bool get isAppSecure => _settingsStore.isAppSecure; @@ -81,32 +76,6 @@ abstract class PrivacySettingsViewModelBase with Store { @computed bool get disableBulletin => _settingsStore.disableBulletin; - @computed - bool get useEtherscan => _settingsStore.useEtherscan; - - @computed - bool get usePolygonScan => _settingsStore.usePolygonScan; - - @computed - bool get useBaseScan => _settingsStore.useBaseScan; - - @computed - bool get useArbiScan => _settingsStore.useArbiScan; - - @computed - bool get useBscScan => _settingsStore.useBscScan; - - @computed - bool get useTronGrid => _settingsStore.useTronGrid; - - @computed - bool get useMempoolFeeAPI => _settingsStore.useMempoolFeeAPI; - - @computed - bool get useBlinkProtection => _settingsStore.useBlinkProtection; - - bool get canUseBlinkProtection => canSupportBlinkProtection(_wallet.chainId); - @computed bool get lookupTwitter => _settingsStore.lookupsTwitter; @@ -135,36 +104,18 @@ abstract class PrivacySettingsViewModelBase with Store { bool get usePayjoin => _settingsStore.usePayjoin; @computed - bool get useLightning => _wallet.type == WalletType.bitcoin && bitcoin!.useLightning(_wallet); - - bool get canUseEtherscan => _wallet.chainId == 1; - - bool get canUsePolygonScan => _wallet.chainId == 137; - - bool get canUseBaseScan => _wallet.chainId == 8453; - - bool get canUseArbiScan => _wallet.chainId == 42161; - - bool get canUseBscScan => _wallet.chainId == 56; - - bool get canUseTronGrid => _wallet.type == WalletType.tron; - - bool get canUseMempoolFeeAPI => _wallet.type == WalletType.bitcoin; - bool get canUsePayjoin => _wallet.type == WalletType.bitcoin && DeviceInfo.instance.isMobile; + @computed bool get canUseLightning => _wallet.type == WalletType.bitcoin; + @computed + bool get useLightning => _wallet.type == WalletType.bitcoin && bitcoin!.useLightning(_wallet); + @action void setShouldSaveRecipientAddress(bool value) => _settingsStore.shouldSaveRecipientAddress = value; - @action - void setExchangeApiMode(ExchangeApiMode value) => _settingsStore.exchangeStatus = value; - - @action - void setFiatMode(FiatApiMode fiatApiMode) => _settingsStore.fiatApiMode = fiatApiMode; - @action void setIsAppSecure(bool value) => _settingsStore.isAppSecure = value; @@ -178,66 +129,6 @@ abstract class PrivacySettingsViewModelBase with Store { @action void setDisableBulletin(bool value) => _settingsStore.disableBulletin = value; - @action - void setLookupsTwitter(bool value) => _settingsStore.lookupsTwitter = value; - - @action - void setLookupsZanoAlias(bool value) => _settingsStore.lookupsZanoAlias = value; - - @action - void setLookupsMastodon(bool value) => _settingsStore.lookupsMastodon = value; - - @action - void setLookupsENS(bool value) => _settingsStore.lookupsENS = value; - - @action - void setLookupsWellKnown(bool value) => _settingsStore.lookupsWellKnown = value; - - @action - void setLookupsYatService(bool value) => _settingsStore.lookupsYatService = value; - - @action - void setLookupsUnstoppableDomains(bool value) => _settingsStore.lookupsUnstoppableDomains = value; - - @action - void setLookupsOpenAlias(bool value) => _settingsStore.lookupsOpenAlias = value; - - @action - void setUseEtherscan(bool value) { - _settingsStore.useEtherscan = value; - evm!.updateScanProviderUsageState(_wallet, value); - } - - @action - void setUsePolygonScan(bool value) { - _settingsStore.usePolygonScan = value; - evm!.updateScanProviderUsageState(_wallet, value); - } - - @action - void setUseBaseScan(bool value) { - _settingsStore.useBaseScan = value; - evm!.updateScanProviderUsageState(_wallet, value); - } - - @action - void setUseTronGrid(bool value) { - _settingsStore.useTronGrid = value; - tron!.updateTronGridUsageState(_wallet, value); - } - - @action - void setUseArbiScan(bool value) { - _settingsStore.useArbiScan = value; - evm!.updateScanProviderUsageState(_wallet, value); - } - - @action - void setUseBscScan(bool value) { - _settingsStore.useBscScan = value; - evm!.updateScanProviderUsageState(_wallet, value); - } - @action void setUseMempoolFeeAPI(bool value) => _settingsStore.useMempoolFeeAPI = value; diff --git a/lib/view_model/settings/security_settings_view_model.dart b/lib/view_model/settings/security_settings_view_model.dart index c7ea6107c..2c4175876 100644 --- a/lib/view_model/settings/security_settings_view_model.dart +++ b/lib/view_model/settings/security_settings_view_model.dart @@ -15,6 +15,9 @@ abstract class SecuritySettingsViewModelBase with Store { final SettingsStore _settingsStore; final AuthService _authService; + @computed + bool get isAppSecure => _settingsStore.isAppSecure; + @computed bool get allowBiometricalAuthentication => _settingsStore.allowBiometricalAuthentication; @@ -36,6 +39,9 @@ abstract class SecuritySettingsViewModelBase with Store { return await _biometricAuth.canCheckBiometrics() && await _biometricAuth.isAuthenticated(); } + @action + void setIsAppSecure(bool value) => _settingsStore.isAppSecure = value; + @action void setAllowBiometricalAuthentication(bool value) => _settingsStore.allowBiometricalAuthentication = value; diff --git a/pubspec_base.yaml b/pubspec_base.yaml index 077d95f99..23e6d4079 100644 --- a/pubspec_base.yaml +++ b/pubspec_base.yaml @@ -20,7 +20,11 @@ dependencies: ref: 69b3276b090fa6ac01b4483ca3adca93a8e615be http: ^1.1.0 path_provider: ^2.0.11 - modal_bottom_sheet: ^3.0.0 + modal_bottom_sheet: + git: + url: https://github.com/cake-tech/modal_bottom_sheet + ref: b1cca111c53d41b9f07123a8c67cfe06fb1b69bf + path: modal_bottom_sheet mobx: ^2.1.4 flutter_mobx: 2.0.6+5 flutter_slidable: ^3.0.1 diff --git a/assets/images/black_accent.svg b/res/pictures/black_accent.svg similarity index 100% rename from assets/images/black_accent.svg rename to res/pictures/black_accent.svg diff --git a/res/pictures/chooser.svg b/res/pictures/chooser.svg index ca5c07d48..8e7c24bf4 100644 --- a/res/pictures/chooser.svg +++ b/res/pictures/chooser.svg @@ -1,10 +1,3 @@ - - - - - - - - - + + diff --git a/assets/images/dark.svg b/res/pictures/dark.svg similarity index 100% rename from assets/images/dark.svg rename to res/pictures/dark.svg diff --git a/assets/images/light.svg b/res/pictures/light.svg similarity index 100% rename from assets/images/light.svg rename to res/pictures/light.svg diff --git a/res/pictures/lightning-icon.svg b/res/pictures/lightning-icon.svg new file mode 100644 index 000000000..272baa52d --- /dev/null +++ b/res/pictures/lightning-icon.svg @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/res/pictures/settings_row_icons/privacy.svg b/res/pictures/settings_row_icons/privacy.svg index 763bfbaf3..3a8a8503f 100644 --- a/res/pictures/settings_row_icons/privacy.svg +++ b/res/pictures/settings_row_icons/privacy.svg @@ -1,40 +1,35 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/res/pictures/settings_row_icons/security.svg b/res/pictures/settings_row_icons/security.svg index 994f17f25..6663c091c 100644 --- a/res/pictures/settings_row_icons/security.svg +++ b/res/pictures/settings_row_icons/security.svg @@ -1,47 +1,42 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/res/pictures/settings_row_icons/wc.svg b/res/pictures/settings_row_icons/wc.svg new file mode 100644 index 000000000..5f334128a --- /dev/null +++ b/res/pictures/settings_row_icons/wc.svg @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/res/values/strings_ar.arb b/res/values/strings_ar.arb index 595e73c8b..ab480a489 100644 --- a/res/values/strings_ar.arb +++ b/res/values/strings_ar.arb @@ -242,6 +242,7 @@ "connected": "متصل", "connection_sync": "الاتصال والمزامنة", "connections": "الاتصالات", + "connections_desc": "إدارة الاتصالات بالخدمات وواجهات برمجة التطبيقات التابعة لجهات خارجية.", "connectWalletPrompt": "قم بتوصيل محفظتك عبر WalletConnect لإجراء المعاملات", "contact": "جهة اتصال", "contact_list_contacts": "جهات الاتصال", @@ -475,6 +476,7 @@ "file_saved": "تم حفظ الملف", "fill_code": "يرجى إدخال رمز التحقق المُرسل إلى بريدك الإلكتروني", "filter_by": "تصفية حسب", + "filters": "المرشحات", "finding_provider": "جارٍ البحث عن مزوّد", "first_wallet_text": "محفظة رائعة لـ Monero وBitcoin وEthereum وLitecoin وHaven", "fixed_pair_not_supported": "هذا الزوج الثابت غير مدعوم مع خدمات المبادلة المحددة", @@ -755,6 +757,7 @@ "privacy": "الخصوصية", "privacy_and_security": "الخصوصية والأمان", "privacy_and_security_desc": "قم بتكوين إعدادات المصادقة والأمان وإعداد المصادقة الثنائية (2FA) في Cake.", + "privacy_desc": "تغيير إعدادات الخصوصية وتبديل ميزات الخصوصية الخاصة.", "privacy_features": "ميزات الخصوصية", "privacy_policy": "سياسة الخصوصية", "privacy_settings": "إعدادات الخصوصية", diff --git a/res/values/strings_bg.arb b/res/values/strings_bg.arb index f98ab5f57..d50474f43 100644 --- a/res/values/strings_bg.arb +++ b/res/values/strings_bg.arb @@ -242,6 +242,7 @@ "connected": "Свързан", "connection_sync": "Връзка и синхронизация", "connections": "Връзки", + "connections_desc": "Управлявайте връзки към услуги и API на трети страни.", "connectWalletPrompt": "Свържете портфейла си с WalletConnect, за да извършвате транзакции", "contact": "Контакт", "contact_list_contacts": "Контакти", @@ -475,6 +476,7 @@ "file_saved": "Файлът е запазен", "fill_code": "Моля, въведете кода за потвърждение, изпратен на вашия имейл", "filter_by": "Филтриране по", + "filters": "Филтри", "finding_provider": "Намиране на доставчик", "first_wallet_text": "Страхотен портфейл за Monero, Bitcoin, Ethereum, Litecoin и Haven", "fixed_pair_not_supported": "Тази фиксирана двойка не се поддържа от избраните услуги за обмен", @@ -755,6 +757,7 @@ "privacy": "Поверителност", "privacy_and_security": "Поверителност и сигурност", "privacy_and_security_desc": "Конфигурирайте настройките за удостоверяване и сигурност и настройте Cake 2FA.", + "privacy_desc": "Променете настройките за поверителност и превключете специални функции за поверителност.", "privacy_features": "Функции за поверителност", "privacy_policy": "Политика за поверителност", "privacy_settings": "Настройки за поверителност", diff --git a/res/values/strings_cs.arb b/res/values/strings_cs.arb index b4d684913..aebca6a93 100644 --- a/res/values/strings_cs.arb +++ b/res/values/strings_cs.arb @@ -242,6 +242,7 @@ "connected": "Připojeno", "connection_sync": "Připojení a synchronizace", "connections": "Připojení", + "connections_desc": "Správa připojení ke službám a rozhraním API třetích stran.", "connectWalletPrompt": "Připojte svou peněženku přes WalletConnect a provádějte transakce", "contact": "Kontakt", "contact_list_contacts": "Kontakty", @@ -475,6 +476,7 @@ "file_saved": "Soubor uložen", "fill_code": "Zadejte prosím ověřovací kód, který vám byl zaslán na e-mail", "filter_by": "Filtrovat podle", + "filters": "Filtry", "finding_provider": "Hledání poskytovatele", "first_wallet_text": "Skvělá peněženka pro Monero, Bitcoin, Ethereum, Litecoin a Haven", "fixed_pair_not_supported": "Tento pevný pár není podporován u vybraných swapovacích služeb", @@ -755,6 +757,7 @@ "privacy": "Soukromí", "privacy_and_security": "Soukromí a zabezpečení", "privacy_and_security_desc": "Nakonfigurujte nastavení ověřování a zabezpečení a nastavte Cake 2FA.", + "privacy_desc": "Změňte nastavení ochrany osobních údajů a přepínejte speciální funkce ochrany osobních údajů.", "privacy_features": "Funkce soukromí", "privacy_policy": "Zásady ochrany osobních údajů", "privacy_settings": "Nastavení soukromí", diff --git a/res/values/strings_de.arb b/res/values/strings_de.arb index fd82ac77b..f3b379170 100644 --- a/res/values/strings_de.arb +++ b/res/values/strings_de.arb @@ -242,6 +242,7 @@ "connected": "Verbunden", "connection_sync": "Verbindung und Synchronisierung", "connections": "Verbindungen", + "connections_desc": "Verwalten Sie Verbindungen zu Diensten und APIs von Drittanbietern.", "connectWalletPrompt": "Verbinden Sie Ihr Wallet mit WalletConnect, um Transaktionen durchzuführen", "contact": "Kontakt", "contact_list_contacts": "Kontakte", @@ -475,6 +476,7 @@ "file_saved": "Datei gespeichert", "fill_code": "Bitte geben Sie den Verifizierungscode ein, den Sie per E-Mail erhalten haben", "filter_by": "Filtern nach", + "filters": "Filter", "finding_provider": "Anbieter wird gesucht", "first_wallet_text": "Eine großartige Wallet für Monero, Bitcoin, Ethereum, Litecoin und Haven", "fixed_pair_not_supported": "Dieses feste Paar wird von den ausgewählten Swap-Diensten nicht unterstützt", @@ -756,6 +758,7 @@ "privacy": "Privatsphäre", "privacy_and_security": "Datenschutz & Sicherheit", "privacy_and_security_desc": "Konfigurieren Sie Authentifizierungs- und Sicherheitseinstellungen und richten Sie Cake 2FA ein.", + "privacy_desc": "Ändern Sie die Datenschutzeinstellungen und schalten Sie spezielle Datenschutzfunktionen ein.", "privacy_features": "Datenschutzfunktionen", "privacy_policy": "Datenschutzerklärung", "privacy_settings": "Datenschutzeinstellungen", diff --git a/res/values/strings_en.arb b/res/values/strings_en.arb index 515b42da9..f55ddf0fa 100644 --- a/res/values/strings_en.arb +++ b/res/values/strings_en.arb @@ -242,6 +242,7 @@ "connected": "Connected", "connection_sync": "Connection and sync", "connections": "Connections", + "connections_desc": "Manage connections to services and third party APIs.", "connectWalletPrompt": "Connect your wallet with WalletConnect to make transactions", "contact": "Contact", "contact_list_contacts": "Contacts", @@ -475,6 +476,7 @@ "file_saved": "File saved", "fill_code": "Please fill in the verification code provided to your email", "filter_by": "Filter by", + "filters": "Filters", "finding_provider": "Finding provider", "first_wallet_text": "Awesome wallet for Monero, Bitcoin, Ethereum, Litecoin, and Haven", "fixed_pair_not_supported": "This fixed pair is not supported with the selected swap services", @@ -756,6 +758,7 @@ "privacy": "Privacy", "privacy_and_security": "Privacy & Security", "privacy_and_security_desc": "Configure authentication and security settings and setup Cake 2FA.", + "privacy_desc": "Change privacy settings and toggle special privacy features.", "privacy_features": "Privacy features", "privacy_policy": "Privacy Policy", "privacy_settings": "Privacy settings", @@ -1365,4 +1368,4 @@ "zcash_card_missing_funds": "Missing funds?", "zcash_card_scan": "Scan", "zcash_card_warning": "Do not close the app until the procedure completes, if you do so this process will need to restart from scratch." -} +} \ No newline at end of file diff --git a/res/values/strings_es.arb b/res/values/strings_es.arb index cc5baa95b..49ce3fc0c 100644 --- a/res/values/strings_es.arb +++ b/res/values/strings_es.arb @@ -242,6 +242,7 @@ "connected": "Conectado", "connection_sync": "Conexión y sincronización", "connections": "Conexiones", + "connections_desc": "Administrar conexiones a servicios y API de terceros.", "connectWalletPrompt": "Conecta tu billetera con WalletConnect para realizar transacciones", "contact": "Contacto", "contact_list_contacts": "Contactos", @@ -475,6 +476,7 @@ "file_saved": "Archivo guardado", "fill_code": "Por favor, introduce el código de verificación enviado a tu correo electrónico", "filter_by": "Filtrar por", + "filters": "Filtros", "finding_provider": "Buscando proveedor", "first_wallet_text": "Billetera increíble para Monero, Bitcoin, Ethereum, Litecoin y Haven", "fixed_pair_not_supported": "Este par fijo no es compatible con los servicios de swap seleccionados", @@ -756,6 +758,7 @@ "privacy": "Privacidad", "privacy_and_security": "Privacidad y seguridad", "privacy_and_security_desc": "Configure los ajustes de autenticación y seguridad y configure Cake 2FA.", + "privacy_desc": "Cambie la configuración de privacidad y alterne funciones de privacidad especiales.", "privacy_features": "Funciones de privacidad", "privacy_policy": "Política de privacidad", "privacy_settings": "Configuración de privacidad", diff --git a/res/values/strings_fa.arb b/res/values/strings_fa.arb index 58aa1f18c..5e1edfa7d 100644 --- a/res/values/strings_fa.arb +++ b/res/values/strings_fa.arb @@ -240,6 +240,7 @@ "connected": "متصل", "connection_sync": "اتصال و همگام‌سازی", "connections": "اتصالات", + "connections_desc": "اتصالات به سرویس ها و API های شخص ثالث را مدیریت کنید.", "connectWalletPrompt": "برای انجام تراکنش‌ها، کیف پول خود را از طریق WalletConnect متصل کنید", "contact": "مخاطب", "contact_list_contacts": "مخاطبین", @@ -473,6 +474,7 @@ "file_saved": "فایل ذخیره شد", "fill_code": "لطفاً کد تأیید ارسال‌شده به ایمیل خود را وارد کنید", "filter_by": "فیلتر بر اساس", + "filters": "فیلترها", "finding_provider": "در حال یافتن ارائه‌دهنده", "first_wallet_text": "کیف پول فوق‌العاده برای مونرو، بیت‌کوین، اتریوم، لایت‌کوین و هیون", "fixed_pair_not_supported": "این جفت‌ارز ثابت توسط سرویس‌های سواپ انتخاب‌شده پشتیبانی نمی‌شود", @@ -754,6 +756,7 @@ "privacy": "حریم خصوصی", "privacy_and_security": "حریم خصوصی و امنیت", "privacy_and_security_desc": "تنظیمات احراز هویت و امنیت را پیکربندی کنید و Cake 2FA را راه‌اندازی کنید.", + "privacy_desc": "تنظیمات حریم خصوصی را تغییر دهید و ویژگی های حریم خصوصی را تغییر دهید.", "privacy_features": "ویژگی‌های حریم خصوصی", "privacy_policy": "سیاست حفظ حریم خصوصی", "privacy_settings": "تنظیمات حریم خصوصی", diff --git a/res/values/strings_fr.arb b/res/values/strings_fr.arb index 5a4eb2569..625bbde3e 100644 --- a/res/values/strings_fr.arb +++ b/res/values/strings_fr.arb @@ -242,6 +242,7 @@ "connected": "Connecté", "connection_sync": "Connexion et synchronisation", "connections": "Connexions", + "connections_desc": "Gérez les connexions aux services et aux API tierces.", "connectWalletPrompt": "Connectez votre portefeuille avec WalletConnect pour effectuer des transactions", "contact": "Contact", "contact_list_contacts": "Contacts", @@ -475,6 +476,7 @@ "file_saved": "Fichier enregistré", "fill_code": "Veuillez saisir le code de vérification envoyé à votre adresse e-mail.", "filter_by": "Filtrer par", + "filters": "Filtres", "finding_provider": "Recherche d’un fournisseur", "first_wallet_text": "Portefeuille génial pour Monero, Bitcoin, Ethereum, Litecoin et Haven", "fixed_pair_not_supported": "Cette paire fixe n'est pas prise en charge par les services d'échange sélectionnés", @@ -755,6 +757,7 @@ "privacy": "Confidentialité", "privacy_and_security": "Confidentialité et sécurité", "privacy_and_security_desc": "Configurez les paramètres d'authentification et de sécurité, et configurez Cake 2FA.", + "privacy_desc": "Modifiez les paramètres de confidentialité et activez les fonctionnalités de confidentialité spéciales.", "privacy_features": "Fonctionnalités de confidentialité", "privacy_policy": "Politique de confidentialité", "privacy_settings": "Paramètres de confidentialité", diff --git a/res/values/strings_gn.arb b/res/values/strings_gn.arb index 45b975714..7c535a745 100644 --- a/res/values/strings_gn.arb +++ b/res/values/strings_gn.arb @@ -192,6 +192,7 @@ "connect_your_hardware_wallet_ios": "Embojoaju nde billetera hardware Bluetooth ndive", "connection_sync": "Joaju ha ñembojuehe", "connections": "Jeikeha", + "connections_desc": "Emohenda joaju umi servicio ha API mbohapýha rehegua.", "connectWalletPrompt": "Embojoaju nde billetera WalletConnect ndive hag̃ua rejapo transacción", "contact": "Contacto", "contact_list_contacts": "Ñe'ẽngára", @@ -371,6 +372,7 @@ "field_required": "Ko kora tekotevẽva", "fill_code": "Ikatúpiko emyenyhẽ pe ayvu ñemi jehechaukaha oñemondóva nde korreópe.", "filter_by": "Mboguaha rupi", + "filters": "Filtro-kuéra rehegua", "finding_provider": "Ojejuhu hag̃ua proveedor", "first_wallet_text": "Viru ñongatuha iporãiterei Monero, Bitcoin, Ethereum, Litecoin ha Haven-pe guarã.", "fixed_pair_not_supported": "Ko mokõi par fijo ndojehepyme’ẽi umi swap rembiapo ojeiporavo va’ekuépe.", @@ -588,6 +590,7 @@ "privacy": "Ñemigua", "privacy_and_security": "Privasida ha Tekorosã", "privacy_and_security_desc": "Emohenda ñemboheko hag̃ua jehechajey hag̃ua hag̃ua hag̃ua hag̃ua seguridad ha emboheko Cake 2FA.", + "privacy_desc": "Emoambue ñemigua ñemboheko ha embohasa ñemigua mba’ekuaarã especial.", "privacy_features": "Tembiapoite ñemigua", "privacy_policy": "Purureko ñemi rehegua", "privacy_settings": "Ñemohenda ñemigua", diff --git a/res/values/strings_ha.arb b/res/values/strings_ha.arb index dc2fa5189..4d57e1b17 100644 --- a/res/values/strings_ha.arb +++ b/res/values/strings_ha.arb @@ -242,6 +242,7 @@ "connected": "An haɗa", "connection_sync": "Haɗi da aiki tare", "connections": "Haɗe-haɗe", + "connections_desc": "Sarrafa haɗin kai zuwa ayyuka da APIs na ɓangare na uku.", "connectWalletPrompt": "Haɗa walat ɗinka da WalletConnect don yin ma'amaloli", "contact": "Tuntuɓa", "contact_list_contacts": "Lambobin sadarwa", @@ -475,6 +476,7 @@ "file_saved": "An ajiye fayil", "fill_code": "Da fatan za a shigar da lambar tabbatarwa da aka aiko zuwa imel ɗin ku", "filter_by": "Tace ta", + "filters": "Tace", "finding_provider": "Ana neman mai ba da sabis", "first_wallet_text": "Kyakkyawan walat don Monero, Bitcoin, Ethereum, Litecoin, da Haven", "fixed_pair_not_supported": "Ba a tallafa wa wannan tsayayyen nau'i ba tare da sabis ɗin musayar da aka zaɓa ba", @@ -757,6 +759,7 @@ "privacy": "Sirri", "privacy_and_security": "Sirri & Tsaro", "privacy_and_security_desc": "Saita tabbatarwa da saitunan tsaro, sannan ka saita Cake 2FA.", + "privacy_desc": "Canja saitunan keɓantawa kuma kunna fasalolin keɓantawa na musamman.", "privacy_features": "Siffofin keɓantawa", "privacy_policy": "Manufar Sirri", "privacy_settings": "Saitunan keɓantawa", diff --git a/res/values/strings_hi.arb b/res/values/strings_hi.arb index ef4d47ecf..dc7790521 100644 --- a/res/values/strings_hi.arb +++ b/res/values/strings_hi.arb @@ -242,6 +242,7 @@ "connected": "कनेक्टेड", "connection_sync": "कनेक्शन और सिंक", "connections": "कनेक्शन", + "connections_desc": "सेवाओं और तृतीय पक्ष एपीआई से कनेक्शन प्रबंधित करें।", "connectWalletPrompt": "लेन-देन करने के लिए अपने वॉलेट को WalletConnect से कनेक्ट करें", "contact": "संपर्क", "contact_list_contacts": "संपर्क", @@ -475,6 +476,7 @@ "file_saved": "फ़ाइल सहेजी गई", "fill_code": "कृपया अपने ईमेल पर भेजा गया सत्यापन कोड दर्ज करें", "filter_by": "इसके अनुसार फ़िल्टर करें", + "filters": "फिल्टर", "finding_provider": "प्रदाता खोजा जा रहा है", "first_wallet_text": "Monero, Bitcoin, Ethereum, Litecoin और Haven के लिए शानदार वॉलेट", "fixed_pair_not_supported": "यह निश्चित जोड़ी चयनित स्वैप सेवाओं के साथ समर्थित नहीं है", @@ -725,8 +727,8 @@ "payjoin_unavailable_sheet_title": "Payjoin उपलब्ध क्यों नहीं है?", "payment_id": "भुगतान आईडी: ", "payment_made_easy": "भुगतान करना आसान", - "payment_was_received": "आपका भुगतान प्राप्त हो गया।", "Payment_was_received": "आपका भुगतान प्राप्त हो गया था।", + "payment_was_received": "आपका भुगतान प्राप्त हो गया।", "payments": "भुगतान", "pending": " (लंबित)", "percentageOf": "${amount} का", @@ -756,6 +758,7 @@ "privacy": "गोपनीयता", "privacy_and_security": "निजता और सुरक्षा", "privacy_and_security_desc": "प्रमाणीकरण और सुरक्षा सेटिंग्स कॉन्फ़िगर करें और Cake 2FA सेट अप करें।", + "privacy_desc": "गोपनीयता सेटिंग्स बदलें और विशेष गोपनीयता सुविधाओं को टॉगल करें।", "privacy_features": "गोपनीयता सुविधाएँ", "privacy_policy": "गोपनीयता नीति", "privacy_settings": "गोपनीयता सेटिंग्स", diff --git a/res/values/strings_hr.arb b/res/values/strings_hr.arb index fe30916aa..4ca1ae54a 100644 --- a/res/values/strings_hr.arb +++ b/res/values/strings_hr.arb @@ -242,6 +242,7 @@ "connected": "Povezano", "connection_sync": "Povezivanje i sinkronizacija", "connections": "Veze", + "connections_desc": "Upravljajte vezama s uslugama i API-jima trećih strana.", "connectWalletPrompt": "Povežite svoj novčanik s WalletConnectom za izvršavanje transakcija", "contact": "Kontakt", "contact_list_contacts": "Kontakti", @@ -475,6 +476,7 @@ "file_saved": "Datoteka je spremljena", "fill_code": "Molimo unesite verifikacijski kod koji ste primili na svoju e-poštu", "filter_by": "Filtriraj po", + "filters": "Filteri", "finding_provider": "Traženje pružatelja", "first_wallet_text": "Odličan novčanik za Monero, Bitcoin, Ethereum, Litecoin i Haven", "fixed_pair_not_supported": "Ovaj fiksni par nije podržan s odabranim uslugama zamjene", @@ -755,6 +757,7 @@ "privacy": "Privatnost", "privacy_and_security": "Privatnost i sigurnost", "privacy_and_security_desc": "Konfigurirajte postavke provjere autentičnosti i sigurnosti te postavite Cake 2FA.", + "privacy_desc": "Promijenite postavke privatnosti i uključite/isključite posebne značajke privatnosti.", "privacy_features": "Značajke privatnosti", "privacy_policy": "Pravila privatnosti", "privacy_settings": "Postavke privatnosti", diff --git a/res/values/strings_hy.arb b/res/values/strings_hy.arb index 1065e7838..f884ebddb 100644 --- a/res/values/strings_hy.arb +++ b/res/values/strings_hy.arb @@ -242,6 +242,7 @@ "connected": "Միացված", "connection_sync": "Կապ և համաժամեցում", "connections": "Միացումներ", + "connections_desc": "Կառավարեք կապերը ծառայությունների և երրորդ կողմի API-ների հետ:", "connectWalletPrompt": "Միացրեք ձեր դրամապանակը WalletConnect-ի միջոցով՝ գործարքներ կատարելու համար", "contact": "Կոնտակտ", "contact_list_contacts": "Կոնտակտներ", @@ -475,6 +476,7 @@ "file_saved": "Ֆայլը պահպանվել է", "fill_code": "Խնդրում ենք մուտքագրել ձեր էլեկտրոնային փոստին ուղարկված հաստատման կոդը", "filter_by": "Զտել ըստ", + "filters": "Զտիչներ", "finding_provider": "Մատակարարի որոնում", "first_wallet_text": "Հիանալի դրամապանակ Monero, Bitcoin, Ethereum, Litecoin և Haven-ի համար", "fixed_pair_not_supported": "Այս ֆիքսված զույգը չի աջակցվում ընտրված փոխանակման ծառայությունների կողմից", @@ -754,6 +756,7 @@ "privacy": "Գաղտնիություն", "privacy_and_security": "Գաղտնիություն և անվտանգություն", "privacy_and_security_desc": "Կազմաձևեք նույնականացման և անվտանգության կարգավորումները և կարգավորեք Cake 2FA-ն։", + "privacy_desc": "Փոխեք գաղտնիության կարգավորումները և միացրեք գաղտնիության հատուկ գործառույթները:", "privacy_features": "Գաղտնիության գործառույթներ", "privacy_policy": "Գաղտնիության քաղաքականություն", "privacy_settings": "Գաղտնիության կարգավորումներ", diff --git a/res/values/strings_id.arb b/res/values/strings_id.arb index 4ce7eb096..99a587392 100644 --- a/res/values/strings_id.arb +++ b/res/values/strings_id.arb @@ -242,6 +242,7 @@ "connected": "Terhubung", "connection_sync": "Koneksi dan sinkronisasi", "connections": "Koneksi", + "connections_desc": "Kelola koneksi ke layanan dan API pihak ketiga.", "connectWalletPrompt": "Hubungkan dompet Anda dengan WalletConnect untuk melakukan transaksi", "contact": "Kontak", "contact_list_contacts": "Kontak", @@ -475,6 +476,7 @@ "file_saved": "File tersimpan", "fill_code": "Silakan isi kode verifikasi yang dikirim ke email Anda", "filter_by": "Filter berdasarkan", + "filters": "Filter", "finding_provider": "Mencari penyedia", "first_wallet_text": "Dompet yang keren untuk Monero, Bitcoin, Ethereum, Litecoin, dan Haven", "fixed_pair_not_supported": "Pasangan tetap ini tidak didukung oleh layanan swap yang dipilih", @@ -757,6 +759,7 @@ "privacy": "Privasi", "privacy_and_security": "Privasi & Keamanan", "privacy_and_security_desc": "Konfigurasikan pengaturan autentikasi dan keamanan serta siapkan Cake 2FA.", + "privacy_desc": "Ubah pengaturan privasi dan aktifkan fitur privasi khusus.", "privacy_features": "Fitur privasi", "privacy_policy": "Kebijakan Privasi", "privacy_settings": "Pengaturan Privasi", diff --git a/res/values/strings_it.arb b/res/values/strings_it.arb index 9aa0c9c39..72ed9288e 100644 --- a/res/values/strings_it.arb +++ b/res/values/strings_it.arb @@ -242,6 +242,7 @@ "connected": "Connesso", "connection_sync": "Connessione e sincronizzazione", "connections": "Connessioni", + "connections_desc": "Gestisci le connessioni a servizi e API di terze parti.", "connectWalletPrompt": "Collega il tuo wallet con WalletConnect per effettuare transazioni", "contact": "Contatto", "contact_list_contacts": "Contatti", @@ -475,6 +476,7 @@ "file_saved": "File salvato", "fill_code": "Inserisci il codice di verifica inviato al tuo indirizzo email", "filter_by": "Filtra per", + "filters": "Filtri", "finding_provider": "Ricerca del fornitore", "first_wallet_text": "Portafoglio fantastico per Monero, Bitcoin, Ethereum, Litecoin e Haven", "fixed_pair_not_supported": "Questa coppia fissa non è supportata dai servizi di swap selezionati", @@ -756,6 +758,7 @@ "privacy": "Privacy", "privacy_and_security": "Privacy e sicurezza", "privacy_and_security_desc": "Configura le impostazioni di autenticazione e sicurezza e imposta Cake 2FA.", + "privacy_desc": "Modifica le impostazioni sulla privacy e attiva le funzionalità speciali sulla privacy.", "privacy_features": "Funzionalità per la privacy", "privacy_policy": "Informativa sulla privacy", "privacy_settings": "Impostazioni sulla privacy", diff --git a/res/values/strings_ja.arb b/res/values/strings_ja.arb index 5978a861a..1597fb304 100644 --- a/res/values/strings_ja.arb +++ b/res/values/strings_ja.arb @@ -242,6 +242,7 @@ "connected": "接続済み", "connection_sync": "接続と同期", "connections": "接続", + "connections_desc": "サービスおよびサードパーティ API への接続を管理します。", "connectWalletPrompt": "WalletConnect でウォレットを接続して取引を行う", "contact": "連絡先", "contact_list_contacts": "連絡先", @@ -475,6 +476,7 @@ "file_saved": "ファイルを保存しました", "fill_code": "メールに記載されている認証コードを入力してください", "filter_by": "絞り込み", + "filters": "フィルター", "finding_provider": "プロバイダーを検索中", "first_wallet_text": "Monero、Bitcoin、Ethereum、Litecoin、Havenに対応した素晴らしいウォレット", "fixed_pair_not_supported": "この固定ペアは、選択したスワップサービスではサポートされていません", @@ -756,6 +758,7 @@ "privacy": "プライバシー", "privacy_and_security": "プライバシーとセキュリティ", "privacy_and_security_desc": "認証とセキュリティの設定を構成し、Cake 2FA をセットアップします。", + "privacy_desc": "プライバシー設定を変更し、特別なプライバシー機能を切り替えます。", "privacy_features": "プライバシー機能", "privacy_policy": "プライバシーポリシー", "privacy_settings": "プライバシー設定", diff --git a/res/values/strings_ko.arb b/res/values/strings_ko.arb index 86cd7d9fc..6df7fd1ba 100644 --- a/res/values/strings_ko.arb +++ b/res/values/strings_ko.arb @@ -242,6 +242,7 @@ "connected": "연결됨", "connection_sync": "연결 및 동기화", "connections": "연결", + "connections_desc": "서비스 및 타사 API에 대한 연결을 관리합니다.", "connectWalletPrompt": "거래를 하려면 WalletConnect로 지갑을 연결하세요", "contact": "연락처", "contact_list_contacts": "연락처", @@ -475,6 +476,7 @@ "file_saved": "파일이 저장되었습니다", "fill_code": "이메일로 받은 인증 코드를 입력하세요", "filter_by": "필터 기준:", + "filters": "필터", "finding_provider": "제공자 찾는 중", "first_wallet_text": "모네로, 비트코인, 이더리움, 라이트코인, 헤이븐을 위한 멋진 지갑", "fixed_pair_not_supported": "이 고정 페어는 선택한 스왑 서비스에서 지원되지 않습니다", @@ -756,6 +758,7 @@ "privacy": "개인정보 보호", "privacy_and_security": "개인정보 보호 및 보안", "privacy_and_security_desc": "인증 및 보안 설정을 구성하고 Cake 2FA를 설정합니다.", + "privacy_desc": "개인정보 보호 설정을 변경하고 특별 개인정보 보호 기능을 전환하세요.", "privacy_features": "개인정보 보호 기능", "privacy_policy": "개인정보처리방침", "privacy_settings": "개인정보 보호 설정", diff --git a/res/values/strings_my.arb b/res/values/strings_my.arb index 768570aac..a9785f5be 100644 --- a/res/values/strings_my.arb +++ b/res/values/strings_my.arb @@ -242,6 +242,7 @@ "connected": "ချိတ်ဆက်ပြီး", "connection_sync": "ချိတ်ဆက်မှုနှင့် ထပ်တူညီစေခြင်း", "connections": "ချိတ်ဆက်မှုများ", + "connections_desc": "ဝန်ဆောင်မှုများနှင့် ပြင်ပကုမ္ပဏီ API များသို့ ချိတ်ဆက်မှုများကို စီမံပါ။", "connectWalletPrompt": "ငွေလွှဲပြောင်းမှုများ ပြုလုပ်ရန် WalletConnect ဖြင့် သင့်ပိုက်ဆံအိတ်ကို ချိတ်ဆက်ပါ", "contact": "ဆက်သွယ်ရန်", "contact_list_contacts": "အဆက်အသွယ်များ", @@ -475,6 +476,7 @@ "file_saved": "ဖိုင်ကို သိမ်းဆည်းပြီးပါပြီ", "fill_code": "သင့်အီးမေးလ်သို့ ပို့ထားသော အတည်ပြုကုဒ်ကို ဖြည့်ပါ။", "filter_by": "အလိုက် စစ်ထုတ်ရန်", + "filters": "စစ်ထုတ်မှုများ", "finding_provider": "ပံ့ပိုးသူကို ရှာဖွေနေသည်", "first_wallet_text": "Monero၊ Bitcoin၊ Ethereum၊ Litecoin နှင့် Haven အတွက် အလွန်ကောင်းမွန်သော Wallet", "fixed_pair_not_supported": "ဤပုံသေစုံတွဲကို ရွေးချယ်ထားသော swap ဝန်ဆောင်မှုများဖြင့် မထောက်ပံ့ပါ", @@ -755,6 +757,7 @@ "privacy": "ကိုယ်ရေးလုံခြုံမှု", "privacy_and_security": "ကိုယ်ရေးကိုယ်တာနှင့် လုံခြုံရေး", "privacy_and_security_desc": "အထောက်အထားစိစစ်ခြင်းနှင့် လုံခြုံရေးဆက်တင်များကို ပြင်ဆင်သတ်မှတ်ပြီး Cake 2FA ကို စနစ်ထည့်သွင်းပါ။", + "privacy_desc": "ကိုယ်ရေးကိုယ်တာဆက်တင်များကိုပြောင်းလဲပြီး အထူးကိုယ်ရေးကိုယ်တာအင်္ဂါရပ်များကို ပြောင်းပါ။", "privacy_features": "ကိုယ်ရေးလုံခြုံရေး အင်္ဂါရပ်များ", "privacy_policy": "ကိုယ်ရေးအချက်အလက်မူဝါဒ", "privacy_settings": "ကိုယ်ရေးကိုယ်တာ ဆက်တင်များ", diff --git a/res/values/strings_nl.arb b/res/values/strings_nl.arb index 515980191..3d85f18fd 100644 --- a/res/values/strings_nl.arb +++ b/res/values/strings_nl.arb @@ -240,6 +240,7 @@ "connected": "Verbonden", "connection_sync": "Verbinding en synchronisatie", "connections": "Verbindingen", + "connections_desc": "Beheer verbindingen met services en API's van derden.", "connectWalletPrompt": "Verbind je wallet met WalletConnect om transacties uit te voeren", "contact": "Contact", "contact_list_contacts": "Contacten", @@ -473,6 +474,7 @@ "file_saved": "Bestand opgeslagen", "fill_code": "Vul de verificatiecode in die je in je e-mail hebt ontvangen", "filter_by": "Filteren op", + "filters": "Filters", "finding_provider": "Aanbieder zoeken", "first_wallet_text": "Geweldige wallet voor Monero, Bitcoin, Ethereum, Litecoin en Haven", "fixed_pair_not_supported": "Dit vaste paar wordt niet ondersteund met de geselecteerde swap-services", @@ -752,6 +754,7 @@ "privacy": "Privacy", "privacy_and_security": "Privacy en beveiliging", "privacy_and_security_desc": "Configureer de authenticatie- en beveiligingsinstellingen en stel Cake 2FA in.", + "privacy_desc": "Wijzig privacy-instellingen en schakel speciale privacyfuncties in.", "privacy_features": "Privacyfuncties", "privacy_policy": "Privacybeleid", "privacy_settings": "Privacy-instellingen", diff --git a/res/values/strings_pl.arb b/res/values/strings_pl.arb index df72e6da5..e80a7d3b3 100644 --- a/res/values/strings_pl.arb +++ b/res/values/strings_pl.arb @@ -242,6 +242,7 @@ "connected": "Połączono", "connection_sync": "Połączenie i synchronizacja", "connections": "Połączenia", + "connections_desc": "Zarządzaj połączeniami z usługami i interfejsami API stron trzecich.", "connectWalletPrompt": "Połącz swój portfel z WalletConnect, aby wykonywać transakcje", "contact": "Kontakt", "contact_list_contacts": "Kontakty", @@ -474,6 +475,7 @@ "file_saved": "Plik zapisany", "fill_code": "Proszę wprowadzić kod weryfikacyjny wysłany na Twój adres e‑mail", "filter_by": "Filtruj według", + "filters": "Filtry", "finding_provider": "Wyszukiwanie dostawcy", "first_wallet_text": "Świetny portfel dla Monero, Bitcoin, Ethereum, Litecoin i Haven", "fixed_pair_not_supported": "Ta stała para nie jest obsługiwana przez wybrane usługi swap.", @@ -754,6 +756,7 @@ "privacy": "Prywatność", "privacy_and_security": "Prywatność i bezpieczeństwo", "privacy_and_security_desc": "Skonfiguruj ustawienia uwierzytelniania i zabezpieczeń oraz skonfiguruj Cake 2FA.", + "privacy_desc": "Zmień ustawienia prywatności i przełącz specjalne funkcje prywatności.", "privacy_features": "Funkcje prywatności", "privacy_policy": "Polityka prywatności", "privacy_settings": "Ustawienia prywatności", diff --git a/res/values/strings_pt.arb b/res/values/strings_pt.arb index 2f8d71e10..d2823434f 100644 --- a/res/values/strings_pt.arb +++ b/res/values/strings_pt.arb @@ -242,6 +242,7 @@ "connected": "Conectado", "connection_sync": "Conexão e sincronização", "connections": "Conexões", + "connections_desc": "Gerencie conexões com serviços e APIs de terceiros.", "connectWalletPrompt": "Conecte sua carteira ao WalletConnect para realizar transações", "contact": "Contato", "contact_list_contacts": "Contatos", @@ -475,6 +476,7 @@ "file_saved": "Arquivo salvo", "fill_code": "Por favor, preencha o código de verificação enviado para o seu e-mail", "filter_by": "Filtrar por", + "filters": "Filtros", "finding_provider": "Procurando provedor", "first_wallet_text": "Carteira incrível para Monero, Bitcoin, Ethereum, Litecoin e Haven", "fixed_pair_not_supported": "Este par fixo não é compatível com os serviços de swap selecionados", @@ -757,6 +759,7 @@ "privacy": "Privacidade", "privacy_and_security": "Privacidade e Segurança", "privacy_and_security_desc": "Configure as definições de autenticação e segurança e configure o Cake 2FA.", + "privacy_desc": "Altere as configurações de privacidade e ative recursos especiais de privacidade.", "privacy_features": "Recursos de privacidade", "privacy_policy": "Política de Privacidade", "privacy_settings": "Configurações de privacidade", diff --git a/res/values/strings_ru.arb b/res/values/strings_ru.arb index ac8e3bfaf..416377e8f 100644 --- a/res/values/strings_ru.arb +++ b/res/values/strings_ru.arb @@ -242,6 +242,7 @@ "connected": "Подключено", "connection_sync": "Подключение и синхронизация", "connections": "Подключения", + "connections_desc": "Управляйте подключениями к сервисам и сторонним API.", "connectWalletPrompt": "Подключите свой кошелек к WalletConnect, чтобы совершать транзакции", "contact": "Контакт", "contact_list_contacts": "Контакты", @@ -475,6 +476,7 @@ "file_saved": "Файл сохранён", "fill_code": "Пожалуйста, введите код подтверждения, отправленный на вашу электронную почту", "filter_by": "Фильтровать по", + "filters": "Фильтры", "finding_provider": "Поиск провайдера", "first_wallet_text": "Отличный кошелёк для Monero, Bitcoin, Ethereum, Litecoin и Haven", "fixed_pair_not_supported": "Эта фиксированная пара не поддерживается выбранными сервисами обмена", @@ -756,6 +758,7 @@ "privacy": "Конфиденциальность", "privacy_and_security": "Конфиденциальность и безопасность", "privacy_and_security_desc": "Настройте параметры аутентификации и безопасности, а также настройте Cake 2FA.", + "privacy_desc": "Измените настройки конфиденциальности и включите специальные функции конфиденциальности.", "privacy_features": "Функции конфиденциальности", "privacy_policy": "Политика конфиденциальности", "privacy_settings": "Настройки конфиденциальности", diff --git a/res/values/strings_th.arb b/res/values/strings_th.arb index e17bf345b..fc986ff8b 100644 --- a/res/values/strings_th.arb +++ b/res/values/strings_th.arb @@ -242,6 +242,7 @@ "connected": "เชื่อมต่อแล้ว", "connection_sync": "การเชื่อมต่อและซิงค์", "connections": "การเชื่อมต่อ", + "connections_desc": "จัดการการเชื่อมต่อกับบริการและ API ของบุคคลที่สาม", "connectWalletPrompt": "เชื่อมต่อกระเป๋าเงินของคุณกับ WalletConnect เพื่อทำธุรกรรม", "contact": "ผู้ติดต่อ", "contact_list_contacts": "รายชื่อผู้ติดต่อ", @@ -475,6 +476,7 @@ "file_saved": "บันทึกไฟล์แล้ว", "fill_code": "โปรดกรอกรหัสยืนยันที่ส่งไปยังอีเมลของคุณ", "filter_by": "กรองตาม", + "filters": "ตัวกรอง", "finding_provider": "กำลังค้นหาผู้ให้บริการ", "first_wallet_text": "กระเป๋าเงินที่ยอดเยี่ยมสำหรับ Monero, Bitcoin, Ethereum, Litecoin และ Haven", "fixed_pair_not_supported": "คู่แบบคงที่นี้ไม่รองรับกับบริการสวอปที่เลือก", @@ -755,6 +757,7 @@ "privacy": "ความเป็นส่วนตัว", "privacy_and_security": "ความเป็นส่วนตัวและความปลอดภัย", "privacy_and_security_desc": "กำหนดค่าการยืนยันตัวตนและการตั้งค่าความปลอดภัย และตั้งค่า Cake 2FA", + "privacy_desc": "เปลี่ยนการตั้งค่าความเป็นส่วนตัวและสลับคุณสมบัติความเป็นส่วนตัวพิเศษ", "privacy_features": "ฟีเจอร์ความเป็นส่วนตัว", "privacy_policy": "นโยบายความเป็นส่วนตัว", "privacy_settings": "การตั้งค่าความเป็นส่วนตัว", diff --git a/res/values/strings_tl.arb b/res/values/strings_tl.arb index 396587d44..2893fdc42 100644 --- a/res/values/strings_tl.arb +++ b/res/values/strings_tl.arb @@ -242,6 +242,7 @@ "connected": "Nakakonekta", "connection_sync": "Koneksyon at pag-sync", "connections": "Mga koneksyon", + "connections_desc": "Pamahalaan ang mga koneksyon sa mga serbisyo at mga third party na API.", "connectWalletPrompt": "Ikonekta ang iyong wallet sa WalletConnect para makapag-transact", "contact": "Kontak", "contact_list_contacts": "Mga Contact", @@ -475,6 +476,7 @@ "file_saved": "Na-save ang file", "fill_code": "Mangyaring ilagay ang verification code na ipinadala sa iyong email", "filter_by": "I-filter ayon sa", + "filters": "Mga filter", "finding_provider": "Naghahanap ng provider", "first_wallet_text": "Napakahusay na wallet para sa Monero, Bitcoin, Ethereum, Litecoin, at Haven", "fixed_pair_not_supported": "Hindi sinusuportahan ang nakapirming pares na ito sa mga napiling serbisyo ng swap", @@ -755,6 +757,7 @@ "privacy": "Pagkapribado", "privacy_and_security": "Pagkapribado at Seguridad", "privacy_and_security_desc": "I-configure ang mga setting ng pagpapatotoo at seguridad at i-set up ang Cake 2FA.", + "privacy_desc": "Baguhin ang mga setting ng privacy at i-toggle ang mga espesyal na feature sa privacy.", "privacy_features": "Mga tampok sa privacy", "privacy_policy": "Patakaran sa Privacy", "privacy_settings": "Mga setting sa privacy", diff --git a/res/values/strings_tr.arb b/res/values/strings_tr.arb index f2d073fb4..f2753f940 100644 --- a/res/values/strings_tr.arb +++ b/res/values/strings_tr.arb @@ -242,6 +242,7 @@ "connected": "Bağlı", "connection_sync": "Bağlantı ve senkronizasyon", "connections": "Bağlantılar", + "connections_desc": "Hizmetlere ve üçüncü taraf API'lere olan bağlantıları yönetin.", "connectWalletPrompt": "İşlem yapmak için cüzdanınızı WalletConnect ile bağlayın", "contact": "Kişi", "contact_list_contacts": "Kişiler", @@ -475,6 +476,7 @@ "file_saved": "Dosya kaydedildi", "fill_code": "Lütfen e-postanıza gönderilen doğrulama kodunu girin", "filter_by": "Filtrele:", + "filters": "Filtreler", "finding_provider": "Sağlayıcı aranıyor", "first_wallet_text": "Monero, Bitcoin, Ethereum, Litecoin ve Haven için harika bir cüzdan", "fixed_pair_not_supported": "Bu sabit parite, seçilen takas hizmetleriyle desteklenmiyor", @@ -755,6 +757,7 @@ "privacy": "Gizlilik", "privacy_and_security": "Gizlilik ve Güvenlik", "privacy_and_security_desc": "Kimlik doğrulama ve güvenlik ayarlarını yapılandırın ve Cake 2FA'yı ayarlayın.", + "privacy_desc": "Gizlilik ayarlarını değiştirin ve özel gizlilik özelliklerini değiştirin.", "privacy_features": "Gizlilik özellikleri", "privacy_policy": "Gizlilik Politikası", "privacy_settings": "Gizlilik ayarları", diff --git a/res/values/strings_uk.arb b/res/values/strings_uk.arb index 4d9148138..fc280695c 100644 --- a/res/values/strings_uk.arb +++ b/res/values/strings_uk.arb @@ -242,6 +242,7 @@ "connected": "Підключено", "connection_sync": "Підключення та синхронізація", "connections": "Підключення", + "connections_desc": "Керуйте підключеннями до служб і сторонніх API.", "connectWalletPrompt": "Підключіть свій гаманець через WalletConnect, щоб здійснювати транзакції", "contact": "Контакт", "contact_list_contacts": "Контакти", @@ -475,6 +476,7 @@ "file_saved": "Файл збережено", "fill_code": "Будь ласка, введіть код підтвердження, надісланий на вашу електронну адресу", "filter_by": "Фільтрувати за", + "filters": "Фільтри", "finding_provider": "Пошук провайдера", "first_wallet_text": "Чудовий гаманець для Monero, Bitcoin, Ethereum, Litecoin та Haven", "fixed_pair_not_supported": "Ця фіксована пара не підтримується вибраними сервісами обміну", @@ -755,6 +757,7 @@ "privacy": "Конфіденційність", "privacy_and_security": "Конфіденційність і безпека", "privacy_and_security_desc": "Налаштуйте параметри автентифікації та безпеки й налаштуйте Cake 2FA.", + "privacy_desc": "Змініть налаштування конфіденційності та ввімкніть спеціальні функції конфіденційності.", "privacy_features": "Функції приватності", "privacy_policy": "Політика конфіденційності", "privacy_settings": "Налаштування конфіденційності", diff --git a/res/values/strings_ur.arb b/res/values/strings_ur.arb index a8853e831..e8de667ac 100644 --- a/res/values/strings_ur.arb +++ b/res/values/strings_ur.arb @@ -242,6 +242,7 @@ "connected": "منسلک", "connection_sync": "کنکشن اور ہم آہنگی", "connections": "کنکشنز", + "connections_desc": "سروسز اور تھرڈ پارٹی APIs کے کنکشنز کا نظم کریں۔", "connectWalletPrompt": "لین دین کرنے کے لیے WalletConnect کے ذریعے اپنا والیٹ کنیکٹ کریں", "contact": "رابطہ", "contact_list_contacts": "رابطے", @@ -475,6 +476,7 @@ "file_saved": "فائل محفوظ ہو گئی", "fill_code": "براہ کرم اپنے ای میل پر موصول ہونے والا تصدیقی کوڈ درج کریں۔", "filter_by": "کے مطابق فلٹر کریں", + "filters": "فلٹرز", "finding_provider": "فراہم کنندہ تلاش کیا جا رہا ہے", "first_wallet_text": "Monero، Bitcoin، Ethereum، Litecoin اور Haven کے لیے بہترین والیٹ", "fixed_pair_not_supported": "منتخب کردہ سواپ سروسز کے ساتھ یہ فکسڈ جوڑی سپورٹ نہیں کی جاتی", @@ -757,6 +759,7 @@ "privacy": "رازداری", "privacy_and_security": "رازداری اور سیکیورٹی", "privacy_and_security_desc": "تصدیق اور سیکیورٹی کی ترتیبات کو کنفیگر کریں اور Cake 2FA سیٹ اپ کریں۔", + "privacy_desc": "رازداری کی ترتیبات کو تبدیل کریں اور خصوصی رازداری کی خصوصیات کو ٹوگل کریں۔", "privacy_features": "رازداری کی خصوصیات", "privacy_policy": "رازداری کی پالیسی", "privacy_settings": "پرائیویسی کی ترتیبات", diff --git a/res/values/strings_vi.arb b/res/values/strings_vi.arb index d4529c144..63efd82e7 100644 --- a/res/values/strings_vi.arb +++ b/res/values/strings_vi.arb @@ -241,6 +241,7 @@ "connected": "Đã kết nối", "connection_sync": "Kết nối và đồng bộ", "connections": "Kết nối", + "connections_desc": "Quản lý kết nối với các dịch vụ và API của bên thứ ba.", "connectWalletPrompt": "Kết nối ví của bạn với WalletConnect để thực hiện các giao dịch", "contact": "Liên hệ", "contact_list_contacts": "Liên hệ", @@ -474,6 +475,7 @@ "file_saved": "Đã lưu tệp", "fill_code": "Vui lòng nhập mã xác minh được gửi đến email của bạn", "filter_by": "Lọc theo", + "filters": "Bộ lọc", "finding_provider": "Đang tìm nhà cung cấp", "first_wallet_text": "Ví tuyệt vời cho Monero, Bitcoin, Ethereum, Litecoin và Haven", "fixed_pair_not_supported": "Cặp tỷ giá cố định này không được hỗ trợ với các dịch vụ hoán đổi đã chọn", @@ -753,6 +755,7 @@ "privacy": "Quyền riêng tư", "privacy_and_security": "Quyền riêng tư & Bảo mật", "privacy_and_security_desc": "Định cấu hình cài đặt xác thực và bảo mật, đồng thời thiết lập Cake 2FA.", + "privacy_desc": "Thay đổi cài đặt quyền riêng tư và chuyển đổi các tính năng bảo mật đặc biệt.", "privacy_features": "Tính năng quyền riêng tư", "privacy_policy": "Chính sách bảo mật", "privacy_settings": "Cài đặt quyền riêng tư", diff --git a/res/values/strings_yo.arb b/res/values/strings_yo.arb index 406a4bf76..177a2422c 100644 --- a/res/values/strings_yo.arb +++ b/res/values/strings_yo.arb @@ -242,6 +242,7 @@ "connected": "Ti sopọ", "connection_sync": "Ìsopọ̀ àti ìbámu", "connections": "Awọn asopọ", + "connections_desc": "Ṣakoso awọn asopọ si awọn iṣẹ ati awọn API ẹnikẹta.", "connectWalletPrompt": "So apamọwọ rẹ pọ̀ mọ́ WalletConnect láti ṣe àwọn ìdúnàdúrà", "contact": "Olùbáṣepọ̀", "contact_list_contacts": "Àwọn olùbásọ̀rọ̀", @@ -476,6 +477,7 @@ "file_saved": "Faili ti fipamọ", "fill_code": "Jọ̀wọ́ tẹ kóòdù ìmúdájú tí a rán sí ímeèlì rẹ.", "filter_by": "Ṣàlẹ̀mọ́ nípa", + "filters": "Ajọ", "finding_provider": "N wa olupese", "first_wallet_text": "Àpamọ́wọ́ tó dára gan-an fún Monero, Bitcoin, Ethereum, Litecoin, àti Haven", "fixed_pair_not_supported": "Akọsopọ paṣipaarọ ti o wa titi yii ko ni atilẹyin pẹlu awọn iṣẹ swap ti o yan", @@ -756,6 +758,7 @@ "privacy": "Àṣírí", "privacy_and_security": "Ìpamọ́ & Ààbò", "privacy_and_security_desc": "Ṣètò ìfàṣẹsí àti àwọn àtúnṣe aabo, kí o sì ṣètò Cake 2FA.", + "privacy_desc": "Yi eto ipamọ pada ki o yi awọn ẹya aṣiri pataki pada.", "privacy_features": "Awọn ẹya ìpamọ́", "privacy_policy": "Ilana Aṣírí", "privacy_settings": "Àwọn ààtò ìpamọ́", diff --git a/res/values/strings_zh.arb b/res/values/strings_zh.arb index 38bdc7242..e8fc8f72e 100644 --- a/res/values/strings_zh.arb +++ b/res/values/strings_zh.arb @@ -242,6 +242,7 @@ "connected": "已连接", "connection_sync": "连接与同步", "connections": "连接", + "connections_desc": "管理与服务和第三方 API 的连接。", "connectWalletPrompt": "通过 WalletConnect 连接您的钱包以进行交易", "contact": "联系人", "contact_list_contacts": "联系人", @@ -475,6 +476,7 @@ "file_saved": "文件已保存", "fill_code": "请填写发送至您邮箱的验证码", "filter_by": "筛选条件", + "filters": "过滤器", "finding_provider": "正在查找服务提供商", "first_wallet_text": "适用于门罗币、比特币、以太坊、莱特币和 Haven 的超棒钱包", "fixed_pair_not_supported": "所选的兑换服务不支持该固定交易对", @@ -755,6 +757,7 @@ "privacy": "隐私", "privacy_and_security": "隐私与安全", "privacy_and_security_desc": "配置身份验证与安全设置,并设置 Cake 2FA。", + "privacy_desc": "更改隐私设置并切换特殊隐私功能。", "privacy_features": "隐私功能", "privacy_policy": "隐私政策", "privacy_settings": "隐私设置",