From cbe93afe7ab5cc7a61d1e2564be7c25fbe7c088b Mon Sep 17 00:00:00 2001 From: Danish Arora Date: Fri, 5 Sep 2025 14:06:31 +0530 Subject: [PATCH] chore: use only one content topic + message channel --- src/components/examples/HookDemoComponent.tsx | 390 ------------------ src/lib/waku/CodecManager.ts | 51 +-- src/lib/waku/constants.ts | 14 +- src/lib/waku/core/ReliableMessaging.ts | 55 ++- 4 files changed, 43 insertions(+), 467 deletions(-) delete mode 100644 src/components/examples/HookDemoComponent.tsx diff --git a/src/components/examples/HookDemoComponent.tsx b/src/components/examples/HookDemoComponent.tsx deleted file mode 100644 index 2e5870a..0000000 --- a/src/components/examples/HookDemoComponent.tsx +++ /dev/null @@ -1,390 +0,0 @@ -import React, { useState, useEffect } from 'react'; -import { - useForumData, - useAuth, - useUserVotes, - useForumActions, - useUserActions, - useAuthActions, - usePermissions, - useNetworkStatus, - useForumSelectors, -} from '@/hooks'; -import { useAuth as useAuthContext } from '@/contexts/useAuth'; -import { DelegationFullStatus } from '@/lib/delegation'; -import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; -import { Badge } from '@/components/ui/badge'; -import { Button } from '@/components/ui/button'; -import { Separator } from '@/components/ui/separator'; - -/** - * Demonstration component showing how to use the new reactive hooks - * This replaces direct context usage and business logic in components - */ -export function HookDemoComponent() { - // Core data hooks - reactive and optimized - const forumData = useForumData(); - const auth = useAuth(); - const { getDelegationStatus } = useAuthContext(); - const [delegationStatus, setDelegationStatus] = useState(null); - - // Load delegation status - useEffect(() => { - getDelegationStatus().then(setDelegationStatus).catch(console.error); - }, [getDelegationStatus]); - - // Derived hooks for specific data - const userVotes = useUserVotes(); - - // Action hooks with loading states and error handling - const forumActions = useForumActions(); - const userActions = useUserActions(); - const authActions = useAuthActions(); - - // Utility hooks for permissions and status - const permissions = usePermissions(); - const networkStatus = useNetworkStatus(); - - // Selector hooks for data transformation - const selectors = useForumSelectors(forumData); - - // Example of using selectors - const trendingPosts = selectors.selectTrendingPosts(); - const stats = selectors.selectStats(); - - // Example action handlers (no business logic in component!) - const handleCreatePost = async () => { - const result = await forumActions.createPost( - 'example-cell-id', - 'Example Post Title', - 'This is an example post created using the new hook system!' - ); - - if (result) { - console.log('Post created successfully:', result); - } - }; - - const handleVotePost = async (postId: string, isUpvote: boolean) => { - const success = await forumActions.votePost(postId, isUpvote); - if (success) { - console.log(`${isUpvote ? 'Upvoted' : 'Downvoted'} post ${postId}`); - } - }; - - const handleUpdateCallSign = async () => { - const success = await userActions.updateCallSign('NewCallSign'); - if (success) { - console.log('Call sign updated successfully'); - } - }; - - const handleDelegateKey = async () => { - const success = await authActions.delegateKey('7days'); - if (success) { - console.log('Key delegated successfully'); - } - }; - - if (forumData.isInitialLoading) { - return
Loading forum data...
; - } - - return ( -
-

Reactive Hook System Demo

- - {/* Network Status */} - - - - Network Status - - {networkStatus.getStatusMessage()} - - - - -
-
- Waku: {networkStatus.connections.waku.status} -
-
- Wallet: {networkStatus.connections.wallet.status} -
-
- Delegation:{' '} - {networkStatus.connections.delegation.status} -
-
-
-
- - {/* Auth Status */} - - - Authentication Status - - -
- User: {auth.getDisplayName()} - {auth.getVerificationBadge() && ( - {auth.getVerificationBadge()} - )} -
- -
-
- Verification Level: {auth.verificationStatus} -
-
- Delegation Active:{' '} - {delegationStatus?.isValid ? 'Yes' : 'No'} -
-
- -
- - -
-
-
- - {/* Permissions */} - - - User Permissions - - -
-
-
- Can Vote: - - {permissions.canVote ? 'Yes' : 'No'} - -
-
- Can Post: - - {permissions.canPost ? 'Yes' : 'No'} - -
-
- Can Comment: - - {permissions.canComment ? 'Yes' : 'No'} - -
-
-
-
- Vote Reason: {permissions.voteReason} -
-
- Post Reason: {permissions.postReason} -
-
- Comment Reason: {permissions.commentReason} -
-
-
-
-
- - {/* Forum Data Overview */} - - - Forum Statistics - - -
-
-
{stats.totalCells}
-
Cells
-
-
-
{stats.totalPosts}
-
Posts
-
-
-
{stats.totalComments}
-
Comments
-
-
-
{stats.verifiedUsers}
-
- Verified Users -
-
-
-
-
- - {/* Trending Posts */} - - - Trending Posts (via Selectors) - - - {trendingPosts.slice(0, 3).map(post => ( -
-

{post.title}

-

- Score: {post.upvotes.length - post.downvotes.length} | Author:{' '} - {post.author.slice(0, 8)}... | Cell:{' '} - {forumData.cells.find(c => c.id === post.cellId)?.name || - 'Unknown'} -

-
- - -
-
- ))} -
-
- - {/* User Voting History */} - - - Your Voting Activity - - -
-
-
{userVotes.totalVotes}
-
Total Votes
-
-
-
- {Math.round(userVotes.upvoteRatio * 100)}% -
-
Upvote Ratio
-
-
-
- {userVotes.votedPosts.size} -
-
Posts Voted
-
-
-
-
- - {/* Action States */} - - - Action States - - -
-
- Creating Post: - - {forumActions.isCreatingPost ? 'Active' : 'Idle'} - -
-
- Voting: - - {forumActions.isVoting ? 'Active' : 'Idle'} - -
-
- Updating Profile: - - {userActions.isUpdatingProfile ? 'Active' : 'Idle'} - -
-
-
-
- - {/* Actions */} - - - Example Actions - - -
- - -
-
-
- - - -
-

- Key Benefits Demonstrated: -

-
    -
  • - ✅ Zero business logic in this component - all handled by hooks -
  • -
  • - ✅ Reactive updates - data changes automatically trigger re-renders -
  • -
  • ✅ Centralized permissions - consistent across all components
  • -
  • ✅ Optimized selectors - expensive computations are memoized
  • -
  • ✅ Loading states and error handling built into actions
  • -
  • ✅ Type-safe interfaces for all hook returns
  • -
-
-
- ); -} diff --git a/src/lib/waku/CodecManager.ts b/src/lib/waku/CodecManager.ts index 559c567..06f9a74 100644 --- a/src/lib/waku/CodecManager.ts +++ b/src/lib/waku/CodecManager.ts @@ -6,27 +6,16 @@ import { CommentMessage, VoteMessage, } from '../../types/waku'; -import { CONTENT_TOPICS } from './constants'; +import { CONTENT_TOPIC } from './constants'; import { OpchanMessage } from '@/types/forum'; export class CodecManager { - private encoders: Map = new Map(); - private decoders: Map> = new Map(); + private encoder: IEncoder; + private decoder: IDecoder; constructor(private node: LightNode) { - this.encoders = new Map( - Object.values(MessageType).map(type => [ - type, - this.node.createEncoder({ contentTopic: CONTENT_TOPICS[type] }), - ]) - ); - - this.decoders = new Map( - Object.values(MessageType).map(type => [ - type, - this.node.createDecoder({ contentTopic: CONTENT_TOPICS[type] }), - ]) - ); + this.encoder = this.node.createEncoder({ contentTopic: CONTENT_TOPIC }); + this.decoder = this.node.createDecoder({ contentTopic: CONTENT_TOPIC }); } /** @@ -61,38 +50,30 @@ export class CodecManager { } /** - * Get encoder for a specific message type + * Get the single encoder for all message types */ - getEncoder(messageType: MessageType): IEncoder { - const encoder = this.encoders.get(messageType); - if (!encoder) { - throw new Error(`No encoder found for message type: ${messageType}`); - } - return encoder; + getEncoder(): IEncoder { + return this.encoder; } /** - * Get decoder for a specific message type + * Get the single decoder for all message types */ - getDecoder(messageType: MessageType): IDecoder { - const decoder = this.decoders.get(messageType); - if (!decoder) { - throw new Error(`No decoder found for message type: ${messageType}`); - } - return decoder; + getDecoder(): IDecoder { + return this.decoder; } /** - * Get all decoders for subscribing to multiple message types + * Get all decoders (returns single decoder in array for compatibility) */ getAllDecoders(): IDecoder[] { - return Array.from(this.decoders.values()); + return [this.decoder]; } /** - * Get decoders for specific message types + * Get decoders for specific message types (returns single decoder for all types) */ - getDecoders(messageTypes: MessageType[]): IDecoder[] { - return messageTypes.map(type => this.getDecoder(type)); + getDecoders(_messageTypes: MessageType[]): IDecoder[] { + return [this.decoder]; } } diff --git a/src/lib/waku/constants.ts b/src/lib/waku/constants.ts index 0319bff..cb8aa17 100644 --- a/src/lib/waku/constants.ts +++ b/src/lib/waku/constants.ts @@ -1,16 +1,8 @@ -import { MessageType } from '../../types/waku'; - /** - * Content topics for different message types + * Single content topic for all message types + * Different message types are parsed from the message content itself */ -export const CONTENT_TOPICS: Record = { - [MessageType.CELL]: '/opchan-sds-ab/1/cell/proto', - [MessageType.POST]: '/opchan-sds-ab/1/post/proto', - [MessageType.COMMENT]: '/opchan-ab-xyz/1/comment/proto', - [MessageType.VOTE]: '/opchan-sds-ab/1/vote/proto', - [MessageType.MODERATE]: '/opchan-sds-ab/1/moderate/proto', - [MessageType.USER_PROFILE_UPDATE]: '/opchan-sds-ab/1/profile/proto', -}; +export const CONTENT_TOPIC = '/opchan-sds-ab/1/messages/proto'; /** * Bootstrap nodes for the Waku network diff --git a/src/lib/waku/core/ReliableMessaging.ts b/src/lib/waku/core/ReliableMessaging.ts index 8ef4c32..1f6dc96 100644 --- a/src/lib/waku/core/ReliableMessaging.ts +++ b/src/lib/waku/core/ReliableMessaging.ts @@ -4,7 +4,6 @@ import { ReliableChannel, ReliableChannelEvent, } from '@waku/sdk'; -import { MessageType } from '../../../types/waku'; import { CodecManager } from '../CodecManager'; import { generateStringId } from '@/lib/utils'; import { OpchanMessage } from '@/types/forum'; @@ -18,43 +17,38 @@ export interface MessageStatusCallback { export type IncomingMessageCallback = (message: OpchanMessage) => void; export class ReliableMessaging { - private channels: Map> = - new Map(); + private channel: ReliableChannel | null = null; private messageCallbacks: Map = new Map(); private incomingMessageCallbacks: Set = new Set(); private codecManager: CodecManager; constructor(node: LightNode) { this.codecManager = new CodecManager(node); - this.initializeChannels(node); + this.initializeChannel(node); } - private async initializeChannels(node: LightNode): Promise { - for (const type of Object.values(MessageType)) { - const encoder = this.codecManager.getEncoder(type); - const decoder = this.codecManager.getDecoder(type); - const senderId = generateStringId(); - const channelId = `opchan-${type}`; + private async initializeChannel(node: LightNode): Promise { + const encoder = this.codecManager.getEncoder(); + const decoder = this.codecManager.getDecoder(); + const senderId = generateStringId(); + const channelId = 'opchan-messages'; - try { - const channel = await ReliableChannel.create( - node, - channelId, - senderId, - encoder, - decoder - ); - this.channels.set(type, channel); - this.setupChannelListeners(channel, type); - } catch (error) { - console.error(`Failed to create reliable channel for ${type}:`, error); - } + try { + this.channel = await ReliableChannel.create( + node, + channelId, + senderId, + encoder, + decoder + ); + this.setupChannelListeners(this.channel); + } catch (error) { + console.error('Failed to create reliable channel:', error); } } private setupChannelListeners( - channel: ReliableChannel, - type: MessageType + channel: ReliableChannel ): void { channel.addEventListener(ReliableChannelEvent.InMessageReceived, event => { try { @@ -68,7 +62,7 @@ export class ReliableMessaging { ); } } catch (error) { - console.error(`Failed to process incoming message for ${type}:`, error); + console.error('Failed to process incoming message:', error); } }); @@ -105,9 +99,8 @@ export class ReliableMessaging { message: OpchanMessage, statusCallback?: MessageStatusCallback ): Promise { - const channel = this.channels.get(message.type); - if (!channel) { - throw new Error(`No reliable channel for message type: ${message.type}`); + if (!this.channel) { + throw new Error('Reliable channel not initialized'); } const encodedMessage = this.codecManager.encodeMessage(message); @@ -118,7 +111,7 @@ export class ReliableMessaging { } try { - return channel.send(encodedMessage); + return this.channel.send(encodedMessage); } catch (error) { this.messageCallbacks.delete(messageId); throw error; @@ -133,6 +126,6 @@ export class ReliableMessaging { public cleanup(): void { this.messageCallbacks.clear(); this.incomingMessageCallbacks.clear(); - this.channels.clear(); + this.channel = null; } }