Files
hash-wallet/lib/new-ui/pages/settings_page.dart
T
Konstantin UllrichandGitHub feeec2e425 CW-1581-trezor-fixes-enhancements (#3462)
* feat: add Trezor auto connect

* chore: update `trezor_flutter`

* fix: correct method name for retrieving Trezor auto-pairing credentials

* fix: update device connection prompt text across all languages [skip ci]

* feat: add Trezor key image synchronization ui and improve hardware wallet UX

* feat: add sync-balance icon asset for settings row visuals [skip ci]

* feat: add sync-balance icon asset for settings row visuals [skip ci]

* fix: remove unused Monero and sync key images settings actions from menus [skip ci]

* fix: re-enable routing for UR QR Animated Page [skip ci]
2026-08-06 14:49:18 +02:00

204 lines
8.1 KiB
Dart

import 'package:cake_wallet/core/auth_service.dart';
import 'package:cake_wallet/entities/new_ui_entities/list_item/list_item.dart';
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/modal_navigator.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/widgets/cake_image_widget.dart';
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:cw_core/wallet_type.dart";
import 'package:flutter/material.dart';
import 'package:modal_bottom_sheet/modal_bottom_sheet.dart';
bool _trueFunc(DashboardViewModel _) => true;
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 _requiresKeyImageSync(DashboardViewModel vm) =>
vm.wallet.type == WalletType.monero &&
[HardwareWalletType.cupcake, HardwareWalletType.trezor].contains(vm.wallet.hardwareWalletType);
class SettingsListItem {
const SettingsListItem(
this.iconPath,
this.title,
this.route, {
this.requireAuth = false,
this.use2fa = _falseFunc,
this.condition = _trueFunc,
this.routeArgs,
});
final String iconPath;
final String title;
final String route;
final Object? routeArgs;
final bool requireAuth;
final bool Function(DashboardViewModel) use2fa;
final bool Function(DashboardViewModel) condition;
}
class SettingsSectionData {
const SettingsSectionData(this.title, this.titleIconPath, this.items);
final String title;
final String titleIconPath;
final List<SettingsListItem> items;
static SettingsSectionData walletSettings =
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: _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/sync-balance.svg",
S.current.resync_device,
Routes.syncKeyImagesDevices,
routeArgs: {'export-outputs': 'export-outputs'},
condition: _requiresKeyImageSync,
),
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/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),
SettingsListItem("assets/new-ui/settings_row_icons/security.svg", S.current.security,
Routes.securityBackupPage),
SettingsListItem("assets/new-ui/settings_row_icons/backup.svg", S.current.backup, Routes.backup,
requireAuth: true,
use2fa: (vm) => vm.settingsStore.shouldRequireTOTP2FAForAllSecurityAndBackupSettings),
]);
static SettingsSectionData otherSettings = SettingsSectionData("", "", [
SettingsListItem(
"assets/new-ui/settings_row_icons/support.svg", S.current.settings_support, Routes.support),
SettingsListItem(
"assets/new-ui/settings_row_icons/info.svg", S.current.about, Routes.aboutPage),
]);
static List<SettingsSectionData> all = [walletSettings, appSettings, otherSettings];
}
class NewSettingsPage extends StatelessWidget {
const NewSettingsPage({super.key, required this.dashboardViewModel, required this.authService});
final DashboardViewModel dashboardViewModel;
final AuthService authService;
@override
Widget build(BuildContext context) {
return ModalNavigator(
parentContext: context,
rootPage: SettingsMainPage(
dashboardViewModel: dashboardViewModel,
authService: authService,
));
}
}
class SettingsMainPage extends StatelessWidget {
const SettingsMainPage({super.key, required this.dashboardViewModel, required this.authService});
final DashboardViewModel dashboardViewModel;
final AuthService authService;
@override
Widget build(BuildContext context) {
List<ListItem> 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<ListItem>()
.toList();
return Container(
color: Theme.of(context).colorScheme.surface,
child: Column(children: [
ModalTopBar(
title: S.of(context).settings_title,
leadingIcon: Icon(Icons.close),
leadingSemanticLabel: S.of(context).close,
onLeadingPressed: Navigator.of(context, rootNavigator: true).pop,
onTrailingPressed: () {},
),
Expanded(
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: [
CakeImageWidget(
imageUrl: 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(),
),
),
]),
),
]),
);
}
}