OpChan/src/contexts/AuthContext.tsx

195 lines
5.7 KiB
TypeScript
Raw Normal View History

2025-04-15 16:28:03 +05:30
import React, { createContext, useContext, useState, useEffect } from 'react';
import { useToast } from '@/components/ui/use-toast';
2025-04-16 14:45:27 +05:30
import { User } from '@/types';
import { OrdinalAPI } from '@/lib/identity/ordinal';
2025-04-15 16:28:03 +05:30
2025-04-24 14:31:00 +05:30
export type VerificationStatus = 'unverified' | 'verified-none' | 'verified-owner' | 'verifying';
2025-04-15 16:28:03 +05:30
interface AuthContextType {
currentUser: User | null;
isAuthenticated: boolean;
isAuthenticating: boolean;
2025-04-24 14:31:00 +05:30
verificationStatus: VerificationStatus;
2025-04-15 16:28:03 +05:30
connectWallet: () => Promise<void>;
disconnectWallet: () => void;
verifyOrdinal: () => Promise<boolean>;
}
const AuthContext = createContext<AuthContextType | undefined>(undefined);
export function AuthProvider({ children }: { children: React.ReactNode }) {
const [currentUser, setCurrentUser] = useState<User | null>(null);
const [isAuthenticating, setIsAuthenticating] = useState(false);
2025-04-24 14:31:00 +05:30
const [verificationStatus, setVerificationStatus] = useState<VerificationStatus>('unverified');
2025-04-15 16:28:03 +05:30
const { toast } = useToast();
const ordinalApi = new OrdinalAPI();
2025-04-15 16:28:03 +05:30
useEffect(() => {
const storedUser = localStorage.getItem('opchan-user');
if (storedUser) {
try {
const user = JSON.parse(storedUser);
const lastChecked = user.lastChecked || 0;
2025-04-24 14:31:00 +05:30
const expiryTime = 24 * 60 * 60 * 1000;
2025-04-15 16:28:03 +05:30
if (Date.now() - lastChecked < expiryTime) {
setCurrentUser(user);
2025-04-24 14:31:00 +05:30
if ('ordinalOwnership' in user) {
setVerificationStatus(user.ordinalOwnership ? 'verified-owner' : 'verified-none');
} else {
setVerificationStatus('unverified');
}
2025-04-15 16:28:03 +05:30
} else {
localStorage.removeItem('opchan-user');
2025-04-24 14:31:00 +05:30
setVerificationStatus('unverified');
2025-04-15 16:28:03 +05:30
}
} catch (e) {
console.error("Failed to parse stored user data", e);
localStorage.removeItem('opchan-user');
2025-04-24 14:31:00 +05:30
setVerificationStatus('unverified');
2025-04-15 16:28:03 +05:30
}
}
}, []);
// Mock wallet connection for development
const connectWallet = async () => {
setIsAuthenticating(true);
try {
//TODO: replace with actual wallet connection
2025-04-15 16:28:03 +05:30
const mockAddress = "bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh";
// Create a new user object
const newUser: User = {
address: mockAddress,
lastChecked: Date.now(),
};
// Store user data
setCurrentUser(newUser);
localStorage.setItem('opchan-user', JSON.stringify(newUser));
2025-04-24 14:31:00 +05:30
setVerificationStatus('unverified');
2025-04-15 16:28:03 +05:30
toast({
title: "Wallet Connected",
description: `Connected with address ${mockAddress.slice(0, 6)}...${mockAddress.slice(-4)}`,
});
// Don't return the address anymore to match the Promise<void> return type
} catch (error) {
console.error("Error connecting wallet:", error);
toast({
title: "Connection Failed",
description: "Failed to connect to wallet. Please try again.",
variant: "destructive",
});
throw error;
} finally {
setIsAuthenticating(false);
}
};
const disconnectWallet = () => {
setCurrentUser(null);
localStorage.removeItem('opchan-user');
2025-04-24 14:31:00 +05:30
setVerificationStatus('unverified');
2025-04-15 16:28:03 +05:30
toast({
title: "Disconnected",
description: "Your wallet has been disconnected.",
});
};
const verifyOrdinal = async () => {
if (!currentUser || !currentUser.address) {
2025-04-15 16:28:03 +05:30
toast({
title: "Not Connected",
description: "Please connect your wallet first.",
variant: "destructive",
});
return false;
}
setIsAuthenticating(true);
2025-04-24 14:31:00 +05:30
setVerificationStatus('verifying');
2025-04-15 16:28:03 +05:30
try {
2025-04-24 14:31:00 +05:30
toast({
title: "Verifying Ordinal",
description: "Checking your wallet for Ordinal Operators..."
});
const response = await ordinalApi.getOperatorDetails(currentUser.address);
const hasOperators = response.has_operators;
2025-04-15 16:28:03 +05:30
const updatedUser = {
...currentUser,
ordinalOwnership: hasOperators,
2025-04-15 16:28:03 +05:30
lastChecked: Date.now(),
};
setCurrentUser(updatedUser);
localStorage.setItem('opchan-user', JSON.stringify(updatedUser));
2025-04-24 14:31:00 +05:30
// Update verification status
setVerificationStatus(hasOperators ? 'verified-owner' : 'verified-none');
if (hasOperators) {
toast({
title: "Ordinal Verified",
2025-04-24 14:31:00 +05:30
description: "You now have full access to post and interact with the forum.",
});
} else {
toast({
2025-04-24 14:31:00 +05:30
title: "Read-Only Access",
description: "No Ordinal Operators found. You have read-only access.",
variant: "default",
});
}
2025-04-15 16:28:03 +05:30
return hasOperators;
2025-04-15 16:28:03 +05:30
} catch (error) {
console.error("Error verifying Ordinal:", error);
2025-04-24 14:31:00 +05:30
setVerificationStatus('unverified');
let errorMessage = "Failed to verify Ordinal ownership. Please try again.";
if (error instanceof Error) {
errorMessage = error.message;
}
2025-04-24 14:31:00 +05:30
2025-04-15 16:28:03 +05:30
toast({
title: "Verification Error",
description: errorMessage,
2025-04-15 16:28:03 +05:30
variant: "destructive",
});
2025-04-24 14:31:00 +05:30
2025-04-15 16:28:03 +05:30
return false;
} finally {
setIsAuthenticating(false);
}
};
return (
<AuthContext.Provider
value={{
currentUser,
isAuthenticated: !!currentUser?.ordinalOwnership,
isAuthenticating,
2025-04-24 14:31:00 +05:30
verificationStatus,
2025-04-15 16:28:03 +05:30
connectWallet,
disconnectWallet,
verifyOrdinal,
}}
>
{children}
</AuthContext.Provider>
);
}
export const useAuth = () => {
const context = useContext(AuthContext);
if (context === undefined) {
throw new Error("useAuth must be used within an AuthProvider");
}
return context;
};