diff --git a/web-chat/src/App.tsx b/web-chat/src/App.tsx index ecd98e9026..bd197bf3ea 100644 --- a/web-chat/src/App.tsx +++ b/web-chat/src/App.tsx @@ -46,15 +46,16 @@ const themes = { export const ChatContentTopic = 'dingpu'; export default function App() { - let [stateMessages, setMessages] = useState([]); + let [newMessages, setNewMessages] = useState([]); + let [archivedMessages, setArchivedMessages] = useState([]); let [stateWaku, setWaku] = useState(undefined); let [nick, setNick] = useState(generate()); useEffect(() => { - const handleNewMessages = (event: { data: Uint8Array }) => { + const handleRelayMessage = (event: { data: Uint8Array }) => { const chatMsg = decodeWakuMessage(event.data); if (chatMsg) { - copyAppendReplace([chatMsg], stateMessages, setMessages); + setNewMessages([chatMsg]); } }; @@ -78,7 +79,7 @@ export default function App() { .map((wakuChatMessage) => ChatMessage.fromWakuChatMessage(wakuChatMessage) ); - copyMergeUniqueReplace(messages, stateMessages, setMessages); + setArchivedMessages(messages); } } }; @@ -88,7 +89,7 @@ export default function App() { .then(() => console.log('Waku init done')) .catch((e) => console.log('Waku init failed ', e)); } else { - stateWaku.libp2p.pubsub.on(RelayDefaultTopic, handleNewMessages); + stateWaku.libp2p.pubsub.on(RelayDefaultTopic, handleRelayMessage); stateWaku.libp2p.peerStore.on( 'change:protocols', @@ -99,7 +100,7 @@ export default function App() { return () => { stateWaku?.libp2p.pubsub.removeListener( RelayDefaultTopic, - handleNewMessages + handleRelayMessage ); stateWaku?.libp2p.peerStore.removeListener( 'change:protocols', @@ -107,7 +108,7 @@ export default function App() { ); }; } - }, [stateWaku, stateMessages]); + }, [stateWaku]); return (
{ const { command, response } = handleCommand( input, @@ -128,7 +130,7 @@ export default function App() { const commandMessages = response.map((msg) => { return new ChatMessage(new Date(), new Date(), command, msg); }); - copyAppendReplace(commandMessages, stateMessages, setMessages); + setNewMessages(commandMessages); }} /> @@ -170,35 +172,3 @@ function decodeWakuMessage(data: Uint8Array): null | ChatMessage { WakuChatMessage.decode(wakuMsg.payload) ); } - -function copyAppendReplace( - newValues: Array, - currentValues: Array, - setter: (val: Array) => void -) { - const copy = currentValues.slice(); - setter(copy.concat(newValues)); -} - -function copyMergeUniqueReplace( - newValues: ChatMessage[], - currentValues: ChatMessage[], - setter: (val: ChatMessage[]) => void -) { - const copy = currentValues.slice(); - newValues.forEach((msg) => { - if (!copy.find(isEqual.bind({}, msg))) { - copy.push(msg); - } - }); - copy.sort((a, b) => a.sentTimestamp.valueOf() - b.sentTimestamp.valueOf()); - setter(copy); -} - -function isEqual(lhs: ChatMessage, rhs: ChatMessage): boolean { - return ( - lhs.nick === rhs.nick && - lhs.message === rhs.message && - lhs.sentTimestamp.toString() === rhs.sentTimestamp.toString() - ); -} diff --git a/web-chat/src/ChatList.tsx b/web-chat/src/ChatList.tsx index 96beaf919c..8a6c076062 100644 --- a/web-chat/src/ChatList.tsx +++ b/web-chat/src/ChatList.tsx @@ -1,4 +1,4 @@ -import { useEffect, useRef } from 'react'; +import { useEffect, useRef, useState } from 'react'; import { ChatMessage } from './ChatMessage'; import { Message, @@ -8,18 +8,40 @@ import { } from '@livechat/ui-kit'; interface Props { - messages: ChatMessage[]; + archivedMessages: ChatMessage[]; + newMessages: ChatMessage[]; } export default function ChatList(props: Props) { - const messages = props.messages; + const [messages, setMessages] = useState([]); + let updatedMessages; - const messagesGroupedBySender = groupMessagesBySender(props.messages).map( + if (IsThereNewMessages(props.newMessages, messages)) { + updatedMessages = messages.slice().concat(props.newMessages); + if (IsThereNewMessages(props.archivedMessages, updatedMessages)) { + updatedMessages = copyMergeUniqueReplace( + props.archivedMessages, + updatedMessages + ); + } + } else { + if (IsThereNewMessages(props.archivedMessages, messages)) { + updatedMessages = copyMergeUniqueReplace( + props.archivedMessages, + messages + ); + } + } + + if (updatedMessages) { + setMessages(updatedMessages); + } + + const messagesGroupedBySender = groupMessagesBySender(messages).map( (currentMessageGroup) => ( {currentMessageGroup.map((currentMessage) => ( {messagesGroupedBySender} - + ); } @@ -71,14 +93,48 @@ function formatDisplayDate(message: ChatMessage): string { }); } -const AlwaysScrollToBottom = (props: Props) => { +const AlwaysScrollToBottom = (props: { newMessages: ChatMessage[] }) => { const elementRef = useRef(); useEffect(() => { // @ts-ignore elementRef.current.scrollIntoView(); - }, [props.messages]); + }, [props.newMessages]); // @ts-ignore return
; }; + +function IsThereNewMessages( + newValues: ChatMessage[], + currentValues: ChatMessage[] +): boolean { + if (newValues.length === 0) return false; + if (currentValues.length === 0) return true; + + return !newValues.find((newMsg) => + currentValues.find(isEqual.bind({}, newMsg)) + ); +} + +function copyMergeUniqueReplace( + newValues: ChatMessage[], + currentValues: ChatMessage[] +) { + const copy = currentValues.slice(); + newValues.forEach((msg) => { + if (!copy.find(isEqual.bind({}, msg))) { + copy.push(msg); + } + }); + copy.sort((a, b) => a.sentTimestamp.valueOf() - b.sentTimestamp.valueOf()); + return copy; +} + +function isEqual(lhs: ChatMessage, rhs: ChatMessage): boolean { + return ( + lhs.nick === rhs.nick && + lhs.message === rhs.message && + lhs.sentTimestamp.toString() === rhs.sentTimestamp.toString() + ); +} diff --git a/web-chat/src/MessageInput.tsx b/web-chat/src/MessageInput.tsx index bdd2f4424a..d8a8bfe208 100644 --- a/web-chat/src/MessageInput.tsx +++ b/web-chat/src/MessageInput.tsx @@ -10,8 +10,7 @@ import { } from '@livechat/ui-kit'; interface Props { - messageHandler: (msg: string) => void; - sendMessage: (() => Promise) | undefined; + sendMessage: ((msg: string) => Promise) | undefined; } export default function MessageInput(props: Props) { @@ -20,14 +19,13 @@ export default function MessageInput(props: Props) { const sendMessage = async () => { if (props.sendMessage) { - await props.sendMessage(); + await props.sendMessage(inputText); setInputText(''); } }; const messageHandler = (event: ChangeEvent) => { setInputText(event.target.value); - props.messageHandler(event.target.value); }; const keyPressHandler = async (event: KeyboardEvent) => { diff --git a/web-chat/src/Room.tsx b/web-chat/src/Room.tsx index 3d1b8b3862..76ccc5cc4b 100644 --- a/web-chat/src/Room.tsx +++ b/web-chat/src/Room.tsx @@ -1,4 +1,3 @@ -import { useState } from 'react'; import { ChatMessage } from './ChatMessage'; import { ChatMessage as WakuChatMessage } from 'waku/chat_message'; import { WakuMessage } from 'waku/waku_message'; @@ -9,13 +8,13 @@ import { useWaku } from './WakuContext'; import { TitleBar } from '@livechat/ui-kit'; interface Props { - lines: ChatMessage[]; + newMessages: ChatMessage[]; + archivedMessages: ChatMessage[]; commandHandler: (cmd: string) => void; nick: string; } export default function Room(props: Props) { - let [messageToSend, setMessageToSend] = useState(''); const { waku } = useWaku(); return ( @@ -24,12 +23,14 @@ export default function Room(props: Props) { style={{ height: '98vh', display: 'flex', flexDirection: 'column' }} > - + { + ? async (messageToSend) => { return handleMessage( messageToSend, props.nick,