mirror of
https://github.com/logos-messaging/OpChan.git
synced 2026-01-05 14:23:06 +00:00
feat: implement waku
This commit is contained in:
parent
95401cdb5b
commit
56c9c8d889
@ -8,7 +8,7 @@ import { Textarea } from '@/components/ui/textarea';
|
|||||||
import { Skeleton } from '@/components/ui/skeleton';
|
import { Skeleton } from '@/components/ui/skeleton';
|
||||||
import { ArrowLeft, ArrowUp, ArrowDown, Clock, MessageCircle, Send } from 'lucide-react';
|
import { ArrowLeft, ArrowUp, ArrowDown, Clock, MessageCircle, Send } from 'lucide-react';
|
||||||
import { formatDistanceToNow } from 'date-fns';
|
import { formatDistanceToNow } from 'date-fns';
|
||||||
import { Comment } from '@/types/forum';
|
import { Comment } from '@/types';
|
||||||
|
|
||||||
const PostDetail = () => {
|
const PostDetail = () => {
|
||||||
const { postId } = useParams<{ postId: string }>();
|
const { postId } = useParams<{ postId: string }>();
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
|
|
||||||
import React, { createContext, useContext, useState, useEffect } from 'react';
|
import React, { createContext, useContext, useState, useEffect } from 'react';
|
||||||
import { useToast } from '@/components/ui/use-toast';
|
import { useToast } from '@/components/ui/use-toast';
|
||||||
import { User } from '@/types/forum';
|
import { User } from '@/types';
|
||||||
|
|
||||||
interface AuthContextType {
|
interface AuthContextType {
|
||||||
currentUser: User | null;
|
currentUser: User | null;
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import React, { createContext, useContext, useState, useEffect } from 'react';
|
import React, { createContext, useContext, useState, useEffect } from 'react';
|
||||||
import { useToast } from '@/components/ui/use-toast';
|
import { useToast } from '@/components/ui/use-toast';
|
||||||
import { Cell, Post, Comment } from '@/types/forum';
|
import { Cell, Post, Comment } from '@/types';
|
||||||
import { mockCells, mockPosts, mockComments } from '@/data/mockData';
|
import { mockCells, mockPosts, mockComments } from '@/data/mockData';
|
||||||
import { useAuth } from './AuthContext';
|
import { useAuth } from './AuthContext';
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
|
|
||||||
import { Cell, Post, Comment } from "../types/forum";
|
import { Cell, Post, Comment } from "../types";
|
||||||
|
|
||||||
export const mockCells: Cell[] = [
|
export const mockCells: Cell[] = [
|
||||||
{
|
{
|
||||||
|
|||||||
@ -1,35 +1,39 @@
|
|||||||
|
import { createDecoder, createEncoder } from '@waku/sdk';
|
||||||
import { MessageType } from './types';
|
import { MessageType } from './types';
|
||||||
import { OpchanMessage, CellMessage, PostMessage, CommentMessage, VoteMessage } from './types';
|
import { CellMessage, PostMessage, CommentMessage, VoteMessage } from './types';
|
||||||
|
import { CONTENT_TOPICS } from './constants';
|
||||||
|
import { OpchanMessage } from '@/types';
|
||||||
|
|
||||||
|
export const encoders = {
|
||||||
|
[MessageType.CELL]: createEncoder({
|
||||||
|
contentTopic: CONTENT_TOPICS['cell'],
|
||||||
|
}),
|
||||||
|
[MessageType.POST]: createEncoder({
|
||||||
|
contentTopic: CONTENT_TOPICS['post'],
|
||||||
|
}),
|
||||||
|
[MessageType.COMMENT]: createEncoder({
|
||||||
|
contentTopic: CONTENT_TOPICS['comment'],
|
||||||
|
}),
|
||||||
|
[MessageType.VOTE]: createEncoder({
|
||||||
|
contentTopic: CONTENT_TOPICS['vote'],
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
|
||||||
|
export const decoders = {
|
||||||
|
[MessageType.CELL]: createDecoder(CONTENT_TOPICS['cell']),
|
||||||
|
[MessageType.POST]: createDecoder(CONTENT_TOPICS['post']),
|
||||||
|
[MessageType.COMMENT]: createDecoder(CONTENT_TOPICS['comment']),
|
||||||
|
[MessageType.VOTE]: createDecoder(CONTENT_TOPICS['vote']),
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Encode a message object into a Uint8Array for transmission
|
* Encode a message object into a Uint8Array for transmission
|
||||||
*/
|
*/
|
||||||
export function encodeMessage(message: OpchanMessage): Uint8Array {
|
export function encodeMessage(message: OpchanMessage): Uint8Array {
|
||||||
// Convert the message to a JSON string
|
|
||||||
const messageJson = JSON.stringify(message);
|
const messageJson = JSON.stringify(message);
|
||||||
|
|
||||||
// Convert the string to a Uint8Array
|
|
||||||
return new TextEncoder().encode(messageJson);
|
return new TextEncoder().encode(messageJson);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Decode a message from a Uint8Array based on its type
|
|
||||||
*/
|
|
||||||
export function decodeMessage(payload: Uint8Array, type?: MessageType): OpchanMessage {
|
|
||||||
// Convert the Uint8Array to a string
|
|
||||||
const messageJson = new TextDecoder().decode(payload);
|
|
||||||
|
|
||||||
// Parse the JSON string to an object
|
|
||||||
const message = JSON.parse(messageJson) as OpchanMessage;
|
|
||||||
|
|
||||||
// Validate the message type if specified
|
|
||||||
if (type && message.type !== type) {
|
|
||||||
throw new Error(`Expected message of type ${type}, but got ${message.type}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return the decoded message
|
|
||||||
return message;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Type-specific decoders
|
* Type-specific decoders
|
||||||
@ -48,4 +52,19 @@ export function decodeCommentMessage(payload: Uint8Array): CommentMessage {
|
|||||||
|
|
||||||
export function decodeVoteMessage(payload: Uint8Array): VoteMessage {
|
export function decodeVoteMessage(payload: Uint8Array): VoteMessage {
|
||||||
return decodeMessage(payload, MessageType.VOTE) as VoteMessage;
|
return decodeMessage(payload, MessageType.VOTE) as VoteMessage;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decode a message from a Uint8Array based on its type
|
||||||
|
*/
|
||||||
|
function decodeMessage(payload: Uint8Array, type?: MessageType): OpchanMessage {
|
||||||
|
const messageJson = new TextDecoder().decode(payload);
|
||||||
|
const message = JSON.parse(messageJson) as OpchanMessage;
|
||||||
|
|
||||||
|
if (type && message.type !== type) {
|
||||||
|
throw new Error(`Expected message of type ${type}, but got ${message.type}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return the decoded message
|
||||||
|
return message;
|
||||||
|
}
|
||||||
@ -1,5 +1,5 @@
|
|||||||
|
import { NetworkConfig, ShardInfo } from "@waku/sdk";
|
||||||
import { MessageType } from "./types";
|
import { MessageType } from "./types";
|
||||||
import type { QueryRequestParams } from '@waku/sdk'
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Content topics for different message types
|
* Content topics for different message types
|
||||||
@ -11,20 +11,15 @@ export const CONTENT_TOPICS: Record<MessageType, string> = {
|
|||||||
[MessageType.VOTE]: '/opchan/1/vote/proto'
|
[MessageType.VOTE]: '/opchan/1/vote/proto'
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const NETWORK_CONFIG: NetworkConfig = {
|
||||||
|
contentTopics: Object.values(CONTENT_TOPICS),
|
||||||
|
shards: [1],
|
||||||
|
clusterId: 42
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Bootstrap nodes for the Waku network
|
* Bootstrap nodes for the Waku network
|
||||||
* These are public Waku nodes that our node will connect to on startup
|
* These are public Waku nodes that our node will connect to on startup
|
||||||
*/
|
*/
|
||||||
export const BOOTSTRAP_NODES = [
|
export const BOOTSTRAP_NODES = [
|
||||||
'/dns4/node-01.ac-cn-hongkong-c.wakuv2.test.statusim.net/tcp/443/wss/p2p/16Uiu2HAkykgaECHswi3YKJ5dMLbq2kPVCo89fcyTd2Hz8tHPeV4y',
|
];
|
||||||
'/dns4/node-01.do-ams3.wakuv2.test.statusim.net/tcp/443/wss/p2p/16Uiu2HAmPLe7Mzm8TsYUubgCAW1aJoeFScxrLj8ppHFivPo97bUZ',
|
|
||||||
'/dns4/node-01.gc-us-central1-a.wakuv2.test.statusim.net/tcp/443/wss/p2p/16Uiu2HAmJb2e28qLXxT5kZxVUUoJt72EMzNGXB47Rxx5hw3q4YjS'
|
|
||||||
];
|
|
||||||
|
|
||||||
// Default store query options
|
|
||||||
// export const DEFAULT_STORE_QUERY_OPTIONS: QueryRequestParams = {
|
|
||||||
// contentTopics: [CONTENT_TOPICS[MessageType.CELL], CONTENT_TOPICS[MessageType.POST], CONTENT_TOPICS[MessageType.COMMENT], CONTENT_TOPICS[MessageType.VOTE]],
|
|
||||||
// includeData: true,
|
|
||||||
// paginationForward: false,
|
|
||||||
// pubsubTopic: ""
|
|
||||||
// };
|
|
||||||
68
src/lib/waku/lightpush_filter.ts
Normal file
68
src/lib/waku/lightpush_filter.ts
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
import { LightNode } from "@waku/sdk";
|
||||||
|
import { decodeCellMessage, decodeCommentMessage, decodePostMessage, decoders, decodeVoteMessage, encodeMessage, encoders } from "./codec";
|
||||||
|
import { CellMessage, CommentMessage, MessageType, PostMessage, VoteMessage } from "./types";
|
||||||
|
import { CONTENT_TOPICS } from "./constants";
|
||||||
|
import { OpchanMessage } from "@/types";
|
||||||
|
|
||||||
|
export class EphemeralProtocolsManager {
|
||||||
|
private node: LightNode;
|
||||||
|
|
||||||
|
constructor(node: LightNode) {
|
||||||
|
this.node = node;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async sendMessage(message: OpchanMessage) {
|
||||||
|
const encodedMessage = encodeMessage(message);
|
||||||
|
await this.node.lightPush.send(encoders[message.type], {
|
||||||
|
payload: encodedMessage
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public async subscribeToMessages(types: MessageType[]) {
|
||||||
|
const result: (CellMessage | PostMessage | CommentMessage | VoteMessage)[] = [];
|
||||||
|
|
||||||
|
const subscription = await this.node.filter.subscribe(Object.values(decoders), async (message) => {
|
||||||
|
const {contentTopic, payload} = message;
|
||||||
|
const toDecode = [
|
||||||
|
types.includes(MessageType.CELL) ? decodeCellMessage(payload) : null,
|
||||||
|
types.includes(MessageType.POST) ? decodePostMessage(payload) : null,
|
||||||
|
types.includes(MessageType.COMMENT) ? decodeCommentMessage(payload) : null,
|
||||||
|
types.includes(MessageType.VOTE) ? decodeVoteMessage(payload) : null
|
||||||
|
]
|
||||||
|
const decodedMessage = await Promise.race(toDecode);
|
||||||
|
|
||||||
|
let parsedMessage: OpchanMessage | null = null;
|
||||||
|
switch(contentTopic) {
|
||||||
|
case CONTENT_TOPICS['cell']:
|
||||||
|
parsedMessage = decodedMessage as CellMessage;
|
||||||
|
break;
|
||||||
|
case CONTENT_TOPICS['post']:
|
||||||
|
parsedMessage = decodedMessage as PostMessage;
|
||||||
|
break;
|
||||||
|
case CONTENT_TOPICS['comment']:
|
||||||
|
parsedMessage = decodedMessage as CommentMessage;
|
||||||
|
break;
|
||||||
|
case CONTENT_TOPICS['vote']:
|
||||||
|
parsedMessage = decodedMessage as VoteMessage;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
console.error(`Unknown content topic: ${contentTopic}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parsedMessage) {
|
||||||
|
result.push(parsedMessage);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (subscription.error) {
|
||||||
|
throw new Error(subscription.error);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (subscription.results.successes.length === 0) {
|
||||||
|
throw new Error("No successes");
|
||||||
|
}
|
||||||
|
|
||||||
|
return {result, subscription};
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,142 +0,0 @@
|
|||||||
import { MessageType } from './constants';
|
|
||||||
import { DecodedMessage } from '@waku/sdk';
|
|
||||||
import { Cell, Post, Comment } from '@/types/forum';
|
|
||||||
|
|
||||||
// Base structure for all messages
|
|
||||||
export interface WakuMessageBase {
|
|
||||||
messageType: MessageType;
|
|
||||||
timestamp: number;
|
|
||||||
sender: string; // Bitcoin address of sender
|
|
||||||
signature?: string; // Signature to verify sender
|
|
||||||
}
|
|
||||||
|
|
||||||
// Message structures for different content types
|
|
||||||
export interface CellMessage extends WakuMessageBase {
|
|
||||||
messageType: MessageType.CELL;
|
|
||||||
cellId: string;
|
|
||||||
name: string;
|
|
||||||
description: string;
|
|
||||||
icon: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface PostMessage extends WakuMessageBase {
|
|
||||||
messageType: MessageType.POST;
|
|
||||||
postId: string;
|
|
||||||
cellId: string;
|
|
||||||
content: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface CommentMessage extends WakuMessageBase {
|
|
||||||
messageType: MessageType.COMMENT;
|
|
||||||
commentId: string;
|
|
||||||
postId: string;
|
|
||||||
content: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface VoteMessage extends WakuMessageBase {
|
|
||||||
messageType: MessageType.VOTE;
|
|
||||||
targetId: string; // postId or commentId
|
|
||||||
isUpvote: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Type for all possible messages
|
|
||||||
export type WakuMessage =
|
|
||||||
| CellMessage
|
|
||||||
| PostMessage
|
|
||||||
| CommentMessage
|
|
||||||
| VoteMessage;
|
|
||||||
|
|
||||||
// Utility functions for converting between message types and application models
|
|
||||||
export function cellToMessage(cell: Cell, sender: string): CellMessage {
|
|
||||||
return {
|
|
||||||
messageType: MessageType.CELL,
|
|
||||||
timestamp: Date.now(),
|
|
||||||
sender,
|
|
||||||
cellId: cell.id,
|
|
||||||
name: cell.name,
|
|
||||||
description: cell.description,
|
|
||||||
icon: cell.icon
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function messageToCell(message: CellMessage): Cell {
|
|
||||||
return {
|
|
||||||
id: message.cellId,
|
|
||||||
name: message.name,
|
|
||||||
description: message.description,
|
|
||||||
icon: message.icon
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function postToMessage(post: Post, sender: string): PostMessage {
|
|
||||||
return {
|
|
||||||
messageType: MessageType.POST,
|
|
||||||
timestamp: Date.now(),
|
|
||||||
sender,
|
|
||||||
postId: post.id,
|
|
||||||
cellId: post.cellId,
|
|
||||||
content: post.content
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function messageToPost(message: PostMessage): Post {
|
|
||||||
return {
|
|
||||||
id: message.postId,
|
|
||||||
cellId: message.cellId,
|
|
||||||
authorAddress: message.sender,
|
|
||||||
content: message.content,
|
|
||||||
timestamp: message.timestamp,
|
|
||||||
upvotes: [],
|
|
||||||
downvotes: []
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function commentToMessage(comment: Comment, sender: string): CommentMessage {
|
|
||||||
return {
|
|
||||||
messageType: MessageType.COMMENT,
|
|
||||||
timestamp: Date.now(),
|
|
||||||
sender,
|
|
||||||
commentId: comment.id,
|
|
||||||
postId: comment.postId,
|
|
||||||
content: comment.content
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function messageToComment(message: CommentMessage): Comment {
|
|
||||||
return {
|
|
||||||
id: message.commentId,
|
|
||||||
postId: message.postId,
|
|
||||||
authorAddress: message.sender,
|
|
||||||
content: message.content,
|
|
||||||
timestamp: message.timestamp,
|
|
||||||
upvotes: [],
|
|
||||||
downvotes: []
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parse message from decoded waku message
|
|
||||||
export function parseMessage(decodedMessage: DecodedMessage): WakuMessage | null {
|
|
||||||
try {
|
|
||||||
if (!decodedMessage.payload) return null;
|
|
||||||
|
|
||||||
const messageString = new TextDecoder().decode(decodedMessage.payload);
|
|
||||||
const message = JSON.parse(messageString) as WakuMessage;
|
|
||||||
|
|
||||||
// Validate message has required fields
|
|
||||||
if (!message.messageType || !message.timestamp || !message.sender) {
|
|
||||||
console.error('Invalid message format:', message);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return message;
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Error parsing message:', error);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Serialize message to payload bytes
|
|
||||||
export function serializeMessage(message: WakuMessage): Uint8Array {
|
|
||||||
const messageString = JSON.stringify(message);
|
|
||||||
return new TextEncoder().encode(messageString);
|
|
||||||
}
|
|
||||||
100
src/lib/waku/messages_parser.ts
Normal file
100
src/lib/waku/messages_parser.ts
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
import { IDecodedMessage } from '@waku/sdk';
|
||||||
|
import { Cell, Post, Comment } from '@/types';
|
||||||
|
import { CellMessage, CommentMessage, MessageType, PostMessage } from './types';
|
||||||
|
import { OpchanMessage } from '@/types';
|
||||||
|
// Utility functions for converting between message types and application models
|
||||||
|
export function cellToMessage(cell: Cell, sender: string): CellMessage {
|
||||||
|
return {
|
||||||
|
type: MessageType.CELL,
|
||||||
|
timestamp: Date.now(),
|
||||||
|
author: sender,
|
||||||
|
id: cell.id,
|
||||||
|
name: cell.name,
|
||||||
|
description: cell.description,
|
||||||
|
icon: cell.icon
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function messageToCell(message: CellMessage): Cell {
|
||||||
|
return {
|
||||||
|
id: message.id,
|
||||||
|
name: message.name,
|
||||||
|
description: message.description,
|
||||||
|
icon: message.icon
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function postToMessage(post: Post, sender: string): PostMessage {
|
||||||
|
return {
|
||||||
|
type: MessageType.POST,
|
||||||
|
timestamp: Date.now(),
|
||||||
|
author: sender,
|
||||||
|
id: post.id,
|
||||||
|
title: post.title,
|
||||||
|
cellId: post.cellId,
|
||||||
|
content: post.content
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function messageToPost(message: PostMessage): Post {
|
||||||
|
return {
|
||||||
|
id: message.id,
|
||||||
|
cellId: message.cellId,
|
||||||
|
authorAddress: message.author,
|
||||||
|
content: message.content,
|
||||||
|
timestamp: message.timestamp,
|
||||||
|
title: message.title,
|
||||||
|
upvotes: [],
|
||||||
|
downvotes: []
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function commentToMessage(comment: Comment, sender: string): CommentMessage {
|
||||||
|
return {
|
||||||
|
type: MessageType.COMMENT,
|
||||||
|
timestamp: Date.now(),
|
||||||
|
author: sender,
|
||||||
|
id: comment.id,
|
||||||
|
postId: comment.postId,
|
||||||
|
content: comment.content
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function messageToComment(message: CommentMessage): Comment {
|
||||||
|
return {
|
||||||
|
id: message.id,
|
||||||
|
postId: message.postId,
|
||||||
|
authorAddress: message.author,
|
||||||
|
content: message.content,
|
||||||
|
timestamp: message.timestamp,
|
||||||
|
upvotes: [],
|
||||||
|
downvotes: []
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse message from decoded waku message
|
||||||
|
export function parseMessage(decodedMessage: IDecodedMessage): OpchanMessage | null {
|
||||||
|
try {
|
||||||
|
if (!decodedMessage.payload) return null;
|
||||||
|
|
||||||
|
const messageString = new TextDecoder().decode(decodedMessage.payload);
|
||||||
|
const message = JSON.parse(messageString) as OpchanMessage;
|
||||||
|
|
||||||
|
// Validate message has required fields
|
||||||
|
if (!message.type || !message.timestamp || !message.author) {
|
||||||
|
console.error('Invalid message format:', message);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return message;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error parsing message:', error);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Serialize message to payload bytes
|
||||||
|
export function serializeMessage(message: OpchanMessage): Uint8Array {
|
||||||
|
const messageString = JSON.stringify(message);
|
||||||
|
return new TextEncoder().encode(messageString);
|
||||||
|
}
|
||||||
50
src/lib/waku/store.ts
Normal file
50
src/lib/waku/store.ts
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
import { IDecodedMessage, LightNode } from "@waku/sdk";
|
||||||
|
import { decoders, decodeCellMessage, decodePostMessage, decodeCommentMessage, decodeVoteMessage } from "./codec";
|
||||||
|
import { CONTENT_TOPICS } from "./constants";
|
||||||
|
import { CellMessage, PostMessage, CommentMessage, VoteMessage } from "./types";
|
||||||
|
|
||||||
|
class StoreManager {
|
||||||
|
private node: LightNode;
|
||||||
|
|
||||||
|
constructor(node: LightNode) {
|
||||||
|
this.node = node;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async queryStore() {
|
||||||
|
const result: (CellMessage | PostMessage | CommentMessage | VoteMessage)[] = [];
|
||||||
|
|
||||||
|
await this.node.store.queryWithOrderedCallback(
|
||||||
|
Object.values(decoders),
|
||||||
|
(message: IDecodedMessage) => {
|
||||||
|
const {contentTopic, payload} = message;
|
||||||
|
let parsedMessage: (CellMessage | PostMessage | CommentMessage | VoteMessage) | null = null;
|
||||||
|
|
||||||
|
switch(contentTopic) {
|
||||||
|
case CONTENT_TOPICS['cell']:
|
||||||
|
parsedMessage = decodeCellMessage(payload) as CellMessage;
|
||||||
|
break;
|
||||||
|
case CONTENT_TOPICS['post']:
|
||||||
|
parsedMessage = decodePostMessage(payload) as PostMessage;
|
||||||
|
break;
|
||||||
|
case CONTENT_TOPICS['comment']:
|
||||||
|
parsedMessage = decodeCommentMessage(payload) as CommentMessage;
|
||||||
|
break;
|
||||||
|
case CONTENT_TOPICS['vote']:
|
||||||
|
parsedMessage = decodeVoteMessage(payload) as VoteMessage;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
console.error(`Unknown content topic: ${contentTopic}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parsedMessage) {
|
||||||
|
result.push(parsedMessage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default StoreManager;
|
||||||
@ -25,6 +25,7 @@ export interface CellMessage extends BaseMessage {
|
|||||||
id: string;
|
id: string;
|
||||||
name: string;
|
name: string;
|
||||||
description: string;
|
description: string;
|
||||||
|
icon: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -45,7 +46,6 @@ export interface CommentMessage extends BaseMessage {
|
|||||||
type: MessageType.COMMENT;
|
type: MessageType.COMMENT;
|
||||||
id: string;
|
id: string;
|
||||||
postId: string;
|
postId: string;
|
||||||
parentId?: string; // Optional for nested comments
|
|
||||||
content: string;
|
content: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -56,24 +56,7 @@ export interface VoteMessage extends BaseMessage {
|
|||||||
type: MessageType.VOTE;
|
type: MessageType.VOTE;
|
||||||
id: string;
|
id: string;
|
||||||
targetId: string; // ID of the post or comment being voted on
|
targetId: string; // ID of the post or comment being voted on
|
||||||
value: number; // 1 for upvote, -1 for downvote
|
value: 1 | -1;
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Union type of all possible message types
|
|
||||||
*/
|
|
||||||
export type OpchanMessage = CellMessage | PostMessage | CommentMessage | VoteMessage;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Listener function type for Waku service events
|
|
||||||
*/
|
|
||||||
export type MessageListener<T extends OpchanMessage> = (message: T) => void;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Subscription object returned when registering listeners
|
|
||||||
*/
|
|
||||||
export interface Subscription {
|
|
||||||
unsubscribe: () => void;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -1,3 +1,6 @@
|
|||||||
|
import { CellMessage, CommentMessage, PostMessage, VoteMessage } from "@/lib/waku/types";
|
||||||
|
|
||||||
|
export type OpchanMessage = CellMessage | PostMessage | CommentMessage | VoteMessage;
|
||||||
|
|
||||||
export interface User {
|
export interface User {
|
||||||
address: string;
|
address: string;
|
||||||
@ -17,10 +20,11 @@ export interface Post {
|
|||||||
id: string;
|
id: string;
|
||||||
cellId: string;
|
cellId: string;
|
||||||
authorAddress: string;
|
authorAddress: string;
|
||||||
|
title: string;
|
||||||
content: string;
|
content: string;
|
||||||
timestamp: number;
|
timestamp: number;
|
||||||
upvotes: string[];
|
upvotes: VoteMessage[];
|
||||||
downvotes: string[];
|
downvotes: VoteMessage[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Comment {
|
export interface Comment {
|
||||||
@ -29,6 +33,6 @@ export interface Comment {
|
|||||||
authorAddress: string;
|
authorAddress: string;
|
||||||
content: string;
|
content: string;
|
||||||
timestamp: number;
|
timestamp: number;
|
||||||
upvotes: string[];
|
upvotes: VoteMessage[];
|
||||||
downvotes: string[];
|
downvotes: VoteMessage[];
|
||||||
}
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user