mirror of
https://github.com/block/buzz.git
synced 2026-11-07 01:41:06 +00:00
refactor(onboarding): clarify identity backup flow
Signed-off-by: Clay Delk <clay.delk@gmail.com>
This commit is contained in:
@@ -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{" "}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -857,6 +857,8 @@ export function EncryptedBackupCreator({
|
||||
<PendingDownloadTicker />
|
||||
) : state.savedPassword ? (
|
||||
"Download backup again"
|
||||
) : previewMode ? (
|
||||
"Save backup"
|
||||
) : (
|
||||
"Backup key"
|
||||
)}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user