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

View File

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

View File

@ -8,6 +8,7 @@ import {
useUserVotes, useUserVotes,
useAuth, useAuth,
} from '@/hooks'; } from '@/hooks';
import { EVerificationStatus } from '@/types/identity';
import { Button } from '@/components/ui/button'; import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input'; import { Input } from '@/components/ui/input';
import { Textarea } from '@/components/ui/textarea'; import { Textarea } from '@/components/ui/textarea';
@ -218,7 +219,8 @@ const PostList = () => {
</div> </div>
)} )}
{!canPost && verificationStatus.level === 'verified-basic' && ( {!canPost &&
verificationStatus === EVerificationStatus.WALLET_CONNECTED && (
<div className="mb-8 p-4 border border-cyber-muted rounded-sm bg-cyber-muted/20"> <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"> <div className="flex items-center gap-2 mb-2">
<Eye className="w-4 h-4 text-cyber-neutral" /> <Eye className="w-4 h-4 text-cyber-neutral" />

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,8 +1,9 @@
import { useCallback, useState } from 'react'; import { useCallback, useState } from 'react';
import { useAuth } from '@/hooks/core/useEnhancedAuth'; import { useAuth } from '@/hooks/core/useAuth';
import { DelegationDuration } from '@/lib/delegation'; import { DelegationDuration } from '@/lib/delegation';
import { useToast } from '@/components/ui/use-toast'; import { useToast } from '@/components/ui/use-toast';
import { useAuth as useAuthContext } from '@/contexts/useAuth'; import { useAuth as useAuthContext } from '@/contexts/useAuth';
import { EVerificationStatus } from '@/types/identity';
export interface AuthActionStates { export interface AuthActionStates {
isConnecting: boolean; isConnecting: boolean;
@ -32,14 +33,13 @@ export interface AuthActions extends AuthActionStates {
* Hook for authentication and verification actions * Hook for authentication and verification actions
*/ */
export function useAuthActions(): AuthActions { export function useAuthActions(): AuthActions {
const { const { isAuthenticated, isAuthenticating, verificationStatus } = useAuth();
isAuthenticated,
isAuthenticating,
delegationInfo,
verificationStatus,
} = useAuth();
const { verifyOwnership, delegateKey: delegateKeyFromContext } = useAuthContext(); const {
verifyOwnership,
delegateKey: delegateKeyFromContext,
getDelegationStatus,
} = useAuthContext();
const { toast } = useToast(); const { toast } = useToast();
const [isConnecting, setIsConnecting] = useState(false); const [isConnecting, setIsConnecting] = useState(false);
@ -145,7 +145,7 @@ export function useAuthActions(): AuthActions {
return false; return false;
} }
if (verificationStatus.level !== 'unverified') { if (verificationStatus !== EVerificationStatus.WALLET_UNCONNECTED) {
toast({ toast({
title: 'Already Verified', title: 'Already Verified',
description: 'Your wallet is already verified.', description: 'Your wallet is already verified.',
@ -184,7 +184,7 @@ export function useAuthActions(): AuthActions {
} finally { } finally {
setIsVerifying(false); setIsVerifying(false);
} }
}, [isAuthenticated, verificationStatus.level, verifyOwnership, toast]); }, [isAuthenticated, verificationStatus, verifyOwnership, toast]);
// Delegate key // Delegate key
const delegateKey = useCallback( const delegateKey = useCallback(
@ -198,7 +198,7 @@ export function useAuthActions(): AuthActions {
return false; return false;
} }
if (verificationStatus.level === 'unverified') { if (verificationStatus === EVerificationStatus.WALLET_UNCONNECTED) {
toast({ toast({
title: 'Verification Required', title: 'Verification Required',
description: 'Please verify your wallet before delegating keys.', description: 'Please verify your wallet before delegating keys.',
@ -240,12 +240,13 @@ export function useAuthActions(): AuthActions {
setIsDelegating(false); setIsDelegating(false);
} }
}, },
[isAuthenticated, verificationStatus.level, delegateKeyFromContext, toast] [isAuthenticated, verificationStatus, delegateKeyFromContext, toast]
); );
// Clear delegation // Clear delegation
const clearDelegation = useCallback(async (): Promise<boolean> => { const clearDelegation = useCallback(async (): Promise<boolean> => {
if (!delegationInfo.isActive) { const delegationInfo = getDelegationStatus();
if (!delegationInfo.isValid) {
toast({ toast({
title: 'No Active Delegation', title: 'No Active Delegation',
description: 'There is no active key delegation to clear.', description: 'There is no active key delegation to clear.',
@ -271,7 +272,7 @@ export function useAuthActions(): AuthActions {
}); });
return false; return false;
} }
}, [delegationInfo.isActive, toast]); }, [getDelegationStatus, toast]);
// Renew delegation // Renew delegation
const renewDelegation = useCallback( const renewDelegation = useCallback(

View File

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

View File

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

View File

@ -1,8 +1,56 @@
// Re-export the enhanced auth hook as the main useAuth import { useAuth as useBaseAuth } from '@/contexts/useAuth';
export { useEnhancedAuth as useAuth } from './useEnhancedAuth'; import { User, EVerificationStatus } from '@/types/identity';
export type {
Permission, export interface AuthState {
DetailedVerificationStatus, currentUser: User | null;
DelegationInfo, isAuthenticated: boolean;
EnhancedAuthState, isAuthenticating: boolean;
} from './useEnhancedAuth'; 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, callSign: null,
ensName: null, ensName: null,
ordinalDetails: null, ordinalDetails: null,
verificationLevel: EVerificationStatus.UNVERIFIED, verificationLevel: EVerificationStatus.WALLET_UNCONNECTED,
displayPreference: null, displayPreference: null,
isLoading: true, isLoading: true,
error: null, error: null,
@ -35,7 +35,7 @@ export function useEnhancedUserDisplay(address: string): UserDisplayInfo {
userVerificationStatus[address] || { userVerificationStatus[address] || {
isVerified: false, isVerified: false,
ensName: null, ensName: null,
verificationStatus: EVerificationStatus.UNVERIFIED, verificationStatus: EVerificationStatus.WALLET_UNCONNECTED,
} }
); );
}, [userVerificationStatus, address]); }, [userVerificationStatus, address]);
@ -63,7 +63,7 @@ export function useEnhancedUserDisplay(address: string): UserDisplayInfo {
ordinalDetails: null, ordinalDetails: null,
verificationLevel: verificationLevel:
verificationInfo.verificationStatus || verificationInfo.verificationStatus ||
EVerificationStatus.UNVERIFIED, EVerificationStatus.WALLET_UNCONNECTED,
displayPreference: null, displayPreference: null,
isLoading: false, isLoading: false,
error: null, error: null,
@ -109,7 +109,7 @@ export function useEnhancedUserDisplay(address: string): UserDisplayInfo {
ordinalDetails: null, ordinalDetails: null,
verificationLevel: verificationLevel:
verificationInfo.verificationStatus || verificationInfo.verificationStatus ||
EVerificationStatus.UNVERIFIED, EVerificationStatus.WALLET_UNCONNECTED,
displayPreference: null, displayPreference: null,
isLoading: false, isLoading: false,
error: null, error: null,
@ -125,7 +125,7 @@ export function useEnhancedUserDisplay(address: string): UserDisplayInfo {
callSign: null, callSign: null,
ensName: null, ensName: null,
ordinalDetails: null, ordinalDetails: null,
verificationLevel: EVerificationStatus.UNVERIFIED, verificationLevel: EVerificationStatus.WALLET_UNCONNECTED,
displayPreference: null, displayPreference: null,
isLoading: false, isLoading: false,
error: error instanceof Error ? error.message : 'Unknown error', error: error instanceof Error ? error.message : 'Unknown error',

View File

@ -2,6 +2,7 @@ import { useMemo } from 'react';
import { useForum } from '@/contexts/useForum'; import { useForum } from '@/contexts/useForum';
import { useAuth } from '@/contexts/useAuth'; import { useAuth } from '@/contexts/useAuth';
import { Cell, Post, Comment, UserVerificationStatus } from '@/types/forum'; import { Cell, Post, Comment, UserVerificationStatus } from '@/types/forum';
import { EVerificationStatus } from '@/types/identity';
export interface CellWithStats extends Cell { export interface CellWithStats extends Cell {
postCount: number; postCount: number;
@ -96,8 +97,9 @@ export function useForumData(): ForumData {
if (!currentUser) return false; if (!currentUser) return false;
return ( return (
currentUser.verificationStatus === 'verified-owner' || currentUser.verificationStatus ===
currentUser.verificationStatus === 'verified-basic' || EVerificationStatus.ENS_ORDINAL_VERIFIED ||
currentUser.verificationStatus === EVerificationStatus.WALLET_CONNECTED ||
Boolean(currentUser.ensDetails) || Boolean(currentUser.ensDetails) ||
Boolean(currentUser.ordinalDetails) 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 { useMemo } from 'react';
import { useForumData, CellWithStats } from '@/hooks/core/useForumData'; 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 { export interface CellData extends CellWithStats {
posts: Array<{ posts: Array<{
@ -49,8 +50,10 @@ export function useCell(cellId: string | undefined): CellData | null {
: false; : false;
const canModerate = isUserAdmin; const canModerate = isUserAdmin;
const canPost = currentUser const canPost = currentUser
? currentUser.verificationStatus === 'verified-owner' || ? currentUser.verificationStatus ===
currentUser.verificationStatus === 'verified-basic' || EVerificationStatus.ENS_ORDINAL_VERIFIED ||
currentUser.verificationStatus ===
EVerificationStatus.WALLET_CONNECTED ||
Boolean(currentUser.ensDetails) || Boolean(currentUser.ensDetails) ||
Boolean(currentUser.ordinalDetails) Boolean(currentUser.ordinalDetails)
: false; : false;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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