chore: update verification status enum

This commit is contained in:
Danish Arora 2025-09-05 13:41:37 +05:30
parent 30e888bae4
commit 9e6be6156f
No known key found for this signature in database
GPG Key ID: 1C6EF37CDAE1426E
33 changed files with 581 additions and 840 deletions

View File

@ -5,6 +5,7 @@ import { TrendingUp, Users, Eye } from 'lucide-react';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
import { Badge } from '@/components/ui/badge';
import { useForumData, useForumSelectors, useAuth } from '@/hooks';
import { EVerificationStatus } from '@/types/identity';
import { CypherImage } from '@/components/ui/CypherImage';
import { useUserDisplay } from '@/hooks';
@ -29,9 +30,9 @@ const FeedSidebar: React.FC = () => {
// User's verification status display
const getVerificationBadge = () => {
if (verificationStatus.level === 'verified-owner') {
if (verificationStatus === EVerificationStatus.ENS_ORDINAL_VERIFIED) {
return { text: 'Verified Owner', color: 'bg-green-500' };
} else if (verificationStatus.level === 'verified-basic') {
} else if (verificationStatus === EVerificationStatus.WALLET_CONNECTED) {
return { text: 'Verified', color: 'bg-blue-500' };
} else if (ensName) {
return { text: 'ENS User', color: 'bg-purple-500' };
@ -67,14 +68,14 @@ const FeedSidebar: React.FC = () => {
</div>
</div>
{verificationStatus.level === 'unverified' && (
{verificationStatus === EVerificationStatus.WALLET_UNCONNECTED && (
<div className="text-xs text-muted-foreground">
<Eye className="w-3 h-3 inline mr-1" />
Read-only mode. Verify wallet to participate.
</div>
)}
{verificationStatus.level === 'verified-basic' &&
{verificationStatus === EVerificationStatus.WALLET_CONNECTED &&
!ordinalDetails && (
<div className="text-xs text-muted-foreground">
<Eye className="w-3 h-3 inline mr-1" />

View File

@ -1,6 +1,8 @@
import React, { useState } from 'react';
import { Link, useLocation } from 'react-router-dom';
import { useAuth, useNetworkStatus } from '@/hooks';
import { useAuth as useAuthContext } from '@/contexts/useAuth';
import { EVerificationStatus } from '@/types/identity';
import { useForum } from '@/contexts/useForum';
import { Button } from '@/components/ui/button';
@ -27,7 +29,9 @@ import { WalletWizard } from '@/components/ui/wallet-wizard';
import { useUserDisplay } from '@/hooks';
const Header = () => {
const { verificationStatus, delegationInfo } = useAuth();
const { verificationStatus } = useAuth();
const { getDelegationStatus } = useAuthContext();
const delegationInfo = getDelegationStatus();
const networkStatus = useNetworkStatus();
const location = useLocation();
const { toast } = useToast();
@ -94,14 +98,10 @@ const Header = () => {
const getAccountStatusText = () => {
if (!isConnected) return 'Connect Wallet';
if (verificationStatus.level === 'verified-owner') {
return delegationInfo.isActive ? 'Ready to Post' : 'Delegation Expired';
} else if (verificationStatus.level === 'verified-basic') {
if (verificationStatus === EVerificationStatus.ENS_ORDINAL_VERIFIED) {
return delegationInfo.isValid ? 'Ready to Post' : 'Delegation Expired';
} else if (verificationStatus === EVerificationStatus.WALLET_CONNECTED) {
return 'Verified (Read-only)';
} else if (verificationStatus.level === 'unverified') {
return verificationStatus.hasOrdinal
? 'Verify Wallet'
: 'No Ordinals Found';
} else {
return 'Verify Wallet';
}
@ -111,13 +111,15 @@ const Header = () => {
if (!isConnected) return 'text-red-400';
if (
verificationStatus.level === 'verified-owner' &&
delegationInfo.isActive
verificationStatus === EVerificationStatus.ENS_ORDINAL_VERIFIED &&
delegationInfo.isValid
) {
return 'text-green-400';
} else if (verificationStatus.level === 'verified-basic') {
} else if (verificationStatus === EVerificationStatus.WALLET_CONNECTED) {
return 'text-yellow-400';
} else if (verificationStatus.hasOrdinal || verificationStatus.hasENS) {
} else if (
verificationStatus === EVerificationStatus.ENS_ORDINAL_VERIFIED
) {
return 'text-orange-400';
} else {
return 'text-red-400';
@ -128,13 +130,15 @@ const Header = () => {
if (!isConnected) return <CircleSlash className="w-4 h-4" />;
if (
verificationStatus.level === 'verified-owner' &&
delegationInfo.isActive
verificationStatus === EVerificationStatus.ENS_ORDINAL_VERIFIED &&
delegationInfo.isValid
) {
return <CheckCircle className="w-4 h-4" />;
} else if (verificationStatus.level === 'verified-basic') {
} else if (verificationStatus === EVerificationStatus.WALLET_CONNECTED) {
return <AlertTriangle className="w-4 h-4" />;
} else if (verificationStatus.hasOrdinal || verificationStatus.hasENS) {
} else if (
verificationStatus === EVerificationStatus.ENS_ORDINAL_VERIFIED
) {
return <Key className="w-4 h-4" />;
} else {
return <AlertTriangle className="w-4 h-4" />;

View File

@ -8,6 +8,7 @@ import {
useUserVotes,
useAuth,
} from '@/hooks';
import { EVerificationStatus } from '@/types/identity';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import { Textarea } from '@/components/ui/textarea';
@ -218,21 +219,22 @@ const PostList = () => {
</div>
)}
{!canPost && verificationStatus.level === 'verified-basic' && (
<div className="mb-8 p-4 border border-cyber-muted rounded-sm bg-cyber-muted/20">
<div className="flex items-center gap-2 mb-2">
<Eye className="w-4 h-4 text-cyber-neutral" />
<h3 className="font-medium">Read-Only Mode</h3>
{!canPost &&
verificationStatus === EVerificationStatus.WALLET_CONNECTED && (
<div className="mb-8 p-4 border border-cyber-muted rounded-sm bg-cyber-muted/20">
<div className="flex items-center gap-2 mb-2">
<Eye className="w-4 h-4 text-cyber-neutral" />
<h3 className="font-medium">Read-Only Mode</h3>
</div>
<p className="text-sm text-cyber-neutral mb-2">
Your wallet does not contain any Ordinal Operators. You can browse
threads but cannot post or interact.
</p>
<Badge variant="outline" className="text-xs">
No Ordinals Found
</Badge>
</div>
<p className="text-sm text-cyber-neutral mb-2">
Your wallet does not contain any Ordinal Operators. You can browse
threads but cannot post or interact.
</p>
<Badge variant="outline" className="text-xs">
No Ordinals Found
</Badge>
</div>
)}
)}
{!canPost && !currentUser && (
<div className="mb-8 p-4 border border-cyber-muted rounded-sm bg-cyber-muted/20 text-center">

View File

@ -9,6 +9,7 @@ import {
useNetworkStatus,
useForumSelectors,
} from '@/hooks';
import { useAuth as useAuthContext } from '@/contexts/useAuth';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
import { Badge } from '@/components/ui/badge';
import { Button } from '@/components/ui/button';
@ -22,6 +23,7 @@ export function HookDemoComponent() {
// Core data hooks - reactive and optimized
const forumData = useForumData();
const auth = useAuth();
const { getDelegationStatus } = useAuthContext();
// Derived hooks for specific data
const userVotes = useUserVotes();
@ -131,12 +133,11 @@ export function HookDemoComponent() {
<div className="grid grid-cols-2 gap-4">
<div>
<strong>Verification Level:</strong>{' '}
{auth.verificationStatus.level}
<strong>Verification Level:</strong> {auth.verificationStatus}
</div>
<div>
<strong>Delegation Active:</strong>{' '}
{auth.delegationInfo.isActive ? 'Yes' : 'No'}
{getDelegationStatus().isValid ? 'Yes' : 'No'}
</div>
</div>

View File

@ -1,6 +1,7 @@
import React from 'react';
import { Button } from './button';
import { useAuth, useAuthActions } from '@/hooks';
import { useAuth as useAuthContext } from '@/contexts/useAuth';
import { CheckCircle, AlertCircle, Trash2 } from 'lucide-react';
import { DelegationDuration } from '@/lib/delegation';
@ -17,7 +18,9 @@ export function DelegationStep({
isLoading,
setIsLoading,
}: DelegationStepProps) {
const { currentUser, delegationInfo, isAuthenticating } = useAuth();
const { currentUser, isAuthenticating } = useAuth();
const { getDelegationStatus } = useAuthContext();
const delegationInfo = getDelegationStatus();
const { delegateKey, clearDelegation } = useAuthActions();
const [selectedDuration, setSelectedDuration] =
@ -125,19 +128,19 @@ export function DelegationStep({
<div className="space-y-3">
{/* Status */}
<div className="flex items-center gap-2">
{delegationInfo.isActive ? (
{delegationInfo.isValid ? (
<CheckCircle className="h-4 w-4 text-green-500" />
) : (
<AlertCircle className="h-4 w-4 text-yellow-500" />
)}
<span
className={`text-sm font-medium ${
delegationInfo.isActive ? 'text-green-400' : 'text-yellow-400'
delegationInfo.isValid ? 'text-green-400' : 'text-yellow-400'
}`}
>
{delegationInfo.isActive ? 'Delegated' : 'Required'}
{delegationInfo.isValid ? 'Delegated' : 'Required'}
</span>
{delegationInfo.isActive && delegationInfo.timeRemaining && (
{delegationInfo.isValid && delegationInfo.timeRemaining && (
<span className="text-xs text-neutral-400">
{delegationInfo.timeRemaining} remaining
</span>
@ -145,7 +148,7 @@ export function DelegationStep({
</div>
{/* Duration Selection */}
{!delegationInfo.isActive && (
{!delegationInfo.isValid && (
<div className="space-y-3">
<label className="text-sm font-medium text-neutral-300">
Delegation Duration:
@ -184,7 +187,7 @@ export function DelegationStep({
)}
{/* Delegated Browser Public Key */}
{delegationInfo.isActive && currentUser?.browserPubKey && (
{delegationInfo.isValid && currentUser?.browserPubKey && (
<div className="text-xs text-neutral-400">
<div className="font-mono break-all bg-neutral-800 p-2 rounded">
{currentUser.browserPubKey}
@ -200,7 +203,7 @@ export function DelegationStep({
)}
{/* Delete Button for Active Delegations */}
{delegationInfo.isActive && (
{delegationInfo.isValid && (
<div className="flex justify-end">
<Button
onClick={clearDelegation}
@ -218,7 +221,7 @@ export function DelegationStep({
{/* Action Buttons */}
<div className="mt-auto space-y-2">
{delegationInfo.isActive ? (
{delegationInfo.isValid ? (
<Button
onClick={handleComplete}
className="w-full bg-green-600 hover:bg-green-700 text-white"

View File

@ -9,6 +9,7 @@ import {
AlertCircle,
} from 'lucide-react';
import { useAuth, useAuthActions } from '@/hooks';
import { EVerificationStatus } from '@/types/identity';
import { useAppKitAccount } from '@reown/appkit/react';
import { OrdinalDetails, EnsDetails } from '@/types/identity';
@ -217,7 +218,7 @@ export function VerificationStep({
}
// Show verification status
if (verificationStatus.level === 'verified-owner') {
if (verificationStatus === EVerificationStatus.ENS_ORDINAL_VERIFIED) {
return (
<div className="flex flex-col h-full">
<div className="flex-1 space-y-4">

View File

@ -9,6 +9,8 @@ import {
import { Button } from '@/components/ui/button';
import { CheckCircle, Circle, Loader2 } from 'lucide-react';
import { useAuth } from '@/hooks';
import { useAuth as useAuthContext } from '@/contexts/useAuth';
import { EVerificationStatus } from '@/types/identity';
import { WalletConnectionStep } from './wallet-connection-step';
import { VerificationStep } from './verification-step';
import { DelegationStep } from './delegation-step';
@ -28,7 +30,9 @@ export function WalletWizard({
}: WalletWizardProps) {
const [currentStep, setCurrentStep] = React.useState<WizardStep>(1);
const [isLoading, setIsLoading] = React.useState(false);
const { isAuthenticated, verificationStatus, delegationInfo } = useAuth();
const { isAuthenticated, verificationStatus } = useAuth();
const { getDelegationStatus } = useAuthContext();
const delegationInfo = getDelegationStatus();
const hasInitialized = React.useRef(false);
// Reset wizard when opened and determine starting step
@ -39,15 +43,14 @@ export function WalletWizard({
setCurrentStep(1); // Start at connection step if not authenticated
} else if (
isAuthenticated &&
(verificationStatus.level === 'unverified' ||
verificationStatus.level === 'verifying')
verificationStatus === EVerificationStatus.WALLET_UNCONNECTED
) {
setCurrentStep(2); // Start at verification step if authenticated but not verified
} else if (
isAuthenticated &&
(verificationStatus.level === 'verified-owner' ||
verificationStatus.level === 'verified-basic') &&
!delegationInfo.isActive
(verificationStatus === EVerificationStatus.ENS_ORDINAL_VERIFIED ||
verificationStatus === EVerificationStatus.WALLET_CONNECTED) &&
!delegationInfo.isValid
) {
setCurrentStep(3); // Start at delegation step if verified but no valid delegation
} else {
@ -79,12 +82,17 @@ export function WalletWizard({
return isAuthenticated ? 'complete' : 'current';
} else if (step === 2) {
if (!isAuthenticated) return 'disabled';
return verificationStatus.level !== 'unverified' ? 'complete' : 'current';
return verificationStatus !== EVerificationStatus.WALLET_UNCONNECTED
? 'complete'
: 'current';
} else if (step === 3) {
if (!isAuthenticated || verificationStatus.level === 'unverified') {
if (
!isAuthenticated ||
verificationStatus === EVerificationStatus.WALLET_UNCONNECTED
) {
return 'disabled';
}
return delegationInfo.isActive ? 'complete' : 'current';
return delegationInfo.isValid ? 'complete' : 'current';
}
return 'disabled';
};

View File

@ -14,18 +14,13 @@ import {
} from '@/lib/delegation';
import { useAppKitAccount, useDisconnect, modal } from '@reown/appkit/react';
export type VerificationStatus =
| 'unverified'
| 'verified-none'
| 'verified-basic'
| 'verified-owner'
| 'verifying';
// Removed VerificationStatus type - using EVerificationStatus enum directly
interface AuthContextType {
currentUser: User | null;
isAuthenticating: boolean;
isAuthenticated: boolean;
verificationStatus: VerificationStatus;
verificationStatus: EVerificationStatus;
connectWallet: () => Promise<boolean>;
disconnectWallet: () => void;
verifyOwnership: () => Promise<boolean>;
@ -44,7 +39,7 @@ export function AuthProvider({ children }: { children: React.ReactNode }) {
const [currentUser, setCurrentUser] = useState<User | null>(null);
const [isAuthenticating, setIsAuthenticating] = useState(false);
const [verificationStatus, setVerificationStatus] =
useState<VerificationStatus>('unverified');
useState<EVerificationStatus>(EVerificationStatus.WALLET_UNCONNECTED);
const { toast } = useToast();
// Use AppKit hooks for multi-chain support
@ -118,8 +113,8 @@ export function AuthProvider({ children }: { children: React.ReactNode }) {
? { ordinalId: 'mock', ordinalDetails: 'Mock ordinal for testing' }
: undefined,
verificationStatus: hasOperators
? EVerificationStatus.VERIFIED_OWNER
: EVerificationStatus.VERIFIED_BASIC,
? EVerificationStatus.ENS_ORDINAL_VERIFIED
: EVerificationStatus.WALLET_CONNECTED,
lastChecked: Date.now(),
};
} else if (user.walletType === 'ethereum') {
@ -134,8 +129,8 @@ export function AuthProvider({ children }: { children: React.ReactNode }) {
...user,
ensDetails: hasENS && ensName ? { ensName } : undefined,
verificationStatus: hasENS
? EVerificationStatus.VERIFIED_OWNER
: EVerificationStatus.VERIFIED_BASIC,
? EVerificationStatus.ENS_ORDINAL_VERIFIED
: EVerificationStatus.WALLET_CONNECTED,
lastChecked: Date.now(),
};
} catch (error) {
@ -143,7 +138,7 @@ export function AuthProvider({ children }: { children: React.ReactNode }) {
return {
...user,
ensDetails: undefined,
verificationStatus: EVerificationStatus.VERIFIED_BASIC,
verificationStatus: EVerificationStatus.WALLET_CONNECTED,
lastChecked: Date.now(),
};
}
@ -204,7 +199,7 @@ export function AuthProvider({ children }: { children: React.ReactNode }) {
const newUser: User = {
address,
walletType: isBitcoinConnected ? 'bitcoin' : 'ethereum',
verificationStatus: EVerificationStatus.VERIFIED_BASIC, // Connected wallets get basic verification by default
verificationStatus: EVerificationStatus.WALLET_CONNECTED, // Connected wallets get basic verification by default
displayPreference: EDisplayPreference.WALLET_ADDRESS,
lastChecked: Date.now(),
};
@ -220,32 +215,35 @@ export function AuthProvider({ children }: { children: React.ReactNode }) {
const updatedUser = {
...newUser,
ensDetails: { ensName: walletInfo.ensName },
verificationStatus: EVerificationStatus.VERIFIED_OWNER,
verificationStatus:
EVerificationStatus.ENS_ORDINAL_VERIFIED,
};
setCurrentUser(updatedUser);
setVerificationStatus('verified-owner');
setVerificationStatus(
EVerificationStatus.ENS_ORDINAL_VERIFIED
);
saveUser(updatedUser);
} else {
setCurrentUser(newUser);
setVerificationStatus('verified-basic');
setVerificationStatus(EVerificationStatus.WALLET_CONNECTED);
saveUser(newUser);
}
})
.catch(() => {
// Fallback to basic verification if ENS check fails
setCurrentUser(newUser);
setVerificationStatus('verified-basic');
setVerificationStatus(EVerificationStatus.WALLET_CONNECTED);
saveUser(newUser);
});
} catch {
// WalletManager not ready, fallback to basic verification
setCurrentUser(newUser);
setVerificationStatus('verified-basic');
setVerificationStatus(EVerificationStatus.WALLET_CONNECTED);
saveUser(newUser);
}
} else {
setCurrentUser(newUser);
setVerificationStatus('verified-basic');
setVerificationStatus(EVerificationStatus.WALLET_CONNECTED);
saveUser(newUser);
}
@ -270,7 +268,7 @@ export function AuthProvider({ children }: { children: React.ReactNode }) {
} else {
// Wallet disconnected
setCurrentUser(null);
setVerificationStatus('unverified');
setVerificationStatus(EVerificationStatus.WALLET_UNCONNECTED);
}
}, [isConnected, address, isBitcoinConnected, isEthereumConnected, toast]);
@ -293,13 +291,17 @@ export function AuthProvider({ children }: { children: React.ReactNode }) {
disconnect();
};
const getVerificationStatus = (user: User): VerificationStatus => {
const getVerificationStatus = (user: User): EVerificationStatus => {
if (user.walletType === 'bitcoin') {
return user.ordinalDetails ? 'verified-owner' : 'verified-basic';
return user.ordinalDetails
? EVerificationStatus.ENS_ORDINAL_VERIFIED
: EVerificationStatus.WALLET_CONNECTED;
} else if (user.walletType === 'ethereum') {
return user.ensDetails ? 'verified-owner' : 'verified-basic';
return user.ensDetails
? EVerificationStatus.ENS_ORDINAL_VERIFIED
: EVerificationStatus.WALLET_CONNECTED;
}
return 'unverified';
return EVerificationStatus.WALLET_UNCONNECTED;
};
const verifyOwnership = async (): Promise<boolean> => {
@ -313,7 +315,7 @@ export function AuthProvider({ children }: { children: React.ReactNode }) {
}
setIsAuthenticating(true);
setVerificationStatus('verifying');
setVerificationStatus(EVerificationStatus.WALLET_CONNECTED); // Temporary state during verification
try {
const verificationType =
@ -363,7 +365,7 @@ export function AuthProvider({ children }: { children: React.ReactNode }) {
);
} catch (error) {
console.error('Error verifying ownership:', error);
setVerificationStatus('unverified');
setVerificationStatus(EVerificationStatus.WALLET_UNCONNECTED);
let errorMessage = 'Failed to verify ownership. Please try again.';
if (error instanceof Error) {

View File

@ -187,7 +187,7 @@ export function ForumProvider({ children }: { children: React.ReactNode }) {
walletType: address.startsWith('0x')
? ('ethereum' as const)
: ('bitcoin' as const),
verificationStatus: EVerificationStatus.UNVERIFIED,
verificationStatus: EVerificationStatus.WALLET_UNCONNECTED,
displayPreference: EDisplayPreference.WALLET_ADDRESS,
};
}

View File

@ -1,8 +1,9 @@
import { useCallback, useState } from 'react';
import { useAuth } from '@/hooks/core/useEnhancedAuth';
import { useAuth } from '@/hooks/core/useAuth';
import { DelegationDuration } from '@/lib/delegation';
import { useToast } from '@/components/ui/use-toast';
import { useAuth as useAuthContext } from '@/contexts/useAuth';
import { EVerificationStatus } from '@/types/identity';
export interface AuthActionStates {
isConnecting: boolean;
@ -32,14 +33,13 @@ export interface AuthActions extends AuthActionStates {
* Hook for authentication and verification actions
*/
export function useAuthActions(): AuthActions {
const {
isAuthenticated,
isAuthenticating,
delegationInfo,
verificationStatus,
} = useAuth();
const { isAuthenticated, isAuthenticating, verificationStatus } = useAuth();
const { verifyOwnership, delegateKey: delegateKeyFromContext } = useAuthContext();
const {
verifyOwnership,
delegateKey: delegateKeyFromContext,
getDelegationStatus,
} = useAuthContext();
const { toast } = useToast();
const [isConnecting, setIsConnecting] = useState(false);
@ -145,7 +145,7 @@ export function useAuthActions(): AuthActions {
return false;
}
if (verificationStatus.level !== 'unverified') {
if (verificationStatus !== EVerificationStatus.WALLET_UNCONNECTED) {
toast({
title: 'Already Verified',
description: 'Your wallet is already verified.',
@ -155,24 +155,24 @@ export function useAuthActions(): AuthActions {
setIsVerifying(true);
try {
// Call the real verification function from AuthContext
const success = await verifyOwnership();
if (success) {
toast({
title: 'Verification Complete',
description: 'Your wallet has been verified successfully.',
});
} else {
toast({
title: 'Verification Failed',
description: 'Failed to verify wallet ownership. Please try again.',
variant: 'destructive',
});
}
return success;
try {
// Call the real verification function from AuthContext
const success = await verifyOwnership();
if (success) {
toast({
title: 'Verification Complete',
description: 'Your wallet has been verified successfully.',
});
} else {
toast({
title: 'Verification Failed',
description: 'Failed to verify wallet ownership. Please try again.',
variant: 'destructive',
});
}
return success;
} catch (error) {
console.error('Failed to verify wallet:', error);
toast({
@ -184,7 +184,7 @@ export function useAuthActions(): AuthActions {
} finally {
setIsVerifying(false);
}
}, [isAuthenticated, verificationStatus.level, verifyOwnership, toast]);
}, [isAuthenticated, verificationStatus, verifyOwnership, toast]);
// Delegate key
const delegateKey = useCallback(
@ -198,7 +198,7 @@ export function useAuthActions(): AuthActions {
return false;
}
if (verificationStatus.level === 'unverified') {
if (verificationStatus === EVerificationStatus.WALLET_UNCONNECTED) {
toast({
title: 'Verification Required',
description: 'Please verify your wallet before delegating keys.',
@ -212,7 +212,7 @@ export function useAuthActions(): AuthActions {
try {
// Call the real delegation function from AuthContext
const success = await delegateKeyFromContext(duration);
if (success) {
const durationLabel = duration === '7days' ? '1 week' : '30 days';
toast({
@ -226,7 +226,7 @@ export function useAuthActions(): AuthActions {
variant: 'destructive',
});
}
return success;
} catch (error) {
console.error('Failed to delegate key:', error);
@ -240,12 +240,13 @@ export function useAuthActions(): AuthActions {
setIsDelegating(false);
}
},
[isAuthenticated, verificationStatus.level, delegateKeyFromContext, toast]
[isAuthenticated, verificationStatus, delegateKeyFromContext, toast]
);
// Clear delegation
const clearDelegation = useCallback(async (): Promise<boolean> => {
if (!delegationInfo.isActive) {
const delegationInfo = getDelegationStatus();
if (!delegationInfo.isValid) {
toast({
title: 'No Active Delegation',
description: 'There is no active key delegation to clear.',
@ -271,7 +272,7 @@ export function useAuthActions(): AuthActions {
});
return false;
}
}, [delegationInfo.isActive, toast]);
}, [getDelegationStatus, toast]);
// Renew delegation
const renewDelegation = useCallback(

View File

@ -1,6 +1,7 @@
import { useCallback } from 'react';
import { useForum } from '@/contexts/useForum';
import { useAuth } from '@/hooks/core/useEnhancedAuth';
import { useAuth } from '@/hooks/core/useAuth';
import { usePermissions } from '@/hooks/core/usePermissions';
import { Cell, Post, Comment } from '@/types/forum';
import { useToast } from '@/components/ui/use-toast';
@ -74,7 +75,8 @@ export function useForumActions(): ForumActions {
getCellById,
} = useForum();
const { currentUser, permissions } = useAuth();
const { currentUser } = useAuth();
const permissions = usePermissions();
const { toast } = useToast();
// Cell creation
@ -87,7 +89,7 @@ export function useForumActions(): ForumActions {
if (!permissions.canCreateCell) {
toast({
title: 'Permission Denied',
description: 'You need to verify Ordinal ownership to create cells.',
description: permissions.createCellReason,
variant: 'destructive',
});
return null;
@ -176,8 +178,7 @@ export function useForumActions(): ForumActions {
if (!permissions.canComment) {
toast({
title: 'Permission Denied',
description:
'You need to verify Ordinal ownership to create comments.',
description: permissions.commentReason,
variant: 'destructive',
});
return null;
@ -219,8 +220,7 @@ export function useForumActions(): ForumActions {
if (!permissions.canVote) {
toast({
title: 'Permission Denied',
description:
'You need to verify wallet ownership or have ENS/Ordinals to vote.',
description: permissions.voteReason,
variant: 'destructive',
});
return false;
@ -253,8 +253,7 @@ export function useForumActions(): ForumActions {
if (!permissions.canVote) {
toast({
title: 'Permission Denied',
description:
'You need to verify wallet ownership or have ENS/Ordinals to vote.',
description: permissions.voteReason,
variant: 'destructive',
});
return false;

View File

@ -1,6 +1,7 @@
import { useCallback, useState } from 'react';
import { useForum } from '@/contexts/useForum';
import { useAuth } from '@/hooks/core/useEnhancedAuth';
import { useAuth } from '@/hooks/core/useAuth';
import { usePermissions } from '@/hooks/core/usePermissions';
import { EDisplayPreference } from '@/types/identity';
import { useToast } from '@/components/ui/use-toast';
@ -25,7 +26,8 @@ export interface UserActions extends UserActionStates {
*/
export function useUserActions(): UserActions {
const { userIdentityService } = useForum();
const { currentUser, permissions } = useAuth();
const { currentUser } = useAuth();
const permissions = usePermissions();
const { toast } = useToast();
const [isUpdatingProfile, setIsUpdatingProfile] = useState(false);

View File

@ -1,8 +1,56 @@
// Re-export the enhanced auth hook as the main useAuth
export { useEnhancedAuth as useAuth } from './useEnhancedAuth';
export type {
Permission,
DetailedVerificationStatus,
DelegationInfo,
EnhancedAuthState,
} from './useEnhancedAuth';
import { useAuth as useBaseAuth } from '@/contexts/useAuth';
import { User, EVerificationStatus } from '@/types/identity';
export interface AuthState {
currentUser: User | null;
isAuthenticated: boolean;
isAuthenticating: boolean;
verificationStatus: EVerificationStatus;
// Helper functions
getDisplayName: () => string;
getVerificationBadge: () => string | null;
}
/**
* Unified authentication hook that provides core auth state and helpers
*/
export function useAuth(): AuthState {
const { currentUser, isAuthenticated, isAuthenticating, verificationStatus } =
useBaseAuth();
// Helper functions
const getDisplayName = (): string => {
if (!currentUser) return 'Anonymous';
if (currentUser.callSign) {
return currentUser.callSign;
}
if (currentUser.ensDetails?.ensName) {
return currentUser.ensDetails.ensName;
}
return `${currentUser.address.slice(0, 6)}...${currentUser.address.slice(-4)}`;
};
const getVerificationBadge = (): string | null => {
switch (verificationStatus) {
case EVerificationStatus.ENS_ORDINAL_VERIFIED:
return '🔑'; // ENS/Ordinal owner
case EVerificationStatus.WALLET_CONNECTED:
return '✅'; // Wallet connected
default:
return null;
}
};
return {
currentUser,
isAuthenticated,
isAuthenticating,
verificationStatus,
getDisplayName,
getVerificationBadge,
};
}

View File

@ -1,248 +0,0 @@
import { useMemo } from 'react';
import { useAuth as useBaseAuth } from '@/contexts/useAuth';
import { User, EVerificationStatus } from '@/types/identity';
export interface Permission {
canPost: boolean;
canComment: boolean;
canVote: boolean;
canCreateCell: boolean;
canModerate: (cellId: string) => boolean;
canDelegate: boolean;
canUpdateProfile: boolean;
}
export interface DetailedVerificationStatus {
level: EVerificationStatus;
hasWallet: boolean;
hasENS: boolean;
hasOrdinal: boolean;
hasCallSign: boolean;
isVerifying: boolean;
canUpgrade: boolean;
nextSteps: string[];
}
export interface DelegationInfo {
isActive: boolean;
isExpired: boolean;
expiresAt: number | null;
timeRemaining: string | null;
canDelegate: boolean;
needsRenewal: boolean;
}
export interface EnhancedAuthState {
// Base auth data
currentUser: User | null;
isAuthenticated: boolean;
isAuthenticating: boolean;
// Enhanced verification info
verificationStatus: DetailedVerificationStatus;
// Delegation info
delegationInfo: DelegationInfo;
// Permissions
permissions: Permission;
// Helper functions
hasPermission: (action: keyof Permission, cellId?: string) => boolean;
getDisplayName: () => string;
getVerificationBadge: () => string | null;
}
/**
* Enhanced authentication hook with detailed status and permissions
*/
export function useEnhancedAuth(): EnhancedAuthState {
const {
currentUser,
isAuthenticated,
isAuthenticating,
verificationStatus: baseVerificationStatus,
getDelegationStatus,
} = useBaseAuth();
// Detailed verification status
const verificationStatus = useMemo((): DetailedVerificationStatus => {
const hasWallet = !!currentUser;
const hasENS = !!currentUser?.ensDetails;
const hasOrdinal = !!currentUser?.ordinalDetails;
const hasCallSign = !!currentUser?.callSign;
const isVerifying = baseVerificationStatus === 'verifying';
let level: EVerificationStatus = EVerificationStatus.UNVERIFIED;
if (currentUser) {
level = currentUser.verificationStatus;
}
const canUpgrade =
hasWallet && !isVerifying && level !== EVerificationStatus.VERIFIED_OWNER;
const nextSteps: string[] = [];
if (!hasWallet) {
nextSteps.push('Connect your wallet');
} else if (level === EVerificationStatus.UNVERIFIED) {
nextSteps.push('Verify wallet ownership');
if (!hasOrdinal && !hasENS) {
nextSteps.push('Acquire Ordinal or ENS for posting privileges');
}
} else if (level === EVerificationStatus.VERIFIED_BASIC && !hasOrdinal) {
nextSteps.push('Acquire Ordinal for full privileges');
}
if (hasWallet && !hasCallSign) {
nextSteps.push('Set up call sign for better identity');
}
return {
level,
hasWallet,
hasENS,
hasOrdinal,
hasCallSign,
isVerifying,
canUpgrade,
nextSteps,
};
}, [currentUser, baseVerificationStatus]);
// Delegation information
const delegationInfo = useMemo((): DelegationInfo => {
const delegationStatus = getDelegationStatus();
const isActive = delegationStatus.isValid;
let expiresAt: number | null = null;
let timeRemaining: string | null = null;
let isExpired = false;
if (currentUser?.delegationExpiry) {
expiresAt = currentUser.delegationExpiry;
const now = Date.now();
isExpired = now > expiresAt;
if (!isExpired) {
const remaining = expiresAt - now;
const hours = Math.floor(remaining / (1000 * 60 * 60));
const days = Math.floor(hours / 24);
if (days > 0) {
timeRemaining = `${days} day${days > 1 ? 's' : ''}`;
} else {
timeRemaining = `${hours} hour${hours > 1 ? 's' : ''}`;
}
}
}
const canDelegate =
isAuthenticated &&
verificationStatus.level !== EVerificationStatus.UNVERIFIED;
const needsRenewal =
isExpired ||
(expiresAt !== null && expiresAt - Date.now() < 24 * 60 * 60 * 1000); // Less than 24 hours
return {
isActive,
isExpired,
expiresAt,
timeRemaining,
canDelegate,
needsRenewal,
};
}, [
currentUser,
getDelegationStatus,
isAuthenticated,
verificationStatus.level,
]);
// Permission calculations
const permissions = useMemo((): Permission => {
const canPost =
verificationStatus.level === EVerificationStatus.VERIFIED_OWNER;
const canComment = canPost; // Same requirements for now
const canVote =
canPost || verificationStatus.hasENS || verificationStatus.hasOrdinal;
const canCreateCell = canPost;
const canDelegate =
verificationStatus.level !== EVerificationStatus.UNVERIFIED;
const canUpdateProfile = isAuthenticated;
const canModerate = (cellId: string): boolean => {
if (!currentUser || !cellId) return false;
// This would need to be enhanced with actual cell ownership data
// For now, we'll return false and let the specific hooks handle this
return false;
};
return {
canPost,
canComment,
canVote,
canCreateCell,
canModerate,
canDelegate,
canUpdateProfile,
};
}, [verificationStatus, currentUser, isAuthenticated]);
// Helper functions
const hasPermission = (
action: keyof Permission,
cellId?: string
): boolean => {
const permission = permissions[action];
if (typeof permission === 'function') {
return permission(cellId || '');
}
return Boolean(permission);
};
const getDisplayName = (): string => {
if (!currentUser) return 'Anonymous';
if (currentUser.callSign) {
return currentUser.callSign;
}
if (currentUser.ensDetails?.ensName) {
return currentUser.ensDetails.ensName;
}
return `${currentUser.address.slice(0, 6)}...${currentUser.address.slice(-4)}`;
};
const getVerificationBadge = (): string | null => {
switch (verificationStatus.level) {
case EVerificationStatus.VERIFIED_OWNER:
return '🔑'; // Ordinal owner
case EVerificationStatus.VERIFIED_BASIC:
return '✅'; // Verified wallet
default:
if (verificationStatus.hasENS) return '🏷️'; // ENS
return null;
}
};
return {
// Base auth data
currentUser,
isAuthenticated,
isAuthenticating,
// Enhanced status
verificationStatus,
delegationInfo,
permissions,
// Helper functions
hasPermission,
getDisplayName,
getVerificationBadge,
};
}
// Export the enhanced hook as the main useAuth hook
export { useEnhancedAuth as useAuth };

View File

@ -23,7 +23,7 @@ export function useEnhancedUserDisplay(address: string): UserDisplayInfo {
callSign: null,
ensName: null,
ordinalDetails: null,
verificationLevel: EVerificationStatus.UNVERIFIED,
verificationLevel: EVerificationStatus.WALLET_UNCONNECTED,
displayPreference: null,
isLoading: true,
error: null,
@ -35,7 +35,7 @@ export function useEnhancedUserDisplay(address: string): UserDisplayInfo {
userVerificationStatus[address] || {
isVerified: false,
ensName: null,
verificationStatus: EVerificationStatus.UNVERIFIED,
verificationStatus: EVerificationStatus.WALLET_UNCONNECTED,
}
);
}, [userVerificationStatus, address]);
@ -63,7 +63,7 @@ export function useEnhancedUserDisplay(address: string): UserDisplayInfo {
ordinalDetails: null,
verificationLevel:
verificationInfo.verificationStatus ||
EVerificationStatus.UNVERIFIED,
EVerificationStatus.WALLET_UNCONNECTED,
displayPreference: null,
isLoading: false,
error: null,
@ -109,7 +109,7 @@ export function useEnhancedUserDisplay(address: string): UserDisplayInfo {
ordinalDetails: null,
verificationLevel:
verificationInfo.verificationStatus ||
EVerificationStatus.UNVERIFIED,
EVerificationStatus.WALLET_UNCONNECTED,
displayPreference: null,
isLoading: false,
error: null,
@ -125,7 +125,7 @@ export function useEnhancedUserDisplay(address: string): UserDisplayInfo {
callSign: null,
ensName: null,
ordinalDetails: null,
verificationLevel: EVerificationStatus.UNVERIFIED,
verificationLevel: EVerificationStatus.WALLET_UNCONNECTED,
displayPreference: null,
isLoading: false,
error: error instanceof Error ? error.message : 'Unknown error',

View File

@ -2,6 +2,7 @@ import { useMemo } from 'react';
import { useForum } from '@/contexts/useForum';
import { useAuth } from '@/contexts/useAuth';
import { Cell, Post, Comment, UserVerificationStatus } from '@/types/forum';
import { EVerificationStatus } from '@/types/identity';
export interface CellWithStats extends Cell {
postCount: number;
@ -96,8 +97,9 @@ export function useForumData(): ForumData {
if (!currentUser) return false;
return (
currentUser.verificationStatus === 'verified-owner' ||
currentUser.verificationStatus === 'verified-basic' ||
currentUser.verificationStatus ===
EVerificationStatus.ENS_ORDINAL_VERIFIED ||
currentUser.verificationStatus === EVerificationStatus.WALLET_CONNECTED ||
Boolean(currentUser.ensDetails) ||
Boolean(currentUser.ordinalDetails)
);

View File

@ -0,0 +1,159 @@
import { useMemo } from 'react';
import { useAuth } from './useAuth';
import { useForumData } from './useForumData';
import { EVerificationStatus } from '@/types/identity';
export interface Permission {
canPost: boolean;
canComment: boolean;
canVote: boolean;
canCreateCell: boolean;
canModerate: (cellId: string) => boolean;
canDelegate: boolean;
canUpdateProfile: boolean;
}
export interface PermissionReasons {
voteReason: string;
postReason: string;
commentReason: string;
createCellReason: string;
moderateReason: (cellId: string) => string;
}
export interface PermissionResult {
allowed: boolean;
reason: string;
}
/**
* Unified permission system with single source of truth for all permission logic
*/
export function usePermissions(): Permission &
PermissionReasons & {
checkPermission: (
action: keyof Permission,
cellId?: string
) => PermissionResult;
} {
const { currentUser, verificationStatus } = useAuth();
const { cellsWithStats } = useForumData();
// Single source of truth for all permission logic
const permissions = useMemo((): Permission => {
const isWalletConnected =
verificationStatus === EVerificationStatus.WALLET_CONNECTED;
const isVerified =
verificationStatus === EVerificationStatus.ENS_ORDINAL_VERIFIED;
return {
canPost: isWalletConnected || isVerified,
canComment: isWalletConnected || isVerified,
canVote: isWalletConnected || isVerified,
canCreateCell: isVerified, // Only ENS/Ordinal owners
canModerate: (cellId: string) => {
if (!currentUser || !cellId) return false;
// Check if user is the creator of the cell
const cell = cellsWithStats.find(c => c.id === cellId);
return cell ? cell.author === currentUser.address : false;
},
canDelegate: isWalletConnected || isVerified,
canUpdateProfile: Boolean(currentUser),
};
}, [currentUser, verificationStatus, cellsWithStats]);
// Single source of truth for permission reasons
const reasons = useMemo((): PermissionReasons => {
if (!currentUser) {
return {
voteReason: 'Connect your wallet to vote',
postReason: 'Connect your wallet to post',
commentReason: 'Connect your wallet to comment',
createCellReason: 'Connect your wallet to create cells',
moderateReason: () => 'Connect your wallet to moderate',
};
}
return {
voteReason: permissions.canVote
? 'You can vote'
: 'Verify ENS or Logos ordinal to vote',
postReason: permissions.canPost
? 'You can post'
: 'Verify ENS or Logos ordinal to post',
commentReason: permissions.canComment
? 'You can comment'
: 'Verify ENS or Logos ordinal to comment',
createCellReason: permissions.canCreateCell
? 'You can create cells'
: 'Verify ENS or Logos ordinal to create cells',
moderateReason: (cellId: string) => {
if (!cellId) return 'Cell ID required';
return permissions.canModerate(cellId)
? 'You can moderate this cell'
: 'Only cell creators can moderate';
},
};
}, [currentUser, verificationStatus, permissions]);
// Unified permission checker
const checkPermission = useMemo(() => {
return (action: keyof Permission, cellId?: string): PermissionResult => {
let allowed = false;
let reason = '';
switch (action) {
case 'canVote':
allowed = permissions.canVote;
reason = reasons.voteReason;
break;
case 'canPost':
allowed = permissions.canPost;
reason = reasons.postReason;
break;
case 'canComment':
allowed = permissions.canComment;
reason = reasons.commentReason;
break;
case 'canCreateCell':
allowed = permissions.canCreateCell;
reason = reasons.createCellReason;
break;
case 'canModerate':
allowed = cellId ? permissions.canModerate(cellId) : false;
reason = cellId ? reasons.moderateReason(cellId) : 'Cell ID required';
break;
case 'canDelegate':
allowed = permissions.canDelegate;
reason = allowed
? 'You can delegate keys'
: 'Connect your wallet to delegate keys';
break;
case 'canUpdateProfile':
allowed = permissions.canUpdateProfile;
reason = allowed
? 'You can update your profile'
: 'Connect your wallet to update profile';
break;
default:
allowed = false;
reason = 'Unknown permission';
}
return { allowed, reason };
};
}, [permissions, reasons]);
return {
...permissions,
...reasons,
checkPermission,
};
}

View File

@ -1,6 +1,7 @@
import { useMemo } from 'react';
import { useForumData, CellWithStats } from '@/hooks/core/useForumData';
import { useAuth } from '@/hooks/core/useEnhancedAuth';
import { useAuth } from '@/hooks/core/useAuth';
import { EVerificationStatus } from '@/types/identity';
export interface CellData extends CellWithStats {
posts: Array<{
@ -49,8 +50,10 @@ export function useCell(cellId: string | undefined): CellData | null {
: false;
const canModerate = isUserAdmin;
const canPost = currentUser
? currentUser.verificationStatus === 'verified-owner' ||
currentUser.verificationStatus === 'verified-basic' ||
? currentUser.verificationStatus ===
EVerificationStatus.ENS_ORDINAL_VERIFIED ||
currentUser.verificationStatus ===
EVerificationStatus.WALLET_CONNECTED ||
Boolean(currentUser.ensDetails) ||
Boolean(currentUser.ordinalDetails)
: false;

View File

@ -1,6 +1,6 @@
import { useMemo } from 'react';
import { useForumData, PostWithVoteStatus } from '@/hooks/core/useForumData';
import { useAuth } from '@/hooks/core/useEnhancedAuth';
import { useAuth } from '@/hooks/core/useAuth';
export interface CellPostsOptions {
includeModerated?: boolean;

View File

@ -4,7 +4,7 @@ import {
PostWithVoteStatus,
CommentWithVoteStatus,
} from '@/hooks/core/useForumData';
import { useAuth } from '@/hooks/core/useEnhancedAuth';
import { useAuth } from '@/hooks/core/useAuth';
export interface PostData extends PostWithVoteStatus {
cell: {

View File

@ -1,6 +1,6 @@
import { useMemo } from 'react';
import { useForumData, CommentWithVoteStatus } from '@/hooks/core/useForumData';
import { useAuth } from '@/hooks/core/useEnhancedAuth';
import { useAuth } from '@/hooks/core/useAuth';
export interface PostCommentsOptions {
includeModerated?: boolean;

View File

@ -1,6 +1,6 @@
import { useMemo } from 'react';
import { useForumData } from '@/hooks/core/useForumData';
import { useAuth } from '@/hooks/core/useEnhancedAuth';
import { useAuth } from '@/hooks/core/useAuth';
export interface UserVoteData {
// Vote status for specific items

View File

@ -11,12 +11,12 @@ export type {
CommentWithVoteStatus,
} from './core/useForumData';
export type { AuthState } from './core/useAuth';
export type {
Permission,
DetailedVerificationStatus,
DelegationInfo,
EnhancedAuthState,
} from './core/useEnhancedAuth';
PermissionReasons,
PermissionResult,
} from './core/usePermissions';
export type { UserDisplayInfo } from './core/useEnhancedUserDisplay';
@ -53,11 +53,7 @@ export { useAuthActions } from './actions/useAuthActions';
export type { AuthActionStates, AuthActions } from './actions/useAuthActions';
// Utility hooks
export { usePermissions } from './utilities/usePermissions';
export type {
PermissionCheck,
DetailedPermissions,
} from './utilities/usePermissions';
export { usePermissions } from './core/usePermissions';
export { useNetworkStatus } from './utilities/useNetworkStatus';
export type {

View File

@ -1,6 +1,7 @@
import { useMemo } from 'react';
import { useForum } from '@/contexts/useForum';
import { useAuth } from '@/hooks/core/useEnhancedAuth';
import { useAuth } from '@/hooks/core/useAuth';
import { useAuth as useAuthContext } from '@/contexts/useAuth';
export interface NetworkHealth {
isConnected: boolean;
@ -61,7 +62,9 @@ export function useNetworkStatus(): NetworkStatusData {
const { isNetworkConnected, isInitialLoading, isRefreshing, error } =
useForum();
const { isAuthenticated, delegationInfo, currentUser } = useAuth();
const { isAuthenticated, currentUser } = useAuth();
const { getDelegationStatus } = useAuthContext();
const delegationInfo = getDelegationStatus();
// Network health assessment
const health = useMemo((): NetworkHealth => {
@ -75,7 +78,7 @@ export function useNetworkStatus(): NetworkStatusData {
issues.push(`Forum error: ${error}`);
}
if (isAuthenticated && delegationInfo.isExpired) {
if (isAuthenticated && !delegationInfo.isValid) {
issues.push('Key delegation expired');
}
@ -90,7 +93,7 @@ export function useNetworkStatus(): NetworkStatusData {
syncAge,
issues,
};
}, [isNetworkConnected, error, isAuthenticated, delegationInfo.isExpired]);
}, [isNetworkConnected, error, isAuthenticated, delegationInfo.isValid]);
// Sync status
const sync = useMemo((): SyncStatus => {
@ -121,13 +124,9 @@ export function useNetworkStatus(): NetworkStatusData {
status: isAuthenticated ? 'connected' : 'disconnected',
},
delegation: {
active: delegationInfo.isActive,
expires: delegationInfo.expiresAt,
status: delegationInfo.isActive
? 'active'
: delegationInfo.isExpired
? 'expired'
: 'none',
active: delegationInfo.isValid,
expires: delegationInfo.timeRemaining || null,
status: delegationInfo.isValid ? 'active' : 'expired',
},
};
}, [isNetworkConnected, isAuthenticated, currentUser, delegationInfo]);
@ -135,7 +134,7 @@ export function useNetworkStatus(): NetworkStatusData {
// Status assessment
const canRefresh = !isRefreshing && !isInitialLoading;
const canSync = isNetworkConnected && !isRefreshing;
const needsAttention = !health.isHealthy || delegationInfo.needsRenewal;
const needsAttention = !health.isHealthy || !delegationInfo.isValid;
// Helper methods
const getStatusMessage = useMemo(() => {
@ -158,16 +157,10 @@ export function useNetworkStatus(): NetworkStatusData {
const getHealthColor = useMemo(() => {
return (): 'green' | 'yellow' | 'red' => {
if (!isNetworkConnected || error) return 'red';
if (health.issues.length > 0 || delegationInfo.needsRenewal)
return 'yellow';
if (health.issues.length > 0 || !delegationInfo.isValid) return 'yellow';
return 'green';
};
}, [
isNetworkConnected,
error,
health.issues.length,
delegationInfo.needsRenewal,
]);
}, [isNetworkConnected, error, health.issues.length, delegationInfo.isValid]);
const getRecommendedActions = useMemo(() => {
return (): string[] => {
@ -182,11 +175,15 @@ export function useNetworkStatus(): NetworkStatusData {
actions.push('Connect your wallet');
}
if (delegationInfo.isExpired) {
if (!delegationInfo.isValid) {
actions.push('Renew key delegation');
}
if (delegationInfo.needsRenewal && !delegationInfo.isExpired) {
if (
delegationInfo.isValid &&
delegationInfo.timeRemaining &&
delegationInfo.timeRemaining < 3600
) {
actions.push('Consider renewing key delegation soon');
}

View File

@ -1,254 +0,0 @@
import { useMemo } from 'react';
import { useAuth } from '@/hooks/core/useEnhancedAuth';
import { useForumData } from '@/hooks/core/useForumData';
export interface PermissionCheck {
canVote: boolean;
canPost: boolean;
canComment: boolean;
canCreateCell: boolean;
canModerate: (cellId: string) => boolean;
canModeratePosts: (cellId: string) => boolean;
canModerateComments: (cellId: string) => boolean;
canModerateUsers: (cellId: string) => boolean;
canUpdateProfile: boolean;
canDelegate: boolean;
}
export interface DetailedPermissions extends PermissionCheck {
// Permission reasons (why user can/cannot do something)
voteReason: string;
postReason: string;
commentReason: string;
createCellReason: string;
moderateReason: (cellId: string) => string;
// Helper methods
checkPermission: (
action: keyof PermissionCheck,
cellId?: string
) => {
allowed: boolean;
reason: string;
};
// Verification requirements
requiresVerification: (action: keyof PermissionCheck) => boolean;
requiresOrdinal: (action: keyof PermissionCheck) => boolean;
requiresENS: (action: keyof PermissionCheck) => boolean;
}
/**
* Hook for checking user permissions with detailed reasons
*/
export function usePermissions(): DetailedPermissions {
const { currentUser, verificationStatus, permissions } = useAuth();
const { cellsWithStats } = useForumData();
const permissionReasons = useMemo(() => {
if (!currentUser) {
return {
voteReason: 'Connect your wallet to vote',
postReason: 'Connect your wallet to post',
commentReason: 'Connect your wallet to comment',
createCellReason: 'Connect your wallet to create cells',
};
}
const hasOrdinal = verificationStatus.hasOrdinal;
const hasENS = verificationStatus.hasENS;
const isVerified = verificationStatus.level !== 'unverified';
return {
voteReason: permissions.canVote
? 'You can vote'
: !isVerified
? 'Verify your wallet to vote'
: !hasOrdinal && !hasENS
? 'Acquire an Ordinal or ENS domain to vote'
: 'Voting not available',
postReason: permissions.canPost
? 'You can post'
: !hasOrdinal
? 'Acquire an Ordinal to post'
: verificationStatus.level !== 'verified-owner'
? 'Verify Ordinal ownership to post'
: 'Posting not available',
commentReason: permissions.canComment
? 'You can comment'
: !hasOrdinal
? 'Acquire an Ordinal to comment'
: verificationStatus.level !== 'verified-owner'
? 'Verify Ordinal ownership to comment'
: 'Commenting not available',
createCellReason: permissions.canCreateCell
? 'You can create cells'
: !hasOrdinal
? 'Acquire an Ordinal to create cells'
: verificationStatus.level !== 'verified-owner'
? 'Verify Ordinal ownership to create cells'
: 'Cell creation not available',
};
}, [currentUser, verificationStatus, permissions]);
const canModerate = useMemo(() => {
return (cellId: string): boolean => {
if (!currentUser || !cellId) return false;
const cell = cellsWithStats.find(c => c.id === cellId);
return cell ? currentUser.address === cell.signature : false;
};
}, [currentUser, cellsWithStats]);
const moderateReason = useMemo(() => {
return (cellId: string): string => {
if (!currentUser) return 'Connect your wallet to moderate';
if (!cellId) return 'Invalid cell';
const cell = cellsWithStats.find(c => c.id === cellId);
if (!cell) return 'Cell not found';
return currentUser.address === cell.signature
? 'You can moderate this cell'
: 'Only cell owners can moderate';
};
}, [currentUser, cellsWithStats]);
const checkPermission = useMemo(() => {
return (action: keyof PermissionCheck, cellId?: string) => {
let allowed = false;
let reason = '';
switch (action) {
case 'canVote':
allowed = permissions.canVote;
reason = permissionReasons.voteReason;
break;
case 'canPost':
allowed = permissions.canPost;
reason = permissionReasons.postReason;
break;
case 'canComment':
allowed = permissions.canComment;
reason = permissionReasons.commentReason;
break;
case 'canCreateCell':
allowed = permissions.canCreateCell;
reason = permissionReasons.createCellReason;
break;
case 'canModerate':
case 'canModeratePosts':
case 'canModerateComments':
case 'canModerateUsers':
allowed = cellId ? canModerate(cellId) : false;
reason = cellId ? moderateReason(cellId) : 'Cell ID required';
break;
case 'canUpdateProfile':
allowed = permissions.canUpdateProfile;
reason = allowed
? 'You can update your profile'
: 'Connect your wallet to update profile';
break;
case 'canDelegate':
allowed = permissions.canDelegate;
reason = allowed
? 'You can delegate keys'
: 'Verify your wallet to delegate keys';
break;
default:
allowed = false;
reason = 'Unknown permission';
}
return { allowed, reason };
};
}, [permissions, permissionReasons, canModerate, moderateReason]);
const requiresVerification = useMemo(() => {
return (action: keyof PermissionCheck): boolean => {
switch (action) {
case 'canVote':
case 'canDelegate':
return true;
case 'canPost':
case 'canComment':
case 'canCreateCell':
case 'canModerate':
case 'canModeratePosts':
case 'canModerateComments':
case 'canModerateUsers':
return true;
case 'canUpdateProfile':
return false;
default:
return false;
}
};
}, []);
const requiresOrdinal = useMemo(() => {
return (action: keyof PermissionCheck): boolean => {
switch (action) {
case 'canPost':
case 'canComment':
case 'canCreateCell':
case 'canModerate':
case 'canModeratePosts':
case 'canModerateComments':
case 'canModerateUsers':
return true;
default:
return false;
}
};
}, []);
const requiresENS = useMemo(() => {
return (action: keyof PermissionCheck): boolean => {
// ENS can substitute for some Ordinal requirements for voting
switch (action) {
case 'canVote':
return !verificationStatus.hasOrdinal; // ENS can substitute for voting if no Ordinal
default:
return false;
}
};
}, [verificationStatus.hasOrdinal]);
return {
// Basic permissions
canVote: permissions.canVote,
canPost: permissions.canPost,
canComment: permissions.canComment,
canCreateCell: permissions.canCreateCell,
canModerate,
canModeratePosts: canModerate,
canModerateComments: canModerate,
canModerateUsers: canModerate,
canUpdateProfile: permissions.canUpdateProfile,
canDelegate: permissions.canDelegate,
// Reasons
voteReason: permissionReasons.voteReason,
postReason: permissionReasons.postReason,
commentReason: permissionReasons.commentReason,
createCellReason: permissionReasons.createCellReason,
moderateReason,
// Helper methods
checkPermission,
requiresVerification,
requiresOrdinal,
requiresENS,
};
}

View File

@ -176,7 +176,7 @@ export class LocalDatabase {
callSign,
displayPreference,
lastUpdated: timestamp,
verificationStatus: EVerificationStatus.UNVERIFIED,
verificationStatus: EVerificationStatus.WALLET_UNCONNECTED,
};
// Persist with address keyPath
this.put(STORE.USER_IDENTITIES, {

View File

@ -30,6 +30,42 @@ export class ForumActions {
this.delegationManager = delegationManager || new DelegationManager();
}
/**
* Unified permission validation system
*/
private validatePermission(
action: 'createCell' | 'createPost' | 'createComment' | 'vote',
currentUser: User | null,
_isAuthenticated: boolean
): { valid: boolean; error?: string } {
const verificationStatus =
currentUser?.verificationStatus || EVerificationStatus.WALLET_UNCONNECTED;
switch (action) {
case 'createCell':
if (verificationStatus !== EVerificationStatus.ENS_ORDINAL_VERIFIED) {
return {
valid: false,
error: 'Only ENS or Logos ordinal owners can create cells',
};
}
break;
case 'createPost':
case 'createComment':
case 'vote':
if (verificationStatus === EVerificationStatus.WALLET_UNCONNECTED) {
return {
valid: false,
error: 'Connect your wallet to perform this action',
};
}
break;
}
return { valid: true };
}
/* ------------------------------------------------------------------
POST / COMMENT / CELL CREATION
-------------------------------------------------------------------*/
@ -40,32 +76,15 @@ export class ForumActions {
): Promise<ActionResult<Post>> {
const { cellId, title, content, currentUser, isAuthenticated } = params;
if (!isAuthenticated || !currentUser) {
return {
success: false,
error:
'Authentication required. You need to connect your wallet to post.',
};
}
// Check if user has basic verification or better, or owns ENS/Ordinal
const hasENSOrOrdinal = !!(
currentUser.ensDetails || currentUser.ordinalDetails
const validation = this.validatePermission(
'createPost',
currentUser,
isAuthenticated
);
const isVerified =
currentUser.verificationStatus === EVerificationStatus.VERIFIED_OWNER ||
currentUser.verificationStatus === EVerificationStatus.VERIFIED_BASIC ||
hasENSOrOrdinal;
if (
!isVerified &&
(currentUser.verificationStatus === EVerificationStatus.UNVERIFIED ||
currentUser.verificationStatus === EVerificationStatus.VERIFYING)
) {
if (!validation.valid) {
return {
success: false,
error:
'Verification required. Please complete wallet verification to post.',
error: validation.error!,
};
}
@ -78,14 +97,14 @@ export class ForumActions {
title,
content,
timestamp: Date.now(),
author: currentUser.address,
author: currentUser!.address, // Safe after validation
};
const signed = this.delegationManager.signMessage(unsignedPost);
if (!signed) {
const status = this.delegationManager.getStatus(
currentUser.address,
currentUser.walletType
currentUser!.address,
currentUser!.walletType
);
return {
success: false,
@ -125,32 +144,16 @@ export class ForumActions {
): Promise<ActionResult<Comment>> {
const { postId, content, currentUser, isAuthenticated } = params;
if (!isAuthenticated || !currentUser) {
return {
success: false,
error:
'Authentication required. You need to connect your wallet to comment.',
};
}
// Check if user has basic verification or better, or owns ENS/Ordinal
const hasENSOrOrdinal = !!(
currentUser.ensDetails || currentUser.ordinalDetails
// Use unified validation
const validation = this.validatePermission(
'createComment',
currentUser,
isAuthenticated
);
const isVerified =
currentUser.verificationStatus === EVerificationStatus.VERIFIED_OWNER ||
currentUser.verificationStatus === EVerificationStatus.VERIFIED_BASIC ||
hasENSOrOrdinal;
if (
!isVerified &&
(currentUser.verificationStatus === EVerificationStatus.UNVERIFIED ||
currentUser.verificationStatus === EVerificationStatus.VERIFYING)
) {
if (!validation.valid) {
return {
success: false,
error:
'Verification required. Please complete wallet verification to comment.',
error: validation.error!,
};
}
@ -162,15 +165,15 @@ export class ForumActions {
postId,
content,
timestamp: Date.now(),
author: currentUser.address,
author: currentUser!.address,
};
// Optimistic path: sign locally, write to cache, mark pending, render immediately
const signed = this.delegationManager.signMessage(unsignedComment);
if (!signed) {
const status = this.delegationManager.getStatus(
currentUser.address,
currentUser.walletType
currentUser!.address,
currentUser!.walletType
);
return {
success: false,
@ -213,11 +216,16 @@ export class ForumActions {
): Promise<ActionResult<Cell>> {
const { name, description, icon, currentUser, isAuthenticated } = params;
if (!isAuthenticated || !currentUser) {
// Use unified validation
const validation = this.validatePermission(
'createCell',
currentUser,
isAuthenticated
);
if (!validation.valid) {
return {
success: false,
error:
'Authentication required. You need to verify Ordinal ownership to create a cell.',
error: validation.error!,
};
}
@ -230,14 +238,14 @@ export class ForumActions {
description,
...(icon && { icon }),
timestamp: Date.now(),
author: currentUser.address,
author: currentUser!.address,
};
const signed = this.delegationManager.signMessage(unsignedCell);
if (!signed) {
const status = this.delegationManager.getStatus(
currentUser.address,
currentUser.walletType
currentUser!.address,
currentUser!.walletType
);
return {
success: false,
@ -281,32 +289,16 @@ export class ForumActions {
): Promise<ActionResult<boolean>> {
const { targetId, isUpvote, currentUser, isAuthenticated } = params;
if (!isAuthenticated || !currentUser) {
return {
success: false,
error:
'Authentication required. You need to connect your wallet to vote.',
};
}
// Check if user has basic verification or better, or owns ENS/Ordinal
const hasENSOrOrdinal = !!(
currentUser.ensDetails || currentUser.ordinalDetails
// Use unified validation
const validation = this.validatePermission(
'vote',
currentUser,
isAuthenticated
);
const isVerified =
currentUser.verificationStatus === EVerificationStatus.VERIFIED_OWNER ||
currentUser.verificationStatus === EVerificationStatus.VERIFIED_BASIC ||
hasENSOrOrdinal;
if (
!isVerified &&
(currentUser.verificationStatus === EVerificationStatus.UNVERIFIED ||
currentUser.verificationStatus === EVerificationStatus.VERIFYING)
) {
if (!validation.valid) {
return {
success: false,
error:
'Verification required. Please complete wallet verification to vote.',
error: validation.error!,
};
}
@ -318,14 +310,14 @@ export class ForumActions {
targetId,
value: isUpvote ? 1 : -1,
timestamp: Date.now(),
author: currentUser.address,
author: currentUser!.address,
};
const signed = this.delegationManager.signMessage(unsignedVote);
if (!signed) {
const status = this.delegationManager.getStatus(
currentUser.address,
currentUser.walletType
currentUser!.address,
currentUser!.walletType
);
return {
success: false,
@ -389,14 +381,14 @@ export class ForumActions {
targetId: postId,
reason,
timestamp: Date.now(),
author: currentUser.address,
author: currentUser!.address,
};
const signed = this.delegationManager.signMessage(unsignedMod);
if (!signed) {
const status = this.delegationManager.getStatus(
currentUser.address,
currentUser.walletType
currentUser!.address,
currentUser!.walletType
);
return {
success: false,
@ -462,14 +454,14 @@ export class ForumActions {
targetId: commentId,
reason,
timestamp: Date.now(),
author: currentUser.address,
author: currentUser!.address,
};
const signed = this.delegationManager.signMessage(unsignedMod);
if (!signed) {
const status = this.delegationManager.getStatus(
currentUser.address,
currentUser.walletType
currentUser!.address,
currentUser!.walletType
);
return {
success: false,
@ -534,15 +526,15 @@ export class ForumActions {
targetType: 'user',
targetId: userAddress,
reason,
author: currentUser.address,
author: currentUser!.address,
timestamp: Date.now(),
};
const signed = this.delegationManager.signMessage(unsignedMod);
if (!signed) {
const status = this.delegationManager.getStatus(
currentUser.address,
currentUser.walletType
currentUser!.address,
currentUser!.walletType
);
return {
success: false,

View File

@ -5,7 +5,7 @@ import {
RelevanceScoreDetails,
UserVerificationStatus,
} from '@/types/forum';
import { User } from '@/types/identity';
import { EVerificationStatus, User } from '@/types/identity';
import { VoteMessage } from '@/types/waku';
export class RelevanceCalculator {
@ -225,7 +225,7 @@ export class RelevanceCalculator {
return !!(
user.ensDetails ||
user.ordinalDetails ||
user.verificationStatus === 'verified-basic'
user.verificationStatus === EVerificationStatus.WALLET_CONNECTED
);
}
@ -286,10 +286,15 @@ export class RelevanceCalculator {
// Apply different bonuses based on verification level
let bonus = 0;
if (authorStatus?.verificationStatus === 'verified-owner') {
if (
authorStatus?.verificationStatus ===
EVerificationStatus.ENS_ORDINAL_VERIFIED
) {
// Full bonus for ENS/Ordinal owners
bonus = score * (RelevanceCalculator.VERIFICATION_BONUS - 1);
} else if (authorStatus?.verificationStatus === 'verified-basic') {
} else if (
authorStatus?.verificationStatus === EVerificationStatus.WALLET_CONNECTED
) {
// Lower bonus for basic verified users
bonus = score * (RelevanceCalculator.BASIC_VERIFICATION_BONUS - 1);
}

View File

@ -81,7 +81,7 @@ describe('RelevanceCalculator', () => {
const verifiedUser: User = {
address: 'user1',
walletType: 'ethereum',
verificationStatus: EVerificationStatus.VERIFIED_OWNER,
verificationStatus: EVerificationStatus.ENS_ORDINAL_VERIFIED,
displayPreference: EDisplayPreference.WALLET_ADDRESS,
ensDetails: {
ensName: 'test.eth',
@ -98,7 +98,7 @@ describe('RelevanceCalculator', () => {
const verifiedUser: User = {
address: 'user3',
walletType: 'bitcoin',
verificationStatus: EVerificationStatus.VERIFIED_OWNER,
verificationStatus: EVerificationStatus.ENS_ORDINAL_VERIFIED,
displayPreference: EDisplayPreference.WALLET_ADDRESS,
ordinalDetails: {
ordinalId: '1',
@ -115,7 +115,7 @@ describe('RelevanceCalculator', () => {
const unverifiedUser: User = {
address: 'user2',
walletType: 'ethereum',
verificationStatus: EVerificationStatus.UNVERIFIED,
verificationStatus: EVerificationStatus.WALLET_UNCONNECTED,
displayPreference: EDisplayPreference.WALLET_ADDRESS,
ensDetails: undefined,
ordinalDetails: undefined,
@ -281,7 +281,7 @@ describe('RelevanceCalculator', () => {
{
address: 'user1',
walletType: 'ethereum',
verificationStatus: EVerificationStatus.VERIFIED_OWNER,
verificationStatus: EVerificationStatus.ENS_ORDINAL_VERIFIED,
displayPreference: EDisplayPreference.WALLET_ADDRESS,
ensDetails: {
ensName: 'test.eth',
@ -292,7 +292,7 @@ describe('RelevanceCalculator', () => {
{
address: 'user2',
walletType: 'bitcoin',
verificationStatus: EVerificationStatus.UNVERIFIED,
verificationStatus: EVerificationStatus.WALLET_UNCONNECTED,
displayPreference: EDisplayPreference.WALLET_ADDRESS,
ensDetails: undefined,
ordinalDetails: undefined,

View File

@ -223,9 +223,9 @@ export class UserIdentityService {
// Default verification status based on what we can resolve
let verificationStatus: EVerificationStatus =
EVerificationStatus.UNVERIFIED;
EVerificationStatus.WALLET_UNCONNECTED;
if (ensName || ordinalDetails) {
verificationStatus = EVerificationStatus.VERIFIED_OWNER;
verificationStatus = EVerificationStatus.ENS_ORDINAL_VERIFIED;
}
return {
@ -300,7 +300,7 @@ export class UserIdentityService {
? EDisplayPreference.CALL_SIGN
: EDisplayPreference.WALLET_ADDRESS,
lastUpdated: timestamp,
verificationStatus: EVerificationStatus.UNVERIFIED,
verificationStatus: EVerificationStatus.WALLET_UNCONNECTED,
};
}
@ -324,13 +324,13 @@ export class UserIdentityService {
private mapVerificationStatus(status: string): EVerificationStatus {
switch (status) {
case 'verified-basic':
return EVerificationStatus.VERIFIED_BASIC;
return EVerificationStatus.WALLET_CONNECTED;
case 'verified-owner':
return EVerificationStatus.VERIFIED_OWNER;
return EVerificationStatus.ENS_ORDINAL_VERIFIED;
case 'verifying':
return EVerificationStatus.VERIFYING;
return EVerificationStatus.WALLET_CONNECTED; // Temporary state during verification
default:
return EVerificationStatus.UNVERIFIED;
return EVerificationStatus.WALLET_UNCONNECTED;
}
}

View File

@ -164,8 +164,8 @@ const FeedPage: React.FC = () => {
<p className="text-cyber-neutral">
Be the first to create a post in a cell!
</p>
{verificationStatus.level !==
EVerificationStatus.VERIFIED_OWNER && (
{verificationStatus !==
EVerificationStatus.ENS_ORDINAL_VERIFIED && (
<p className="text-sm text-cyber-neutral/80">
Connect your wallet and verify Ordinal ownership to
start posting

View File

@ -1,5 +1,6 @@
import { useState, useEffect } from 'react';
import { useAuth, useUserActions, useForumActions } from '@/hooks';
import { useAuth as useAuthContext } from '@/contexts/useAuth';
import { useUserDisplay } from '@/hooks';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
@ -34,7 +35,9 @@ export default function ProfilePage() {
const { toast } = useToast();
// Get current user from auth context for the address
const { currentUser, delegationInfo } = useAuth();
const { currentUser } = useAuth();
const { getDelegationStatus } = useAuthContext();
const delegationInfo = getDelegationStatus();
const address = currentUser?.address;
// Get comprehensive user information from the unified hook
@ -137,11 +140,11 @@ export default function ProfilePage() {
const getVerificationIcon = () => {
switch (userInfo.verificationLevel) {
case EVerificationStatus.VERIFIED_OWNER:
case EVerificationStatus.ENS_ORDINAL_VERIFIED:
return <CheckCircle className="h-4 w-4 text-green-500" />;
case EVerificationStatus.VERIFIED_BASIC:
case EVerificationStatus.WALLET_CONNECTED:
return <Shield className="h-4 w-4 text-blue-500" />;
case EVerificationStatus.UNVERIFIED:
case EVerificationStatus.WALLET_UNCONNECTED:
return <AlertTriangle className="h-4 w-4 text-yellow-500" />;
default:
return <XCircle className="h-4 w-4 text-red-500" />;
@ -150,12 +153,12 @@ export default function ProfilePage() {
const getVerificationText = () => {
switch (userInfo.verificationLevel) {
case EVerificationStatus.VERIFIED_OWNER:
return 'Fully Verified';
case EVerificationStatus.VERIFIED_BASIC:
return 'Basic Verification';
case EVerificationStatus.UNVERIFIED:
return 'Unverified';
case EVerificationStatus.ENS_ORDINAL_VERIFIED:
return 'Owns ENS or Ordinal';
case EVerificationStatus.WALLET_CONNECTED:
return 'Connected Wallet';
case EVerificationStatus.WALLET_UNCONNECTED:
return 'Unconnected Wallet';
default:
return 'Unknown';
}
@ -163,11 +166,11 @@ export default function ProfilePage() {
const getVerificationColor = () => {
switch (userInfo.verificationLevel) {
case EVerificationStatus.VERIFIED_OWNER:
case EVerificationStatus.ENS_ORDINAL_VERIFIED:
return 'bg-green-100 text-green-800 border-green-200';
case EVerificationStatus.VERIFIED_BASIC:
case EVerificationStatus.WALLET_CONNECTED:
return 'bg-blue-100 text-blue-800 border-blue-200';
case EVerificationStatus.UNVERIFIED:
case EVerificationStatus.WALLET_UNCONNECTED:
return 'bg-yellow-100 text-yellow-800 border-yellow-200';
default:
return 'bg-gray-100 text-gray-800 border-gray-200';
@ -337,26 +340,31 @@ export default function ProfilePage() {
<div className="space-y-4">
{/* Delegation Status */}
<div className="flex items-center gap-3">
<Badge
variant={delegationInfo.isActive ? "default" : "secondary"}
className={delegationInfo.isActive ? "bg-green-600 hover:bg-green-700" : ""}
<Badge
variant={delegationInfo.isValid ? 'default' : 'secondary'}
className={
delegationInfo.isValid
? 'bg-green-600 hover:bg-green-700'
: ''
}
>
{delegationInfo.isActive ? 'Active' : 'Inactive'}
{delegationInfo.isValid ? 'Active' : 'Inactive'}
</Badge>
{delegationInfo.isActive && delegationInfo.timeRemaining && (
{delegationInfo.isValid && delegationInfo.timeRemaining && (
<span className="text-sm text-muted-foreground">
{delegationInfo.timeRemaining} remaining
</span>
)}
{delegationInfo.needsRenewal && !delegationInfo.isExpired && (
<Badge variant="outline" className="text-yellow-600 border-yellow-600">
{!delegationInfo.isValid && (
<Badge
variant="outline"
className="text-yellow-600 border-yellow-600"
>
Renewal Recommended
</Badge>
)}
{delegationInfo.isExpired && (
<Badge variant="destructive">
Expired
</Badge>
{!delegationInfo.isValid && (
<Badge variant="destructive">Expired</Badge>
)}
</div>
@ -367,19 +375,22 @@ export default function ProfilePage() {
Browser Public Key
</Label>
<div className="mt-1 text-sm font-mono bg-muted px-3 py-2 rounded-md break-all">
{currentUser.browserPubKey ? (
`${currentUser.browserPubKey.slice(0, 12)}...${currentUser.browserPubKey.slice(-8)}`
) : 'Not delegated'}
{currentUser.browserPubKey
? `${currentUser.browserPubKey.slice(0, 12)}...${currentUser.browserPubKey.slice(-8)}`
: 'Not delegated'}
</div>
</div>
<div>
<Label className="text-sm font-medium text-muted-foreground">
Delegation Signature
</Label>
<div className="mt-1 text-sm">
{currentUser.delegationSignature === 'valid' ? (
<Badge variant="outline" className="text-green-600 border-green-600">
<Badge
variant="outline"
className="text-green-600 border-green-600"
>
Valid
</Badge>
) : (
@ -404,10 +415,9 @@ export default function ProfilePage() {
Last Updated
</Label>
<div className="mt-1 text-sm">
{currentUser.lastChecked ?
new Date(currentUser.lastChecked).toLocaleString() :
'Never'
}
{currentUser.lastChecked
? new Date(currentUser.lastChecked).toLocaleString()
: 'Never'}
</div>
</div>
@ -416,12 +426,18 @@ export default function ProfilePage() {
Can Delegate
</Label>
<div className="mt-1 text-sm">
{delegationInfo.canDelegate ? (
<Badge variant="outline" className="text-green-600 border-green-600">
{delegationInfo.hasDelegation ? (
<Badge
variant="outline"
className="text-green-600 border-green-600"
>
Yes
</Badge>
) : (
<Badge variant="outline" className="text-red-600 border-red-600">
<Badge
variant="outline"
className="text-red-600 border-red-600"
>
No
</Badge>
)}
@ -430,14 +446,16 @@ export default function ProfilePage() {
</div>
{/* Delegation Actions */}
{delegationInfo.canDelegate && (
{delegationInfo.hasDelegation && (
<div className="pt-2">
<Button
variant="outline"
<Button
variant="outline"
size="sm"
onClick={() => setWalletWizardOpen(true)}
>
{delegationInfo.isActive ? 'Renew Delegation' : 'Delegate Key'}
{delegationInfo.isValid
? 'Renew Delegation'
: 'Delegate Key'}
</Button>
</div>
)}

View File

@ -19,10 +19,9 @@ export type User = {
};
export enum EVerificationStatus {
UNVERIFIED = 'unverified',
VERIFIED_BASIC = 'verified-basic',
VERIFIED_OWNER = 'verified-owner',
VERIFYING = 'verifying',
WALLET_UNCONNECTED = 'wallet-unconnected',
WALLET_CONNECTED = 'wallet-connected',
ENS_ORDINAL_VERIFIED = 'ens-ordinal-verified',
}
export interface OrdinalDetails {