Onboarding: "Save your login" banner + collapse FAQ to reduce load (1.3.7)
Two greenlit onboarding improvements, keeping the existing layout intact. #5 Save-your-login banner (the safety one): - Right under the green "You're in!" bar, logged-in users who haven't backed up see a prominent amber banner — "Save your login — do this now" — that taps straight to Security settings. It reads the shared `backup-confirmed` flag (same source of truth as BackupReminder), so it PERSISTS until they actually back up; unlike the dismissible reminder sheet, it can't silently turn into "never". Once confirmed it flips to a calm green "Login saved" note. Plain language ("save your login", not "back up your nsec"). Pulse dot respects reduced-motion. #1 Lighten first-glance load: - The 9-item FAQ ("Common questions") is now a collapse-by-default section — one tidy row until tapped — via a small reusable CollapsibleSection (aria-expanded, reduced-motion aware). Nothing removed; the page just feels lighter on arrival. Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
This commit is contained in:
parent
67e422307a
commit
1d769ed564
2 changed files with 83 additions and 4 deletions
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"name": "skate-spots",
|
"name": "skate-spots",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "1.3.6",
|
"version": "1.3.7",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"homepage": "https://skate-spots.com",
|
"homepage": "https://skate-spots.com",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|
|
||||||
|
|
@ -35,6 +35,7 @@ import {
|
||||||
MessageCircle,
|
MessageCircle,
|
||||||
Lock,
|
Lock,
|
||||||
Users,
|
Users,
|
||||||
|
Download,
|
||||||
} from 'lucide-react';
|
} from 'lucide-react';
|
||||||
import { FounderFollow } from '@/components/FounderFollow';
|
import { FounderFollow } from '@/components/FounderFollow';
|
||||||
|
|
||||||
|
|
@ -69,6 +70,47 @@ function FAQ({ q, a }: { q: string; a: React.ReactNode }) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ─── Collapsible section ──────────────────────────────────────────────────────
|
||||||
|
// Keeps heavier, optional content (Nostr explainer, FAQ) tucked away by default
|
||||||
|
// so the page feels light on arrival — tap to expand. Reduces first-glance
|
||||||
|
// cognitive load without removing anything.
|
||||||
|
function CollapsibleSection({
|
||||||
|
title,
|
||||||
|
icon,
|
||||||
|
defaultOpen = false,
|
||||||
|
children,
|
||||||
|
}: {
|
||||||
|
title: string;
|
||||||
|
icon?: React.ReactNode;
|
||||||
|
defaultOpen?: boolean;
|
||||||
|
children: React.ReactNode;
|
||||||
|
}) {
|
||||||
|
const [open, setOpen] = useState(defaultOpen);
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<button
|
||||||
|
onClick={() => setOpen(o => !o)}
|
||||||
|
aria-expanded={open}
|
||||||
|
className="w-full flex items-center gap-2 mb-3 group"
|
||||||
|
>
|
||||||
|
{icon}
|
||||||
|
<h2 className="text-xs font-black uppercase tracking-widest text-gray-500 group-hover:text-gray-300 transition-colors">
|
||||||
|
{title}
|
||||||
|
</h2>
|
||||||
|
<ChevronDown className={cn(
|
||||||
|
'h-3.5 w-3.5 text-gray-600 ml-auto transition-transform duration-200',
|
||||||
|
open && 'rotate-180 text-[#ff0040]'
|
||||||
|
)} />
|
||||||
|
</button>
|
||||||
|
{open && (
|
||||||
|
<div className="animate-in fade-in slide-in-from-top-1 duration-200 motion-reduce:animate-none">
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// ─── Feature pill ─────────────────────────────────────────────────────────────
|
// ─── Feature pill ─────────────────────────────────────────────────────────────
|
||||||
function FeaturePill({ emoji, label }: { emoji: string; label: string }) {
|
function FeaturePill({ emoji, label }: { emoji: string; label: string }) {
|
||||||
return (
|
return (
|
||||||
|
|
@ -90,6 +132,10 @@ export default function OnboardingPage() {
|
||||||
const { toast } = useToast();
|
const { toast } = useToast();
|
||||||
const [showRelayEducation, setShowRelayEducation] = useState(false);
|
const [showRelayEducation, setShowRelayEducation] = useState(false);
|
||||||
const [, setHasSeenRelayEducation] = useLocalStorage('seen-relay-education', false);
|
const [, setHasSeenRelayEducation] = useLocalStorage('seen-relay-education', false);
|
||||||
|
// Shared with BackupReminder — the single source of truth for "has the user
|
||||||
|
// saved their login/key?". The banner below stays until this is true, so
|
||||||
|
// "remind me later" can never silently become "never" for this critical step.
|
||||||
|
const [backupConfirmed] = useLocalStorage('backup-confirmed', false);
|
||||||
|
|
||||||
const handleRelayEducationComplete = () => {
|
const handleRelayEducationComplete = () => {
|
||||||
setShowRelayEducation(false);
|
setShowRelayEducation(false);
|
||||||
|
|
@ -169,7 +215,7 @@ export default function OnboardingPage() {
|
||||||
|
|
||||||
{/* ── Logged in: step 1 complete ── */}
|
{/* ── Logged in: step 1 complete ── */}
|
||||||
{user && (
|
{user && (
|
||||||
<div className="flex items-center gap-4 p-4 rounded-2xl bg-green-500/8 border border-green-500/25 mb-8">
|
<div className="flex items-center gap-4 p-4 rounded-2xl bg-green-500/8 border border-green-500/25 mb-4">
|
||||||
<div className="w-9 h-9 rounded-full bg-green-500 flex items-center justify-center flex-shrink-0">
|
<div className="w-9 h-9 rounded-full bg-green-500 flex items-center justify-center flex-shrink-0">
|
||||||
<CheckCircle2 className="h-5 w-5 text-white" />
|
<CheckCircle2 className="h-5 w-5 text-white" />
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -183,6 +229,38 @@ export default function OnboardingPage() {
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{/* ── Save your login (critical) — stays until actually backed up ── */}
|
||||||
|
{user && !backupConfirmed && (
|
||||||
|
<Link to="/settings?section=security" className="block mb-8 group">
|
||||||
|
<div className="flex items-center gap-4 p-4 rounded-2xl bg-amber-500/10 border border-amber-500/30 hover:bg-amber-500/15 hover:border-amber-500/45 transition-all">
|
||||||
|
<div className="relative flex-shrink-0">
|
||||||
|
<div className="w-9 h-9 rounded-full bg-amber-500/20 flex items-center justify-center">
|
||||||
|
<Key className="h-4 w-4 text-amber-400" />
|
||||||
|
</div>
|
||||||
|
<span className="absolute -top-0.5 -right-0.5 w-3 h-3 rounded-full bg-amber-500 border-2 border-black animate-pulse motion-reduce:animate-none" />
|
||||||
|
</div>
|
||||||
|
<div className="flex-1 min-w-0">
|
||||||
|
<p className="font-bold text-amber-300 text-sm">Save your login — do this now</p>
|
||||||
|
<p className="text-xs text-amber-100/70 leading-snug mt-0.5">
|
||||||
|
It's the only way back into your account. Lose it and there's no reset — no email, no password recovery.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div className="flex-shrink-0 flex items-center gap-1.5 rounded-full bg-amber-500 px-3 py-1.5 text-[11px] font-black uppercase tracking-wide text-black group-hover:bg-amber-400 transition-colors">
|
||||||
|
<Download className="h-3.5 w-3.5" />
|
||||||
|
Save
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Link>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* ── Login saved confirmation ── */}
|
||||||
|
{user && backupConfirmed && (
|
||||||
|
<div className="flex items-center gap-3 p-3 rounded-2xl bg-green-500/6 border border-green-500/20 mb-8">
|
||||||
|
<CheckCircle2 className="h-4 w-4 text-green-400 flex-shrink-0" />
|
||||||
|
<p className="text-xs text-green-400/90 font-medium">Login saved — your account is safe to recover.</p>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
<div className="space-y-6">
|
<div className="space-y-6">
|
||||||
|
|
||||||
{/* ── Why skaters care ── */}
|
{/* ── Why skaters care ── */}
|
||||||
|
|
@ -347,9 +425,9 @@ export default function OnboardingPage() {
|
||||||
<p className="text-[11px] text-gray-600 text-center mt-3">💡 $5 of Bitcoin = hundreds of zaps to spread across the community</p>
|
<p className="text-[11px] text-gray-600 text-center mt-3">💡 $5 of Bitcoin = hundreds of zaps to spread across the community</p>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
{/* ── FAQ ── */}
|
{/* ── FAQ (collapsed by default to keep the page light) ── */}
|
||||||
<section>
|
<section>
|
||||||
<h2 className="text-xs font-black uppercase tracking-widest text-gray-600 mb-4">Common questions</h2>
|
<CollapsibleSection title="Common questions">
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<FAQ
|
<FAQ
|
||||||
q="Is SkateSpots free?"
|
q="Is SkateSpots free?"
|
||||||
|
|
@ -408,6 +486,7 @@ export default function OnboardingPage() {
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
</CollapsibleSection>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
{/* ── Final CTA ── */}
|
{/* ── Final CTA ── */}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue