feat: remind users with a balance to back up their recovery phrase

This commit is contained in:
Blazebrain
2026-09-01 10:01:07 +01:00
parent 837c4d7d22
commit 323a1df6e9
8 changed files with 249 additions and 12 deletions
@@ -1,4 +1,4 @@
import "package:cake_wallet/new-ui/pages/seed/pre_seed_page.dar"';
import "package:cake_wallet/new-ui/pages/seed/pre_seed_page.dart";
import 'package:flutter_test/flutter_test.dart';
import '../components/common_test_cases.dart';
+36 -6
View File
@@ -1,15 +1,18 @@
import 'package:cake_wallet/core/auth_service.dart';
import 'package:cake_wallet/di.dart';
import 'package:cake_wallet/generated/i18n.dart';
import "package:cake_wallet/main.dart";
import 'package:cake_wallet/new-ui/modal_navigator.dart';
import 'package:cake_wallet/new-ui/pages/account_customizer.dart';
import 'package:cake_wallet/new-ui/pages/card_customizer.dart';
import "package:cake_wallet/new-ui/pages/seed/seed_backup_reminder_page.dart";
import 'package:cake_wallet/new-ui/pages/settings_page.dart';
import 'package:cake_wallet/new-ui/viewmodels/card_customizer/card_customizer_bloc.dart';
import 'package:cake_wallet/new-ui/widgets/coins_page/action_row/coin_action_row.dart';
import 'package:cake_wallet/new-ui/widgets/coins_page/assets_history/assets_history_section.dart';
import 'package:cake_wallet/new-ui/widgets/coins_page/cards/cards_view.dart';
import 'package:cake_wallet/new-ui/widgets/coins_page/mweb_ad.dart';
import "package:cake_wallet/new-ui/widgets/coins_page/seed_backup_reminder_card.dart";
import 'package:cake_wallet/new-ui/widgets/coins_page/top_bar_widget/top_bar.dart';
import 'package:cake_wallet/new-ui/widgets/coins_page/unconfirmed_balance_widget.dart';
import 'package:cake_wallet/new-ui/widgets/coins_page/wallet_info.dart';
@@ -18,9 +21,6 @@ import 'package:cake_wallet/view_model/dashboard/dashboard_view_model.dart';
import 'package:cake_wallet/view_model/dashboard/nft_view_model.dart';
import 'package:cake_wallet/view_model/monero_account_list/monero_account_edit_or_create_view_model.dart';
import 'package:cake_wallet/view_model/monero_account_list/monero_account_list_view_model.dart';
import "package:cw_core/amount/money.dart";
import "package:cw_core/crypto_currency.dart";
import "package:cw_core/transaction_direction.dart";
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
@@ -38,7 +38,7 @@ class NewHomePage extends StatefulWidget {
State<NewHomePage> createState() => _NewHomePageState();
}
class _NewHomePageState extends State<NewHomePage> {
class _NewHomePageState extends State<NewHomePage> with RouteAware {
MoneroAccountListViewModel? accountListViewModel;
bool _lightningMode = false;
@@ -53,9 +53,9 @@ class _NewHomePageState extends State<NewHomePage> {
});
});
reaction((_) => widget.dashboardViewModel.isMigratingToIronwood, (val) {
reaction((_) => widget.dashboardViewModel.isMigratingToIronwood, (val) {
if (val && !widget.dashboardViewModel.settingsStore.zcashMigrationModalViewed) {
if(!context.mounted) {
if (!context.mounted) {
return;
}
widget.dashboardViewModel.settingsStore.zcashMigrationModalViewed = true;
@@ -72,6 +72,21 @@ class _NewHomePageState extends State<NewHomePage> {
});
}
@override
void didChangeDependencies() {
super.didChangeDependencies();
routeObserver.subscribe(this, ModalRoute.of(context) as PageRoute);
}
@override
void dispose() {
routeObserver.unsubscribe(this);
super.dispose();
}
@override
void didPopNext() => widget.dashboardViewModel.loadSeedBackupReminder();
void _setAccountViewModel() {
accountListViewModel = widget.dashboardViewModel.balanceViewModel.hasAccounts
? getIt.get<MoneroAccountListViewModel>()
@@ -191,6 +206,10 @@ class _NewHomePageState extends State<NewHomePage> {
MwebAd(
dashboardViewModel: widget.dashboardViewModel,
),
SeedBackupReminderCard(
dashboardViewModel: widget.dashboardViewModel,
onTap: openSeedBackupReminder,
),
],
);
},
@@ -231,6 +250,17 @@ class _NewHomePageState extends State<NewHomePage> {
);
}
void openSeedBackupReminder() {
Navigator.of(context).push(
CupertinoPageRoute<void>(
builder: (_) => SeedBackupReminderPage(
dashboardViewModel: widget.dashboardViewModel,
authService: getIt.get<AuthService>(),
),
),
);
}
void openAccountCustomizer() async {
await CupertinoScaffold.showCupertinoModalBottomSheet(
barrierColor: Colors.black.withAlpha(60),
@@ -0,0 +1,57 @@
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:flutter/material.dart";
class DismissSeedVerificationPage extends StatelessWidget {
const DismissSeedVerificationPage({required this.onConfirm, super.key});
final VoidCallback onConfirm;
@override
Widget build(BuildContext context) {
final colors = Theme.of(context).colorScheme;
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).dismiss_verification_title,
description: Text.rich(
TextSpan(
children: [
TextSpan(text: "${S.of(context).dismiss_verification_description}\n"),
TextSpan(
text: S.of(context).seed_display_path,
style: TextStyle(color: colors.onSurface),
),
],
),
textAlign: TextAlign.center,
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
color: colors.onSurfaceVariant,
letterSpacing: -0.07,
),
),
),
),
footer: StackedButtons(
primaryKey: const ValueKey("dismiss_seed_verification_page_confirm_button_key"),
primaryText: S.of(context).dismiss_verification_confirm,
onPrimary: onConfirm,
secondaryKey: const ValueKey("dismiss_seed_verification_page_back_button_key"),
secondaryText: S.of(context).seed_alert_back,
onSecondary: () => Navigator.of(context).pop(),
),
);
}
}
@@ -2,7 +2,6 @@ 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);
@@ -0,0 +1,71 @@
import "package:cake_wallet/core/auth_service.dart";
import "package:cake_wallet/generated/i18n.dart";
import "package:cake_wallet/new-ui/pages/seed/dismiss_seed_verification_page.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/view_model/dashboard/dashboard_view_model.dart";
import "package:flutter/cupertino.dart";
import "package:flutter/material.dart";
class SeedBackupReminderPage extends StatelessWidget {
const SeedBackupReminderPage({
required this.dashboardViewModel,
required this.authService,
super.key,
});
final DashboardViewModel dashboardViewModel;
final AuthService authService;
@override
Widget build(BuildContext context) {
final colors = Theme.of(context).colorScheme;
return SeedPageScaffold(
content: 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: Theme.of(context).textTheme.bodyMedium?.copyWith(
color: colors.onSurfaceVariant,
letterSpacing: -0.07,
),
),
),
footer: StackedButtons(
primaryKey: const ValueKey("seed_backup_reminder_page_button_key"),
primaryText: S.of(context).show_recovery_phrase,
onPrimary: () => authService.authenticateAction(
context,
route: Routes.seed,
arguments: false,
conditionToDetermineIfToUse2FA:
dashboardViewModel.settingsStore.shouldRequireTOTP2FAForAllSecurityAndBackupSettings,
),
secondaryKey: const ValueKey("seed_backup_reminder_page_dismiss_button_key"),
secondaryText: S.of(context).dismiss_this_step,
onSecondary: () => Navigator.of(context).push(
CupertinoPageRoute<void>(
builder: (_) => DismissSeedVerificationPage(onConfirm: () => _dismiss(context)),
),
),
secondaryAsLink: true,
),
);
}
Future<void> _dismiss(BuildContext context) async {
await dashboardViewModel.dismissSeedBackupReminder();
if (!context.mounted) {
return;
}
Navigator.of(context).popUntil((route) => route.isFirst);
}
}
@@ -0,0 +1,80 @@
import "package:cake_wallet/generated/i18n.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";
import "package:flutter_mobx/flutter_mobx.dart";
class SeedBackupReminderCard extends StatelessWidget {
const SeedBackupReminderCard({required this.dashboardViewModel, required this.onTap, super.key});
final DashboardViewModel dashboardViewModel;
final VoidCallback onTap;
@override
Widget build(BuildContext context) => Observer(
builder: (_) {
if (!dashboardViewModel.shouldShowSeedBackupReminder) {
return const SizedBox.shrink();
}
final colors = Theme.of(context).colorScheme;
final textTheme = Theme.of(context).textTheme;
return Padding(
padding: const EdgeInsets.only(left: 18, right: 18, top: 24),
child: MergeSemantics(
child: Semantics(
button: true,
child: GestureDetector(
onTap: onTap,
child: Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: colors.surfaceContainer,
borderRadius: BorderRadius.circular(20),
),
child: Row(
children: [
ExcludeSemantics(
child: CakeImageWidget(
imageUrl: "assets/new-ui/key.svg",
width: 24,
height: 24,
colorFilter: ColorFilter.mode(colors.primary, BlendMode.srcIn),
),
),
const SizedBox(width: 12),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
spacing: 4,
children: [
Text(
S.of(context).back_up_recovery_phrase,
style: textTheme.labelLarge?.copyWith(
color: colors.onSurface,
letterSpacing: -0.07,
),
),
Text(
S.of(context).back_up_recovery_phrase_description,
style: textTheme.bodySmall?.copyWith(
color: colors.onSurfaceVariant,
letterSpacing: -0.06,
),
),
],
),
),
const SizedBox(width: 8),
Icon(Icons.chevron_right, size: 16, color: colors.onSurfaceVariant),
],
),
),
),
),
),
);
},
);
}
+2 -2
View File
@@ -10,8 +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/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';
@@ -1,9 +1,9 @@
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/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:cake_wallet/view_model/wallet_seed_view_model.dart";
import 'package:cw_core/wallet_type.dart';
import 'package:flutter/material.dart';