2025-08-05 10:51:21 +05:30
|
|
|
import React, { useState } from 'react';
|
2025-08-06 15:37:48 +05:30
|
|
|
import { Link, useLocation } from 'react-router-dom';
|
2025-09-03 15:56:00 +05:30
|
|
|
import { useAuth, useNetworkStatus } from '@/hooks';
|
2025-09-05 13:41:37 +05:30
|
|
|
import { useAuth as useAuthContext } from '@/contexts/useAuth';
|
|
|
|
|
import { EVerificationStatus } from '@/types/identity';
|
2025-09-04 13:27:47 +05:30
|
|
|
import { useForum } from '@/contexts/useForum';
|
2025-09-05 14:03:29 +05:30
|
|
|
import { localDatabase } from '@/lib/database/LocalDatabase';
|
|
|
|
|
import { DelegationFullStatus } from '@/lib/delegation';
|
2025-04-15 16:28:03 +05:30
|
|
|
import { Button } from '@/components/ui/button';
|
2025-09-03 15:56:00 +05:30
|
|
|
|
2025-08-30 18:34:50 +05:30
|
|
|
import {
|
|
|
|
|
LogOut,
|
|
|
|
|
Terminal,
|
|
|
|
|
AlertTriangle,
|
|
|
|
|
CheckCircle,
|
|
|
|
|
Key,
|
|
|
|
|
CircleSlash,
|
|
|
|
|
Home,
|
|
|
|
|
Grid3X3,
|
2025-09-05 12:53:15 +05:30
|
|
|
User,
|
2025-08-30 18:34:50 +05:30
|
|
|
} 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';
|
2025-08-05 10:51:21 +05:30
|
|
|
import { useAppKitAccount, useDisconnect } from '@reown/appkit/react';
|
|
|
|
|
import { WalletWizard } from '@/components/ui/wallet-wizard';
|
2025-09-03 15:56:00 +05:30
|
|
|
|
|
|
|
|
import { useUserDisplay } from '@/hooks';
|
2025-04-15 16:28:03 +05:30
|
|
|
|
|
|
|
|
const Header = () => {
|
2025-09-05 13:41:37 +05:30
|
|
|
const { verificationStatus } = useAuth();
|
|
|
|
|
const { getDelegationStatus } = useAuthContext();
|
2025-09-05 14:03:29 +05:30
|
|
|
const [delegationInfo, setDelegationInfo] =
|
|
|
|
|
useState<DelegationFullStatus | null>(null);
|
2025-09-03 15:56:00 +05:30
|
|
|
const networkStatus = useNetworkStatus();
|
2025-08-06 15:37:48 +05:30
|
|
|
const location = useLocation();
|
2025-07-30 15:55:13 +05:30
|
|
|
const { toast } = useToast();
|
2025-09-04 13:27:47 +05:30
|
|
|
const forum = useForum();
|
2025-09-03 15:56:00 +05:30
|
|
|
|
2025-08-05 10:51:21 +05:30
|
|
|
// 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' });
|
2025-08-05 10:51:21 +05:30
|
|
|
const { disconnect } = useDisconnect();
|
2025-08-30 18:34:50 +05:30
|
|
|
|
2025-08-05 10:51:21 +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;
|
|
|
|
|
|
2025-08-05 10:51:21 +05:30
|
|
|
const [walletWizardOpen, setWalletWizardOpen] = useState(false);
|
2025-08-30 18:34:50 +05:30
|
|
|
|
2025-09-03 15:56:00 +05:30
|
|
|
// ✅ Get display name from enhanced hook
|
2025-09-03 15:01:57 +05:30
|
|
|
const { displayName } = useUserDisplay(address || '');
|
|
|
|
|
|
2025-09-05 14:03:29 +05:30
|
|
|
// Load delegation status
|
|
|
|
|
React.useEffect(() => {
|
|
|
|
|
getDelegationStatus().then(setDelegationInfo).catch(console.error);
|
|
|
|
|
}, [getDelegationStatus]);
|
|
|
|
|
|
|
|
|
|
// Use LocalDatabase to persist wizard state across navigation
|
|
|
|
|
const getHasShownWizard = async (): Promise<boolean> => {
|
2025-08-05 12:03:47 +05:30
|
|
|
try {
|
2025-09-05 14:03:29 +05:30
|
|
|
const value = await localDatabase.loadUIState('hasShownWalletWizard');
|
|
|
|
|
return value === true;
|
2025-08-05 12:03:47 +05:30
|
|
|
} catch {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
};
|
2025-08-30 18:34:50 +05:30
|
|
|
|
2025-09-05 14:03:29 +05:30
|
|
|
const setHasShownWizard = async (value: boolean): Promise<void> => {
|
2025-08-05 12:03:47 +05:30
|
|
|
try {
|
2025-09-05 14:03:29 +05:30
|
|
|
await localDatabase.storeUIState('hasShownWalletWizard', value);
|
|
|
|
|
} catch (e) {
|
|
|
|
|
console.error('Failed to store wizard state', e);
|
2025-08-05 12:03:47 +05:30
|
|
|
}
|
|
|
|
|
};
|
2025-08-30 18:34:50 +05:30
|
|
|
|
2025-08-05 10:51:21 +05:30
|
|
|
// Auto-open wizard when wallet connects for the first time
|
|
|
|
|
React.useEffect(() => {
|
2025-09-05 14:03:29 +05:30
|
|
|
if (isConnected) {
|
|
|
|
|
getHasShownWizard().then(hasShown => {
|
|
|
|
|
if (!hasShown) {
|
|
|
|
|
setWalletWizardOpen(true);
|
|
|
|
|
setHasShownWizard(true).catch(console.error);
|
|
|
|
|
}
|
|
|
|
|
});
|
2025-08-05 10:51:21 +05:30
|
|
|
}
|
2025-08-05 12:03:47 +05:30
|
|
|
}, [isConnected]);
|
2025-08-30 18:34:50 +05:30
|
|
|
|
2025-04-15 16:28:03 +05:30
|
|
|
const handleConnect = async () => {
|
2025-08-05 10:51:21 +05:30
|
|
|
setWalletWizardOpen(true);
|
2025-04-15 16:28:03 +05:30
|
|
|
};
|
2025-08-30 18:34:50 +05:30
|
|
|
|
2025-08-05 10:51:21 +05:30
|
|
|
const handleDisconnect = async () => {
|
|
|
|
|
await disconnect();
|
2025-09-05 14:03:29 +05:30
|
|
|
await setHasShownWizard(false); // Reset so wizard can show again on next connection
|
2025-08-05 10:51:21 +05:30
|
|
|
toast({
|
2025-08-30 18:34:50 +05:30
|
|
|
title: 'Wallet Disconnected',
|
|
|
|
|
description: 'Your wallet has been disconnected successfully.',
|
2025-08-05 10:51:21 +05:30
|
|
|
});
|
2025-04-15 16:28:03 +05:30
|
|
|
};
|
2025-07-30 15:55:13 +05:30
|
|
|
|
2025-08-05 10:51:21 +05:30
|
|
|
const getAccountStatusText = () => {
|
2025-09-03 15:56:00 +05:30
|
|
|
if (!isConnected) return 'Connect Wallet';
|
|
|
|
|
|
2025-09-05 13:41:37 +05:30
|
|
|
if (verificationStatus === EVerificationStatus.ENS_ORDINAL_VERIFIED) {
|
2025-09-05 14:03:29 +05:30
|
|
|
return delegationInfo?.isValid ? 'Ready to Post' : 'Delegation Expired';
|
2025-09-05 13:41:37 +05:30
|
|
|
} else if (verificationStatus === EVerificationStatus.WALLET_CONNECTED) {
|
2025-09-03 15:56:00 +05:30
|
|
|
return 'Verified (Read-only)';
|
|
|
|
|
} else {
|
|
|
|
|
return 'Verify Wallet';
|
2025-07-30 15:55:13 +05:30
|
|
|
}
|
2025-04-24 16:30:50 +05:30
|
|
|
};
|
|
|
|
|
|
2025-09-03 15:56:00 +05:30
|
|
|
const getStatusColor = () => {
|
|
|
|
|
if (!isConnected) return 'text-red-400';
|
|
|
|
|
|
|
|
|
|
if (
|
2025-09-05 13:41:37 +05:30
|
|
|
verificationStatus === EVerificationStatus.ENS_ORDINAL_VERIFIED &&
|
2025-09-05 14:03:29 +05:30
|
|
|
delegationInfo?.isValid
|
2025-09-03 15:56:00 +05:30
|
|
|
) {
|
|
|
|
|
return 'text-green-400';
|
2025-09-05 13:41:37 +05:30
|
|
|
} else if (verificationStatus === EVerificationStatus.WALLET_CONNECTED) {
|
2025-09-03 15:56:00 +05:30
|
|
|
return 'text-yellow-400';
|
2025-09-05 13:41:37 +05:30
|
|
|
} else if (
|
|
|
|
|
verificationStatus === EVerificationStatus.ENS_ORDINAL_VERIFIED
|
|
|
|
|
) {
|
2025-09-03 15:56:00 +05:30
|
|
|
return 'text-orange-400';
|
|
|
|
|
} else {
|
|
|
|
|
return 'text-red-400';
|
2025-08-05 10:51:21 +05:30
|
|
|
}
|
2025-04-24 16:30:50 +05:30
|
|
|
};
|
|
|
|
|
|
2025-09-03 15:56:00 +05:30
|
|
|
const getStatusIcon = () => {
|
|
|
|
|
if (!isConnected) return <CircleSlash className="w-4 h-4" />;
|
|
|
|
|
|
|
|
|
|
if (
|
2025-09-05 13:41:37 +05:30
|
|
|
verificationStatus === EVerificationStatus.ENS_ORDINAL_VERIFIED &&
|
2025-09-05 14:03:29 +05:30
|
|
|
delegationInfo?.isValid
|
2025-09-03 15:56:00 +05:30
|
|
|
) {
|
|
|
|
|
return <CheckCircle className="w-4 h-4" />;
|
2025-09-05 13:41:37 +05:30
|
|
|
} else if (verificationStatus === EVerificationStatus.WALLET_CONNECTED) {
|
2025-09-03 15:56:00 +05:30
|
|
|
return <AlertTriangle className="w-4 h-4" />;
|
2025-09-05 13:41:37 +05:30
|
|
|
} else if (
|
|
|
|
|
verificationStatus === EVerificationStatus.ENS_ORDINAL_VERIFIED
|
|
|
|
|
) {
|
2025-09-03 15:56:00 +05:30
|
|
|
return <Key className="w-4 h-4" />;
|
|
|
|
|
} else {
|
|
|
|
|
return <AlertTriangle className="w-4 h-4" />;
|
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-09-03 15:56:00 +05:30
|
|
|
<header className="bg-cyber-muted/20 border-b border-cyber-muted sticky top-0 z-50 backdrop-blur-sm">
|
|
|
|
|
<div className="container mx-auto px-4 py-3">
|
|
|
|
|
<div className="flex items-center justify-between">
|
|
|
|
|
{/* Logo and Navigation */}
|
|
|
|
|
<div className="flex items-center space-x-6">
|
|
|
|
|
<Link
|
|
|
|
|
to="/"
|
|
|
|
|
className="text-xl font-bold text-glow hover:text-cyber-accent transition-colors"
|
|
|
|
|
>
|
|
|
|
|
<Terminal className="w-6 h-6 inline mr-2" />
|
|
|
|
|
opchan
|
|
|
|
|
</Link>
|
2025-08-30 18:34:50 +05:30
|
|
|
|
2025-09-03 15:56:00 +05:30
|
|
|
<nav className="hidden md:flex space-x-4">
|
2025-08-06 15:37:48 +05:30
|
|
|
<Link
|
|
|
|
|
to="/"
|
2025-09-03 15:56:00 +05:30
|
|
|
className={`flex items-center space-x-1 px-3 py-1 rounded-sm text-sm transition-colors ${
|
2025-08-30 18:34:50 +05:30
|
|
|
location.pathname === '/'
|
|
|
|
|
? 'bg-cyber-accent/20 text-cyber-accent'
|
2025-09-03 15:56:00 +05:30
|
|
|
: 'text-cyber-neutral hover:text-cyber-accent hover:bg-cyber-muted/50'
|
2025-08-06 15:37:48 +05:30
|
|
|
}`}
|
|
|
|
|
>
|
|
|
|
|
<Home className="w-4 h-4" />
|
2025-09-03 15:56:00 +05:30
|
|
|
<span>Home</span>
|
2025-08-06 15:37:48 +05:30
|
|
|
</Link>
|
|
|
|
|
<Link
|
|
|
|
|
to="/cells"
|
2025-09-03 15:56:00 +05:30
|
|
|
className={`flex items-center space-x-1 px-3 py-1 rounded-sm text-sm transition-colors ${
|
2025-08-30 18:34:50 +05:30
|
|
|
location.pathname === '/cells'
|
|
|
|
|
? 'bg-cyber-accent/20 text-cyber-accent'
|
2025-09-03 15:56:00 +05:30
|
|
|
: 'text-cyber-neutral hover:text-cyber-accent hover:bg-cyber-muted/50'
|
2025-08-06 15:37:48 +05:30
|
|
|
}`}
|
|
|
|
|
>
|
|
|
|
|
<Grid3X3 className="w-4 h-4" />
|
|
|
|
|
<span>Cells</span>
|
|
|
|
|
</Link>
|
2025-09-05 12:53:15 +05:30
|
|
|
{isConnected && (
|
|
|
|
|
<Link
|
|
|
|
|
to="/profile"
|
|
|
|
|
className={`flex items-center space-x-1 px-3 py-1 rounded-sm text-sm transition-colors ${
|
|
|
|
|
location.pathname === '/profile'
|
|
|
|
|
? 'bg-cyber-accent/20 text-cyber-accent'
|
|
|
|
|
: 'text-cyber-neutral hover:text-cyber-accent hover:bg-cyber-muted/50'
|
|
|
|
|
}`}
|
|
|
|
|
>
|
|
|
|
|
<User className="w-4 h-4" />
|
|
|
|
|
<span>Profile</span>
|
|
|
|
|
</Link>
|
|
|
|
|
)}
|
2025-08-06 15:37:48 +05:30
|
|
|
</nav>
|
2025-08-05 10:51:21 +05:30
|
|
|
</div>
|
2025-08-30 18:34:50 +05:30
|
|
|
|
2025-09-03 15:56:00 +05:30
|
|
|
{/* Right side - Status and User */}
|
|
|
|
|
<div className="flex items-center space-x-4">
|
|
|
|
|
{/* Network Status */}
|
|
|
|
|
<div className="flex items-center space-x-2">
|
|
|
|
|
<div
|
|
|
|
|
className={`w-2 h-2 rounded-full ${
|
|
|
|
|
networkStatus.health.isConnected
|
|
|
|
|
? 'bg-green-400'
|
|
|
|
|
: 'bg-red-400'
|
|
|
|
|
}`}
|
|
|
|
|
/>
|
|
|
|
|
<span className="text-xs text-cyber-neutral">
|
|
|
|
|
{networkStatus.getStatusMessage()}
|
|
|
|
|
</span>
|
2025-09-04 13:27:47 +05:30
|
|
|
{forum.lastSync && (
|
|
|
|
|
<span className="text-xs text-cyber-neutral ml-2">
|
2025-09-05 12:53:15 +05:30
|
|
|
Last updated{' '}
|
|
|
|
|
{new Date(forum.lastSync).toLocaleTimeString([], {
|
|
|
|
|
hour: '2-digit',
|
|
|
|
|
minute: '2-digit',
|
|
|
|
|
second: '2-digit',
|
|
|
|
|
})}
|
2025-09-04 13:27:47 +05:30
|
|
|
{forum.isSyncing ? ' • syncing…' : ''}
|
|
|
|
|
</span>
|
|
|
|
|
)}
|
2025-09-03 15:56:00 +05:30
|
|
|
</div>
|
2025-08-30 18:34:50 +05:30
|
|
|
|
2025-09-03 15:56:00 +05:30
|
|
|
{/* User Status */}
|
|
|
|
|
{isConnected ? (
|
|
|
|
|
<div className="flex items-center space-x-3">
|
2025-08-05 10:51:21 +05:30
|
|
|
<Tooltip>
|
|
|
|
|
<TooltipTrigger asChild>
|
2025-09-03 15:56:00 +05:30
|
|
|
<div className="flex items-center space-x-2">
|
|
|
|
|
<div className={getStatusColor()}>{getStatusIcon()}</div>
|
|
|
|
|
<div className="text-sm">
|
|
|
|
|
<div className="font-medium">{displayName}</div>
|
|
|
|
|
<div className={`text-xs ${getStatusColor()}`}>
|
|
|
|
|
{getAccountStatusText()}
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
2025-08-05 10:51:21 +05:30
|
|
|
</TooltipTrigger>
|
2025-09-03 15:56:00 +05:30
|
|
|
<TooltipContent>
|
|
|
|
|
<div className="text-xs">
|
|
|
|
|
<div>Address: {address?.slice(0, 8)}...</div>
|
|
|
|
|
<div>Status: {getAccountStatusText()}</div>
|
2025-09-05 14:03:29 +05:30
|
|
|
{delegationInfo?.timeRemaining && (
|
2025-09-03 15:56:00 +05:30
|
|
|
<div>
|
|
|
|
|
Delegation: {delegationInfo.timeRemaining} remaining
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
2025-08-30 18:34:50 +05:30
|
|
|
</TooltipContent>
|
2025-08-05 10:51:21 +05:30
|
|
|
</Tooltip>
|
2025-09-03 15:56:00 +05:30
|
|
|
|
|
|
|
|
<Button
|
|
|
|
|
variant="ghost"
|
|
|
|
|
size="sm"
|
|
|
|
|
onClick={handleDisconnect}
|
|
|
|
|
className="text-cyber-neutral hover:text-red-400"
|
|
|
|
|
>
|
|
|
|
|
<LogOut className="w-4 h-4" />
|
|
|
|
|
</Button>
|
2025-08-05 10:51:21 +05:30
|
|
|
</div>
|
2025-09-03 15:56:00 +05:30
|
|
|
) : (
|
|
|
|
|
<Button
|
|
|
|
|
onClick={handleConnect}
|
|
|
|
|
className="bg-cyber-accent hover:bg-cyber-accent/80"
|
|
|
|
|
>
|
|
|
|
|
Connect Wallet
|
|
|
|
|
</Button>
|
2025-08-05 10:51:21 +05:30
|
|
|
)}
|
|
|
|
|
</div>
|
2025-04-15 16:28:03 +05:30
|
|
|
</div>
|
2025-09-03 15:56:00 +05:30
|
|
|
</div>
|
2025-08-30 18:34:50 +05:30
|
|
|
|
2025-09-03 15:56:00 +05:30
|
|
|
{/* Wallet Wizard */}
|
2025-08-05 10:51:21 +05:30
|
|
|
<WalletWizard
|
|
|
|
|
open={walletWizardOpen}
|
|
|
|
|
onOpenChange={setWalletWizardOpen}
|
|
|
|
|
onComplete={() => {
|
2025-09-03 15:56:00 +05:30
|
|
|
setWalletWizardOpen(false);
|
2025-08-05 10:51:21 +05:30
|
|
|
toast({
|
2025-08-30 18:34:50 +05:30
|
|
|
title: 'Setup Complete',
|
2025-09-03 15:56:00 +05:30
|
|
|
description: 'Your wallet is ready to use!',
|
2025-08-05 10:51:21 +05:30
|
|
|
});
|
|
|
|
|
}}
|
|
|
|
|
/>
|
2025-09-03 15:56:00 +05:30
|
|
|
</header>
|
2025-04-15 16:28:03 +05:30
|
|
|
);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export default Header;
|