mirror of
https://github.com/logos-messaging/OpChan.git
synced 2026-01-04 22:03:07 +00:00
chore: update verification status enum
This commit is contained in:
parent
30e888bae4
commit
9e6be6156f
@ -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" />
|
||||
|
||||
@ -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" />;
|
||||
|
||||
@ -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">
|
||||
|
||||
@ -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>
|
||||
|
||||
|
||||
@ -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"
|
||||
|
||||
@ -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">
|
||||
|
||||
@ -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';
|
||||
};
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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,
|
||||
};
|
||||
}
|
||||
|
||||
@ -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(
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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,
|
||||
};
|
||||
}
|
||||
|
||||
@ -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 };
|
||||
@ -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',
|
||||
|
||||
@ -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)
|
||||
);
|
||||
|
||||
159
src/hooks/core/usePermissions.ts
Normal file
159
src/hooks/core/usePermissions.ts
Normal 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,
|
||||
};
|
||||
}
|
||||
@ -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;
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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: {
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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');
|
||||
}
|
||||
|
||||
|
||||
@ -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,
|
||||
};
|
||||
}
|
||||
@ -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, {
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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>
|
||||
)}
|
||||
|
||||
@ -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 {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user