mirror of
https://github.com/logos-messaging/examples.waku.org.git
synced 2026-01-02 12:53:08 +00:00
add messages functionality
This commit is contained in:
parent
1b30a24183
commit
0e5764139a
@ -4,12 +4,18 @@ import { Subtitle } from "@/components/Subtitle";
|
||||
import { Status } from "@/components/Status";
|
||||
import { Button } from "@/components/Button";
|
||||
import { useStore, useWaku } from "@/hooks";
|
||||
import { MessageContent } from "@/services/waku";
|
||||
|
||||
export const Waku: React.FunctionComponent<{}> = () => {
|
||||
const { wakuStatus } = useStore();
|
||||
const { onSend, messages } = useWaku();
|
||||
|
||||
const { nick, message, onNickChange, onMessageChange } = useMessage();
|
||||
const { nick, text, onNickChange, onMessageChange, resetText } = useMessage();
|
||||
|
||||
const onSendClick = async () => {
|
||||
await onSend(nick, text);
|
||||
resetText();
|
||||
};
|
||||
|
||||
const renderedMessages = React.useMemo(
|
||||
() => messages.map(renderMessage),
|
||||
@ -18,7 +24,9 @@ export const Waku: React.FunctionComponent<{}> = () => {
|
||||
|
||||
return (
|
||||
<Block className="mt-10">
|
||||
<Subtitle>Waku</Subtitle>
|
||||
<Subtitle>
|
||||
Waku<p className="text-xs">(select credentials to initialize)</p>
|
||||
</Subtitle>
|
||||
<Status text="Waku status" mark={wakuStatus} />
|
||||
|
||||
<Block className="mt-4">
|
||||
@ -49,16 +57,16 @@ export const Waku: React.FunctionComponent<{}> = () => {
|
||||
<input
|
||||
type="text"
|
||||
id="message-input"
|
||||
value={message}
|
||||
value={text}
|
||||
onChange={onMessageChange}
|
||||
placeholder="Text your message here"
|
||||
className="w-full mr-2 bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"
|
||||
/>
|
||||
</Block>
|
||||
<Button onClick={() => onSend({ nick, message })}>Send</Button>
|
||||
<Button onClick={onSendClick}>Send</Button>
|
||||
</Block>
|
||||
|
||||
<Block className="mt-4">
|
||||
<Block className="mt-8">
|
||||
<p className="text-l mb-4">Messages</p>
|
||||
<div>
|
||||
<ul>{renderedMessages}</ul>
|
||||
@ -70,36 +78,40 @@ export const Waku: React.FunctionComponent<{}> = () => {
|
||||
|
||||
function useMessage() {
|
||||
const [nick, setNick] = React.useState<string>("");
|
||||
const [message, setMessage] = React.useState<string>("");
|
||||
const [text, setText] = React.useState<string>("");
|
||||
|
||||
const onNickChange = (e: React.SyntheticEvent<HTMLInputElement>) => {
|
||||
setNick(e.currentTarget.value || "");
|
||||
};
|
||||
|
||||
const onMessageChange = (e: React.SyntheticEvent<HTMLInputElement>) => {
|
||||
setMessage(e.currentTarget.value || "");
|
||||
setText(e.currentTarget.value || "");
|
||||
};
|
||||
|
||||
const resetText = () => {
|
||||
setText("");
|
||||
};
|
||||
|
||||
return {
|
||||
nick,
|
||||
message,
|
||||
text,
|
||||
resetText,
|
||||
onNickChange,
|
||||
onMessageChange,
|
||||
};
|
||||
}
|
||||
|
||||
function renderMessage() {
|
||||
/*
|
||||
wakuMessage.proofState = !!wakuMessage.rateLimitProof
|
||||
? "verifying..."
|
||||
: "no proof attached";
|
||||
|
||||
wakuMessage.msg = `
|
||||
(${nick})
|
||||
<strong>${utils.bytesToUtf8(text)}</strong>
|
||||
<i>[${time.toISOString()}]</i>
|
||||
`;
|
||||
messagesList.innerHTML += `<li>${message.msg} - [epoch: ${message.epoch}, proof: ${message.proofState} ]</li>`;
|
||||
*/
|
||||
return <></>;
|
||||
function renderMessage(content: MessageContent) {
|
||||
return (
|
||||
<li key={`${content.nick}-${content.time}`} className="mb-4">
|
||||
<p>
|
||||
<span className="text-lg">{content.nick}</span>
|
||||
<span className="text-sm font-bold">
|
||||
({content.proofStatus}, {content.time})
|
||||
</span>
|
||||
:
|
||||
</p>
|
||||
<p>{content.text}</p>
|
||||
</li>
|
||||
);
|
||||
}
|
||||
|
||||
@ -1,9 +1,15 @@
|
||||
import protobuf from "protobufjs";
|
||||
|
||||
export type ProtoChatMessageType = {
|
||||
timestamp: number;
|
||||
nick: string;
|
||||
text: string;
|
||||
};
|
||||
|
||||
export const ProtoChatMessage = new protobuf.Type("ChatMessage")
|
||||
.add(new protobuf.Field("timestamp", 1, "uint64"))
|
||||
.add(new protobuf.Field("nick", 2, "string"))
|
||||
.add(new protobuf.Field("text", 3, "bytes"));
|
||||
.add(new protobuf.Field("text", 3, "string"));
|
||||
|
||||
export const CONTENT_TOPIC = "/toy-chat/2/luzhou/proto";
|
||||
|
||||
|
||||
@ -1,11 +1,11 @@
|
||||
import React from "react";
|
||||
import { waku, Waku, WakuEventsNames } from "@/services/waku";
|
||||
import { waku, Waku, WakuEventsNames, MessageContent } from "@/services/waku";
|
||||
import { useStore } from "./useStore";
|
||||
import { useRLN } from "./useRLN";
|
||||
|
||||
export const useWaku = () => {
|
||||
const messages: string[] = [];
|
||||
const wakuRef = React.useRef<Waku>();
|
||||
const [messages, setMessages] = React.useState<MessageContent[]>([]);
|
||||
|
||||
const { rln } = useRLN();
|
||||
const { activeMembershipID, credentials, setWakuStatus } = useStore();
|
||||
@ -20,6 +20,11 @@ export const useWaku = () => {
|
||||
};
|
||||
waku.addEventListener(WakuEventsNames.Status, statusListener);
|
||||
|
||||
const messagesListener = (event: CustomEvent) => {
|
||||
setMessages((prev) => [...prev, event.detail as MessageContent]);
|
||||
};
|
||||
waku.addEventListener(WakuEventsNames.Message, messagesListener);
|
||||
|
||||
let terminated = false;
|
||||
const run = async () => {
|
||||
if (terminated) {
|
||||
@ -44,15 +49,16 @@ export const useWaku = () => {
|
||||
return () => {
|
||||
terminated = true;
|
||||
waku.removeEventListener(WakuEventsNames.Status, statusListener);
|
||||
waku.removeEventListener(WakuEventsNames.Message, messagesListener);
|
||||
};
|
||||
}, [activeMembershipID, credentials, rln, setWakuStatus]);
|
||||
|
||||
const onSend = React.useCallback(
|
||||
async (nick: string, message: string) => {
|
||||
async (nick: string, text: string) => {
|
||||
if (!wakuRef.current) {
|
||||
return;
|
||||
}
|
||||
// await wakuRef.current.node?.lightPush.send()
|
||||
await wakuRef.current.sendMessage(nick, text);
|
||||
},
|
||||
[wakuRef]
|
||||
);
|
||||
|
||||
@ -6,8 +6,18 @@ import {
|
||||
LightNode,
|
||||
waitForRemotePeer,
|
||||
} from "@waku/sdk";
|
||||
import { CONTENT_TOPIC } from "@/constants";
|
||||
import { RLNDecoder, RLNEncoder, IdentityCredential } from "@waku/rln";
|
||||
import {
|
||||
CONTENT_TOPIC,
|
||||
ProtoChatMessage,
|
||||
ProtoChatMessageType,
|
||||
} from "@/constants";
|
||||
import {
|
||||
RLNDecoder,
|
||||
RLNEncoder,
|
||||
IdentityCredential,
|
||||
RLNInstance,
|
||||
RLNContract,
|
||||
} from "@waku/rln";
|
||||
import { RLN } from "@/services/rln";
|
||||
|
||||
type InitOptions = {
|
||||
@ -16,8 +26,22 @@ type InitOptions = {
|
||||
rln: RLN;
|
||||
};
|
||||
|
||||
export type MessageContent = {
|
||||
nick: string;
|
||||
text: string;
|
||||
time: string;
|
||||
proofStatus: string;
|
||||
};
|
||||
|
||||
type SubscribeOptions = {
|
||||
rlnContract: RLNContract;
|
||||
node: LightNode;
|
||||
decoder: RLNDecoder<IDecodedMessage>;
|
||||
};
|
||||
|
||||
export enum WakuEventsNames {
|
||||
Status = "status",
|
||||
Message = "message",
|
||||
}
|
||||
|
||||
export enum WakuStatusEventPayload {
|
||||
@ -51,7 +75,6 @@ export class Waku implements IWaku {
|
||||
constructor() {}
|
||||
|
||||
public async init(options: InitOptions) {
|
||||
const { rln } = options;
|
||||
if (this.initialized || this.initializing || !options.rln.rlnInstance) {
|
||||
return;
|
||||
}
|
||||
@ -72,6 +95,14 @@ export class Waku implements IWaku {
|
||||
this.emitStatusEvent(WakuStatusEventPayload.WAITING_FOR_PEERS);
|
||||
await waitForRemotePeer(this.node);
|
||||
this.emitStatusEvent(WakuStatusEventPayload.READY);
|
||||
|
||||
if (options.rln.rlnContract) {
|
||||
await this.subscribeToMessages({
|
||||
node: this.node,
|
||||
decoder: this.decoder,
|
||||
rlnContract: options.rln.rlnContract,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
this.initialized = true;
|
||||
@ -95,6 +126,58 @@ export class Waku implements IWaku {
|
||||
);
|
||||
}
|
||||
|
||||
public async sendMessage(nick: string, text: string): Promise<void> {
|
||||
if (!this.node || !this.encoder) {
|
||||
return;
|
||||
}
|
||||
|
||||
const timestamp = new Date();
|
||||
const msg = ProtoChatMessage.create({
|
||||
text,
|
||||
nick,
|
||||
timestamp: Math.floor(timestamp.valueOf() / 1000),
|
||||
});
|
||||
const payload = ProtoChatMessage.encode(msg).finish();
|
||||
console.log("Sending message with proof...");
|
||||
|
||||
await this.node.lightPush.send(this.encoder, { payload, timestamp });
|
||||
console.log("Message sent!");
|
||||
}
|
||||
|
||||
private async subscribeToMessages(options: SubscribeOptions) {
|
||||
await options.node.filter.subscribe(options.decoder, (message) => {
|
||||
try {
|
||||
const { timestamp, nick, text } = ProtoChatMessage.decode(
|
||||
message.payload
|
||||
) as unknown as ProtoChatMessageType;
|
||||
|
||||
let proofStatus = "no proof";
|
||||
if (message.rateLimitProof) {
|
||||
console.log("Proof received: ", message.rateLimitProof);
|
||||
|
||||
try {
|
||||
console.time("Proof verification took:");
|
||||
const res = message.verify(options.rlnContract.roots());
|
||||
console.timeEnd("Proof verification took:");
|
||||
proofStatus = res ? "verified" : "not verified";
|
||||
} catch (error) {
|
||||
proofStatus = "invalid";
|
||||
console.error("Failed to verify proof: ", error);
|
||||
}
|
||||
}
|
||||
|
||||
this.emitMessageEvent({
|
||||
nick,
|
||||
text,
|
||||
proofStatus,
|
||||
time: new Date(timestamp).toDateString(),
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("Failed in subscription listener: ", error);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public addEventListener(name: WakuEventsNames, fn: EventListener) {
|
||||
return this.emitter.addEventListener(name, fn as any);
|
||||
}
|
||||
@ -108,6 +191,12 @@ export class Waku implements IWaku {
|
||||
new CustomEvent(WakuEventsNames.Status, { detail: payload })
|
||||
);
|
||||
}
|
||||
|
||||
private emitMessageEvent(payload: MessageContent) {
|
||||
this.emitter.dispatchEvent(
|
||||
new CustomEvent(WakuEventsNames.Message, { detail: payload })
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export const waku = new Waku();
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user