discourse/frontend/components/wallet-connection-modal.tsx
2025-03-25 03:52:30 -04:00

281 lines
14 KiB
TypeScript

"use client"
import { useState } from "react"
import { Wallet, HelpCircle, Check, AlertTriangle, Loader2 } from "lucide-react"
import { Button } from "@/components/ui/button"
import {
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@/components/ui/dialog"
import { useWalletStore } from "@/lib/wallet-store"
import { cn } from "@/lib/utils"
const walletOptions = [
{
id: "metamask",
name: "MetaMask",
icon: "/images/wallets/metamask.svg",
description: "Connect to your MetaMask wallet",
popular: true,
verifiesAutomatically: false
},
{
id: "walletconnect",
name: "WalletConnect",
icon: "/images/wallets/walletconnect.svg",
description: "Connect using WalletConnect",
popular: true,
verifiesAutomatically: false
},
{
id: "coinbase",
name: "Coinbase Wallet",
icon: "/images/wallets/coinbase.svg",
description: "Connect to your Coinbase wallet",
popular: false,
verifiesAutomatically: false
},
{
id: "privadoid",
name: "Privado ID",
icon: "/images/wallets/privado.svg",
description: "Connect with automatic identity verification",
popular: true,
verifiesAutomatically: true
}
]
export function WalletConnectionModal() {
const { walletConnected, connectWallet } = useWalletStore()
const [isOpen, setIsOpen] = useState(false)
const [selectedWallet, setSelectedWallet] = useState<string | null>(null)
const [connectionState, setConnectionState] = useState<"idle" | "connecting" | "success" | "error">("idle")
const [errorMessage, setErrorMessage] = useState("")
const handleWalletSelect = (walletId: string) => {
setSelectedWallet(walletId)
}
const handleConnect = async () => {
if (!selectedWallet) return
setConnectionState("connecting")
setErrorMessage("")
try {
// This would connect to the actual wallet in production
await new Promise(resolve => setTimeout(resolve, 2000)) // Simulate connection delay
// Simulate success or failure (in production, would use actual wallet connection)
const success = Math.random() > 0.2 // 80% success rate for demo
if (success) {
// Check if the selected wallet provides automatic verification
const selectedWalletDetails = walletOptions.find(w => w.id === selectedWallet);
const isVerified = selectedWalletDetails?.verifiesAutomatically || false;
// Connect the wallet and update verification status
connectWallet(undefined, undefined, isVerified);
setConnectionState("success")
setTimeout(() => {
setIsOpen(false)
setConnectionState("idle")
setSelectedWallet(null)
}, 1500)
} else {
throw new Error("Could not connect to wallet. Please try again.")
}
} catch (error) {
console.error("Wallet connection error:", error)
setConnectionState("error")
setErrorMessage(error instanceof Error ? error.message : "An unknown error occurred")
}
}
const closeAndReset = () => {
setIsOpen(false)
setTimeout(() => {
setConnectionState("idle")
setSelectedWallet(null)
setErrorMessage("")
}, 300)
}
return (
<Dialog open={isOpen} onOpenChange={setIsOpen}>
<DialogTrigger asChild>
<Button variant={walletConnected ? "outline" : "default"}>
<Wallet className="mr-2 h-4 w-4" />
{walletConnected ? "Wallet Connected" : "Connect Wallet"}
</Button>
</DialogTrigger>
<DialogContent className="sm:max-w-md">
<DialogHeader>
<DialogTitle>Connect your wallet</DialogTitle>
<DialogDescription>
Select a wallet to connect and authenticate with the platform.
Privado ID provides automatic identity verification.
</DialogDescription>
</DialogHeader>
{connectionState === "success" ? (
<div className="flex flex-col items-center justify-center py-6">
<div className="rounded-full bg-green-100 p-3 mb-4">
<Check className="h-8 w-8 text-green-600" />
</div>
<h3 className="text-xl font-semibold">Connected Successfully</h3>
<p className="text-muted-foreground mt-2">Your wallet is now connected</p>
{selectedWallet === "privadoid" && (
<div className="mt-2 text-green-600 text-sm flex items-center">
<Check className="h-4 w-4 mr-1" />
Identity verified automatically
</div>
)}
{selectedWallet && selectedWallet !== "privadoid" && (
<div className="mt-4">
<p className="text-sm text-amber-600 mb-2">
Your wallet is connected but your identity is not verified.
</p>
<Button size="sm" variant="outline" asChild>
<a href="/profile/verify">Verify Your Identity</a>
</Button>
</div>
)}
</div>
) : connectionState === "error" ? (
<div className="flex flex-col items-center justify-center py-6">
<div className="rounded-full bg-red-100 p-3 mb-4">
<AlertTriangle className="h-8 w-8 text-red-600" />
</div>
<h3 className="text-xl font-semibold">Connection Failed</h3>
<p className="text-muted-foreground mt-2">{errorMessage}</p>
<Button variant="default" onClick={() => setConnectionState("idle")} className="mt-4">
Try Again
</Button>
</div>
) : (
<>
<div className="grid gap-4 py-4">
<div className="space-y-2">
<h3 className="text-sm font-medium">Popular Wallets</h3>
<div className="space-y-2">
{walletOptions.filter(w => w.popular).map((wallet) => (
<div
key={wallet.id}
className={cn(
"flex items-center space-x-4 rounded-md border p-4 cursor-pointer transition-colors",
selectedWallet === wallet.id
? "border-primary bg-primary/5"
: "hover:bg-muted"
)}
onClick={() => handleWalletSelect(wallet.id)}
>
<div className="h-10 w-10 flex-shrink-0 overflow-hidden rounded-full border">
<div className="h-full w-full flex items-center justify-center bg-muted">
<img
src={wallet.icon}
alt={wallet.name}
className="h-6 w-6"
onError={(e) => {
const target = e.target as HTMLImageElement;
target.src = "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='2' y='6' width='20' height='12' rx='2'/%3E%3Cpath d='M22 10H18a2 2 0 0 0-2 2v0a2 2 0 0 0 2 2h4'/%3E%3C/svg%3E";
}}
/>
</div>
</div>
<div className="flex-1 space-y-1">
<div className="flex items-center gap-2">
<p className="font-medium leading-none">{wallet.name}</p>
{wallet.verifiesAutomatically && (
<span className="text-xs bg-green-100 text-green-800 px-1.5 py-0.5 rounded-full">
Verifies Identity
</span>
)}
</div>
<p className="text-sm text-muted-foreground">{wallet.description}</p>
</div>
{selectedWallet === wallet.id && <Check className="h-5 w-5 text-primary" />}
</div>
))}
</div>
</div>
<div className="space-y-2">
<h3 className="text-sm font-medium">Other Wallets</h3>
<div className="space-y-2">
{walletOptions.filter(w => !w.popular).map((wallet) => (
<div
key={wallet.id}
className={cn(
"flex items-center space-x-4 rounded-md border p-4 cursor-pointer transition-colors",
selectedWallet === wallet.id
? "border-primary bg-primary/5"
: "hover:bg-muted"
)}
onClick={() => handleWalletSelect(wallet.id)}
>
<div className="h-10 w-10 flex-shrink-0 overflow-hidden rounded-full border">
<div className="h-full w-full flex items-center justify-center bg-muted">
<img
src={wallet.icon}
alt={wallet.name}
className="h-6 w-6"
onError={(e) => {
const target = e.target as HTMLImageElement;
target.src = "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='2' y='6' width='20' height='12' rx='2'/%3E%3Cpath d='M22 10H18a2 2 0 0 0-2 2v0a2 2 0 0 0 2 2h4'/%3E%3C/svg%3E";
}}
/>
</div>
</div>
<div className="flex-1 space-y-1">
<p className="font-medium leading-none">{wallet.name}</p>
<p className="text-sm text-muted-foreground">{wallet.description}</p>
</div>
{selectedWallet === wallet.id && <Check className="h-5 w-5 text-primary" />}
</div>
))}
</div>
</div>
</div>
<DialogFooter className="flex flex-col sm:flex-row gap-2">
<Button variant="outline" onClick={closeAndReset}>
Cancel
</Button>
<Button
onClick={handleConnect}
disabled={!selectedWallet || connectionState === "connecting"}
className="w-full sm:w-auto"
>
{connectionState === "connecting" ? (
<>
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
Connecting...
</>
) : (
"Connect"
)}
</Button>
</DialogFooter>
</>
)}
<div className="text-xs text-center text-muted-foreground mt-2">
<Button variant="link" size="sm" className="h-auto p-0">
<HelpCircle className="h-3 w-3 mr-1" />
What is a wallet?
</Button>
</div>
</DialogContent>
</Dialog>
)
}