+
+
+
-
{post.upvotes.length - post.downvotes.length}
+
{post.upvotes.length - post.downvotes.length}
@@ -181,7 +157,7 @@ const PostDetail = () => {
- {isAuthenticated ? (
+ {verificationStatus === 'verified-owner' ? (
+ ) : verificationStatus === 'verified-none' ? (
+
+
+
+
Read-Only Mode
+
+
+ Your wallet has been verified but does not contain any Ordinal Operators.
+ You can browse threads but cannot comment or vote.
+
+
) : (
Connect wallet and verify Ordinal ownership to comment
@@ -224,8 +211,8 @@ const PostDetail = () => {
@@ -233,24 +220,29 @@ const PostDetail = () => {
-
-
-
{comment.content}
-
-
-
+
+
+
+
+
+ {comment.authorAddress.slice(0, 6)}...{comment.authorAddress.slice(-4)}
+
+
+
{formatDistanceToNow(comment.timestamp, { addSuffix: true })}
-
- {comment.authorAddress.slice(0, 6)}...{comment.authorAddress.slice(-4)}
-
+
{comment.content}
diff --git a/src/components/PostList.tsx b/src/components/PostList.tsx
index 1b5920c..98f0143 100644
--- a/src/components/PostList.tsx
+++ b/src/components/PostList.tsx
@@ -6,9 +6,10 @@ import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import { Textarea } from '@/components/ui/textarea';
import { Skeleton } from '@/components/ui/skeleton';
-import { ArrowLeft, MessageSquare, MessageCircle, ArrowUp, ArrowDown, Clock, RefreshCw } from 'lucide-react';
+import { ArrowLeft, MessageSquare, MessageCircle, ArrowUp, ArrowDown, Clock, RefreshCw, Eye } from 'lucide-react';
import { formatDistanceToNow } from 'date-fns';
import { CypherImage } from './ui/CypherImage';
+import { Badge } from '@/components/ui/badge';
const PostList = () => {
const { cellId } = useParams<{ cellId: string }>();
@@ -20,9 +21,12 @@ const PostList = () => {
isInitialLoading,
isPostingPost,
isRefreshing,
- refreshData
+ refreshData,
+ votePost,
+ isVoting,
+ posts
} = useForum();
- const { isAuthenticated } = useAuth();
+ const { isAuthenticated, currentUser, verificationStatus } = useAuth();
const [newPostTitle, setNewPostTitle] = useState('');
const [newPostContent, setNewPostContent] = useState('');
@@ -54,7 +58,7 @@ const PostList = () => {
}
const cell = getCellById(cellId);
- const posts = getPostsByCell(cellId);
+ const cellPosts = getPostsByCell(cellId);
if (!cell) {
return (
@@ -91,6 +95,19 @@ const PostList = () => {
}
};
+ const handleVotePost = async (postId: string, isUpvote: boolean) => {
+ if (!isAuthenticated) return;
+ await votePost(postId, isUpvote);
+ };
+
+ const isPostVoted = (postId: string, isUpvote: boolean) => {
+ if (!currentUser) return false;
+ const post = posts.find(p => p.id === postId);
+ if (!post) return false;
+ const votes = isUpvote ? post.upvotes : post.downvotes;
+ return votes.some(vote => vote.author === currentUser.address);
+ };
+
return (
@@ -123,7 +140,7 @@ const PostList = () => {
- {isAuthenticated && (
+ {verificationStatus === 'verified-owner' && (
)}
+ {verificationStatus === 'verified-none' && (
+
+
+
+
Read-Only Mode
+
+
+ Your wallet does not contain any Ordinal Operators. You can browse threads but cannot post or interact.
+
+
No Ordinals Found
+
+ )}
+
+ {!currentUser && (
+
+
Connect wallet and verify Ordinal ownership to post
+
+
+ )}
+
- {posts.length === 0 ? (
+ {cellPosts.length === 0 ? (
No Threads Yet
@@ -170,32 +209,41 @@ const PostList = () => {
) : (
- posts.map(post => (
-
-
-
{post.title}
-
{post.content}
-
-
-
- {formatDistanceToNow(post.timestamp, { addSuffix: true })}
-
-
-
- {getCommentsByPost(post.id).length} comments
-
-
-
-
{post.upvotes.length}
-
-
{post.downvotes.length}
-
-
- {post.authorAddress.slice(0, 6)}...{post.authorAddress.slice(-4)}
-
+ cellPosts.map(post => (
+
+
+
+
+
{post.upvotes.length - post.downvotes.length}
+
+
+
+
+
+
{post.title}
+
{post.content}
+
+ {formatDistanceToNow(post.timestamp, { addSuffix: true })}
+ by {post.authorAddress.slice(0, 6)}...{post.authorAddress.slice(-4)}
+
+
-
+
))
)}
diff --git a/src/contexts/AuthContext.tsx b/src/contexts/AuthContext.tsx
index 3be908b..72d057f 100644
--- a/src/contexts/AuthContext.tsx
+++ b/src/contexts/AuthContext.tsx
@@ -3,10 +3,13 @@ import { useToast } from '@/components/ui/use-toast';
import { User } from '@/types';
import { OrdinalAPI } from '@/lib/identity/ordinal';
+export type VerificationStatus = 'unverified' | 'verified-none' | 'verified-owner' | 'verifying';
+
interface AuthContextType {
currentUser: User | null;
isAuthenticated: boolean;
isAuthenticating: boolean;
+ verificationStatus: VerificationStatus;
connectWallet: () => Promise
;
disconnectWallet: () => void;
verifyOrdinal: () => Promise;
@@ -17,28 +20,34 @@ const AuthContext = createContext(undefined);
export function AuthProvider({ children }: { children: React.ReactNode }) {
const [currentUser, setCurrentUser] = useState(null);
const [isAuthenticating, setIsAuthenticating] = useState(false);
+ const [verificationStatus, setVerificationStatus] = useState('unverified');
const { toast } = useToast();
const ordinalApi = new OrdinalAPI();
- // Check for existing session on mount
useEffect(() => {
const storedUser = localStorage.getItem('opchan-user');
if (storedUser) {
try {
const user = JSON.parse(storedUser);
- // Check if the stored authentication is still valid (not expired)
const lastChecked = user.lastChecked || 0;
- const expiryTime = 24 * 60 * 60 * 1000; // 24 hours in milliseconds
+ const expiryTime = 24 * 60 * 60 * 1000;
if (Date.now() - lastChecked < expiryTime) {
setCurrentUser(user);
+
+ if ('ordinalOwnership' in user) {
+ setVerificationStatus(user.ordinalOwnership ? 'verified-owner' : 'verified-none');
+ } else {
+ setVerificationStatus('unverified');
+ }
} else {
- // Clear expired session
localStorage.removeItem('opchan-user');
+ setVerificationStatus('unverified');
}
} catch (e) {
console.error("Failed to parse stored user data", e);
localStorage.removeItem('opchan-user');
+ setVerificationStatus('unverified');
}
}
}, []);
@@ -59,6 +68,7 @@ export function AuthProvider({ children }: { children: React.ReactNode }) {
// Store user data
setCurrentUser(newUser);
localStorage.setItem('opchan-user', JSON.stringify(newUser));
+ setVerificationStatus('unverified');
toast({
title: "Wallet Connected",
@@ -82,6 +92,7 @@ export function AuthProvider({ children }: { children: React.ReactNode }) {
const disconnectWallet = () => {
setCurrentUser(null);
localStorage.removeItem('opchan-user');
+ setVerificationStatus('unverified');
toast({
title: "Disconnected",
description: "Your wallet has been disconnected.",
@@ -99,8 +110,13 @@ export function AuthProvider({ children }: { children: React.ReactNode }) {
}
setIsAuthenticating(true);
+ setVerificationStatus('verifying');
+
try {
- toast({ title: "Verifying Ordinal", description: "Checking your wallet for Ordinal Operators..." });
+ toast({
+ title: "Verifying Ordinal",
+ description: "Checking your wallet for Ordinal Operators..."
+ });
const response = await ordinalApi.getOperatorDetails(currentUser.address);
const hasOperators = response.has_operators;
@@ -114,31 +130,38 @@ export function AuthProvider({ children }: { children: React.ReactNode }) {
setCurrentUser(updatedUser);
localStorage.setItem('opchan-user', JSON.stringify(updatedUser));
+ // Update verification status
+ setVerificationStatus(hasOperators ? 'verified-owner' : 'verified-none');
+
if (hasOperators) {
toast({
title: "Ordinal Verified",
- description: "You can now post and interact with the forum.",
+ description: "You now have full access to post and interact with the forum.",
});
} else {
toast({
- title: "Verification Failed",
- description: "No Ordinal Operators found in the connected wallet.",
- variant: "destructive",
+ title: "Read-Only Access",
+ description: "No Ordinal Operators found. You have read-only access.",
+ variant: "default",
});
}
return hasOperators;
} catch (error) {
console.error("Error verifying Ordinal:", error);
+ setVerificationStatus('unverified');
+
let errorMessage = "Failed to verify Ordinal ownership. Please try again.";
if (error instanceof Error) {
errorMessage = error.message;
}
+
toast({
title: "Verification Error",
description: errorMessage,
variant: "destructive",
});
+
return false;
} finally {
setIsAuthenticating(false);
@@ -151,6 +174,7 @@ export function AuthProvider({ children }: { children: React.ReactNode }) {
currentUser,
isAuthenticated: !!currentUser?.ordinalOwnership,
isAuthenticating,
+ verificationStatus,
connectWallet,
disconnectWallet,
verifyOrdinal,