From c07d912c39123e2a984d990d3d693843a3d121f0 Mon Sep 17 00:00:00 2001 From: Szymon Szlachtowicz <38212223+Szymx95@users.noreply.github.com> Date: Wed, 29 Sep 2021 15:05:43 +0200 Subject: [PATCH] Improve use messenger (#18) --- .../react-chat/src/components/Channels.tsx | 34 ++++- packages/react-chat/src/components/Chat.tsx | 34 ++++- .../src/components/Chat/ChatBody.tsx | 13 +- packages/react-chat/src/hooks/useMessenger.ts | 125 +++++++++--------- 4 files changed, 133 insertions(+), 73 deletions(-) diff --git a/packages/react-chat/src/components/Channels.tsx b/packages/react-chat/src/components/Channels.tsx index 77813c63..99c0b3c1 100644 --- a/packages/react-chat/src/components/Channels.tsx +++ b/packages/react-chat/src/components/Channels.tsx @@ -1,4 +1,4 @@ -import React from "react"; +import React, { useEffect } from "react"; import styled from "styled-components"; import { ChannelData, channels } from "../helpers/channelsMock"; @@ -8,6 +8,8 @@ interface ChannelsProps { theme: Theme; icon: string; name: string; + notifications: { [id: string]: number }; + clearNotifications: (id: string) => void; members: number; setActiveChannel: (val: ChannelData) => void; activeChannelId: number; @@ -18,9 +20,20 @@ export function Channels({ icon, name, members, + notifications, setActiveChannel, + clearNotifications, activeChannelId, }: ChannelsProps) { + useEffect(() => { + const channel = channels.find((channel) => channel.id === activeChannelId); + if (channel) { + if (notifications[channel.name] > 0) { + clearNotifications(channel.name); + } + } + }, [notifications, activeChannelId]); + return ( @@ -37,6 +50,11 @@ export function Channels({ channel={channel} theme={theme} isActive={channel.id === activeChannelId} + notification={ + notifications[channel.name] > 0 + ? notifications[channel.name] + : undefined + } onClick={() => { setActiveChannel(channel); }} @@ -50,6 +68,7 @@ export function Channels({ interface ChannelProps { theme: Theme; channel: ChannelData; + notification?: number; isActive: boolean; activeView?: boolean; onClick?: () => void; @@ -61,6 +80,7 @@ export function Channel({ isActive, activeView, onClick, + notification, }: ChannelProps) { return ( 0 + ? "notified" + : "" } > # {channel.name} @@ -95,10 +119,8 @@ export function Channel({ )} - {channel.notifications && !activeView && ( - - {channel.notifications} - + {notification && notification > 0 && !activeView && ( + {notification} )} ); diff --git a/packages/react-chat/src/components/Chat.tsx b/packages/react-chat/src/components/Chat.tsx index 3249760f..cb94aa81 100644 --- a/packages/react-chat/src/components/Chat.tsx +++ b/packages/react-chat/src/components/Chat.tsx @@ -2,6 +2,7 @@ import React, { useState } from "react"; import styled from "styled-components"; import { ChannelData, channels } from "../helpers/channelsMock"; +import { useMessenger } from "../hooks/useMessenger"; import { Theme } from "../styles/themes"; import { Channels } from "./Channels"; @@ -16,11 +17,23 @@ interface ChatProps { export function Chat({ theme, channelsON, membersON }: ChatProps) { const [activeChannel, setActiveChannel] = useState(channels[0]); + const { + messenger, + messages, + sendMessage, + notifications, + clearNotifications, + } = useMessenger( + activeChannel.name, + channels.map((channel) => channel.name) + ); return ( {channelsON && ( )} - + {messenger ? ( + + ) : ( + Connecting to waku + )} {membersON && } ); } +const Loading = styled.div` + display: flex; + flex-direction: column; + flex: 1; + height: 100%; + text-align: center; + justify-content: center; + align-items: center; +`; + const ChatWrapper = styled.div` width: 100%; height: 100vh; diff --git a/packages/react-chat/src/components/Chat/ChatBody.tsx b/packages/react-chat/src/components/Chat/ChatBody.tsx index 6aa6b758..82fbab93 100644 --- a/packages/react-chat/src/components/Chat/ChatBody.tsx +++ b/packages/react-chat/src/components/Chat/ChatBody.tsx @@ -2,7 +2,7 @@ import React from "react"; import styled from "styled-components"; import { ChannelData } from "../../helpers/channelsMock"; -import { useMessenger } from "../../hooks/useMessenger"; +import { ChatMessage } from "../../models/ChatMessage"; import { Theme } from "../../styles/themes"; import { Channel } from "../Channels"; @@ -12,11 +12,16 @@ import { ChatMessages } from "./ChatMessages"; interface ChatBodyProps { theme: Theme; channel: ChannelData; + messages: ChatMessage[]; + sendMessage: (text: string) => void; } -export function ChatBody({ theme, channel }: ChatBodyProps) { - const { messages, sendMessage } = useMessenger(channel.name); - +export function ChatBody({ + theme, + channel, + messages, + sendMessage, +}: ChatBodyProps) { return ( diff --git a/packages/react-chat/src/hooks/useMessenger.ts b/packages/react-chat/src/hooks/useMessenger.ts index 5d71ad26..f68d8464 100644 --- a/packages/react-chat/src/hooks/useMessenger.ts +++ b/packages/react-chat/src/hooks/useMessenger.ts @@ -1,17 +1,29 @@ -import { StoreCodec } from "js-waku"; +// import { StoreCodec } from "js-waku"; import { useCallback, useEffect, useState } from "react"; import { Identity, Messenger } from "status-communities/dist/cjs"; import { ChatMessage } from "../models/ChatMessage"; -export function useMessenger(chatId: string) { +export function useMessenger(chatId: string, chatIdList: string[]) { const [messenger, setMessenger] = useState(undefined); const [messages, setMessages] = useState<{ [chatId: string]: ChatMessage[] }>( {} ); + const [notifications, setNotifications] = useState<{ + [chatId: string]: number; + }>({}); + + const clearNotifications = useCallback((id: string) => { + setNotifications((prevNotifications) => { + return { + ...prevNotifications, + [id]: 0, + }; + }); + }, []); const addNewMessage = useCallback( - (sender: Uint8Array, content: string, date: Date) => { + (sender: Uint8Array, content: string, date: Date, id: string) => { let signer = "0x"; sender.forEach((e) => { signer = signer + e.toString(16); @@ -22,87 +34,76 @@ export function useMessenger(chatId: string) { content: content, date: date, }; - return { - ...prevMessages, - [chatId]: [...prevMessages[chatId], newMessage], - }; + if (prevMessages?.[id]) { + return { + ...prevMessages, + [id]: [...prevMessages[id], newMessage], + }; + } else { + return { + ...prevMessages, + [id]: [newMessage], + }; + } }); + if (chatId != id) { + setNotifications((prevNotifications) => { + return { + ...prevNotifications, + [id]: prevNotifications[id] + 1, + }; + }); + } }, - [chatId] + [] ); useEffect(() => { const createMessenger = async () => { const identity = Identity.generate(); const messenger = await Messenger.create(identity); - await new Promise((resolve) => { - messenger.waku?.libp2p.peerStore.on( - "change:protocols", - ({ protocols }) => { - if (protocols.includes(StoreCodec)) { - resolve(""); - } - } - ); - }); - await messenger.joinChat(chatId); - setMessages((prevMessages) => { - return { ...prevMessages, [chatId]: [] }; - }); - const chat = messenger.chatsById.get(chatId); - const contentTopic = chat?.contentTopic; - - if (contentTopic) { - const messages = await messenger.waku.store.queryHistory( - [contentTopic], - { decryptionKeys: [chat?.symKey] } - ); - console.log(messages); - } - messenger.addObserver((message) => { - addNewMessage( - message.signer ?? new Uint8Array(), - message.chatMessage?.text ?? "", - new Date(message.chatMessage?.proto.timestamp ?? 0) - ); - }, chatId); + await new Promise((resolve) => + messenger.waku.libp2p.pubsub.once("pubsub:subscription-change", () => + resolve(null) + ) + ); + await Promise.all( + chatIdList.map(async (id) => { + await messenger.joinChat(id); + clearNotifications(id); + messenger.addObserver((message) => { + addNewMessage( + message.signer ?? new Uint8Array(), + message.chatMessage?.text ?? "", + new Date(message.chatMessage?.proto.timestamp ?? 0), + id + ); + }, id); + }) + ); setMessenger(messenger); }; createMessenger(); }, []); - useEffect(() => { - const joinNewChat = async () => { - try { - await messenger?.joinChat(chatId); - setMessages((prevMessages) => { - return { ...prevMessages, [chatId]: [] }; - }); - messenger?.addObserver((message) => { - addNewMessage( - message.signer ?? new Uint8Array(), - message.chatMessage?.text ?? "", - new Date(message.chatMessage?.proto.timestamp ?? 0) - ); - }, chatId); - } catch { - return; - } - }; - joinNewChat(); - }, [chatId]); - const sendMessage = useCallback( async (messageText: string) => { await messenger?.sendMessage(messageText, chatId); addNewMessage( messenger?.identity.publicKey ?? new Uint8Array(), messageText, - new Date() + new Date(), + chatId ); }, [chatId, messenger] ); - return { messenger, messages: messages?.[chatId] ?? [], sendMessage }; + return { + messenger, + messages: messages?.[chatId] ?? [], + sendMessage, + notifications, + clearNotifications, + }; }