From 837c4d7d22a16b5cbcdded560fd9a2701c7cc1b8 Mon Sep 17 00:00:00 2001 From: Blazebrain Date: Fri, 28 Aug 2026 23:07:43 +0100 Subject: [PATCH] feat: redesign the pre-seed and seed verification screens --- .../robots/pre_seed_page_robot.dart | 5 +- lib/di.dart | 12 +- .../seed/open_wallet_after_seed_flow.dart | 13 ++ lib/new-ui/pages/seed/pre_seed_page.dart | 125 ++++++++++++++++++ .../pages/seed/show_keys_disclaimer_page.dart | 62 +++++++++ .../seed/skip_seed_verification_page.dart | 64 +++++++++ lib/new-ui/pages/settings_page.dart | 7 +- .../widgets/seed/seed_checkbox_row.dart | 72 ++++++++++ lib/new-ui/widgets/seed/seed_hero_image.dart | 20 +++ lib/new-ui/widgets/seed/seed_page_header.dart | 37 ++++++ .../widgets/seed/seed_page_scaffold.dart | 58 ++++++++ lib/new-ui/widgets/stacked_buttons.dart | 65 +++++++++ lib/router.dart | 10 +- lib/routes.dart | 1 + ..._group_existing_seed_description_page.dart | 2 +- lib/src/screens/seed/pre_seed_page.dart | 31 ----- .../seed_verification_page.dart | 6 +- .../seed_verification_success_view.dart | 28 ++-- lib/src/screens/seed/wallet_seed_page.dart | 9 +- res/pictures/hero/seed_shield_dark.svg | 80 +++++++++++ res/pictures/hero/seed_shield_light.svg | 106 +++++++++++++++ .../pictures/hero}/seed_warning_dark.svg | 0 .../pictures/hero}/seed_warning_light.svg | 0 res/pictures/key.svg | 5 + res/pictures/visibility.svg | 5 + res/pictures/warning_triangle.svg | 28 ++++ 26 files changed, 795 insertions(+), 56 deletions(-) create mode 100644 lib/new-ui/pages/seed/open_wallet_after_seed_flow.dart create mode 100644 lib/new-ui/pages/seed/pre_seed_page.dart create mode 100644 lib/new-ui/pages/seed/show_keys_disclaimer_page.dart create mode 100644 lib/new-ui/pages/seed/skip_seed_verification_page.dart create mode 100644 lib/new-ui/widgets/seed/seed_checkbox_row.dart create mode 100644 lib/new-ui/widgets/seed/seed_hero_image.dart create mode 100644 lib/new-ui/widgets/seed/seed_page_header.dart create mode 100644 lib/new-ui/widgets/seed/seed_page_scaffold.dart create mode 100644 lib/new-ui/widgets/stacked_buttons.dart delete mode 100644 lib/src/screens/seed/pre_seed_page.dart create mode 100644 res/pictures/hero/seed_shield_dark.svg create mode 100644 res/pictures/hero/seed_shield_light.svg rename {assets/images => res/pictures/hero}/seed_warning_dark.svg (100%) rename {assets/images => res/pictures/hero}/seed_warning_light.svg (100%) create mode 100644 res/pictures/key.svg create mode 100644 res/pictures/visibility.svg create mode 100644 res/pictures/warning_triangle.svg diff --git a/integration_test/robots/pre_seed_page_robot.dart b/integration_test/robots/pre_seed_page_robot.dart index 6f46e3ac1..c3eeb5fe5 100644 --- a/integration_test/robots/pre_seed_page_robot.dart +++ b/integration_test/robots/pre_seed_page_robot.dart @@ -1,4 +1,4 @@ -import 'package:cake_wallet/src/screens/seed/pre_seed_page.dart'; +import "package:cake_wallet/new-ui/pages/seed/pre_seed_page.dar"'; import 'package:flutter_test/flutter_test.dart'; import '../components/common_test_cases.dart'; @@ -15,6 +15,9 @@ class PreSeedPageRobot { } Future onConfirmButtonPressed() async { + await commonTestCases.tapItemByKey("pre_seed_page_only_way_checkbox_key"); + await commonTestCases.tapItemByKey("pre_seed_page_write_down_checkbox_key"); + await commonTestCases.tapItemByKey("pre_seed_page_never_share_checkbox_key"); await commonTestCases.tapItemByKey('pre_seed_page_button_key'); await commonTestCases.defaultSleepTime(); } diff --git a/lib/di.dart b/lib/di.dart index ee904e21f..4e64d9c24 100644 --- a/lib/di.dart +++ b/lib/di.dart @@ -63,6 +63,8 @@ import 'package:cake_wallet/new-ui/pages/send_page.dart'; import "package:cake_wallet/new-ui/services/wallet_switch_service.dart"; import 'package:cake_wallet/new-ui/pages/lightning_username_page.dart'; import 'package:cake_wallet/new-ui/pages/receive_page.dart'; +import "package:cake_wallet/new-ui/pages/seed/pre_seed_page.dart"; +import "package:cake_wallet/new-ui/pages/seed/show_keys_disclaimer_page.dart"; import 'package:cake_wallet/new-ui/viewmodels/lightning_username/lightning_username_bloc.dart'; import 'package:cake_wallet/new-ui/widgets/addresses_page/address_label_input.dart'; import 'package:cake_wallet/new-ui/widgets/coins_page/assets_history/transaction_details_modal.dart'; @@ -130,7 +132,6 @@ import 'package:cake_wallet/src/screens/restore/restore_from_backup_page.dart'; import 'package:cake_wallet/src/screens/restore/restore_options_page.dart'; import 'package:cake_wallet/src/screens/restore/wallet_restore_choose_derivation.dart'; import 'package:cake_wallet/src/screens/restore/wallet_restore_page.dart'; -import 'package:cake_wallet/src/screens/seed/pre_seed_page.dart'; import 'package:cake_wallet/src/screens/seed/seed_verification/seed_verification_page.dart'; import 'package:cake_wallet/src/screens/seed/wallet_seed_page.dart'; import 'package:cake_wallet/src/screens/send/send_page.dart'; @@ -1089,6 +1090,9 @@ Future setup({ getIt.registerFactory(() => WalletKeysPage(getIt.get())); + getIt.registerFactory(() => ShowKeysDisclaimerPage( + authService: getIt.get(), settingsStore: getIt.get())); + getIt.registerFactory(() => AnimatedURModel(getIt.get())); getIt.registerFactoryParam, void>( @@ -1448,7 +1452,7 @@ Future setup({ ); }); - getIt.registerFactory(() => PreSeedPage()); + getIt.registerFactory(() => PreSeedPage(getIt.get().wallet!)); getIt.registerFactoryParam( (content, _) => TransactionSuccessPage(content: content)); @@ -1702,7 +1706,9 @@ Future setup({ getIt.registerFactory(() => SignViewModel(getIt.get().wallet!)); - getIt.registerFactory(() => SeedVerificationPage(getIt.get())); + getIt.registerFactoryParam((bool isNewWalletCreated, _) => + SeedVerificationPage(getIt.get(), + isNewWalletCreated: isNewWalletCreated)); getIt.registerFactory(() => DevMoneroBackgroundSyncPage(getIt.get())); diff --git a/lib/new-ui/pages/seed/open_wallet_after_seed_flow.dart b/lib/new-ui/pages/seed/open_wallet_after_seed_flow.dart new file mode 100644 index 000000000..22d55117d --- /dev/null +++ b/lib/new-ui/pages/seed/open_wallet_after_seed_flow.dart @@ -0,0 +1,13 @@ +import "package:cake_wallet/routes.dart"; +import "package:cw_core/wallet_type.dart"; +import "package:flutter/widgets.dart"; + +// Bitcoin wallets set up lightning username right after the seed step. +void openWalletAfterSeedFlow(BuildContext context, WalletType walletType) { + if (walletType == WalletType.bitcoin) { + Navigator.of(context).pushNamed(Routes.lightningUsernamePage, arguments: true); + return; + } + + Navigator.of(context).popUntil((route) => route.isFirst); +} diff --git a/lib/new-ui/pages/seed/pre_seed_page.dart b/lib/new-ui/pages/seed/pre_seed_page.dart new file mode 100644 index 000000000..67642bea9 --- /dev/null +++ b/lib/new-ui/pages/seed/pre_seed_page.dart @@ -0,0 +1,125 @@ +import "package:cake_wallet/generated/i18n.dart"; +import "package:cake_wallet/new-ui/pages/seed/open_wallet_after_seed_flow.dart"; +import "package:cake_wallet/new-ui/pages/seed/skip_seed_verification_page.dart"; +import "package:cake_wallet/new-ui/widgets/seed/seed_checkbox_row.dart"; +import "package:cake_wallet/new-ui/widgets/seed/seed_hero_image.dart"; +import "package:cake_wallet/new-ui/widgets/seed/seed_page_header.dart"; +import "package:cake_wallet/new-ui/widgets/seed/seed_page_scaffold.dart"; +import "package:cake_wallet/new-ui/widgets/stacked_buttons.dart"; +import "package:cake_wallet/routes.dart"; +import "package:cake_wallet/themes/core/theme_extension.dart"; +import "package:cw_core/wallet_base.dart"; +import "package:flutter/cupertino.dart"; +import "package:flutter/material.dart"; + +class PreSeedPage extends StatefulWidget { + const PreSeedPage(this.wallet, {super.key}); + + final WalletBase wallet; + + @override + State createState() => _PreSeedPageState(); +} + +class _PreSeedPageState extends State { + bool _understandsOnlyWayToRecover = false; + bool _willWriteItDown = false; + bool _willNeverShareIt = false; + + bool get _hasCheckedAll => _understandsOnlyWayToRecover && _willWriteItDown && _willNeverShareIt; + + @override + Widget build(BuildContext context) { + final colors = Theme.of(context).colorScheme; + final textTheme = Theme.of(context).textTheme; + + return PopScope( + canPop: false, + child: SeedPageScaffold( + content: Column( + mainAxisSize: MainAxisSize.min, + spacing: 24, + children: [ + SeedPageHeader( + image: const SeedHeroImage(), + title: S.of(context).recovery_phrase_intro_title, + description: Text( + S.of(context).recovery_phrase_intro_description, + textAlign: TextAlign.center, + style: textTheme.bodyMedium?.copyWith( + color: colors.onSurfaceVariant, + letterSpacing: -0.07, + ), + ), + ), + Column( + spacing: 12, + children: [ + SeedCheckboxRow( + key: const ValueKey("pre_seed_page_only_way_checkbox_key"), + iconPath: "assets/new-ui/key.svg", + text: S.of(context).recovery_phrase_check_only_way, + isChecked: _understandsOnlyWayToRecover, + onChanged: (value) => setState(() => _understandsOnlyWayToRecover = value), + ), + SeedCheckboxRow( + key: const ValueKey("pre_seed_page_write_down_checkbox_key"), + iconPath: "assets/new-ui/set-amount.svg", + text: S.of(context).recovery_phrase_check_write_down, + isChecked: _willWriteItDown, + onChanged: (value) => setState(() => _willWriteItDown = value), + ), + SeedCheckboxRow( + key: const ValueKey("pre_seed_page_never_share_checkbox_key"), + iconPath: "assets/new-ui/visibility.svg", + text: S.of(context).recovery_phrase_check_never_share, + isChecked: _willNeverShareIt, + onChanged: (value) => setState(() => _willNeverShareIt = value), + ), + ], + ), + if (!_hasCheckedAll) + Text( + S.of(context).recovery_phrase_check_all_to_proceed, + textAlign: TextAlign.center, + style: textTheme.bodyMedium?.copyWith( + color: context.customColors.warningOutlineColor, + letterSpacing: -0.07, + ), + ), + ], + ), + footer: _hasCheckedAll + ? StackedButtons( + primaryKey: const ValueKey("pre_seed_page_button_key"), + primaryText: S.of(context).show_recovery_phrase, + onPrimary: () => Navigator.of(context).pushNamed(Routes.seed, arguments: true), + secondaryKey: const ValueKey("pre_seed_page_skip_button_key"), + secondaryText: S.of(context).skip_this_step, + onSecondary: _confirmSkip, + secondaryAsLink: true, + ) + : null, + alignTop: true, + ), + ); + } + + void _confirmSkip() { + Navigator.of(context).push( + CupertinoPageRoute( + builder: (_) => SkipSeedVerificationPage(onConfirm: _skipVerification), + ), + ); + } + + Future _skipVerification() async { + await widget.wallet.walletInfo.updateShowSeedBackupReminder(true); + + if (!mounted) { + return; + } + + openWalletAfterSeedFlow(context, widget.wallet.type); + } +} diff --git a/lib/new-ui/pages/seed/show_keys_disclaimer_page.dart b/lib/new-ui/pages/seed/show_keys_disclaimer_page.dart new file mode 100644 index 000000000..4b523a20c --- /dev/null +++ b/lib/new-ui/pages/seed/show_keys_disclaimer_page.dart @@ -0,0 +1,62 @@ +import "package:cake_wallet/core/auth_service.dart"; +import "package:cake_wallet/generated/i18n.dart"; +import "package:cake_wallet/new-ui/widgets/seed/seed_hero_image.dart"; +import "package:cake_wallet/new-ui/widgets/seed/seed_page_header.dart"; +import "package:cake_wallet/new-ui/widgets/seed/seed_page_scaffold.dart"; +import "package:cake_wallet/new-ui/widgets/stacked_buttons.dart"; +import "package:cake_wallet/routes.dart"; +import "package:cake_wallet/store/settings_store.dart"; +import "package:cake_wallet/themes/core/theme_extension.dart"; +import "package:flutter/material.dart"; + +class ShowKeysDisclaimerPage extends StatelessWidget { + const ShowKeysDisclaimerPage({ + required this.authService, + required this.settingsStore, + super.key, + }); + + final AuthService authService; + final SettingsStore settingsStore; + + @override + Widget build(BuildContext context) { + final colors = Theme.of(context).colorScheme; + + return SeedPageScaffold( + content: SeedPageHeader( + image: const SeedHeroImage(showWarningBadge: true), + title: S.of(context).secret_information_ahead, + description: Text.rich( + TextSpan( + children: [ + TextSpan(text: "${S.of(context).secret_information_ahead_description}\n\n"), + TextSpan( + text: S.of(context).secret_information_ahead_warning, + style: TextStyle(color: context.customColors.warningOutlineColor), + ), + ], + ), + textAlign: TextAlign.center, + style: Theme.of(context).textTheme.bodyMedium?.copyWith( + color: colors.onSurfaceVariant, + letterSpacing: -0.07, + ), + ), + ), + footer: StackedButtons( + primaryKey: const ValueKey("show_keys_disclaimer_page_button_key"), + primaryText: S.of(context).show_recovery_phrase_and_keys, + onPrimary: () => authService.authenticateAction( + context, + route: Routes.showKeys, + conditionToDetermineIfToUse2FA: + settingsStore.shouldRequireTOTP2FAForAllSecurityAndBackupSettings, + ), + secondaryKey: const ValueKey("show_keys_disclaimer_page_back_button_key"), + secondaryText: S.of(context).seed_alert_back, + onSecondary: () => Navigator.of(context).pop(), + ), + ); + } +} diff --git a/lib/new-ui/pages/seed/skip_seed_verification_page.dart b/lib/new-ui/pages/seed/skip_seed_verification_page.dart new file mode 100644 index 000000000..8d87601e9 --- /dev/null +++ b/lib/new-ui/pages/seed/skip_seed_verification_page.dart @@ -0,0 +1,64 @@ +import "package:cake_wallet/generated/i18n.dart"; +import "package:cake_wallet/new-ui/widgets/seed/seed_page_header.dart"; +import "package:cake_wallet/new-ui/widgets/seed/seed_page_scaffold.dart"; +import "package:cake_wallet/new-ui/widgets/stacked_buttons.dart"; +import "package:cake_wallet/src/widgets/cake_image_widget.dart"; +import "package:cake_wallet/themes/core/theme_extension.dart"; +import "package:flutter/material.dart"; + +class SkipSeedVerificationPage extends StatelessWidget { + const SkipSeedVerificationPage({required this.onConfirm, super.key}); + + final VoidCallback onConfirm; + + @override + Widget build(BuildContext context) { + final colors = Theme.of(context).colorScheme; + final textTheme = Theme.of(context).textTheme; + + return SeedPageScaffold( + content: ConstrainedBox( + constraints: const BoxConstraints(maxWidth: 295), + child: SeedPageHeader( + image: const CakeImageWidget( + imageUrl: "assets/new-ui/warning_triangle.svg", + width: 125, + height: 111.72, + ), + imageSpacing: 48, + titleSpacing: 24, + title: S.of(context).skip_verification_title, + description: Column( + spacing: 24, + children: [ + Text( + S.of(context).skip_verification_warning, + textAlign: TextAlign.center, + style: textTheme.bodyMedium?.copyWith( + color: context.customColors.warningOutlineColor, + letterSpacing: -0.07, + ), + ), + Text( + S.of(context).skip_verification_question, + textAlign: TextAlign.center, + style: textTheme.bodyMedium?.copyWith( + color: colors.onSurfaceVariant, + letterSpacing: -0.07, + ), + ), + ], + ), + ), + ), + footer: StackedButtons( + primaryKey: const ValueKey("skip_seed_verification_page_confirm_button_key"), + primaryText: S.of(context).skip_verification_confirm, + onPrimary: onConfirm, + secondaryKey: const ValueKey("skip_seed_verification_page_back_button_key"), + secondaryText: S.of(context).seed_alert_back, + onSecondary: () => Navigator.of(context).pop(), + ), + ); + } +} diff --git a/lib/new-ui/pages/settings_page.dart b/lib/new-ui/pages/settings_page.dart index 8aaec520d..e60865d23 100644 --- a/lib/new-ui/pages/settings_page.dart +++ b/lib/new-ui/pages/settings_page.dart @@ -62,11 +62,8 @@ class SettingsSectionData { "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/seed.svg", S.current.recovery_and_keys, + Routes.showKeysDisclaimer), SettingsListItem("assets/new-ui/settings_row_icons/lightning_username.svg", "Lightning ${S.current.username}", Routes.lightningUsernamePage, condition: _hasLightning), diff --git a/lib/new-ui/widgets/seed/seed_checkbox_row.dart b/lib/new-ui/widgets/seed/seed_checkbox_row.dart new file mode 100644 index 000000000..9291e9858 --- /dev/null +++ b/lib/new-ui/widgets/seed/seed_checkbox_row.dart @@ -0,0 +1,72 @@ +import "package:cake_wallet/src/widgets/cake_image_widget.dart"; +import "package:flutter/material.dart"; + +class SeedCheckboxRow extends StatelessWidget { + const SeedCheckboxRow({ + required this.iconPath, + required this.text, + required this.isChecked, + required this.onChanged, + super.key, + }); + + final String iconPath; + final String text; + final bool isChecked; + final ValueChanged onChanged; + + @override + Widget build(BuildContext context) { + final colors = Theme.of(context).colorScheme; + + return MergeSemantics( + child: Semantics( + checked: isChecked, + child: GestureDetector( + behavior: HitTestBehavior.opaque, + onTap: () => onChanged(!isChecked), + child: Container( + padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 16), + decoration: BoxDecoration( + color: colors.surfaceContainer, + borderRadius: BorderRadius.circular(18), + ), + child: Row( + spacing: 12, + children: [ + ExcludeSemantics( + child: CakeImageWidget( + imageUrl: iconPath, + width: 24, + height: 24, + colorFilter: ColorFilter.mode(colors.primary, BlendMode.srcIn), + ), + ), + Expanded( + child: Text( + text, + style: Theme.of(context).textTheme.bodySmall?.copyWith( + color: colors.primary, + letterSpacing: -0.06, + ), + ), + ), + ExcludeSemantics( + child: Container( + width: 24, + height: 24, + decoration: BoxDecoration( + shape: BoxShape.circle, + color: isChecked ? colors.primary : colors.surfaceContainerHighest, + ), + child: isChecked ? Icon(Icons.check, size: 16, color: colors.onPrimary) : null, + ), + ), + ], + ), + ), + ), + ), + ); + } +} diff --git a/lib/new-ui/widgets/seed/seed_hero_image.dart b/lib/new-ui/widgets/seed/seed_hero_image.dart new file mode 100644 index 000000000..0dddeb558 --- /dev/null +++ b/lib/new-ui/widgets/seed/seed_hero_image.dart @@ -0,0 +1,20 @@ +import "package:cake_wallet/src/widgets/cake_image_widget.dart"; +import "package:flutter/material.dart"; + +class SeedHeroImage extends StatelessWidget { + const SeedHeroImage({this.showWarningBadge = false, super.key}); + + final bool showWarningBadge; + + @override + Widget build(BuildContext context) { + final name = showWarningBadge ? "seed_warning" : "seed_shield"; + final variant = Theme.of(context).brightness == Brightness.dark ? "dark" : "light"; + + return CakeImageWidget( + imageUrl: "assets/new-ui/hero/${name}_$variant.svg", + width: 200, + height: 228.57, + ); + } +} diff --git a/lib/new-ui/widgets/seed/seed_page_header.dart b/lib/new-ui/widgets/seed/seed_page_header.dart new file mode 100644 index 000000000..55a61cf9e --- /dev/null +++ b/lib/new-ui/widgets/seed/seed_page_header.dart @@ -0,0 +1,37 @@ +import "package:flutter/material.dart"; + +class SeedPageHeader extends StatelessWidget { + const SeedPageHeader({ + required this.image, + required this.title, + required this.description, + this.imageSpacing = 24, + this.titleSpacing = 12, + super.key, + }); + + final Widget image; + final String title; + final Widget description; + final double imageSpacing; + final double titleSpacing; + + @override + Widget build(BuildContext context) => Column( + mainAxisSize: MainAxisSize.min, + children: [ + ExcludeSemantics(child: image), + SizedBox(height: imageSpacing), + Semantics( + header: true, + child: Text( + title, + textAlign: TextAlign.center, + style: Theme.of(context).textTheme.headlineMedium?.copyWith(letterSpacing: -0.09), + ), + ), + SizedBox(height: titleSpacing), + description, + ], + ); +} diff --git a/lib/new-ui/widgets/seed/seed_page_scaffold.dart b/lib/new-ui/widgets/seed/seed_page_scaffold.dart new file mode 100644 index 000000000..cfdbcfa64 --- /dev/null +++ b/lib/new-ui/widgets/seed/seed_page_scaffold.dart @@ -0,0 +1,58 @@ +import "package:flutter/material.dart"; + +class SeedPageScaffold extends StatelessWidget { + const SeedPageScaffold({ + required this.content, + this.footer, + this.alignTop = false, + super.key, + }); + + final Widget content; + final Widget? footer; + final bool alignTop; + + @override + Widget build(BuildContext context) { + final colors = Theme.of(context).colorScheme; + + return Material( + child: Container( + decoration: BoxDecoration( + gradient: LinearGradient( + colors: [colors.surface, colors.surfaceDim], + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + ), + ), + child: SafeArea( + child: Column( + children: [ + Expanded( + child: LayoutBuilder( + builder: (context, constraints) => SingleChildScrollView( + child: ConstrainedBox( + constraints: BoxConstraints(minHeight: constraints.maxHeight), + child: Align( + alignment: alignTop ? Alignment.topCenter : Alignment.center, + child: Padding( + padding: EdgeInsets.fromLTRB(18, alignTop ? 0 : 16, 18, 16), + child: content, + ), + ), + ), + ), + ), + ), + if (footer != null) + Padding( + padding: const EdgeInsets.fromLTRB(16, 0, 16, 16), + child: footer, + ), + ], + ), + ), + ), + ); + } +} diff --git a/lib/new-ui/widgets/stacked_buttons.dart b/lib/new-ui/widgets/stacked_buttons.dart new file mode 100644 index 000000000..0e14e6788 --- /dev/null +++ b/lib/new-ui/widgets/stacked_buttons.dart @@ -0,0 +1,65 @@ +import "package:cake_wallet/new-ui/widgets/new_primary_button.dart"; +import "package:flutter/material.dart"; + +class StackedButtons extends StatelessWidget { + const StackedButtons({ + required this.primaryText, + required this.onPrimary, + required this.secondaryText, + required this.onSecondary, + this.secondaryAsLink = false, + this.primaryKey, + this.secondaryKey, + super.key, + }); + + final String primaryText; + final VoidCallback onPrimary; + final String secondaryText; + final VoidCallback onSecondary; + final bool secondaryAsLink; + final Key? primaryKey; + final Key? secondaryKey; + + @override + Widget build(BuildContext context) { + final colors = Theme.of(context).colorScheme; + + return Column( + mainAxisSize: MainAxisSize.min, + children: [ + NewPrimaryButton( + key: primaryKey, + onPressed: onPrimary, + text: primaryText, + color: colors.primary, + textColor: colors.onPrimary, + ), + if (secondaryAsLink) ...[ + const SizedBox(height: 9), + TextButton( + key: secondaryKey, + onPressed: onSecondary, + style: TextButton.styleFrom(minimumSize: const Size(48, 48)), + child: Text( + secondaryText, + style: Theme.of(context).textTheme.labelLarge?.copyWith( + color: colors.primary, + letterSpacing: -0.07, + ), + ), + ), + ] else ...[ + const SizedBox(height: 12), + NewPrimaryButton( + key: secondaryKey, + onPressed: onSecondary, + text: secondaryText, + color: colors.surfaceContainer, + textColor: colors.primary, + ), + ], + ], + ); + } +} diff --git a/lib/router.dart b/lib/router.dart index 2b01b079f..256f92bb1 100644 --- a/lib/router.dart +++ b/lib/router.dart @@ -10,6 +10,8 @@ import 'package:cake_wallet/new-ui/pages/bridge/bridge_receiving_wallet_page.dar import 'package:cake_wallet/new-ui/pages/coin_control_page.dart'; import 'package:cake_wallet/new-ui/pages/addresses_page.dart'; import 'package:cake_wallet/new-ui/pages/lightning_username_page.dart'; +import 'package:cake_wallet/new-ui/pages/seed/pre_seed_page.dart'; +import 'package:cake_wallet/new-ui/pages/seed/show_keys_disclaimer_page.dart'; import "package:cake_wallet/new-ui/pages/receive_page.dart"; import 'package:cake_wallet/new-ui/pages/send_page.dart'; import 'package:cake_wallet/new-ui/widgets/hardware_wallet/sync_key_images_sheet.dart'; @@ -92,7 +94,6 @@ import 'package:cake_wallet/src/screens/restore/restore_options_page.dart'; import 'package:cake_wallet/src/screens/restore/sweeping_wallet_page.dart'; import 'package:cake_wallet/src/screens/restore/wallet_restore_choose_derivation.dart'; import 'package:cake_wallet/src/screens/restore/wallet_restore_page.dart'; -import 'package:cake_wallet/src/screens/seed/pre_seed_page.dart'; import 'package:cake_wallet/src/screens/seed/seed_verification/seed_verification_page.dart'; import 'package:cake_wallet/src/screens/seed/wallet_seed_page.dart'; import 'package:cake_wallet/src/screens/send/send_template_page.dart'; @@ -687,6 +688,11 @@ Route createRoute(RouteSettings settings) { (context) => getIt.get(), ); + case Routes.showKeysDisclaimer: + return handleRouteWithPlatformAwareness( + (context) => getIt.get(), + ); + case Routes.exchangeTrade: return CupertinoPageRoute(builder: (_) => getIt.get()); @@ -996,7 +1002,7 @@ Route createRoute(RouteSettings settings) { case Routes.walletSeedVerificationPage: return MaterialPageRoute( fullscreenDialog: true, - builder: (_) => getIt.get(), + builder: (_) => getIt.get(param1: settings.arguments as bool), ); case Routes.exchangeTradeExternalSendPage: diff --git a/lib/routes.dart b/lib/routes.dart index b21e2bc38..a8b2c423c 100644 --- a/lib/routes.dart +++ b/lib/routes.dart @@ -38,6 +38,7 @@ class Routes { static const pickerWalletAddress = '/picker_wallet_address'; static const addressBookAddContact = '/address_book_add_contact'; static const showKeys = '/show_keys'; + static const showKeysDisclaimer = "/show_keys_disclaimer"; static const exchangeConfirm = '/exchange_confirm'; static const tradeHistory = '/trade_history'; static const tradeDetails = '/trade_details'; diff --git a/lib/src/screens/new_wallet/wallet_group_existing_seed_description_page.dart b/lib/src/screens/new_wallet/wallet_group_existing_seed_description_page.dart index d7d240494..ae3309405 100644 --- a/lib/src/screens/new_wallet/wallet_group_existing_seed_description_page.dart +++ b/lib/src/screens/new_wallet/wallet_group_existing_seed_description_page.dart @@ -79,7 +79,7 @@ class WalletGroupExistingSeedDescriptionPage extends BasePage { key: ValueKey( 'wallet_group_existing_seed_description_page_verify_seed_button_key'), onPressed: () => Navigator.pushNamed(context, Routes.preSeedPage), - text: S.current.verify_seed, + text: S.current.verify_phrase, color: Theme.of(context).colorScheme.surfaceContainer, textColor: Theme.of(context).colorScheme.onSecondaryContainer, ), diff --git a/lib/src/screens/seed/pre_seed_page.dart b/lib/src/screens/seed/pre_seed_page.dart deleted file mode 100644 index 4ff9fceea..000000000 --- a/lib/src/screens/seed/pre_seed_page.dart +++ /dev/null @@ -1,31 +0,0 @@ -import 'package:cake_wallet/generated/i18n.dart'; -import 'package:cake_wallet/routes.dart'; -import 'package:cake_wallet/src/screens/Info_page.dart'; -import 'package:flutter/cupertino.dart'; - -class PreSeedPage extends InfoPage { - PreSeedPage() - : super( - imageLightPath: 'assets/images/seed_warning_light.svg', - imageDarkPath: 'assets/images/seed_warning_dark.svg', - ); - - @override - bool get onWillPop => false; - - @override - String get pageTitle => S.current.pre_seed_title; - - @override - String get pageDescription => S.current.pre_seed_description; - - @override - String get buttonText => S.current.pre_seed_button_text; - - @override - Key? get buttonKey => ValueKey('pre_seed_page_button_key'); - - @override - void Function(BuildContext) get onPressed => - (BuildContext context) => Navigator.of(context).pushNamed(Routes.seed, arguments: true); -} diff --git a/lib/src/screens/seed/seed_verification/seed_verification_page.dart b/lib/src/screens/seed/seed_verification/seed_verification_page.dart index 0daefc70d..f26d5dfb6 100644 --- a/lib/src/screens/seed/seed_verification/seed_verification_page.dart +++ b/lib/src/screens/seed/seed_verification/seed_verification_page.dart @@ -9,8 +9,9 @@ import 'package:flutter_mobx/flutter_mobx.dart'; class SeedVerificationPage extends BasePage { final WalletSeedViewModel walletSeedViewModel; + final bool isNewWalletCreated; - SeedVerificationPage(this.walletSeedViewModel); + SeedVerificationPage(this.walletSeedViewModel, {required this.isNewWalletCreated}); @override bool get gradientBackground => true; @@ -32,7 +33,8 @@ class SeedVerificationPage extends BasePage { walletSeedViewModel.verificationIndices.isEmpty ? SeedVerificationSuccessView( key: ValueKey('seed_verification_success_view_page'), - walletType: walletSeedViewModel.walletType, + walletSeedViewModel: walletSeedViewModel, + isNewWalletCreated: isNewWalletCreated, currentTheme: currentTheme, ) : SeedVerificationStepView( diff --git a/lib/src/screens/seed/seed_verification/seed_verification_success_view.dart b/lib/src/screens/seed/seed_verification/seed_verification_success_view.dart index 320fe3deb..ad64a2a81 100644 --- a/lib/src/screens/seed/seed_verification/seed_verification_success_view.dart +++ b/lib/src/screens/seed/seed_verification/seed_verification_success_view.dart @@ -1,17 +1,23 @@ import 'package:cake_wallet/generated/i18n.dart'; -import 'package:cake_wallet/routes.dart'; +import 'package:cake_wallet/new-ui/pages/seed/open_wallet_after_seed_flow.dart'; import 'package:cake_wallet/src/widgets/cake_image_widget.dart'; import 'package:cake_wallet/src/widgets/primary_button.dart'; import 'package:cake_wallet/themes/core/material_base_theme.dart'; +import 'package:cake_wallet/view_model/wallet_seed_view_model.dart'; import 'package:cw_core/wallet_type.dart'; import 'package:flutter/material.dart'; class SeedVerificationSuccessView extends StatelessWidget { - const SeedVerificationSuccessView( - {required this.currentTheme, super.key, required this.walletType}); + const SeedVerificationSuccessView({ + required this.walletSeedViewModel, + required this.isNewWalletCreated, + required this.currentTheme, + super.key, + }); + final WalletSeedViewModel walletSeedViewModel; + final bool isNewWalletCreated; final MaterialThemeBase currentTheme; - final WalletType walletType; @override Widget build(BuildContext context) { @@ -65,14 +71,20 @@ class SeedVerificationSuccessView extends StatelessWidget { Spacer(), PrimaryButton( key: ValueKey('wallet_seed_page_open_wallet_button_key'), - onPressed: () { - if (walletType == WalletType.bitcoin) { - Navigator.of(context).pushNamed(Routes.lightningUsernamePage, arguments: true); + onPressed: () async { + await walletSeedViewModel.markSeedVerified(); + + if (!context.mounted) { + return; + } + + if (isNewWalletCreated) { + openWalletAfterSeedFlow(context, walletSeedViewModel.walletType); } else { Navigator.of(context).popUntil((route) => route.isFirst); } }, - text: (walletType == WalletType.bitcoin) + text: isNewWalletCreated && walletSeedViewModel.walletType == WalletType.bitcoin ? S.current.continue_text : S.current.open_wallet, color: Theme.of(context).colorScheme.primary, diff --git a/lib/src/screens/seed/wallet_seed_page.dart b/lib/src/screens/seed/wallet_seed_page.dart index 27fcf341b..49d5fb945 100644 --- a/lib/src/screens/seed/wallet_seed_page.dart +++ b/lib/src/screens/seed/wallet_seed_page.dart @@ -124,9 +124,12 @@ class WalletSeedPage extends BasePage { padding: EdgeInsets.only(left: 8.0, top: 8.0), child: PrimaryButton( key: ValueKey('wallet_seed_page_verify_seed_button_key'), - onPressed: () => - Navigator.pushNamed(context, Routes.walletSeedVerificationPage), - text: S.current.verify_seed, + onPressed: () => Navigator.pushNamed( + context, + Routes.walletSeedVerificationPage, + arguments: isNewWalletCreated, + ), + text: S.current.verify_phrase, color: Theme.of(context).colorScheme.primary, textColor: Theme.of(context).colorScheme.onPrimary, ), diff --git a/res/pictures/hero/seed_shield_dark.svg b/res/pictures/hero/seed_shield_dark.svg new file mode 100644 index 000000000..4bf4a7788 --- /dev/null +++ b/res/pictures/hero/seed_shield_dark.svg @@ -0,0 +1,80 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/res/pictures/hero/seed_shield_light.svg b/res/pictures/hero/seed_shield_light.svg new file mode 100644 index 000000000..e021c8efb --- /dev/null +++ b/res/pictures/hero/seed_shield_light.svg @@ -0,0 +1,106 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/images/seed_warning_dark.svg b/res/pictures/hero/seed_warning_dark.svg similarity index 100% rename from assets/images/seed_warning_dark.svg rename to res/pictures/hero/seed_warning_dark.svg diff --git a/assets/images/seed_warning_light.svg b/res/pictures/hero/seed_warning_light.svg similarity index 100% rename from assets/images/seed_warning_light.svg rename to res/pictures/hero/seed_warning_light.svg diff --git a/res/pictures/key.svg b/res/pictures/key.svg new file mode 100644 index 000000000..ffba77c68 --- /dev/null +++ b/res/pictures/key.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/res/pictures/visibility.svg b/res/pictures/visibility.svg new file mode 100644 index 000000000..9bee407d2 --- /dev/null +++ b/res/pictures/visibility.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/res/pictures/warning_triangle.svg b/res/pictures/warning_triangle.svg new file mode 100644 index 000000000..884b0b0b6 --- /dev/null +++ b/res/pictures/warning_triangle.svg @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + +