From 635e3eb80a3d78d4ffaabc0143f98d0b249e743e Mon Sep 17 00:00:00 2001 From: Danish Arora Date: Mon, 1 Sep 2025 16:31:57 +0530 Subject: [PATCH] chore: convert forum actions into a class --- src/components/CellList.tsx | 2 +- src/contexts/ForumContext.tsx | 74 +-- src/lib/forum/ForumActions.ts | 534 ++++++++++++++++++ .../{relevance.ts => RelevanceCalculator.ts} | 0 src/lib/forum/__tests__/relevance.test.ts | 2 +- src/lib/forum/actions.ts | 504 ----------------- src/lib/forum/transformers.ts | 2 +- src/lib/{forum => utils}/sorting.ts | 0 src/pages/FeedPage.tsx | 2 +- 9 files changed, 559 insertions(+), 561 deletions(-) create mode 100644 src/lib/forum/ForumActions.ts rename src/lib/forum/{relevance.ts => RelevanceCalculator.ts} (100%) delete mode 100644 src/lib/forum/actions.ts rename src/lib/{forum => utils}/sorting.ts (100%) diff --git a/src/components/CellList.tsx b/src/components/CellList.tsx index cc545c3..2ce0a30 100644 --- a/src/components/CellList.tsx +++ b/src/components/CellList.tsx @@ -20,7 +20,7 @@ import { } from '@/components/ui/select'; import { CypherImage } from './ui/CypherImage'; import { RelevanceIndicator } from './ui/relevance-indicator'; -import { sortCells, SortOption } from '@/lib/forum/sorting'; +import { sortCells, SortOption } from '@/lib/utils/sorting'; const CellList = () => { const { cells, isInitialLoading, posts, refreshData, isRefreshing } = diff --git a/src/contexts/ForumContext.tsx b/src/contexts/ForumContext.tsx index 173dbd4..d2dd19c 100644 --- a/src/contexts/ForumContext.tsx +++ b/src/contexts/ForumContext.tsx @@ -9,15 +9,7 @@ import { Cell, Post, Comment, OpchanMessage } from '@/types/forum'; import { User, EVerificationStatus, DisplayPreference } from '@/types/identity'; import { useToast } from '@/components/ui/use-toast'; import { useAuth } from '@/contexts/useAuth'; -import { - createPost, - createComment, - vote, - createCell, - moderatePost, - moderateComment, - moderateUser, -} from '@/lib/forum/actions'; +import { ForumActions } from '@/lib/forum/ForumActions'; import { setupPeriodicQueries, monitorNetworkHealth, @@ -25,7 +17,7 @@ import { } from '@/lib/waku/network'; import messageManager from '@/lib/waku'; import { getDataFromCache } from '@/lib/forum/transformers'; -import { RelevanceCalculator } from '@/lib/forum/relevance'; +import { RelevanceCalculator } from '@/lib/forum/RelevanceCalculator'; import { UserVerificationStatus } from '@/types/forum'; import { CryptoService } from '@/lib/services'; import { getEnsName } from '@wagmi/core'; @@ -107,6 +99,10 @@ export function ForumProvider({ children }: { children: React.ReactNode }) { const { currentUser, isAuthenticated } = useAuth(); const cryptoService = useMemo(() => new CryptoService(), []); + const forumActions = useMemo( + () => new ForumActions(cryptoService), + [cryptoService] + ); // Transform message cache data to the expected types const updateStateFromCache = useCallback(() => { @@ -289,7 +285,7 @@ export function ForumProvider({ children }: { children: React.ReactNode }) { description: 'Sending your post to the network...', }); - const result = await createPost( + const result = await forumActions.createPost( { cellId, title, content, currentUser, isAuthenticated }, updateStateFromCache ); @@ -322,11 +318,8 @@ export function ForumProvider({ children }: { children: React.ReactNode }) { description: 'Sending your comment to the network...', }); - const result = await createComment( - postId, - content, - currentUser, - isAuthenticated, + const result = await forumActions.createComment( + { postId, content, currentUser, isAuthenticated }, updateStateFromCache ); @@ -359,11 +352,8 @@ export function ForumProvider({ children }: { children: React.ReactNode }) { description: 'Recording your vote on the network...', }); - const result = await vote( - postId, - isUpvote, - currentUser, - isAuthenticated, + const result = await forumActions.vote( + { targetId: postId, isUpvote, currentUser, isAuthenticated }, updateStateFromCache ); @@ -397,11 +387,8 @@ export function ForumProvider({ children }: { children: React.ReactNode }) { description: 'Recording your vote on the network...', }); - const result = await vote( - commentId, - isUpvote, - currentUser, - isAuthenticated, + const result = await forumActions.vote( + { targetId: commentId, isUpvote, currentUser, isAuthenticated }, updateStateFromCache ); @@ -435,12 +422,8 @@ export function ForumProvider({ children }: { children: React.ReactNode }) { description: 'Sending your cell to the network...', }); - const result = await createCell( - name, - description, - icon, - currentUser, - isAuthenticated, + const result = await forumActions.createCell( + { name, description, icon, currentUser, isAuthenticated }, updateStateFromCache ); @@ -473,13 +456,8 @@ export function ForumProvider({ children }: { children: React.ReactNode }) { description: 'Sending moderation message to the network...', }); - const result = await moderatePost( - cellId, - postId, - reason, - currentUser, - isAuthenticated, - cellOwner, + const result = await forumActions.moderatePost( + { cellId, postId, reason, currentUser, isAuthenticated, cellOwner }, updateStateFromCache ); @@ -511,13 +489,8 @@ export function ForumProvider({ children }: { children: React.ReactNode }) { description: 'Sending moderation message to the network...', }); - const result = await moderateComment( - cellId, - commentId, - reason, - currentUser, - isAuthenticated, - cellOwner, + const result = await forumActions.moderateComment( + { cellId, commentId, reason, currentUser, isAuthenticated, cellOwner }, updateStateFromCache ); @@ -544,13 +517,8 @@ export function ForumProvider({ children }: { children: React.ReactNode }) { reason: string | undefined, cellOwner: string ) => { - const result = await moderateUser( - cellId, - userAddress, - reason, - currentUser, - isAuthenticated, - cellOwner, + const result = await forumActions.moderateUser( + { cellId, userAddress, reason, currentUser, isAuthenticated, cellOwner }, updateStateFromCache ); diff --git a/src/lib/forum/ForumActions.ts b/src/lib/forum/ForumActions.ts new file mode 100644 index 0000000..09fdf56 --- /dev/null +++ b/src/lib/forum/ForumActions.ts @@ -0,0 +1,534 @@ +import { v4 as uuidv4 } from 'uuid'; +import { + MessageType, + UnsignedCellMessage, + UnsignedCommentMessage, + UnsignedPostMessage, + UnsignedVoteMessage, + UnsignedModerateMessage, + CellMessage, + CommentMessage, + PostMessage, +} from '@/types/waku'; +import { Cell, Comment, Post } from '@/types/forum'; +import { EVerificationStatus, User } from '@/types/identity'; +import { transformCell, transformComment, transformPost } from './transformers'; +import { MessageService, CryptoService } from '@/lib/services'; + +type ActionResult = { + success: boolean; + data?: T; + error?: string; +}; + +export class ForumActions { + private cryptoService: CryptoService; + private messageService: MessageService; + + constructor(cryptoService?: CryptoService) { + this.cryptoService = cryptoService || new CryptoService(); + this.messageService = new MessageService(this.cryptoService); + } + + /* ------------------------------------------------------------------ + POST / COMMENT / CELL CREATION + -------------------------------------------------------------------*/ + + async createPost( + params: PostCreationParams, + updateStateFromCache: () => void + ): Promise> { + const { cellId, title, content, currentUser, isAuthenticated } = params; + + if (!isAuthenticated || !currentUser) { + return { + success: false, + error: + 'Authentication required. You need to connect your wallet to post.', + }; + } + + // Check if user has basic verification or better, or owns ENS/Ordinal + const hasENSOrOrdinal = !!( + currentUser.ensDetails || currentUser.ordinalDetails + ); + const isVerified = + currentUser.verificationStatus === EVerificationStatus.VERIFIED_OWNER || + currentUser.verificationStatus === EVerificationStatus.VERIFIED_BASIC || + hasENSOrOrdinal; + + if ( + !isVerified && + (currentUser.verificationStatus === EVerificationStatus.UNVERIFIED || + currentUser.verificationStatus === EVerificationStatus.VERIFYING) + ) { + return { + success: false, + error: + 'Verification required. Please complete wallet verification to post.', + }; + } + + try { + const postId = uuidv4(); + const postMessage: UnsignedPostMessage = { + type: MessageType.POST, + id: postId, + cellId, + title, + content, + timestamp: Date.now(), + author: currentUser.address, + }; + + const result = await this.messageService.sendMessage(postMessage); + if (!result.success) { + return { + success: false, + error: result.error || 'Failed to create post. Please try again.', + }; + } + + updateStateFromCache(); + const transformedPost = transformPost(result.message! as PostMessage); + if (!transformedPost) { + return { + success: false, + error: 'Failed to transform post data.', + }; + } + return { + success: true, + data: transformedPost, + }; + } catch (error) { + console.error('Error creating post:', error); + return { + success: false, + error: 'Failed to create post. Please try again.', + }; + } + } + + async createComment( + params: CommentCreationParams, + updateStateFromCache: () => void + ): Promise> { + const { postId, content, currentUser, isAuthenticated } = params; + + if (!isAuthenticated || !currentUser) { + return { + success: false, + error: + 'Authentication required. You need to connect your wallet to comment.', + }; + } + + // Check if user has basic verification or better, or owns ENS/Ordinal + const hasENSOrOrdinal = !!( + currentUser.ensDetails || currentUser.ordinalDetails + ); + const isVerified = + currentUser.verificationStatus === EVerificationStatus.VERIFIED_OWNER || + currentUser.verificationStatus === EVerificationStatus.VERIFIED_BASIC || + hasENSOrOrdinal; + + if ( + !isVerified && + (currentUser.verificationStatus === EVerificationStatus.UNVERIFIED || + currentUser.verificationStatus === EVerificationStatus.VERIFYING) + ) { + return { + success: false, + error: + 'Verification required. Please complete wallet verification to comment.', + }; + } + + try { + const commentId = uuidv4(); + const commentMessage: UnsignedCommentMessage = { + type: MessageType.COMMENT, + id: commentId, + postId, + content, + timestamp: Date.now(), + author: currentUser.address, + }; + + const result = await this.messageService.sendMessage(commentMessage); + if (!result.success) { + return { + success: false, + error: result.error || 'Failed to add comment. Please try again.', + }; + } + + updateStateFromCache(); + const transformedComment = transformComment( + result.message! as CommentMessage + ); + if (!transformedComment) { + return { + success: false, + error: 'Failed to transform comment data.', + }; + } + return { + success: true, + data: transformedComment, + }; + } catch (error) { + console.error('Error creating comment:', error); + return { + success: false, + error: 'Failed to add comment. Please try again.', + }; + } + } + + async createCell( + params: CellCreationParams, + updateStateFromCache: () => void + ): Promise> { + const { name, description, icon, currentUser, isAuthenticated } = params; + + if (!isAuthenticated || !currentUser) { + return { + success: false, + error: + 'Authentication required. You need to verify Ordinal ownership to create a cell.', + }; + } + + try { + const cellId = uuidv4(); + const cellMessage: UnsignedCellMessage = { + type: MessageType.CELL, + id: cellId, + name, + description, + ...(icon && { icon }), + timestamp: Date.now(), + author: currentUser.address, + }; + + const result = await this.messageService.sendMessage(cellMessage); + if (!result.success) { + return { + success: false, + error: result.error || 'Failed to create cell. Please try again.', + }; + } + + updateStateFromCache(); + const transformedCell = transformCell(result.message! as CellMessage); + if (!transformedCell) { + return { + success: false, + error: 'Failed to transform cell data.', + }; + } + return { + success: true, + data: transformedCell, + }; + } catch (error) { + console.error('Error creating cell:', error); + return { + success: false, + error: 'Failed to create cell. Please try again.', + }; + } + } + + /* ------------------------------------------------------------------ + VOTING + -------------------------------------------------------------------*/ + + async vote( + params: VoteParams, + updateStateFromCache: () => void + ): Promise> { + const { targetId, isUpvote, currentUser, isAuthenticated } = params; + + if (!isAuthenticated || !currentUser) { + return { + success: false, + error: + 'Authentication required. You need to connect your wallet to vote.', + }; + } + + // Check if user has basic verification or better, or owns ENS/Ordinal + const hasENSOrOrdinal = !!( + currentUser.ensDetails || currentUser.ordinalDetails + ); + const isVerified = + currentUser.verificationStatus === EVerificationStatus.VERIFIED_OWNER || + currentUser.verificationStatus === EVerificationStatus.VERIFIED_BASIC || + hasENSOrOrdinal; + + if ( + !isVerified && + (currentUser.verificationStatus === EVerificationStatus.UNVERIFIED || + currentUser.verificationStatus === EVerificationStatus.VERIFYING) + ) { + return { + success: false, + error: + 'Verification required. Please complete wallet verification to vote.', + }; + } + + try { + const voteId = uuidv4(); + const voteMessage: UnsignedVoteMessage = { + type: MessageType.VOTE, + id: voteId, + targetId, + value: isUpvote ? 1 : -1, + timestamp: Date.now(), + author: currentUser.address, + }; + + const result = await this.messageService.sendMessage(voteMessage); + if (!result.success) { + return { + success: false, + error: + result.error || 'Failed to register your vote. Please try again.', + }; + } + + updateStateFromCache(); + return { + success: true, + data: true, + }; + } catch (error) { + console.error('Error voting:', error); + return { + success: false, + error: 'Failed to register your vote. Please try again.', + }; + } + } + + /* ------------------------------------------------------------------ + MODERATION + -------------------------------------------------------------------*/ + + async moderatePost( + params: PostModerationParams, + updateStateFromCache: () => void + ): Promise> { + const { cellId, postId, reason, currentUser, isAuthenticated, cellOwner } = params; + + if (!isAuthenticated || !currentUser) { + return { + success: false, + error: + 'Authentication required. You need to verify Ordinal ownership to moderate posts.', + }; + } + if (currentUser.address !== cellOwner) { + return { + success: false, + error: 'Not authorized. Only the cell admin can moderate posts.', + }; + } + + try { + const modMsg: UnsignedModerateMessage = { + type: MessageType.MODERATE, + id: uuidv4(), + cellId, + targetType: 'post', + targetId: postId, + reason, + timestamp: Date.now(), + author: currentUser.address, + }; + + const result = await this.messageService.sendMessage(modMsg); + if (!result.success) { + return { + success: false, + error: result.error || 'Failed to moderate post. Please try again.', + }; + } + + updateStateFromCache(); + return { + success: true, + data: true, + }; + } catch (error) { + console.error('Error moderating post:', error); + return { + success: false, + error: 'Failed to moderate post. Please try again.', + }; + } + } + + async moderateComment( + params: CommentModerationParams, + updateStateFromCache: () => void + ): Promise> { + const { cellId, commentId, reason, currentUser, isAuthenticated, cellOwner } = params; + + if (!isAuthenticated || !currentUser) { + return { + success: false, + error: + 'Authentication required. You need to verify Ordinal ownership to moderate comments.', + }; + } + if (currentUser.address !== cellOwner) { + return { + success: false, + error: 'Not authorized. Only the cell admin can moderate comments.', + }; + } + + try { + const modMsg: UnsignedModerateMessage = { + type: MessageType.MODERATE, + id: uuidv4(), + cellId, + targetType: 'comment', + targetId: commentId, + reason, + timestamp: Date.now(), + author: currentUser.address, + }; + + const result = await this.messageService.sendMessage(modMsg); + if (!result.success) { + return { + success: false, + error: + result.error || 'Failed to moderate comment. Please try again.', + }; + } + + updateStateFromCache(); + return { + success: true, + data: true, + }; + } catch (error) { + console.error('Error moderating comment:', error); + return { + success: false, + error: 'Failed to moderate comment. Please try again.', + }; + } + } + + async moderateUser( + params: UserModerationParams, + updateStateFromCache: () => void + ): Promise> { + const { cellId, userAddress, reason, currentUser, isAuthenticated, cellOwner } = params; + + if (!isAuthenticated || !currentUser) { + return { + success: false, + error: + 'Authentication required. You need to verify Ordinal ownership to moderate users.', + }; + } + if (currentUser.address !== cellOwner) { + return { + success: false, + error: 'Not authorized. Only the cell admin can moderate users.', + }; + } + + try { + const modMsg: UnsignedModerateMessage = { + type: MessageType.MODERATE, + id: uuidv4(), + cellId, + targetType: 'user', + targetId: userAddress, + reason, + author: currentUser.address, + timestamp: Date.now(), + }; + + const result = await this.messageService.sendMessage(modMsg); + if (!result.success) { + return { + success: false, + error: result.error || 'Failed to moderate user. Please try again.', + }; + } + + updateStateFromCache(); + return { + success: true, + data: true, + }; + } catch (error) { + console.error('Error moderating user:', error); + return { + success: false, + error: 'Failed to moderate user. Please try again.', + }; + } + } +} + +// Base interface for all actions that require user authentication +interface BaseActionParams { + currentUser: User | null; + isAuthenticated: boolean; +} + +// Parameter interfaces for all action methods +interface PostCreationParams extends BaseActionParams { + cellId: string; + title: string; + content: string; +} + +interface CommentCreationParams extends BaseActionParams { + postId: string; + content: string; +} + +interface CellCreationParams extends BaseActionParams { + name: string; + description: string; + icon?: string; +} + +interface VoteParams extends BaseActionParams { + targetId: string; + isUpvote: boolean; +} + +interface PostModerationParams extends BaseActionParams { + cellId: string; + postId: string; + reason?: string; + cellOwner: string; +} + +interface CommentModerationParams extends BaseActionParams { + cellId: string; + commentId: string; + reason?: string; + cellOwner: string; +} + +interface UserModerationParams extends BaseActionParams { + cellId: string; + userAddress: string; + reason?: string; + cellOwner: string; +} \ No newline at end of file diff --git a/src/lib/forum/relevance.ts b/src/lib/forum/RelevanceCalculator.ts similarity index 100% rename from src/lib/forum/relevance.ts rename to src/lib/forum/RelevanceCalculator.ts diff --git a/src/lib/forum/__tests__/relevance.test.ts b/src/lib/forum/__tests__/relevance.test.ts index 113eb77..25366ba 100644 --- a/src/lib/forum/__tests__/relevance.test.ts +++ b/src/lib/forum/__tests__/relevance.test.ts @@ -1,4 +1,4 @@ -import { RelevanceCalculator } from '../relevance'; +import { RelevanceCalculator } from '../RelevanceCalculator'; import { Post, Comment, UserVerificationStatus } from '@/types/forum'; import { User, EVerificationStatus, DisplayPreference } from '@/types/identity'; import { VoteMessage, MessageType } from '@/types/waku'; diff --git a/src/lib/forum/actions.ts b/src/lib/forum/actions.ts deleted file mode 100644 index a85a0a0..0000000 --- a/src/lib/forum/actions.ts +++ /dev/null @@ -1,504 +0,0 @@ -import { v4 as uuidv4 } from 'uuid'; -import { - MessageType, - UnsignedCellMessage, - UnsignedCommentMessage, - UnsignedPostMessage, - UnsignedVoteMessage, - UnsignedModerateMessage, - CellMessage, - CommentMessage, - PostMessage, -} from '@/types/waku'; -import { Cell, Comment, Post } from '@/types/forum'; -import { EVerificationStatus, User } from '@/types/identity'; -import { transformCell, transformComment, transformPost } from './transformers'; -import { MessageService, CryptoService } from '@/lib/services'; - -// Result types for action functions -type ActionResult = { - success: boolean; - data?: T; - error?: string; -}; - -/* ------------------------------------------------------------------ - POST / COMMENT / CELL CREATION --------------------------------------------------------------------*/ - -interface PostCreationParams { - cellId: string; - title: string; - content: string; - currentUser: User | null; - isAuthenticated: boolean; -} - -export const createPost = async ( - { cellId, title, content, currentUser, isAuthenticated }: PostCreationParams, - updateStateFromCache: () => void -): Promise> => { - if (!isAuthenticated || !currentUser) { - return { - success: false, - error: - 'Authentication required. You need to connect your wallet to post.', - }; - } - - // Check if user has basic verification or better, or owns ENS/Ordinal - const hasENSOrOrdinal = !!( - currentUser.ensDetails || currentUser.ordinalDetails - ); - const isVerified = - currentUser.verificationStatus === EVerificationStatus.VERIFIED_OWNER || - currentUser.verificationStatus === EVerificationStatus.VERIFIED_BASIC || - hasENSOrOrdinal; - - if ( - !isVerified && - (currentUser.verificationStatus === EVerificationStatus.UNVERIFIED || - currentUser.verificationStatus === EVerificationStatus.VERIFYING) - ) { - return { - success: false, - error: - 'Verification required. Please complete wallet verification to post.', - }; - } - - try { - const postId = uuidv4(); - const postMessage: UnsignedPostMessage = { - type: MessageType.POST, - id: postId, - cellId, - title, - content, - timestamp: Date.now(), - author: currentUser.address, - }; - - const cryptoService = new CryptoService(); - const messageService = new MessageService(cryptoService); - const result = await messageService.sendMessage(postMessage); - if (!result.success) { - return { - success: false, - error: result.error || 'Failed to create post. Please try again.', - }; - } - - updateStateFromCache(); - const transformedPost = transformPost(result.message! as PostMessage); - if (!transformedPost) { - return { - success: false, - error: 'Failed to transform post data.', - }; - } - return { - success: true, - data: transformedPost, - }; - } catch (error) { - console.error('Error creating post:', error); - return { - success: false, - error: 'Failed to create post. Please try again.', - }; - } -}; - -export const createComment = async ( - postId: string, - content: string, - currentUser: User | null, - isAuthenticated: boolean, - updateStateFromCache: () => void -): Promise> => { - if (!isAuthenticated || !currentUser) { - return { - success: false, - error: - 'Authentication required. You need to connect your wallet to comment.', - }; - } - - // Check if user has basic verification or better, or owns ENS/Ordinal - const hasENSOrOrdinal = !!( - currentUser.ensDetails || currentUser.ordinalDetails - ); - const isVerified = - currentUser.verificationStatus === EVerificationStatus.VERIFIED_OWNER || - currentUser.verificationStatus === EVerificationStatus.VERIFIED_BASIC || - hasENSOrOrdinal; - - if ( - !isVerified && - (currentUser.verificationStatus === EVerificationStatus.UNVERIFIED || - currentUser.verificationStatus === EVerificationStatus.VERIFYING) - ) { - return { - success: false, - error: - 'Verification required. Please complete wallet verification to comment.', - }; - } - - try { - const commentId = uuidv4(); - const commentMessage: UnsignedCommentMessage = { - type: MessageType.COMMENT, - id: commentId, - postId, - content, - timestamp: Date.now(), - author: currentUser.address, - }; - - const cryptoService = new CryptoService(); - const messageService = new MessageService(cryptoService); - const result = await messageService.sendMessage(commentMessage); - if (!result.success) { - return { - success: false, - error: result.error || 'Failed to add comment. Please try again.', - }; - } - - updateStateFromCache(); - const transformedComment = transformComment( - result.message! as CommentMessage - ); - if (!transformedComment) { - return { - success: false, - error: 'Failed to transform comment data.', - }; - } - return { - success: true, - data: transformedComment, - }; - } catch (error) { - console.error('Error creating comment:', error); - return { - success: false, - error: 'Failed to add comment. Please try again.', - }; - } -}; - -export const createCell = async ( - name: string, - description: string, - icon: string | undefined, - currentUser: User | null, - isAuthenticated: boolean, - updateStateFromCache: () => void -): Promise> => { - if (!isAuthenticated || !currentUser) { - return { - success: false, - error: - 'Authentication required. You need to verify Ordinal ownership to create a cell.', - }; - } - - try { - const cellId = uuidv4(); - const cellMessage: UnsignedCellMessage = { - type: MessageType.CELL, - id: cellId, - name, - description, - ...(icon && { icon }), - timestamp: Date.now(), - author: currentUser.address, - }; - - const cryptoService = new CryptoService(); - const messageService = new MessageService(cryptoService); - const result = await messageService.sendMessage(cellMessage); - if (!result.success) { - return { - success: false, - error: result.error || 'Failed to create cell. Please try again.', - }; - } - - updateStateFromCache(); - const transformedCell = transformCell(result.message! as CellMessage); - if (!transformedCell) { - return { - success: false, - error: 'Failed to transform cell data.', - }; - } - return { - success: true, - data: transformedCell, - }; - } catch (error) { - console.error('Error creating cell:', error); - return { - success: false, - error: 'Failed to create cell. Please try again.', - }; - } -}; - -/* ------------------------------------------------------------------ - VOTING --------------------------------------------------------------------*/ - -export const vote = async ( - targetId: string, - isUpvote: boolean, - currentUser: User | null, - isAuthenticated: boolean, - updateStateFromCache: () => void -): Promise> => { - if (!isAuthenticated || !currentUser) { - return { - success: false, - error: - 'Authentication required. You need to connect your wallet to vote.', - }; - } - - // Check if user has basic verification or better, or owns ENS/Ordinal - const hasENSOrOrdinal = !!( - currentUser.ensDetails || currentUser.ordinalDetails - ); - const isVerified = - currentUser.verificationStatus === EVerificationStatus.VERIFIED_OWNER || - currentUser.verificationStatus === EVerificationStatus.VERIFIED_BASIC || - hasENSOrOrdinal; - - if ( - !isVerified && - (currentUser.verificationStatus === EVerificationStatus.UNVERIFIED || - currentUser.verificationStatus === EVerificationStatus.VERIFYING) - ) { - return { - success: false, - error: - 'Verification required. Please complete wallet verification to vote.', - }; - } - - try { - const voteId = uuidv4(); - const voteMessage: UnsignedVoteMessage = { - type: MessageType.VOTE, - id: voteId, - targetId, - value: isUpvote ? 1 : -1, - timestamp: Date.now(), - author: currentUser.address, - }; - - const cryptoService = new CryptoService(); - const messageService = new MessageService(cryptoService); - const result = await messageService.sendMessage(voteMessage); - if (!result.success) { - return { - success: false, - error: - result.error || 'Failed to register your vote. Please try again.', - }; - } - - updateStateFromCache(); - return { - success: true, - data: true, - }; - } catch (error) { - console.error('Error voting:', error); - return { - success: false, - error: 'Failed to register your vote. Please try again.', - }; - } -}; - -/* ------------------------------------------------------------------ - MODERATION --------------------------------------------------------------------*/ - -export const moderatePost = async ( - cellId: string, - postId: string, - reason: string | undefined, - currentUser: User | null, - isAuthenticated: boolean, - cellOwner: string, - updateStateFromCache: () => void -): Promise> => { - if (!isAuthenticated || !currentUser) { - return { - success: false, - error: - 'Authentication required. You need to verify Ordinal ownership to moderate posts.', - }; - } - if (currentUser.address !== cellOwner) { - return { - success: false, - error: 'Not authorized. Only the cell admin can moderate posts.', - }; - } - - try { - const modMsg: UnsignedModerateMessage = { - type: MessageType.MODERATE, - id: uuidv4(), - cellId, - targetType: 'post', - targetId: postId, - reason, - timestamp: Date.now(), - author: currentUser.address, - }; - const cryptoService = new CryptoService(); - const messageService = new MessageService(cryptoService); - const result = await messageService.sendMessage(modMsg); - if (!result.success) { - return { - success: false, - error: result.error || 'Failed to moderate post. Please try again.', - }; - } - - updateStateFromCache(); - return { - success: true, - data: true, - }; - } catch (error) { - console.error('Error moderating post:', error); - return { - success: false, - error: 'Failed to moderate post. Please try again.', - }; - } -}; - -export const moderateComment = async ( - cellId: string, - commentId: string, - reason: string | undefined, - currentUser: User | null, - isAuthenticated: boolean, - cellOwner: string, - updateStateFromCache: () => void -): Promise> => { - if (!isAuthenticated || !currentUser) { - return { - success: false, - error: - 'Authentication required. You need to verify Ordinal ownership to moderate comments.', - }; - } - if (currentUser.address !== cellOwner) { - return { - success: false, - error: 'Not authorized. Only the cell admin can moderate comments.', - }; - } - - try { - const modMsg: UnsignedModerateMessage = { - type: MessageType.MODERATE, - id: uuidv4(), - cellId, - targetType: 'comment', - targetId: commentId, - reason, - timestamp: Date.now(), - author: currentUser.address, - }; - const cryptoService = new CryptoService(); - const messageService = new MessageService(cryptoService); - const result = await messageService.sendMessage(modMsg); - if (!result.success) { - return { - success: false, - error: result.error || 'Failed to moderate comment. Please try again.', - }; - } - - updateStateFromCache(); - return { - success: true, - data: true, - }; - } catch (error) { - console.error('Error moderating comment:', error); - return { - success: false, - error: 'Failed to moderate comment. Please try again.', - }; - } -}; - -export const moderateUser = async ( - cellId: string, - userAddress: string, - reason: string | undefined, - currentUser: User | null, - isAuthenticated: boolean, - cellOwner: string, - updateStateFromCache: () => void -): Promise> => { - if (!isAuthenticated || !currentUser) { - return { - success: false, - error: - 'Authentication required. You need to verify Ordinal ownership to moderate users.', - }; - } - if (currentUser.address !== cellOwner) { - return { - success: false, - error: 'Not authorized. Only the cell admin can moderate users.', - }; - } - - try { - const modMsg: UnsignedModerateMessage = { - type: MessageType.MODERATE, - id: uuidv4(), - cellId, - targetType: 'user', - targetId: userAddress, - reason, - author: currentUser.address, - timestamp: Date.now(), - }; - const cryptoService = new CryptoService(); - const messageService = new MessageService(cryptoService); - const result = await messageService.sendMessage(modMsg); - if (!result.success) { - return { - success: false, - error: result.error || 'Failed to moderate user. Please try again.', - }; - } - - updateStateFromCache(); - return { - success: true, - data: true, - }; - } catch (error) { - console.error('Error moderating user:', error); - return { - success: false, - error: 'Failed to moderate user. Please try again.', - }; - } -}; diff --git a/src/lib/forum/transformers.ts b/src/lib/forum/transformers.ts index da762f7..dbc9c65 100644 --- a/src/lib/forum/transformers.ts +++ b/src/lib/forum/transformers.ts @@ -6,7 +6,7 @@ import { VoteMessage, } from '@/types/waku'; import messageManager from '@/lib/waku'; -import { RelevanceCalculator } from './relevance'; +import { RelevanceCalculator } from './RelevanceCalculator'; import { UserVerificationStatus } from '@/types/forum'; import { MessageValidator } from '@/lib/utils/MessageValidator'; diff --git a/src/lib/forum/sorting.ts b/src/lib/utils/sorting.ts similarity index 100% rename from src/lib/forum/sorting.ts rename to src/lib/utils/sorting.ts diff --git a/src/pages/FeedPage.tsx b/src/pages/FeedPage.tsx index 772c9c4..0ae7120 100644 --- a/src/pages/FeedPage.tsx +++ b/src/pages/FeedPage.tsx @@ -13,7 +13,7 @@ import PostCard from '@/components/PostCard'; import FeedSidebar from '@/components/FeedSidebar'; import { useForum } from '@/contexts/useForum'; import { useAuth } from '@/contexts/useAuth'; -import { sortPosts, SortOption } from '@/lib/forum/sorting'; +import { sortPosts, SortOption } from '@/lib/utils/sorting'; const FeedPage: React.FC = () => { const { posts, comments, isInitialLoading, isRefreshing, refreshData } =