From e83dd1393162ca85eded7d3af7faf27d96d14a51 Mon Sep 17 00:00:00 2001 From: Sasha <118575614+weboko@users.noreply.github.com> Date: Sat, 22 Apr 2023 01:04:39 +0200 Subject: [PATCH] fix: populate peers without waiting for an event (#230) --- examples/web-chat/src/MessageInput.tsx | 9 ++- examples/web-chat/src/Room.tsx | 4 +- examples/web-chat/src/hooks.ts | 81 ++++++++++++++++++++++++-- 3 files changed, 86 insertions(+), 8 deletions(-) diff --git a/examples/web-chat/src/MessageInput.tsx b/examples/web-chat/src/MessageInput.tsx index 86b0f4c..da7488d 100644 --- a/examples/web-chat/src/MessageInput.tsx +++ b/examples/web-chat/src/MessageInput.tsx @@ -24,7 +24,11 @@ export default function MessageInput(props: Props) { const onMessage = async () => { if (props.sendMessage && inputText) { - await props.sendMessage(inputText); + try { + await props.sendMessage(inputText); + } catch (e) { + console.error(`Failed to send message: ${e}`); + } setInputText(""); } }; @@ -36,6 +40,7 @@ export default function MessageInput(props: Props) { const onKeyDown = async (event: KeyboardEvent) => { if ( + isActive && event.key === "Enter" && !event.altKey && !event.ctrlKey && @@ -63,7 +68,7 @@ export default function MessageInput(props: Props) { > - + diff --git a/examples/web-chat/src/Room.tsx b/examples/web-chat/src/Room.tsx index 432f567..5fb1f41 100644 --- a/examples/web-chat/src/Room.tsx +++ b/examples/web-chat/src/Room.tsx @@ -1,11 +1,11 @@ import type { LightNode } from "@waku/interfaces"; import ChatList from "./ChatList"; import MessageInput from "./MessageInput"; -import { useWaku, useContentPair, useLightPush, usePeers } from "@waku/react"; +import { useWaku, useContentPair, useLightPush } from "@waku/react"; import { TitleBar } from "@livechat/ui-kit"; import { Message } from "./Message"; import { ChatMessage } from "./chat_message"; -import { useNodePeers } from "./hooks"; +import { useNodePeers, usePeers } from "./hooks"; interface Props { messages: Message[]; diff --git a/examples/web-chat/src/hooks.ts b/examples/web-chat/src/hooks.ts index bc9d8db..58e4d79 100644 --- a/examples/web-chat/src/hooks.ts +++ b/examples/web-chat/src/hooks.ts @@ -2,7 +2,11 @@ import React, { useEffect, useState } from "react"; import { generate } from "server-name-generator"; import { Message } from "./Message"; import { Decoder } from "@waku/core/lib/message/version_0"; -import { LightNode, StoreQueryOptions } from "@waku/interfaces"; +import type { + Peer, + PeerProtocolsChangeData, +} from "@libp2p/interface-peer-store"; +import type { LightNode, StoreQueryOptions, Waku } from "@waku/interfaces"; import { useFilterMessages, useStoreMessages } from "@waku/react"; @@ -64,8 +68,7 @@ export const useNodePeers = (node: undefined | LightNode) => { useEffect(() => { if (!node) return; - // Update store peer when new peer connected & identified - node.libp2p.peerStore.addEventListener("change:protocols", async (evt) => { + const listener = async (evt: any) => { const { peerId } = evt.detail; const tags = (await node.libp2p.peerStore.getTags(peerId)).map( (t) => t.name @@ -75,7 +78,13 @@ export const useNodePeers = (node: undefined | LightNode) => { } 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(() => { @@ -91,3 +100,67 @@ export const useNodePeers = (node: undefined | LightNode) => { peerExchangePeers, }; }; + +type UsePeersParams = { + node: undefined | Waku; +}; + +type UsePeersResults = { + storePeers?: undefined | Peer[]; + filterPeers?: undefined | Peer[]; + lightPushPeers?: undefined | Peer[]; + peerExchangePeers?: 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({}); + + React.useEffect(() => { + if (!node) { + return; + } + + const listener = async (_event?: CustomEvent) => { + const peers = await Promise.all([ + handleCatch(node?.store?.peers()), + handleCatch(node?.filter?.peers()), + handleCatch(node?.lightPush?.peers()), + handleCatch(node?.peerExchange?.peers()), + ]); + + setPeers({ + storePeers: peers[0], + filterPeers: peers[1], + lightPushPeers: peers[2], + peerExchangePeers: peers[3], + }); + }; + + 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): Promise { + if (!promise) { + return Promise.resolve(undefined); + } + + return promise.catch((_) => { + return undefined; + }); +}