refactor(onboarding): clarify identity backup flow

Signed-off-by: Clay Delk <clay.delk@gmail.com>
This commit is contained in:
Clay Delk
2026-08-28 14:25:56 -04:00
parent dce214a7eb
commit 56a25110e5
5 changed files with 61 additions and 65 deletions

View File

@@ -214,8 +214,14 @@ export function BackupStep({
{previewMode ? "Create a private identity key" : "Backup options"}
</h1>
{previewMode ? (
<div className="mt-3">
<IdentityKeyHelpDialog inline previewMode />
<div className="mt-5">
<p className="text-sm leading-6 text-foreground/75">
This key will be how you log into Buzz. You can use it across
Buzz communities and other platforms.
</p>
<div className="mt-2">
<IdentityKeyHelpDialog inline previewMode />
</div>
</div>
) : (
<p className="mt-5 text-sm leading-6 text-foreground/75">
@@ -237,7 +243,12 @@ export function BackupStep({
<ShieldCheck className="h-5 w-5" aria-hidden="true" />
</span>
<p className="text-base leading-6 text-foreground">
Keep it private and secure at all times.
<span className="block font-medium">
Protected on this device.
</span>
<span className="block text-foreground/75">
Buzz stores your key securely on this device.
</span>
</p>
</div>
<div className="flex min-h-14 items-center gap-4 text-left">
@@ -245,7 +256,10 @@ export function BackupStep({
<CircleSlash2 className="h-5 w-5" aria-hidden="true" />
</span>
<p className="text-base leading-6 text-foreground">
Don’t share it with anyone.
<span className="block font-medium">Never share it.</span>
<span className="block text-foreground/75">
Anyone with your private key can use your identity.
</span>
</p>
</div>
<div className="flex min-h-14 items-center gap-4 text-left">
@@ -253,7 +267,10 @@ export function BackupStep({
<HardDriveDownload className="h-5 w-5" aria-hidden="true" />
</span>
<p className="text-base leading-6 text-foreground">
Keep a backup because Buzz can’t reset or recover it.
<span className="block font-medium">Keep a backup.</span>
<span className="block text-foreground/75">
Buzz can’t reset or recover your key if you lose access.
</span>
</p>
</div>
</div>
@@ -368,7 +385,7 @@ export function BackupStep({
onClick={onNext}
type="button"
>
I understand, generate key
Create my private key
</Button>
</OnboardingFooter>
) : null}
@@ -404,7 +421,7 @@ export function BackupStep({
)}
>
{previewMode ? (
"Keep this key private. Anyone who has it can access your account."
"Don’t share this key. Anyone who has it can access your account."
) : (
<>
{introStorageDescription} You can continue now, or{" "}

View File

@@ -1,4 +1,3 @@
import { Check } from "lucide-react";
import { motion, useReducedMotion } from "motion/react";
import * as React from "react";
@@ -24,10 +23,6 @@ type DownloadKeyStepProps = {
onBack: () => void;
/** Use in-memory backup operations inside the dev-only workshop preview. */
previewMode?: boolean;
/** Whether the workshop user explicitly chose to test their saved backup. */
previewTestRequested?: boolean;
/** Opens the existing guided test from the preview's saved confirmation. */
onPreviewTestRequest?: () => void;
};
/**
@@ -40,8 +35,6 @@ export function DownloadKeyStep({
session,
onBack,
previewMode = false,
previewTestRequested = false,
onPreviewTestRequest,
}: DownloadKeyStepProps) {
const reduceMotion = useReducedMotion() ?? false;
// Once the encrypted payload is saved, the creator advances to its guided
@@ -49,7 +42,8 @@ export function DownloadKeyStep({
const hasCreated = session.created;
const hasVerifiedBackup = session.verified;
const hasSelectedBackup = session.test.stage === "password";
const showPreviewSaved = previewMode && hasCreated && !previewTestRequested;
const showPreviewSaved =
previewMode && hasCreated && session.test.stage === "drop";
const [primaryActionSlot, setPrimaryActionSlot] =
React.useState<HTMLElement | null>(null);
@@ -86,18 +80,18 @@ export function DownloadKeyStep({
? "That’s your backup file"
: hasCreated
? "Optionally, test your backup"
: "Backup your key with a password"}
: "Create a secure backup file"}
</h1>
<p className="mt-5 text-sm leading-6 text-foreground/80">
{showPreviewSaved
? "Keep the backup file and password somewhere safe. You’ll need both to restore your identity."
? "Test your backup now to make sure it works. Drop the file here or select it from your device."
: hasVerifiedBackup
? "Your file and password can restore your identity."
: hasSelectedBackup
? "Now enter your password to prove you can unlock it."
: hasCreated
? "Learn how your backup works. Drop the file you just saved and unlock it with your password."
: "Keep the downloaded file private — you need both it and your password to restore your identity. Save the backup password somewhere safe; Buzz cannot reset it if lost."}
: "This creates a password-protected file with your private key. Remember, Buzz can’t recover your key if you lose it."}
</p>
</motion.div>
@@ -123,38 +117,14 @@ export function DownloadKeyStep({
className="mx-auto flex w-full max-w-140 justify-center px-6 py-5"
data-testid="backup-password-panel"
>
{showPreviewSaved ? (
<div className="flex flex-col items-center gap-5">
<div
className="flex size-20 items-center justify-center rounded-full bg-white text-black/80"
data-testid="onboarding-preview-backup-saved"
>
<Check
aria-hidden="true"
className="size-10"
strokeWidth={3}
/>
</div>
<Button
className={ONBOARDING_SECONDARY_CTA_CLASS}
data-testid="onboarding-preview-test-backup"
onClick={onPreviewTestRequest}
type="button"
variant="ghost"
>
Test your backup
</Button>
</div>
) : (
<EncryptedBackupCreator
createButtonClassName={ONBOARDING_SECURITY_PRIMARY_CTA_CLASS}
createButtonPortal={primaryActionSlot}
previewMode={previewMode}
session={session}
variant="spotlight"
verifyButtonPortal={primaryActionSlot}
/>
)}
<EncryptedBackupCreator
createButtonClassName={ONBOARDING_SECURITY_PRIMARY_CTA_CLASS}
createButtonPortal={primaryActionSlot}
previewMode={previewMode}
session={session}
variant="spotlight"
verifyButtonPortal={primaryActionSlot}
/>
</div>
</motion.div>
</div>
@@ -163,12 +133,13 @@ export function DownloadKeyStep({
<OnboardingFooter>
{showPreviewSaved ? (
<Button
className={ONBOARDING_SECURITY_PRIMARY_CTA_CLASS}
className={ONBOARDING_SECONDARY_CTA_CLASS}
data-testid="onboarding-preview-backup-done"
onClick={onBack}
type="button"
variant="ghost"
>
Done
Skip for now
</Button>
) : (
<div
@@ -193,7 +164,11 @@ export function DownloadKeyStep({
type="button"
variant="ghost"
>
{hasVerifiedBackup ? "Finish" : "Skip for now"}
{hasVerifiedBackup
? previewMode
? "Continue"
: "Finish"
: "Skip for now"}
</Button>
) : null}
</OnboardingFooter>

View File

@@ -857,6 +857,8 @@ export function EncryptedBackupCreator({
<PendingDownloadTicker />
) : state.savedPassword ? (
"Download backup again"
) : previewMode ? (
"Save backup"
) : (
"Backup key"
)}

View File

@@ -58,7 +58,11 @@ export function IdentityKeyHelpDialog({
const trigger = (
<DialogTrigger asChild>
<Button
className={`text-foreground/70 transition-opacity duration-300 hover:text-foreground motion-reduce:transition-none ${
className={`${
previewMode
? "text-foreground underline decoration-foreground/45 underline-offset-4 hover:decoration-foreground"
: "text-foreground/70 hover:text-foreground"
} transition-opacity duration-300 motion-reduce:transition-none ${
isVisible ? "opacity-100" : "pointer-events-none opacity-0"
}`}
data-testid="identity-key-help-trigger"
@@ -66,7 +70,9 @@ export function IdentityKeyHelpDialog({
type="button"
variant="link"
>
What’s an identity key?
{previewMode
? "Learn how identity keys work"
: "What’s an identity key?"}
</Button>
</DialogTrigger>
);
@@ -96,15 +102,15 @@ export function IdentityKeyHelpDialog({
>
<div>
<p>
Your Nostr identity has two parts: a private key that signs you
in and a public key you can safely share. You can find your
public identity anytime in Buzz Settings.
Buzz will create a Nostr identity with two parts: a private key
that signs you in and a public key you can safely share. You can
find your public identity anytime in Buzz settings.
</p>
<p>
Your identity belongs to you, not Buzz, and can move with you to
This identity belongs to you, not Buzz, and can move with you to
another device or compatible Nostr app. Because only you control
the private key, Buzz can’t reset or recover it. Keep a backup
somewhere safe and never share it.
somewhere safe, and never share it.
</p>
</div>
</DialogDescription>

View File

@@ -33,20 +33,16 @@ export function BackupPasswordPreview({
onDone: () => void;
session: EncryptedBackupSession;
}) {
const [testRequested, setTestRequested] = React.useState(false);
return (
<OnboardingPreviewStep
onBack={testRequested ? () => setTestRequested(false) : onBack}
onBack={onBack}
security
testId="onboarding-preview-backup-password"
>
<DownloadKeyStep
direction="forward"
onBack={onDone}
onPreviewTestRequest={() => setTestRequested(true)}
previewMode
previewTestRequested={testRequested}
session={session}
/>
</OnboardingPreviewStep>