mirror of
https://github.com/logos-messaging/examples.waku.org.git
synced 2026-01-06 23:03:10 +00:00
168 lines
4.7 KiB
TypeScript
168 lines
4.7 KiB
TypeScript
import React, { useEffect, useState } from "react";
|
|
import { generate } from "server-name-generator";
|
|
import { Message } from "./Message";
|
|
import type {
|
|
Peer,
|
|
PeerProtocolsChangeData,
|
|
} from "@libp2p/interface-peer-store";
|
|
import type {
|
|
LightNode,
|
|
StoreQueryOptions,
|
|
Waku,
|
|
IDecoder,
|
|
} from "@waku/interfaces";
|
|
|
|
import { useFilterMessages, useStoreMessages } from "@waku/react";
|
|
|
|
export const usePersistentNick = (): [
|
|
string,
|
|
React.Dispatch<React.SetStateAction<string>>
|
|
] => {
|
|
const [nick, setNick] = useState<string>(() => {
|
|
const persistedNick = window.localStorage.getItem("nick");
|
|
return persistedNick !== null ? persistedNick : generate();
|
|
});
|
|
useEffect(() => {
|
|
localStorage.setItem("nick", nick);
|
|
}, [nick]);
|
|
|
|
return [nick, setNick];
|
|
};
|
|
|
|
type UseMessagesParams = {
|
|
node: undefined | LightNode;
|
|
decoder: undefined | IDecoder<any>;
|
|
options: StoreQueryOptions;
|
|
};
|
|
|
|
type UseMessagesResult = [Message[], (v: Message[]) => void];
|
|
|
|
export const useMessages = (params: UseMessagesParams): UseMessagesResult => {
|
|
const { messages: newMessages } = useFilterMessages(params);
|
|
const { messages: storedMessages } = useStoreMessages(params);
|
|
const [localMessages, setLocalMessages] = useState<Message[]>([]);
|
|
|
|
const pushMessages = (msgs: Message[]) => {
|
|
if (!msgs || !msgs.length) {
|
|
return;
|
|
}
|
|
setLocalMessages((prev) => [...prev, ...msgs]);
|
|
};
|
|
|
|
const allMessages = React.useMemo((): Message[] => {
|
|
return [...storedMessages, ...newMessages]
|
|
.map(Message.fromWakuMessage)
|
|
.concat(localMessages)
|
|
.filter((v): v is Message => !!v)
|
|
.filter((v) => v.payloadAsUtf8 !== "")
|
|
.sort(
|
|
(left, right) => left.timestamp.getTime() - right.timestamp.getTime()
|
|
);
|
|
}, [storedMessages, newMessages, localMessages]);
|
|
|
|
return [allMessages, pushMessages];
|
|
};
|
|
|
|
// can be safely ignored
|
|
// this is for experiments on waku side around new discovery options
|
|
export const useNodePeers = (node: undefined | LightNode) => {
|
|
const [bootstrapPeers, setBootstrapPeers] = useState(new Set<string>());
|
|
const [peerExchangePeers, setPeerExchangePeers] = useState(new Set<string>());
|
|
|
|
useEffect(() => {
|
|
if (!node) return;
|
|
|
|
const listener = async (evt: any) => {
|
|
const { peerId } = evt.detail;
|
|
const tags = (await node.libp2p.peerStore.getTags(peerId)).map(
|
|
(t) => t.name
|
|
);
|
|
if (tags.includes("peer-exchange")) {
|
|
setPeerExchangePeers((peers) => new Set(peers).add(peerId.toString()));
|
|
} else {
|
|
setBootstrapPeers((peers) => new Set(peers).add(peerId.toString()));
|
|
}
|
|
};
|
|
|
|
// Update store peer when new peer connected & identified
|
|
node.libp2p.peerStore.addEventListener("change:protocols", listener);
|
|
return () => {
|
|
node.libp2p.peerStore.removeEventListener("change:protocols", listener);
|
|
};
|
|
}, [node]);
|
|
|
|
useEffect(() => {
|
|
console.log("Bootstrap Peers:");
|
|
console.table(bootstrapPeers);
|
|
|
|
console.log("Peer Exchange Peers:");
|
|
console.table(peerExchangePeers);
|
|
}, [bootstrapPeers, peerExchangePeers]);
|
|
|
|
return {
|
|
bootstrapPeers,
|
|
peerExchangePeers,
|
|
};
|
|
};
|
|
|
|
type UsePeersParams = {
|
|
node: undefined | Waku;
|
|
};
|
|
|
|
type UsePeersResults = {
|
|
storePeers?: undefined | Peer[];
|
|
filterPeers?: undefined | Peer[];
|
|
lightPushPeers?: undefined | Peer[];
|
|
};
|
|
|
|
/**
|
|
* Hook returns map of peers for different protocols.
|
|
* If protocol is not implemented on the node peers are undefined.
|
|
* @example
|
|
* const { storePeers } = usePeers({ node });
|
|
* @param {Waku} params.node - Waku node, if not set then no peers will be returned
|
|
* @returns {Object} map of peers, if some of the protocols is not implemented then undefined
|
|
*/
|
|
export const usePeers = (params: UsePeersParams): UsePeersResults => {
|
|
const { node } = params;
|
|
const [peers, setPeers] = React.useState<UsePeersResults>({});
|
|
|
|
React.useEffect(() => {
|
|
if (!node) {
|
|
return;
|
|
}
|
|
|
|
const listener = async (_event?: CustomEvent<PeerProtocolsChangeData>) => {
|
|
const peers = await Promise.all([
|
|
handleCatch(node?.store?.peers()),
|
|
handleCatch(node?.filter?.peers()),
|
|
handleCatch(node?.lightPush?.peers()),
|
|
]);
|
|
|
|
setPeers({
|
|
storePeers: peers[0],
|
|
filterPeers: peers[1],
|
|
lightPushPeers: peers[2],
|
|
});
|
|
};
|
|
|
|
listener(); // populate peers before event is invoked
|
|
node.libp2p.peerStore.addEventListener("change:protocols", listener);
|
|
return () => {
|
|
node.libp2p.peerStore.removeEventListener("change:protocols", listener);
|
|
};
|
|
}, [node, setPeers]);
|
|
|
|
return peers;
|
|
};
|
|
|
|
function handleCatch(promise?: Promise<Peer[]>): Promise<Peer[] | undefined> {
|
|
if (!promise) {
|
|
return Promise.resolve(undefined);
|
|
}
|
|
|
|
return promise.catch((_) => {
|
|
return undefined;
|
|
});
|
|
}
|