+
+
+
+
+
+ Delegate Signing Key
+
+
+ Create a browser-based signing key for better user experience
+
+
+
+
+
+
+ What is key delegation?
+
+
+ - • Creates a browser-based signing key for 24 hours
+ - • Allows posting, commenting, and voting without wallet approval
+ - • Automatically expires for security
+ - • Can be renewed anytime
+
+
+
+ {currentUser?.browserPubKey && (
+
+
+
+ Browser Key Generated
+
+
+ {currentUser.browserPubKey.slice(0, 20)}...{currentUser.browserPubKey.slice(-20)}
+
+
+ )}
+
+
+
+
+
+
+
+
+
Key delegation is optional but recommended for better UX
+
You can still use the forum without delegation
+
+
+ );
+}
\ No newline at end of file
diff --git a/src/components/ui/verification-step.tsx b/src/components/ui/verification-step.tsx
new file mode 100644
index 0000000..7d1787c
--- /dev/null
+++ b/src/components/ui/verification-step.tsx
@@ -0,0 +1,261 @@
+import * as React from "react";
+import { Button } from "@/components/ui/button";
+import { Badge } from "@/components/ui/badge";
+import { Bitcoin, Coins, Shield, ShieldCheck, Loader2, AlertCircle } from "lucide-react";
+import { useAuth } from "@/contexts/useAuth";
+import { useAppKitAccount } from "@reown/appkit/react";
+
+interface VerificationStepProps {
+ onComplete: () => void;
+ onBack: () => void;
+ isLoading: boolean;
+ setIsLoading: (loading: boolean) => void;
+}
+
+export function VerificationStep({
+ onComplete,
+ onBack,
+ isLoading,
+ setIsLoading,
+}: VerificationStepProps) {
+ const {
+ currentUser,
+ verificationStatus,
+ verifyOwnership,
+ isAuthenticating
+ } = useAuth();
+
+ // Get account info to determine wallet type
+ const bitcoinAccount = useAppKitAccount({ namespace: "bip122" });
+ const ethereumAccount = useAppKitAccount({ namespace: "eip155" });
+
+ const isBitcoinConnected = bitcoinAccount.isConnected;
+ const isEthereumConnected = ethereumAccount.isConnected;
+ const walletType = isBitcoinConnected ? 'bitcoin' : 'ethereum';
+
+ const [verificationResult, setVerificationResult] = React.useState<{
+ success: boolean;
+ message: string;
+ details?: any;
+ } | null>(null);
+
+ const handleVerify = async () => {
+ if (!currentUser) return;
+
+ setIsLoading(true);
+ setVerificationResult(null);
+
+ try {
+ const success = await verifyOwnership();
+
+ if (success) {
+ setVerificationResult({
+ success: true,
+ message: walletType === 'bitcoin'
+ ? "Ordinal ownership verified successfully!"
+ : "ENS ownership verified successfully!",
+ details: walletType === 'bitcoin'
+ ? currentUser.ordinalOwnership
+ : { ensName: currentUser.ensName, ensAvatar: currentUser.ensAvatar }
+ });
+ } else {
+ setVerificationResult({
+ success: false,
+ message: walletType === 'bitcoin'
+ ? "No Ordinal ownership found. You'll have read-only access."
+ : "No ENS ownership found. You'll have read-only access."
+ });
+ }
+ } catch (error) {
+ setVerificationResult({
+ success: false,
+ message: "Verification failed. Please try again."
+ });
+ } finally {
+ setIsLoading(false);
+ }
+ };
+
+ const handleNext = () => {
+ onComplete();
+ };
+
+ const getVerificationType = () => {
+ return walletType === 'bitcoin' ? 'Bitcoin Ordinal' : 'ENS Domain';
+ };
+
+ const getVerificationIcon = () => {
+ return walletType === 'bitcoin' ? Bitcoin : Coins;
+ };
+
+ const getVerificationColor = () => {
+ return walletType === 'bitcoin' ? 'text-orange-500' : 'text-blue-500';
+ };
+
+ const getVerificationDescription = () => {
+ if (walletType === 'bitcoin') {
+ return "Verify that you own Bitcoin Ordinals to get full posting and voting access.";
+ } else {
+ return "Verify that you own an ENS domain to get full posting and voting access.";
+ }
+ };
+
+ // Show verification result
+ if (verificationResult) {
+ return (
+
+
+
+ {React.createElement(getVerificationIcon(), {
+ className: `h-8 w-8 ${getVerificationColor()}`
+ })}
+
+
+ Verify {getVerificationType()} Ownership
+
+
+ {getVerificationDescription()}
+
+
+
+
+
+
+ What happens during verification?
+
+
+ {walletType === 'bitcoin' ? (
+ <>
+ - • We'll check your wallet for Bitcoin Ordinal ownership
+ - • If found, you'll get full posting and voting access
+ - • If not found, you'll have read-only access
+ >
+ ) : (
+ <>
+ - • We'll check your wallet for ENS domain ownership
+ - • If found, you'll get full posting and voting access
+ - • If not found, you'll have read-only access
+ >
+ )}
+
+
+
+
+
+
+
+
+
+
+ Verification is required to access posting and voting features
+
+
+ );
+}
\ No newline at end of file
diff --git a/src/components/ui/wallet-connection-step.tsx b/src/components/ui/wallet-connection-step.tsx
new file mode 100644
index 0000000..54301b2
--- /dev/null
+++ b/src/components/ui/wallet-connection-step.tsx
@@ -0,0 +1,215 @@
+import * as React from "react";
+import { Button } from "@/components/ui/button";
+import { Badge } from "@/components/ui/badge";
+import { Bitcoin, Coins, Loader2 } from "lucide-react";
+import {
+ useAppKit,
+ useAppKitAccount,
+ useAppKitState
+} from "@reown/appkit/react";
+import { useAuth } from "@/contexts/useAuth";
+
+interface WalletConnectionStepProps {
+ onComplete: () => void;
+ isLoading: boolean;
+ setIsLoading: (loading: boolean) => void;
+}
+
+export function WalletConnectionStep({
+ onComplete,
+ isLoading,
+ setIsLoading,
+}: WalletConnectionStepProps) {
+ const { initialized } = useAppKitState();
+ const appKit = useAppKit();
+ const { isAuthenticated } = useAuth();
+
+ // Get account info for different chains
+ const bitcoinAccount = useAppKitAccount({ namespace: "bip122" });
+ const ethereumAccount = useAppKitAccount({ namespace: "eip155" });
+
+ // Determine which account is connected
+ const isBitcoinConnected = bitcoinAccount.isConnected;
+ const isEthereumConnected = ethereumAccount.isConnected;
+ const isConnected = isBitcoinConnected || isEthereumConnected;
+
+ // Get the active account info
+ const activeAccount = isBitcoinConnected ? bitcoinAccount : ethereumAccount;
+ const activeAddress = activeAccount.address;
+ const activeChain = isBitcoinConnected ? "Bitcoin" : "Ethereum";
+
+ const handleBitcoinConnect = async () => {
+ if (!initialized || !appKit) {
+ console.error('AppKit not initialized');
+ return;
+ }
+
+ setIsLoading(true);
+ try {
+ await appKit.open({
+ view: "Connect",
+ namespace: "bip122"
+ });
+ // The wizard will automatically advance when connection is detected
+ } catch (error) {
+ console.error('Error connecting Bitcoin wallet:', error);
+ } finally {
+ setIsLoading(false);
+ }
+ };
+
+ const handleEthereumConnect = async () => {
+ if (!initialized || !appKit) {
+ console.error('AppKit not initialized');
+ return;
+ }
+
+ setIsLoading(true);
+ try {
+ await appKit.open({
+ view: "Connect",
+ namespace: "eip155"
+ });
+ // The wizard will automatically advance when connection is detected
+ } catch (error) {
+ console.error('Error connecting Ethereum wallet:', error);
+ } finally {
+ setIsLoading(false);
+ }
+ };
+
+ const handleNext = () => {
+ onComplete();
+ };
+
+ // Show loading state if AppKit is not initialized
+ if (!initialized) {
+ return (
+
+
+ Choose a network and wallet to connect to OpChan
+
+
+ {/* Bitcoin Section */}
+
+
+
+
Bitcoin
+
+ Ordinal Verification Required
+
+
+
+
+
+ {/* Divider */}
+
+
+ {/* Ethereum Section */}
+
+
+
+
Ethereum
+
+ ENS Ownership Required
+
+
+
+
+
+
+ Connect your wallet to use OpChan's features
+
+
+ );
+}
\ No newline at end of file
diff --git a/src/components/ui/wallet-dialog.tsx b/src/components/ui/wallet-dialog.tsx
index 256b701..eed00a1 100644
--- a/src/components/ui/wallet-dialog.tsx
+++ b/src/components/ui/wallet-dialog.tsx
@@ -8,60 +8,212 @@ import {
DialogTitle,
} from "@/components/ui/dialog";
import { Button } from "@/components/ui/button";
-import { WalletConnectionStatus } from "@/lib/identity/wallets/phantom";
+import { Badge } from "@/components/ui/badge";
+import { Bitcoin, Coins } from "lucide-react";
+import {
+ useAppKit,
+ useAppKitAccount,
+ useDisconnect,
+ useAppKitState
+} from "@reown/appkit/react";
interface WalletDialogProps {
open: boolean;
onOpenChange: (open: boolean) => void;
- onConnectPhantom: () => void;
- onInstallPhantom: () => void;
- status: WalletConnectionStatus;
- isAuthenticating: boolean;
+ onConnect: () => void;
}
export function WalletConnectionDialog({
open,
onOpenChange,
- onConnectPhantom,
- onInstallPhantom,
- status,
- isAuthenticating,
+ onConnect,
}: WalletDialogProps) {
+ // Always call hooks to follow React rules
+ const { initialized } = useAppKitState();
+ const appKit = useAppKit();
+ const { disconnect } = useDisconnect();
+
+ // Get account info for different chains
+ const bitcoinAccount = useAppKitAccount({ namespace: "bip122" });
+ const ethereumAccount = useAppKitAccount({ namespace: "eip155" });
+
+ // Determine which account is connected
+ const isBitcoinConnected = bitcoinAccount.isConnected;
+ const isEthereumConnected = ethereumAccount.isConnected;
+ const isConnected = isBitcoinConnected || isEthereumConnected;
+
+ // Get the active account info
+ const activeAccount = isBitcoinConnected ? bitcoinAccount : ethereumAccount;
+ const activeAddress = activeAccount.address;
+ const activeChain = isBitcoinConnected ? "Bitcoin" : "Ethereum";
+
+ const handleDisconnect = async () => {
+ await disconnect();
+ onOpenChange(false);
+ };
+
+ const handleBitcoinConnect = () => {
+ if (!initialized || !appKit) {
+ console.error('AppKit not initialized');
+ return;
+ }
+
+ appKit.open({
+ view: "Connect",
+ namespace: "bip122"
+ });
+ onConnect();
+ onOpenChange(false);
+ };
+
+ const handleEthereumConnect = () => {
+ if (!initialized || !appKit) {
+ console.error('AppKit not initialized');
+ return;
+ }
+
+ appKit.open({
+ view: "Connect",
+ namespace: "eip155"
+ });
+ onConnect();
+ onOpenChange(false);
+ };
+
+ // Show loading state if AppKit is not initialized
+ if (!initialized) {
+ return (
+