OpChan/src/components/Header.tsx

308 lines
10 KiB
TypeScript
Raw Normal View History

import React, { useState } from 'react';
import { Link, useLocation } from 'react-router-dom';
2025-07-30 13:22:06 +05:30
import { useAuth } from '@/contexts/useAuth';
import { useForum } from '@/contexts/useForum';
2025-04-15 16:28:03 +05:30
import { Button } from '@/components/ui/button';
import { Badge } from '@/components/ui/badge';
2025-08-30 18:34:50 +05:30
import {
LogOut,
Terminal,
Wifi,
WifiOff,
AlertTriangle,
CheckCircle,
Key,
RefreshCw,
CircleSlash,
Home,
Grid3X3,
} from 'lucide-react';
import {
Tooltip,
TooltipContent,
TooltipTrigger,
} from '@/components/ui/tooltip';
2025-07-30 15:55:13 +05:30
import { useToast } from '@/components/ui/use-toast';
import { useAppKitAccount, useDisconnect } from '@reown/appkit/react';
import { WalletWizard } from '@/components/ui/wallet-wizard';
2025-09-03 15:01:57 +05:30
import { CallSignSetupDialog } from '@/components/ui/call-sign-setup-dialog';
import { useUserDisplay } from '@/hooks/useUserDisplay';
2025-04-15 16:28:03 +05:30
const Header = () => {
2025-09-03 15:01:57 +05:30
const { verificationStatus, getDelegationStatus } = useAuth();
const { isNetworkConnected, isRefreshing } = useForum();
const location = useLocation();
2025-07-30 15:55:13 +05:30
const { toast } = useToast();
// Use AppKit hooks for multi-chain support
2025-08-30 18:34:50 +05:30
const bitcoinAccount = useAppKitAccount({ namespace: 'bip122' });
const ethereumAccount = useAppKitAccount({ namespace: 'eip155' });
const { disconnect } = useDisconnect();
2025-08-30 18:34:50 +05:30
// Determine which account is connected
const isBitcoinConnected = bitcoinAccount.isConnected;
const isEthereumConnected = ethereumAccount.isConnected;
const isConnected = isBitcoinConnected || isEthereumConnected;
2025-08-30 18:34:50 +05:30
const address = isConnected
? isBitcoinConnected
? bitcoinAccount.address
: ethereumAccount.address
: undefined;
const [walletWizardOpen, setWalletWizardOpen] = useState(false);
2025-08-30 18:34:50 +05:30
2025-09-03 15:01:57 +05:30
// Get display name from hook
const { displayName } = useUserDisplay(address || '');
// Use sessionStorage to persist wizard state across navigation
const getHasShownWizard = () => {
try {
return sessionStorage.getItem('hasShownWalletWizard') === 'true';
} catch {
return false;
}
};
2025-08-30 18:34:50 +05:30
const setHasShownWizard = (value: boolean) => {
try {
sessionStorage.setItem('hasShownWalletWizard', value.toString());
} catch {
// Fallback if sessionStorage is not available
}
};
2025-08-30 18:34:50 +05:30
// Auto-open wizard when wallet connects for the first time
React.useEffect(() => {
if (isConnected && !getHasShownWizard()) {
setWalletWizardOpen(true);
setHasShownWizard(true);
}
}, [isConnected]);
2025-08-30 18:34:50 +05:30
2025-04-15 16:28:03 +05:30
const handleConnect = async () => {
setWalletWizardOpen(true);
2025-04-15 16:28:03 +05:30
};
2025-08-30 18:34:50 +05:30
const handleDisconnect = async () => {
await disconnect();
setHasShownWizard(false); // Reset so wizard can show again on next connection
toast({
2025-08-30 18:34:50 +05:30
title: 'Wallet Disconnected',
description: 'Your wallet has been disconnected successfully.',
});
2025-04-15 16:28:03 +05:30
};
2025-07-30 15:55:13 +05:30
const getAccountStatusText = () => {
switch (verificationStatus) {
case 'unverified':
return 'Setup Required';
case 'verifying':
return 'Verifying...';
case 'verified-none':
return 'Read-Only Access';
2025-08-18 14:07:01 +05:30
case 'verified-basic':
return getDelegationStatus().isValid ? 'Full Access' : 'Setup Key';
2025-08-18 14:07:01 +05:30
case 'verified-owner':
return getDelegationStatus().isValid ? 'Premium Access' : 'Setup Key';
default:
return 'Setup Account';
2025-07-30 15:55:13 +05:30
}
};
const getAccountStatusIcon = () => {
switch (verificationStatus) {
case 'unverified':
return <AlertTriangle className="w-3 h-3" />;
case 'verifying':
return <RefreshCw className="w-3 h-3 animate-spin" />;
case 'verified-none':
return <CircleSlash className="w-3 h-3" />;
2025-08-18 14:07:01 +05:30
case 'verified-basic':
return getDelegationStatus().isValid ? (
2025-08-30 18:34:50 +05:30
<CheckCircle className="w-3 h-3" />
) : (
<CheckCircle className="w-3 h-3" />
2025-08-30 18:34:50 +05:30
);
case 'verified-owner':
return getDelegationStatus().isValid ? (
2025-08-30 18:34:50 +05:30
<CheckCircle className="w-3 h-3" />
) : (
<Key className="w-3 h-3" />
);
default:
return <AlertTriangle className="w-3 h-3" />;
}
};
const getAccountStatusVariant = () => {
switch (verificationStatus) {
case 'unverified':
return 'destructive';
case 'verifying':
return 'outline';
case 'verified-none':
return 'secondary';
2025-08-18 14:07:01 +05:30
case 'verified-basic':
return getDelegationStatus().isValid ? 'default' : 'outline';
case 'verified-owner':
return getDelegationStatus().isValid ? 'default' : 'outline';
default:
return 'outline';
2025-04-24 14:31:00 +05:30
}
};
2025-08-30 18:34:50 +05:30
2025-04-15 16:28:03 +05:30
return (
<>
2025-08-30 18:34:50 +05:30
<header className="border-b border-cyber-muted bg-cyber-dark fixed top-0 left-0 right-0 z-50 h-16">
<div className="container mx-auto px-4 h-full flex justify-between items-center">
<div className="flex items-center gap-6">
<div className="flex items-center gap-2">
<Terminal className="text-cyber-accent w-6 h-6" />
2025-08-30 18:34:50 +05:30
<Link
to="/"
className="text-xl font-bold text-glow text-cyber-accent"
>
OpChan
</Link>
</div>
2025-08-30 18:34:50 +05:30
{/* Navigation Tabs */}
<nav className="hidden md:flex items-center space-x-1">
<Link
to="/"
className={`flex items-center space-x-2 px-3 py-2 text-sm font-medium rounded-sm transition-colors ${
2025-08-30 18:34:50 +05:30
location.pathname === '/'
? 'bg-cyber-accent/20 text-cyber-accent'
: 'text-gray-300 hover:text-cyber-accent hover:bg-cyber-accent/10'
}`}
>
<Home className="w-4 h-4" />
<span>Feed</span>
</Link>
<Link
to="/cells"
className={`flex items-center space-x-2 px-3 py-2 text-sm font-medium rounded-sm transition-colors ${
2025-08-30 18:34:50 +05:30
location.pathname === '/cells'
? 'bg-cyber-accent/20 text-cyber-accent'
: 'text-gray-300 hover:text-cyber-accent hover:bg-cyber-accent/10'
}`}
>
<Grid3X3 className="w-4 h-4" />
<span>Cells</span>
</Link>
</nav>
</div>
2025-08-30 18:34:50 +05:30
<div className="flex gap-3 items-center">
<Tooltip>
<TooltipTrigger asChild>
2025-08-30 18:34:50 +05:30
<Badge
variant={isNetworkConnected ? 'default' : 'destructive'}
className="flex items-center gap-1 text-xs px-2 h-7 cursor-help"
>
{isNetworkConnected ? (
<>
<Wifi className="w-3 h-3" />
<span>WAKU: Connected</span>
</>
) : (
<>
<WifiOff className="w-3 h-3" />
<span>WAKU: Offline</span>
</>
)}
</Badge>
</TooltipTrigger>
<TooltipContent className="text-sm">
2025-08-30 18:34:50 +05:30
<p>
{isNetworkConnected
? 'Waku network connection active.'
: 'Waku network connection lost.'}
</p>
{isRefreshing && <p>Refreshing data...</p>}
</TooltipContent>
</Tooltip>
2025-08-30 18:34:50 +05:30
{!isConnected ? (
2025-08-30 18:34:50 +05:30
<Button
variant="outline"
size="sm"
onClick={handleConnect}
2025-08-30 18:34:50 +05:30
className="text-xs px-2 h-7"
>
Connect Wallet
</Button>
) : (
2025-08-30 18:34:50 +05:30
<div className="flex gap-2 items-center">
<Tooltip>
<TooltipTrigger asChild>
<Button
variant={getAccountStatusVariant()}
size="sm"
onClick={() => setWalletWizardOpen(true)}
2025-08-30 18:34:50 +05:30
className="flex items-center gap-1 text-xs px-2 h-7"
>
{getAccountStatusIcon()}
<span>{getAccountStatusText()}</span>
</Button>
</TooltipTrigger>
<TooltipContent className="max-w-[260px] text-sm">
<p className="font-semibold mb-1">Account Setup</p>
2025-08-30 18:34:50 +05:30
<p>
Click to view and manage your wallet connection,
verification status, and key delegation.
</p>
</TooltipContent>
</Tooltip>
<Tooltip>
<TooltipTrigger asChild>
2025-08-30 18:34:50 +05:30
<span className="hidden md:flex items-center text-xs text-muted-foreground cursor-default px-2 h-7">
2025-09-03 15:01:57 +05:30
{displayName}
</span>
</TooltipTrigger>
<TooltipContent className="text-sm">
2025-08-30 18:34:50 +05:30
<p>
2025-09-03 15:01:57 +05:30
{displayName !==
`${address?.slice(0, 5)}...${address?.slice(-4)}`
? `${displayName} (${address})`
2025-08-30 18:34:50 +05:30
: address}
</p>
</TooltipContent>
</Tooltip>
2025-09-03 15:01:57 +05:30
<CallSignSetupDialog />
<Tooltip>
2025-08-30 18:34:50 +05:30
<TooltipTrigger asChild>
<Button
variant="ghost"
size="icon"
onClick={handleDisconnect}
2025-08-30 18:34:50 +05:30
className="w-7 h-7"
>
<LogOut className="w-4 h-4" />
</Button>
2025-08-30 18:34:50 +05:30
</TooltipTrigger>
<TooltipContent className="text-sm">
Disconnect Wallet
</TooltipContent>
</Tooltip>
</div>
)}
</div>
2025-04-15 16:28:03 +05:30
</div>
</header>
2025-08-30 18:34:50 +05:30
<WalletWizard
open={walletWizardOpen}
onOpenChange={setWalletWizardOpen}
onComplete={() => {
toast({
2025-08-30 18:34:50 +05:30
title: 'Setup Complete',
description: 'You can now use all OpChan features!',
});
}}
/>
</>
2025-04-15 16:28:03 +05:30
);
};
export default Header;