All checks were successful
CI / lint-and-test (push) Successful in 9m52s
Co-authored-by: Cursor <cursoragent@cursor.com>
114 lines
4.0 KiB
TypeScript
114 lines
4.0 KiB
TypeScript
"use client";
|
|
|
|
import { useState, useEffect, useRef } from "react";
|
|
import { useAccount, useConnect, useDisconnect } from "wagmi";
|
|
import { formatAddress } from "@/lib/utils";
|
|
import { isValidWalletConnectProjectId, getWalletConnectProjectId } from "@/lib/web3/walletconnect";
|
|
import { WalletBalancePanel } from "@/components/web3/WalletBalancePanel";
|
|
|
|
export function WalletConnect() {
|
|
const { address, isConnected } = useAccount();
|
|
const { connectors, connect, isPending } = useConnect();
|
|
const { disconnect } = useDisconnect();
|
|
const [menuOpen, setMenuOpen] = useState(false);
|
|
const menuRef = useRef<HTMLDivElement>(null);
|
|
|
|
useEffect(() => {
|
|
if (!menuOpen) return;
|
|
const onPointerDown = (event: MouseEvent) => {
|
|
if (menuRef.current && !menuRef.current.contains(event.target as Node)) {
|
|
setMenuOpen(false);
|
|
}
|
|
};
|
|
document.addEventListener("mousedown", onPointerDown);
|
|
return () => document.removeEventListener("mousedown", onPointerDown);
|
|
}, [menuOpen]);
|
|
|
|
const walletConnectConfigured = isValidWalletConnectProjectId(
|
|
getWalletConnectProjectId()
|
|
);
|
|
|
|
const availableConnectors = connectors.filter((connector) => {
|
|
if (connector.id === "walletConnect" && !walletConnectConfigured) {
|
|
return false;
|
|
}
|
|
return true;
|
|
});
|
|
|
|
if (isConnected && address) {
|
|
return (
|
|
<div ref={menuRef} className="relative w-full sm:w-auto">
|
|
<button
|
|
type="button"
|
|
onClick={() => setMenuOpen((open) => !open)}
|
|
className="flex flex-wrap items-center gap-2 text-sm rounded-lg px-3 py-2 bg-gray-800/80 hover:bg-gray-800 border border-gray-700 transition-colors w-full sm:w-auto"
|
|
aria-expanded={menuOpen}
|
|
aria-haspopup="dialog"
|
|
>
|
|
<span className="text-gray-400">Connected:</span>
|
|
<span className="font-mono text-white">{formatAddress(address)}</span>
|
|
<span className="text-gray-500 text-xs" aria-hidden>
|
|
{menuOpen ? "▲" : "▼"}
|
|
</span>
|
|
</button>
|
|
{menuOpen && (
|
|
<div
|
|
role="dialog"
|
|
aria-label="Wallet balances"
|
|
className="absolute right-0 mt-2 bg-gray-900 border border-gray-700 rounded-xl shadow-xl z-50 overflow-hidden"
|
|
>
|
|
<WalletBalancePanel
|
|
address={address}
|
|
onDisconnect={() => {
|
|
disconnect();
|
|
setMenuOpen(false);
|
|
}}
|
|
/>
|
|
</div>
|
|
)}
|
|
</div>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<div ref={menuRef} className="relative w-full sm:w-auto">
|
|
<button
|
|
type="button"
|
|
onClick={() => setMenuOpen((open) => !open)}
|
|
disabled={isPending || availableConnectors.length === 0}
|
|
className="w-full sm:w-auto px-4 py-2 bg-blue-600 hover:bg-blue-700 disabled:bg-gray-700 disabled:cursor-not-allowed rounded-lg transition-colors text-sm font-medium"
|
|
aria-expanded={menuOpen}
|
|
aria-haspopup="menu"
|
|
>
|
|
{isPending ? "Connecting…" : "Connect Wallet"}
|
|
</button>
|
|
{menuOpen && (
|
|
<div
|
|
role="menu"
|
|
className="absolute right-0 mt-2 w-full sm:w-56 bg-gray-900 border border-gray-700 rounded-lg shadow-xl z-50 overflow-hidden"
|
|
>
|
|
{availableConnectors.map((connector) => (
|
|
<button
|
|
key={connector.uid}
|
|
type="button"
|
|
role="menuitem"
|
|
onClick={() => {
|
|
connect({ connector });
|
|
setMenuOpen(false);
|
|
}}
|
|
className="w-full text-left px-4 py-3 text-sm hover:bg-gray-800 transition-colors border-b border-gray-800 last:border-b-0"
|
|
>
|
|
{connector.name}
|
|
</button>
|
|
))}
|
|
{!walletConnectConfigured && (
|
|
<p className="px-4 py-2 text-xs text-gray-500 border-t border-gray-800">
|
|
WalletConnect disabled — set NEXT_PUBLIC_WALLETCONNECT_PROJECT_ID
|
|
</p>
|
|
)}
|
|
</div>
|
|
)}
|
|
</div>
|
|
);
|
|
}
|