wallet: remove duplicate balance hero, single CashuWalletCard as source of truth
Co-authored-by: shakespeare.diy <assistant@shakespeare.diy>
This commit is contained in:
parent
1876d14b0f
commit
f0eadf307b
1 changed files with 50 additions and 119 deletions
|
|
@ -7,7 +7,6 @@ import { useWallet } from '@/hooks/useWallet';
|
|||
import { useNWC } from '@/hooks/useNWCContext';
|
||||
import { useCurrentUser } from '@/hooks/useCurrentUser';
|
||||
import { useCashu } from '@/hooks/wallet/useCashu';
|
||||
import { useSatsToUsd } from '@/hooks/useExchangeRate';
|
||||
import { LoginArea } from '@/components/auth/LoginArea';
|
||||
import { CashuWalletCard } from '@/components/wallet/CashuWalletCard';
|
||||
import { cn } from '@/lib/utils';
|
||||
|
|
@ -24,115 +23,43 @@ import {
|
|||
ChevronDown,
|
||||
ChevronUp,
|
||||
Wallet,
|
||||
Bitcoin,
|
||||
} from 'lucide-react';
|
||||
|
||||
// ─── Balance Hero ─────────────────────────────────────────────────────────────
|
||||
// ─── Wallet status bar ────────────────────────────────────────────────────────
|
||||
|
||||
function BalanceHero() {
|
||||
const { totalBalance: cashuBalance, isInitialized: hasCashu } = useCashu();
|
||||
const cashuUsd = useSatsToUsd(cashuBalance);
|
||||
function WalletStatusBar() {
|
||||
const { webln } = useWallet();
|
||||
const { connections } = useNWC();
|
||||
const { isInitialized: hasCashu } = useCashu();
|
||||
const hasNWC = connections.length > 0 && connections.some((c) => c.isConnected);
|
||||
const isReady = webln || hasNWC || hasCashu;
|
||||
|
||||
return (
|
||||
<div className="relative overflow-hidden rounded-2xl bg-gradient-to-br from-orange-950/60 via-black to-yellow-950/30 border border-orange-500/20 p-6">
|
||||
{/* Background glows */}
|
||||
<div className="absolute -top-10 -right-10 w-40 h-40 bg-orange-500/10 rounded-full blur-3xl pointer-events-none" />
|
||||
<div className="absolute -bottom-8 -left-8 w-32 h-32 bg-yellow-500/8 rounded-full blur-2xl pointer-events-none" />
|
||||
|
||||
<div className="relative space-y-5">
|
||||
{/* Balance */}
|
||||
<div className="text-center">
|
||||
<p className="text-[11px] font-black uppercase tracking-widest text-orange-400/70 mb-1">Cashu Balance</p>
|
||||
<div className="flex items-baseline justify-center gap-2">
|
||||
<span className="text-5xl font-black tabular-nums leading-none text-white">
|
||||
{hasCashu ? cashuBalance.toLocaleString() : '0'}
|
||||
</span>
|
||||
<span className="text-lg font-bold text-orange-400/80">sats</span>
|
||||
</div>
|
||||
{cashuUsd !== null && cashuBalance > 0 ? (
|
||||
<p className="text-sm text-muted-foreground mt-1">
|
||||
≈ ${cashuUsd.toLocaleString('en-US', { minimumFractionDigits: 2, maximumFractionDigits: 2 })}
|
||||
</p>
|
||||
) : (
|
||||
<p className="text-xs text-muted-foreground/50 mt-1">no balance yet</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Wallet status — compact icon row */}
|
||||
<div className="flex items-center justify-center gap-2">
|
||||
<WalletStatusPill
|
||||
icon={Nut}
|
||||
label="Cashu"
|
||||
active={hasCashu}
|
||||
color="text-orange-400"
|
||||
activeBg="bg-orange-500/20 border-orange-500/30"
|
||||
/>
|
||||
<WalletStatusPill
|
||||
icon={Globe}
|
||||
label="WebLN"
|
||||
active={!!webln}
|
||||
color="text-blue-400"
|
||||
activeBg="bg-blue-500/20 border-blue-500/30"
|
||||
/>
|
||||
<WalletStatusPill
|
||||
icon={WalletMinimal}
|
||||
label="NWC"
|
||||
active={hasNWC}
|
||||
color="text-purple-400"
|
||||
activeBg="bg-purple-500/20 border-purple-500/30"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Primary action buttons — scroll down to Cashu card to act */}
|
||||
<div className="grid grid-cols-2 gap-3">
|
||||
<button
|
||||
onClick={() => document.getElementById('cashu-card')?.scrollIntoView({ behavior: 'smooth' })}
|
||||
className="w-full h-12 rounded-xl bg-orange-500 hover:bg-orange-400 active:scale-[0.98] transition-all font-black text-sm uppercase tracking-wide text-white flex items-center justify-center gap-2"
|
||||
>
|
||||
<Bitcoin className="h-4 w-4" />
|
||||
Receive
|
||||
</button>
|
||||
<button
|
||||
onClick={() => document.getElementById('cashu-card')?.scrollIntoView({ behavior: 'smooth' })}
|
||||
className="w-full h-12 rounded-xl bg-white/10 hover:bg-white/15 active:scale-[0.98] transition-all font-black text-sm uppercase tracking-wide text-white flex items-center justify-center gap-2 border border-white/10"
|
||||
>
|
||||
<Zap className="h-4 w-4 text-yellow-400" />
|
||||
Send
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Manage wallets */}
|
||||
<WalletModal>
|
||||
<button className="w-full flex items-center justify-between px-4 py-3 rounded-xl bg-white/5 hover:bg-white/8 active:scale-[0.99] border border-white/8 transition-all">
|
||||
<div className="flex items-center gap-2">
|
||||
<Settings className="h-4 w-4 text-orange-400" />
|
||||
<span className="text-sm font-bold text-white">Manage Wallets</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
{isReady ? (
|
||||
<Badge className="bg-green-500/20 text-green-400 border-green-500/30 text-[10px] px-1.5 py-0">Connected</Badge>
|
||||
) : (
|
||||
<Badge variant="secondary" className="text-[10px] px-1.5 py-0">Not connected</Badge>
|
||||
)}
|
||||
<ChevronRight className="h-4 w-4 text-muted-foreground" />
|
||||
</div>
|
||||
</button>
|
||||
</WalletModal>
|
||||
<div className="flex items-center justify-between gap-3">
|
||||
{/* Status pills */}
|
||||
<div className="flex items-center gap-1.5">
|
||||
<StatusPill icon={Nut} label="Cashu" active={hasCashu} color="text-orange-400" activeBg="bg-orange-500/20 border-orange-500/30" />
|
||||
<StatusPill icon={Globe} label="WebLN" active={!!webln} color="text-blue-400" activeBg="bg-blue-500/20 border-blue-500/30" />
|
||||
<StatusPill icon={WalletMinimal} label="NWC" active={hasNWC} color="text-purple-400" activeBg="bg-purple-500/20 border-purple-500/30" />
|
||||
</div>
|
||||
|
||||
{/* Manage wallets */}
|
||||
<WalletModal>
|
||||
<button className="flex items-center gap-1.5 px-3 py-1.5 rounded-xl bg-white/5 hover:bg-white/10 border border-white/8 active:scale-95 transition-all flex-shrink-0">
|
||||
<Settings className="h-3.5 w-3.5 text-orange-400" />
|
||||
<span className="text-xs font-bold text-white">Manage</span>
|
||||
{isReady
|
||||
? <Badge className="bg-green-500/20 text-green-400 border-green-500/30 text-[9px] px-1 py-0 leading-none">ON</Badge>
|
||||
: <ChevronRight className="h-3 w-3 text-muted-foreground" />
|
||||
}
|
||||
</button>
|
||||
</WalletModal>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function WalletStatusPill({
|
||||
icon: Icon,
|
||||
label,
|
||||
active,
|
||||
color,
|
||||
activeBg,
|
||||
function StatusPill({
|
||||
icon: Icon, label, active, color, activeBg,
|
||||
}: {
|
||||
icon: React.ComponentType<{ className?: string }>;
|
||||
label: string;
|
||||
|
|
@ -142,22 +69,22 @@ function WalletStatusPill({
|
|||
}) {
|
||||
return (
|
||||
<div className={cn(
|
||||
'flex items-center gap-1.5 px-2.5 py-1.5 rounded-full border text-[11px] font-bold transition-all',
|
||||
active ? `${activeBg} ${color}` : 'bg-white/5 border-white/10 text-gray-600'
|
||||
'flex items-center gap-1 px-2 py-1 rounded-full border text-[10px] font-bold transition-all',
|
||||
active ? `${activeBg} ${color}` : 'bg-white/3 border-white/8 text-gray-600'
|
||||
)}>
|
||||
<Icon className="h-3 w-3" />
|
||||
<Icon className="h-2.5 w-2.5" />
|
||||
{label}
|
||||
{active
|
||||
? <CheckCircle className="h-3 w-3 text-green-500" />
|
||||
: <XCircle className="h-3 w-3 text-gray-700" />
|
||||
? <CheckCircle className="h-2.5 w-2.5 text-green-500" />
|
||||
: <XCircle className="h-2.5 w-2.5 text-gray-700" />
|
||||
}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// ─── Recommended Wallets (collapsible) ───────────────────────────────────────
|
||||
// ─── Get a wallet (collapsible) ───────────────────────────────────────────────
|
||||
|
||||
function RecommendedWallets() {
|
||||
function GetAWallet() {
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
const wallets = [
|
||||
|
|
@ -177,7 +104,10 @@ function RecommendedWallets() {
|
|||
<Zap className="h-4 w-4 text-yellow-400" />
|
||||
<span className="text-sm font-black uppercase tracking-wide text-white">Get a Wallet</span>
|
||||
</div>
|
||||
{open ? <ChevronUp className="h-4 w-4 text-muted-foreground" /> : <ChevronDown className="h-4 w-4 text-muted-foreground" />}
|
||||
{open
|
||||
? <ChevronUp className="h-4 w-4 text-muted-foreground" />
|
||||
: <ChevronDown className="h-4 w-4 text-muted-foreground" />
|
||||
}
|
||||
</button>
|
||||
|
||||
{open && (
|
||||
|
|
@ -188,7 +118,7 @@ function RecommendedWallets() {
|
|||
href={w.url}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="flex items-center justify-between px-4 py-3 hover:bg-white/5 active:scale-[0.99] transition-all group"
|
||||
className="flex items-center justify-between px-4 py-3 hover:bg-white/5 active:scale-[0.99] transition-all"
|
||||
>
|
||||
<div className="min-w-0">
|
||||
<p className={cn('font-bold text-sm', w.color)}>{w.name}</p>
|
||||
|
|
@ -214,7 +144,7 @@ function RecommendedWallets() {
|
|||
);
|
||||
}
|
||||
|
||||
// ─── Zaps explainer (collapsible, hidden once wallet connected) ───────────────
|
||||
// ─── What are zaps (collapsible, hidden once wallet connected) ────────────────
|
||||
|
||||
function WhatAreZaps({ walletConnected }: { walletConnected: boolean }) {
|
||||
const [open, setOpen] = useState(!walletConnected);
|
||||
|
|
@ -223,7 +153,7 @@ function WhatAreZaps({ walletConnected }: { walletConnected: boolean }) {
|
|||
return (
|
||||
<button
|
||||
onClick={() => setOpen(true)}
|
||||
className="text-xs text-muted-foreground/50 hover:text-muted-foreground transition-colors flex items-center gap-1 mx-auto"
|
||||
className="text-xs text-muted-foreground/40 hover:text-muted-foreground transition-colors flex items-center gap-1 mx-auto"
|
||||
>
|
||||
<ChevronDown className="h-3 w-3" />
|
||||
What are zaps?
|
||||
|
|
@ -241,13 +171,16 @@ function WhatAreZaps({ walletConnected }: { walletConnected: boolean }) {
|
|||
<Zap className="h-4 w-4 text-amber-500" />
|
||||
<span className="text-sm font-black uppercase tracking-wide text-white">What are Zaps?</span>
|
||||
</div>
|
||||
{open ? <ChevronUp className="h-4 w-4 text-muted-foreground" /> : <ChevronDown className="h-4 w-4 text-muted-foreground" />}
|
||||
{open
|
||||
? <ChevronUp className="h-4 w-4 text-muted-foreground" />
|
||||
: <ChevronDown className="h-4 w-4 text-muted-foreground" />
|
||||
}
|
||||
</button>
|
||||
|
||||
{open && (
|
||||
<div className="px-4 pb-4 pt-3 space-y-3">
|
||||
<p className="text-sm text-muted-foreground leading-relaxed">
|
||||
Zaps are instant Bitcoin Lightning payments. Send sats to any skater, spot creator, or coach — no middleman, no platform cut.
|
||||
Instant Bitcoin Lightning payments. Send sats to any skater, spot creator, or coach — no middleman, no platform cut.
|
||||
</p>
|
||||
<div className="grid grid-cols-2 gap-2">
|
||||
{[
|
||||
|
|
@ -308,7 +241,7 @@ export default function WalletPage() {
|
|||
<div className="container px-4 py-5 max-w-lg mx-auto">
|
||||
|
||||
{/* Header */}
|
||||
<div className="mb-5">
|
||||
<div className="mb-4">
|
||||
<h1 className="text-3xl font-black uppercase leading-none mb-0.5">
|
||||
<span className="neon-orange">WALLET</span>
|
||||
</h1>
|
||||
|
|
@ -317,18 +250,16 @@ export default function WalletPage() {
|
|||
|
||||
{user ? (
|
||||
<div className="space-y-4">
|
||||
{/* 1. Balance hero — always first */}
|
||||
<BalanceHero />
|
||||
{/* Status bar + manage — compact single row */}
|
||||
<WalletStatusBar />
|
||||
|
||||
{/* 2. Cashu wallet card — the main interactive section */}
|
||||
<div id="cashu-card">
|
||||
<CashuWalletCard />
|
||||
</div>
|
||||
{/* Cashu wallet — the one and only balance + send/receive UI */}
|
||||
<CashuWalletCard />
|
||||
|
||||
{/* 3. Get a wallet — collapsed by default */}
|
||||
<RecommendedWallets />
|
||||
{/* Get a wallet — collapsed by default */}
|
||||
<GetAWallet />
|
||||
|
||||
{/* 4. Zaps explainer — auto-collapsed once wallet is connected */}
|
||||
{/* What are zaps — auto-collapses once wallet connected */}
|
||||
<WhatAreZaps walletConnected={walletConnected} />
|
||||
</div>
|
||||
) : (
|
||||
|
|
|
|||
Loading…
Reference in a new issue