Skip to content

Commit eea2ca8

Browse files
feat(backup): make create-backup a button and tag the vault as Tor-using
- start-backup becomes an outlined button between the hero and the settings list instead of a menu row: creating a backup is an action, not a setting, and it should read as one even when a backup exists. It keeps the translated label (27 locales) and is still suppressed in the zero-backup state, where the hero already offers it. - BackupOptionCard takes a list of tags so a card can carry more than one; the encrypted vault card now also shows 'Uses Tor', since reaching the key server over Tor is a property worth knowing before choosing that option. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent eeb2b9f commit eea2ca8

4 files changed

Lines changed: 54 additions & 18 deletions

File tree

lib/core/widgets/cards/backup_option_card.dart

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,18 @@ class BackupOptionCard extends StatelessWidget {
88
final Widget icon;
99
final String title;
1010
final String description;
11-
final String? tag;
11+
12+
/// Zero or more short labels shown under the description. A card may carry
13+
/// several (e.g. how easy an option is AND how it reaches the network).
14+
final List<String> tags;
1215
final VoidCallback onTap;
1316

1417
const BackupOptionCard({
1518
super.key,
1619
required this.icon,
1720
required this.title,
1821
required this.description,
19-
this.tag,
22+
this.tags = const [],
2023
required this.onTap,
2124
});
2225

@@ -60,7 +63,16 @@ class BackupOptionCard extends StatelessWidget {
6063
maxLines: 3,
6164
),
6265
const Gap(10),
63-
if (tag != null) OptionsTag(text: tag!),
66+
// Tags wrap so a card can carry more than one without
67+
// overflowing on narrow screens.
68+
if (tags.isNotEmpty)
69+
Wrap(
70+
spacing: 8,
71+
runSpacing: 8,
72+
children: [
73+
for (final tag in tags) OptionsTag(text: tag),
74+
],
75+
),
6476
],
6577
),
6678
),

lib/features/backup_settings/ui/screens/backup_options_screen.dart

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,10 @@ class _BackupOptionsScreenState extends State<BackupOptionsScreen> {
5959
),
6060
title: context.loc.backupWalletEncryptedVaultTitle,
6161
description: context.loc.backupWalletEncryptedVaultDescription,
62-
tag: context.loc.backupWalletEncryptedVaultTag,
62+
tags: [
63+
context.loc.backupWalletEncryptedVaultTag,
64+
context.loc.backupWalletEncryptedVaultUsesTorTag,
65+
],
6366
onTap: () => context.pushNamed(
6467
RecoverBullRoute.recoverbullFlows.name,
6568
extra: RecoverBullFlowsExtra(
@@ -82,7 +85,7 @@ class _BackupOptionsScreenState extends State<BackupOptionsScreen> {
8285
),
8386
title: context.loc.backupWalletPhysicalBackupTitle,
8487
description: context.loc.backupWalletPhysicalBackupDescription,
85-
tag: context.loc.backupWalletPhysicalBackupTag,
88+
tags: [context.loc.backupWalletPhysicalBackupTag],
8689
onTap: () {
8790
context.pushNamed(
8891
TestWalletBackupRoute.testPhysicalBackupFlow.name,

lib/features/backup_settings/ui/screens/backup_settings_screen.dart

Lines changed: 30 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,14 @@ class _Screen extends StatelessWidget {
8585
),
8686
),
8787
if (hero != null) ...[const Gap(32), hero],
88+
// The create-backup action sits between the hero and the
89+
// settings list: it is an action, not a setting, and it
90+
// stays available in every state except the zero-backup
91+
// one, where the hero already offers it.
92+
if (!_heroOffersStartBackup(state)) ...[
93+
const Gap(24),
94+
const _StartBackupButton(),
95+
],
8896
const Gap(24),
8997
..._menuRows(state),
9098
],
@@ -144,12 +152,8 @@ class _Screen extends StatelessWidget {
144152
/// The settings rows. Insertion point for the fork's metadata backup menu
145153
/// row: add one [SettingsEntryItem] to this list.
146154
List<Widget> _menuRows(BackupSettingsState state) => [
147-
// Availability is not encouragement. The hero never asks for another
148-
// backup once a physical one is fresh, but adding one deliberately has to
149-
// stay possible from here in every state — EXCEPT the one state where the
150-
// hero is already offering this exact action, where a second identical
151-
// entry a few pixels below it is just noise.
152-
if (!_heroOffersStartBackup(state)) const _StartBackupButton(),
155+
// Start-backup is NOT here: it is an action, rendered as a button above
156+
// this list (see the body), not a settings row.
153157
if (state.lastEncryptedBackup != null) const _ViewVaultKeyButton(),
154158
if (state.lastEncryptedBackup != null || state.lastPhysicalBackup != null)
155159
const _TestBackupButton(),
@@ -341,18 +345,31 @@ class _TestBackupHero extends StatelessWidget {
341345
}
342346
}
343347

348+
/// Creating a backup is an ACTION, not a setting, so it is a button rather
349+
/// than a menu row — obvious at a glance even when a backup already exists.
350+
/// Outlined rather than filled: it must not compete with a hero's own CTA when
351+
/// one is present, and it is an offer, never a warning.
344352
class _StartBackupButton extends StatelessWidget {
345353
const _StartBackupButton();
346354

347355
@override
348356
Widget build(BuildContext context) {
349-
return SettingsEntryItem(
350-
icon: Icons.save_as,
351-
iconColor: context.appColors.primary,
352-
title: context.loc.backupSettingsStartBackup,
353-
onTap: () => context.pushNamed(
354-
BackupSettingsSubroute.backupOptions.name,
355-
extra: BackupSettingsFlow.backup,
357+
return Padding(
358+
padding: const EdgeInsets.symmetric(horizontal: 16),
359+
child: BBButton.big(
360+
// The translated row label (27 locales), not the hero's EN-only CTA
361+
// string: a button in the user's own language beats an English one.
362+
label: context.loc.backupSettingsStartBackup,
363+
iconData: Icons.save_as,
364+
iconFirst: true,
365+
onPressed: () => context.pushNamed(
366+
BackupSettingsSubroute.backupOptions.name,
367+
extra: BackupSettingsFlow.backup,
368+
),
369+
outlined: true,
370+
bgColor: context.appColors.transparent,
371+
textColor: context.appColors.onSurface,
372+
borderColor: context.appColors.outline,
356373
),
357374
);
358375
}

localization/app_en.arb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15069,5 +15069,9 @@
1506915069
"recoverbullServerCustomWarning": "You are about to use a custom Recoverbull server. Make sure you trust this server.",
1507015070
"@recoverbullServerCustomWarning": {
1507115071
"description": "Warning shown before saving a custom key server URL in the encrypted vault settings"
15072+
},
15073+
"backupWalletEncryptedVaultUsesTorTag": "Uses Tor",
15074+
"@backupWalletEncryptedVaultUsesTorTag": {
15075+
"description": "Tag on the encrypted vault option card indicating the key server is reached over Tor"
1507215076
}
1507315077
}

0 commit comments

Comments
 (0)