r/{cellName}
•
-
Posted by u/{post.authorAddress.slice(0, 6)}...{post.authorAddress.slice(-4)}
+
Posted by u/
+
•
{formatDistanceToNow(new Date(post.timestamp), { addSuffix: true })}
{post.relevanceScore !== undefined && (
diff --git a/src/components/PostDetail.tsx b/src/components/PostDetail.tsx
index 6b956c8..516f877 100644
--- a/src/components/PostDetail.tsx
+++ b/src/components/PostDetail.tsx
@@ -10,6 +10,7 @@ import { Comment } from '@/types';
import { CypherImage } from './ui/CypherImage';
import { Badge } from '@/components/ui/badge';
import { RelevanceIndicator } from './ui/relevance-indicator';
+import { AuthorDisplay } from './ui/author-display';
const PostDetail = () => {
const { postId } = useParams<{ postId: string }>();
@@ -28,7 +29,8 @@ const PostDetail = () => {
isRefreshing,
refreshData,
moderateComment,
- moderateUser
+ moderateUser,
+ userVerificationStatus
} = useForum();
const { currentUser, isAuthenticated, verificationStatus } = useAuth();
const [newComment, setNewComment] = useState('');
@@ -163,9 +165,11 @@ const PostDetail = () => {
{postComments.length} {postComments.length === 1 ? 'comment' : 'comments'}
-
- {post.authorAddress.slice(0, 6)}...{post.authorAddress.slice(-4)}
-
+
{post.relevanceScore !== undefined && (
{
alt={comment.authorAddress.slice(0, 6)}
className="rounded-sm w-5 h-5 bg-secondary"
/>
-
- {comment.authorAddress.slice(0, 6)}...{comment.authorAddress.slice(-4)}
-
+
{comment.relevanceScore !== undefined && (
diff --git a/src/components/PostList.tsx b/src/components/PostList.tsx
index 4f5b424..446a79e 100644
--- a/src/components/PostList.tsx
+++ b/src/components/PostList.tsx
@@ -10,6 +10,7 @@ import { ArrowLeft, MessageSquare, MessageCircle, ArrowUp, ArrowDown, Clock, Ref
import { formatDistanceToNow } from 'date-fns';
import { CypherImage } from './ui/CypherImage';
import { Badge } from '@/components/ui/badge';
+import { AuthorDisplay } from './ui/author-display';
const PostList = () => {
const { cellId } = useParams<{ cellId: string }>();
@@ -26,7 +27,8 @@ const PostList = () => {
isVoting,
posts,
moderatePost,
- moderateUser
+ moderateUser,
+ userVerificationStatus
} = useForum();
const { isAuthenticated, currentUser, verificationStatus } = useAuth();
const [newPostTitle, setNewPostTitle] = useState('');
@@ -258,7 +260,13 @@ const PostList = () => {
{post.content}
{formatDistanceToNow(post.timestamp, { addSuffix: true })}
-
by {post.authorAddress.slice(0, 6)}...{post.authorAddress.slice(-4)}
+
by
+
{isCellAdmin && !post.moderated && (
diff --git a/src/components/ui/author-display.tsx b/src/components/ui/author-display.tsx
new file mode 100644
index 0000000..1b2731f
--- /dev/null
+++ b/src/components/ui/author-display.tsx
@@ -0,0 +1,59 @@
+import React from 'react';
+import { Badge } from '@/components/ui/badge';
+import { Shield, Crown } from 'lucide-react';
+import { UserVerificationStatus } from '@/lib/forum/types';
+
+interface AuthorDisplayProps {
+ address: string;
+ userVerificationStatus?: UserVerificationStatus;
+ className?: string;
+ showBadge?: boolean;
+}
+
+export function AuthorDisplay({
+ address,
+ userVerificationStatus,
+ className = "",
+ showBadge = true
+}: AuthorDisplayProps) {
+ const userStatus = userVerificationStatus?.[address];
+ const isVerified = userStatus?.isVerified || false;
+ const hasENS = userStatus?.hasENS || false;
+ const hasOrdinal = userStatus?.hasOrdinal || false;
+
+ // Get ENS name from user verification status if available
+ const ensName = userStatus?.ensName;
+ const displayName = ensName || `${address.slice(0, 6)}...${address.slice(-4)}`;
+
+ return (
+
+
+ {displayName}
+
+
+ {showBadge && isVerified && (
+
+ {hasENS ? (
+ <>
+
+ ENS
+ >
+ ) : hasOrdinal ? (
+ <>
+
+ Ordinal
+ >
+ ) : (
+ <>
+
+ Verified
+ >
+ )}
+
+ )}
+
+ );
+}
diff --git a/src/contexts/ForumContext.tsx b/src/contexts/ForumContext.tsx
index 227d25d..c62f81a 100644
--- a/src/contexts/ForumContext.tsx
+++ b/src/contexts/ForumContext.tsx
@@ -18,13 +18,16 @@ import {
} from '@/lib/waku/network';
import messageManager from '@/lib/waku';
import { getDataFromCache } from '@/lib/forum/transformers';
-import { RelevanceCalculator, UserVerificationStatus } from '@/lib/forum/relevance';
+import { RelevanceCalculator } from '@/lib/forum/relevance';
+import { UserVerificationStatus } from '@/lib/forum/types';
import { AuthService } from '@/lib/identity/services/AuthService';
interface ForumContextType {
cells: Cell[];
posts: Post[];
comments: Comment[];
+ // User verification status for display
+ userVerificationStatus: UserVerificationStatus;
// Granular loading states
isInitialLoading: boolean;
isPostingCell: boolean;
@@ -80,6 +83,7 @@ export function ForumProvider({ children }: { children: React.ReactNode }) {
const [isRefreshing, setIsRefreshing] = useState(false);
const [isNetworkConnected, setIsNetworkConnected] = useState(false);
const [error, setError] = useState
(null);
+ const [userVerificationStatus, setUserVerificationStatus] = useState({});
const { toast } = useToast();
const { currentUser, isAuthenticated } = useAuth();
@@ -117,11 +121,27 @@ export function ForumProvider({ children }: { children: React.ReactNode }) {
// Create user objects for verification status
Array.from(userAddresses).forEach(address => {
- allUsers.push({
- address,
- walletType: 'bitcoin', // Default, will be updated if we have more info
- verificationStatus: 'unverified'
- });
+ // Check if this address matches the current user's address
+ if (currentUser && currentUser.address === address) {
+ // Use the current user's actual verification status
+ allUsers.push({
+ address,
+ walletType: currentUser.walletType,
+ verificationStatus: currentUser.verificationStatus,
+ ensOwnership: currentUser.ensOwnership,
+ ensName: currentUser.ensName,
+ ensAvatar: currentUser.ensAvatar,
+ ordinalOwnership: currentUser.ordinalOwnership,
+ lastChecked: currentUser.lastChecked
+ });
+ } else {
+ // Create generic user object for other addresses
+ allUsers.push({
+ address,
+ walletType: 'bitcoin', // Default, will be updated if we have more info
+ verificationStatus: 'unverified'
+ });
+ }
});
const userVerificationStatus = relevanceCalculator.buildUserVerificationStatus(allUsers);
@@ -132,7 +152,8 @@ export function ForumProvider({ children }: { children: React.ReactNode }) {
setCells(cells);
setPosts(posts);
setComments(comments);
- }, [authService, isAuthenticated]);
+ setUserVerificationStatus(userVerificationStatus);
+ }, [authService, isAuthenticated, currentUser]);
const handleRefreshData = async () => {
setIsRefreshing(true);
@@ -327,6 +348,7 @@ export function ForumProvider({ children }: { children: React.ReactNode }) {
cells,
posts,
comments,
+ userVerificationStatus,
isInitialLoading,
isPostingCell,
isPostingPost,
diff --git a/src/lib/forum/relevance.test.ts b/src/lib/forum/relevance.test.ts
index 5f89371..81caf21 100644
--- a/src/lib/forum/relevance.test.ts
+++ b/src/lib/forum/relevance.test.ts
@@ -1,12 +1,12 @@
import { RelevanceCalculator } from './relevance';
import { Post, Comment, Cell, User } from '@/types';
-import { MessageType, VoteMessage } from '@/lib/waku/types';
+import { VoteMessage, MessageType } from '@/lib/waku/types';
import { expect, describe, beforeEach, it } from 'vitest';
import { UserVerificationStatus } from './types';
describe('RelevanceCalculator', () => {
let calculator: RelevanceCalculator;
- let mockUserVerificationStatus: UserVerificationStatus;
+ let mockUserVerificationStatus: any;
beforeEach(() => {
calculator = new RelevanceCalculator();
@@ -55,6 +55,46 @@ describe('RelevanceCalculator', () => {
expect(result.details.authorVerificationBonus).toBeGreaterThan(0);
});
+ it('should correctly identify verified users with ENS ownership', () => {
+ const verifiedUser: User = {
+ address: 'user1',
+ walletType: 'ethereum',
+ verificationStatus: 'verified-owner',
+ ensOwnership: true,
+ ensName: 'test.eth',
+ lastChecked: Date.now()
+ };
+
+ const isVerified = calculator.isUserVerified(verifiedUser);
+ expect(isVerified).toBe(true);
+ });
+
+ it('should correctly identify verified users with Ordinal ownership', () => {
+ const verifiedUser: User = {
+ address: 'user3',
+ walletType: 'bitcoin',
+ verificationStatus: 'verified-owner',
+ ordinalOwnership: true,
+ lastChecked: Date.now()
+ };
+
+ const isVerified = calculator.isUserVerified(verifiedUser);
+ expect(isVerified).toBe(true);
+ });
+
+ it('should correctly identify unverified users', () => {
+ const unverifiedUser: User = {
+ address: 'user2',
+ walletType: 'ethereum',
+ verificationStatus: 'unverified',
+ ensOwnership: false,
+ lastChecked: Date.now()
+ };
+
+ const isVerified = calculator.isUserVerified(unverifiedUser);
+ expect(isVerified).toBe(false);
+ });
+
it('should apply moderation penalty', () => {
const post: Post = {
id: '1',
@@ -138,4 +178,36 @@ describe('RelevanceCalculator', () => {
expect(recentResult.score).toBeGreaterThan(oldResult.score);
});
});
+
+ describe('buildUserVerificationStatus', () => {
+ it('should correctly build verification status map from users array', () => {
+ const users: User[] = [
+ {
+ address: 'user1',
+ walletType: 'ethereum',
+ verificationStatus: 'verified-owner',
+ ensOwnership: true,
+ ensName: 'test.eth',
+ lastChecked: Date.now()
+ },
+ {
+ address: 'user2',
+ walletType: 'bitcoin',
+ verificationStatus: 'unverified',
+ ordinalOwnership: false,
+ lastChecked: Date.now()
+ }
+ ];
+
+ const status = calculator.buildUserVerificationStatus(users);
+
+ expect(status['user1'].isVerified).toBe(true);
+ expect(status['user1'].hasENS).toBe(true);
+ expect(status['user1'].hasOrdinal).toBe(false);
+
+ expect(status['user2'].isVerified).toBe(false);
+ expect(status['user2'].hasENS).toBe(false);
+ expect(status['user2'].hasOrdinal).toBe(false);
+ });
+ });
});
diff --git a/src/lib/forum/relevance.ts b/src/lib/forum/relevance.ts
index 037bb54..39d7140 100644
--- a/src/lib/forum/relevance.ts
+++ b/src/lib/forum/relevance.ts
@@ -217,7 +217,8 @@ export class RelevanceCalculator {
status[user.address] = {
isVerified: this.isUserVerified(user),
hasENS: !!user.ensOwnership,
- hasOrdinal: !!user.ordinalOwnership
+ hasOrdinal: !!user.ordinalOwnership,
+ ensName: user.ensName
};
});
diff --git a/src/lib/forum/types.ts b/src/lib/forum/types.ts
index 4016bef..985c7f3 100644
--- a/src/lib/forum/types.ts
+++ b/src/lib/forum/types.ts
@@ -21,5 +21,6 @@ export interface RelevanceScoreDetails {
isVerified: boolean;
hasENS: boolean;
hasOrdinal: boolean;
+ ensName?: string;
};
}
\ No newline at end of file