OpChan/app/src/components/ui/verification-step.tsx

337 lines
11 KiB
TypeScript
Raw Normal View History

2025-08-30 18:34:50 +05:30
import * as React from 'react';
import { Button } from '@/components/ui/button';
import {
Bitcoin,
Coins,
Shield,
ShieldCheck,
Loader2,
AlertCircle,
} from 'lucide-react';
import { useAuth, useAuthActions } from '@/hooks';
2025-09-18 11:08:42 +05:30
import { EVerificationStatus } from '@opchan/core';
2025-08-30 18:34:50 +05:30
import { useAppKitAccount } from '@reown/appkit/react';
2025-09-18 11:08:42 +05:30
import { OrdinalDetails, EnsDetails } from '@opchan/core';
interface VerificationStepProps {
onComplete: () => void;
onBack: () => void;
isLoading: boolean;
setIsLoading: (loading: boolean) => void;
}
export function VerificationStep({
onComplete,
onBack,
isLoading,
setIsLoading,
}: VerificationStepProps) {
const { currentUser, verificationStatus, isAuthenticating } = useAuth();
const { verifyWallet } = useAuthActions();
2025-08-30 18:34:50 +05:30
// Get account info to determine wallet type
2025-08-30 18:34:50 +05:30
const bitcoinAccount = useAppKitAccount({ namespace: 'bip122' });
const ethereumAccount = useAppKitAccount({ namespace: 'eip155' });
const isBitcoinConnected = bitcoinAccount.isConnected;
const isEthereumConnected = ethereumAccount.isConnected;
2025-08-30 18:34:50 +05:30
const walletType = isBitcoinConnected
? 'bitcoin'
: isEthereumConnected
? 'ethereum'
: undefined;
const [verificationResult, setVerificationResult] = React.useState<{
success: boolean;
message: string;
2025-08-30 18:34:50 +05:30
details?: OrdinalDetails | EnsDetails;
} | null>(null);
2025-09-05 12:53:15 +05:30
// Watch for changes in user state after verification
React.useEffect(() => {
if (
verificationResult?.success &&
verificationResult.message.includes('Checking ownership')
) {
// Check if actual ownership was verified
2025-09-11 12:36:06 +05:30
// Treat centralized verification status as source of truth
const isOwnerVerified =
verificationStatus === EVerificationStatus.ENS_ORDINAL_VERIFIED;
2025-09-05 12:53:15 +05:30
const hasOwnership =
walletType === 'bitcoin'
2025-09-11 12:36:06 +05:30
? isOwnerVerified && !!currentUser?.ordinalDetails
: isOwnerVerified && !!currentUser?.ensDetails;
2025-09-05 12:53:15 +05:30
if (hasOwnership) {
setVerificationResult({
success: true,
message:
walletType === 'bitcoin'
? 'Ordinal ownership verified successfully!'
: 'ENS ownership verified successfully!',
details:
walletType === 'bitcoin'
? currentUser?.ordinalDetails
: currentUser?.ensDetails,
});
} else {
setVerificationResult({
success: false,
message:
walletType === 'bitcoin'
? 'No Ordinal ownership found. You can still participate in the forum with your connected wallet!'
: 'No ENS ownership found. You can still participate in the forum with your connected wallet!',
});
}
}
2025-09-11 12:36:06 +05:30
}, [currentUser, verificationResult, walletType, verificationStatus]);
2025-09-05 12:53:15 +05:30
const handleVerify = async () => {
if (!currentUser) return;
2025-08-30 18:34:50 +05:30
setIsLoading(true);
setVerificationResult(null);
2025-08-30 18:34:50 +05:30
try {
const success = await verifyWallet();
2025-08-30 18:34:50 +05:30
if (success) {
2025-09-05 12:53:15 +05:30
// For now, just show success - the actual ownership check will be done
// by the useEffect when the user state updates
setVerificationResult({
success: true,
2025-08-30 18:34:50 +05:30
message:
walletType === 'bitcoin'
2025-09-05 12:53:15 +05:30
? 'Verification process completed. Checking ownership...'
: 'Verification process completed. Checking ownership...',
details: undefined,
});
} else {
setVerificationResult({
success: false,
2025-08-30 18:34:50 +05:30
message:
walletType === 'bitcoin'
? 'No Ordinal ownership found. You can still participate in the forum with your connected wallet!'
: 'No ENS ownership found. You can still participate in the forum with your connected wallet!',
});
}
} catch (error) {
setVerificationResult({
success: false,
2025-08-30 18:34:50 +05:30
message: `Verification failed. Please try again: ${error}`,
});
} finally {
setIsLoading(false);
}
};
const handleNext = () => {
onComplete();
};
const getVerificationType = () => {
2025-08-18 14:07:01 +05:30
return walletType === 'bitcoin' ? 'Bitcoin Ordinal' : 'Ethereum ENS';
};
const getVerificationIcon = () => {
return walletType === 'bitcoin' ? Bitcoin : Coins;
};
const getVerificationColor = () => {
return walletType === 'bitcoin' ? 'text-orange-500' : 'text-blue-500';
};
const getVerificationDescription = () => {
if (walletType === 'bitcoin') {
2025-08-18 14:07:01 +05:30
return "Verify your Bitcoin Ordinal ownership to unlock premium features. If you don't own any Ordinals, you can still participate in the forum with your connected wallet.";
} else {
2025-08-18 14:07:01 +05:30
return "Verify your Ethereum ENS ownership to unlock premium features. If you don't own any ENS, you can still participate in the forum with your connected wallet.";
}
};
// Show verification result
if (verificationResult) {
return (
2025-08-06 17:21:56 +05:30
<div className="flex flex-col h-full">
<div className="flex-1 space-y-4">
2025-08-30 18:34:50 +05:30
<div
className={`p-4 rounded-lg border ${
verificationResult.success
? 'bg-green-900/20 border-green-500/30'
: 'bg-yellow-900/20 border-yellow-500/30'
}`}
>
2025-08-06 17:21:56 +05:30
<div className="flex items-center gap-2 mb-2">
{verificationResult.success ? (
<ShieldCheck className="h-5 w-5 text-green-500" />
) : (
2025-08-06 17:21:56 +05:30
<AlertCircle className="h-5 w-5 text-yellow-500" />
)}
2025-08-30 18:34:50 +05:30
<span
className={`font-medium ${
verificationResult.success
? 'text-green-400'
: 'text-yellow-400'
}`}
>
{verificationResult.success
? 'Verification Complete'
: 'Verification Result'}
2025-08-06 17:21:56 +05:30
</span>
</div>
2025-08-06 17:21:56 +05:30
<p className="text-sm text-neutral-300 mb-2">
{verificationResult.message}
</p>
{verificationResult.details && (
<div className="text-xs text-neutral-400">
{walletType === 'bitcoin' ? (
2025-08-30 18:34:50 +05:30
<p>
Ordinal ID:{' '}
{typeof verificationResult.details === 'object' &&
'ordinalId' in verificationResult.details
? verificationResult.details.ordinalId
: 'Verified'}
</p>
2025-08-06 17:21:56 +05:30
) : (
2025-08-30 18:34:50 +05:30
<p>
ENS Name:{' '}
{typeof verificationResult.details === 'object' &&
'ensName' in verificationResult.details
? verificationResult.details.ensName
: 'Verified'}
</p>
2025-08-06 17:21:56 +05:30
)}
</div>
)}
</div>
</div>
2025-08-30 18:34:50 +05:30
2025-08-06 17:21:56 +05:30
{/* Action Button */}
<div className="mt-auto">
<Button
onClick={handleNext}
className="w-full bg-blue-600 hover:bg-blue-700 text-white"
disabled={isLoading}
>
Next
</Button>
</div>
</div>
);
}
// Show verification status
2025-09-05 13:41:37 +05:30
if (verificationStatus === EVerificationStatus.ENS_ORDINAL_VERIFIED) {
return (
2025-08-06 17:21:56 +05:30
<div className="flex flex-col h-full">
<div className="flex-1 space-y-4">
<div className="p-4 bg-green-900/20 border border-green-500/30 rounded-lg">
<div className="flex items-center gap-2 mb-2">
<ShieldCheck className="h-5 w-5 text-green-500" />
2025-08-30 18:34:50 +05:30
<span className="text-green-400 font-medium">
Already Verified
</span>
</div>
2025-08-06 17:21:56 +05:30
<p className="text-sm text-neutral-300 mb-2">
Your {getVerificationType()} ownership has been verified.
</p>
{currentUser && (
<div className="text-xs text-neutral-400">
2025-09-03 15:01:57 +05:30
{walletType === 'bitcoin' && <p>Ordinal ID: Verified</p>}
{walletType === 'ethereum' && <p>ENS Name: Verified</p>}
2025-08-06 17:21:56 +05:30
</div>
)}
</div>
</div>
2025-08-30 18:34:50 +05:30
2025-08-06 17:21:56 +05:30
{/* Action Button */}
<div className="mt-auto">
<Button
onClick={handleNext}
className="w-full bg-blue-600 hover:bg-blue-700 text-white"
disabled={isLoading}
>
Next
</Button>
</div>
</div>
);
}
// Show verification form
return (
2025-08-06 17:21:56 +05:30
<div className="flex flex-col h-full">
<div className="flex-1 space-y-4">
<div className="text-center space-y-2">
<div className="flex justify-center">
2025-08-30 18:34:50 +05:30
{React.createElement(getVerificationIcon(), {
className: `h-8 w-8 ${getVerificationColor()}`,
2025-08-06 17:21:56 +05:30
})}
</div>
<h3 className="text-lg font-semibold text-white">
Verify {getVerificationType()} Ownership
</h3>
<p className="text-sm text-neutral-400">
{getVerificationDescription()}
</p>
</div>
2025-08-06 17:21:56 +05:30
<div className="p-4 bg-neutral-900/50 border border-neutral-700 rounded-lg">
<div className="flex items-center gap-2 mb-2">
<Shield className="h-4 w-4 text-blue-500" />
2025-08-30 18:34:50 +05:30
<span className="text-sm font-medium text-white">
What happens during verification?
</span>
2025-08-06 17:21:56 +05:30
</div>
<ul className="text-xs text-neutral-400 space-y-1">
{walletType === 'bitcoin' ? (
<>
<li> We'll check your wallet for Bitcoin Ordinal ownership</li>
<li> If found, you'll get full posting and voting access</li>
<li> If not found, you'll have read-only access</li>
</>
) : (
<>
<li> We'll check your wallet for ENS domain ownership</li>
<li> If found, you'll get full posting and voting access</li>
<li> If not found, you'll have read-only access</li>
</>
)}
</ul>
</div>
<div className="text-xs text-neutral-500 text-center">
Verification is required to access posting and voting features
</div>
</div>
2025-08-06 17:21:56 +05:30
{/* Action Buttons */}
<div className="mt-auto space-y-3">
<Button
onClick={handleVerify}
disabled={isLoading || isAuthenticating}
className="w-full bg-blue-600 hover:bg-blue-700 text-white"
>
{isLoading || isAuthenticating ? (
<>
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
Verifying...
</>
) : (
`Verify ${getVerificationType()} Ownership`
)}
</Button>
2025-08-30 18:34:50 +05:30
<Button
onClick={onBack}
variant="outline"
className="w-full border-neutral-600 text-neutral-400 hover:bg-neutral-800"
disabled={isLoading || isAuthenticating}
>
2025-08-06 17:21:56 +05:30
Back
</Button>
</div>
</div>
);
2025-08-30 18:34:50 +05:30
}