diff --git a/chatter/src/components/Messages/components/Footer.js b/chatter/src/components/Messages/components/Footer.js index a0f36e352..a79bbd0a1 100644 --- a/chatter/src/components/Messages/components/Footer.js +++ b/chatter/src/components/Messages/components/Footer.js @@ -16,6 +16,7 @@ export default function Footer({ sendMessage, onChangeMessage, message }) { placeholder="Write a message..." id="user-message-input" onChange={onChangeMessage} + value={message} />
diff --git a/chatter/src/components/Messages/components/Message.js b/chatter/src/components/Messages/components/Message.js index 5b51bd06b..0ff8c016b 100644 --- a/chatter/src/components/Messages/components/Message.js +++ b/chatter/src/components/Messages/components/Message.js @@ -10,9 +10,9 @@ export default function Message({ nextMessage, message, botTyping }) { 'messages__message', 'animate__animated animate__rubberBand', { - 'messages__message--me': message.user === ME, - 'messages__message--last': (!nextMessage && (!botTyping || message.user === ME)) - || (nextMessage && nextMessage.user !== message.user) + 'messages__message--me': message?.user === ME, + 'messages__message--last': (!nextMessage && (!botTyping || message?.user === ME)) + || (nextMessage && nextMessage?.user !== message?.user) } )} key={message.id} diff --git a/chatter/src/components/Messages/components/Messages.js b/chatter/src/components/Messages/components/Messages.js index 4d388a8b0..9c616be21 100644 --- a/chatter/src/components/Messages/components/Messages.js +++ b/chatter/src/components/Messages/components/Messages.js @@ -1,30 +1,32 @@ -import React, { useContext } from 'react'; -import io from 'socket.io-client'; -import useSound from 'use-sound'; -import config from '../../../config'; -import LatestMessagesContext from '../../../contexts/LatestMessages/LatestMessages'; -import TypingMessage from './TypingMessage'; -import Header from './Header'; -import Footer from './Footer'; -import Message from './Message'; -import '../styles/_messages.scss'; - -const socket = io( - config.BOT_SERVER_ENDPOINT, - { transports: ['websocket', 'polling', 'flashsocket'] } -); +import React from "react"; +import Header from "./Header"; +import Footer from "./Footer"; +import Message from "./Message"; +import { useMessages } from "../../../hook/useMessages"; +import "../styles/_messages.scss"; function Messages() { - const [playSend] = useSound(config.SEND_AUDIO_URL); - const [playReceive] = useSound(config.RECEIVE_AUDIO_URL); - const { setLatestMessage } = useContext(LatestMessagesContext); + const { messageList, message, messageListElem, handleOnChange, handleSendMessage } = + useMessages(); return (
-
+
+ {messageList.map((data, index) => ( + + ))} +
-
+
); } diff --git a/chatter/src/components/Messages/styles/_messages.scss b/chatter/src/components/Messages/styles/_messages.scss index a5b66ee84..b081787cb 100644 --- a/chatter/src/components/Messages/styles/_messages.scss +++ b/chatter/src/components/Messages/styles/_messages.scss @@ -244,3 +244,12 @@ } } } +.messages__list { + & > * { + overflow-anchor: none; + } + & .anchor { + overflow-anchor: auto; + height: 1px; + } +} diff --git a/chatter/src/hook/useMessages.js b/chatter/src/hook/useMessages.js new file mode 100644 index 000000000..af0704b72 --- /dev/null +++ b/chatter/src/hook/useMessages.js @@ -0,0 +1,127 @@ +import { useCallback, useContext, useEffect, useRef, useState } from "react"; +import io from "socket.io-client"; +import useSound from "use-sound"; +import config from "../config"; +import LatestMessagesContext from "../contexts/LatestMessages/LatestMessages"; +import INITIAL_BOTTY_MESSAGE from "../common/constants/initialBottyMessage"; + +const socket = io(config.BOT_SERVER_ENDPOINT, { + transports: ["websocket", "polling", "flashsocket"], +}); + +const botTypingEvent = "bot-typing"; +const botMessageEvent = "bot-message"; + +export function useMessages() { + const [playSend] = useSound(config.SEND_AUDIO_URL); + const [playReceive] = useSound(config.RECEIVE_AUDIO_URL); + const { setLatestMessage } = useContext(LatestMessagesContext); + const [userInput, setUserInput] = useState(""); + const [message, setMessage] = useState(null); + const [messageList, setMessageList] = useState([ + { + message: { + user: "bot", + id: "id" + new Date().getTime(), + message: INITIAL_BOTTY_MESSAGE, + }, + botTyping: false, + nextMessage: { + user: "bot", + }, + }, + ]); + const messageListElem = useRef(null); + + // listen to bot response + const listenToSocketEvents = () => { + socket.on(botTypingEvent, () => { + setMessage({ + message: { + user: "bot", + id: "id" + new Date().getTime(), + message: null, + }, + botTyping: true, + nextMessage: { + user: "bot", + }, + }); + }); + + socket.on(botMessageEvent, (msg) => { + playReceive(); + setMessage({ + message: { + user: "bot", + id: "id" + new Date().getTime(), + message: msg, + }, + botTyping: false, + nextMessage: { + user: "bot", + }, + }); + + setLatestMessage("charles", msg); + }); + }; + + // handle user input + const handleOnChange = useCallback( + (e) => { + const value = e.target.value; + setUserInput(value); + }, + [userInput] + ); + + // handle send message + const handleSendMessage = () => { + socket.emit("user-message", userInput); + playSend(); + setMessage({ + message: { + user: "me", + id: "id" + new Date().getTime(), + message: userInput, + }, + botTyping: false, + nextMessage: { + user: "me", + }, + }); + setLatestMessage("charles", userInput); + setUserInput(""); + }; + + const scrollToBottom = () => { + const elem = messageListElem.current; + elem.scrollTop = elem.scrollHeight; + }; + + useEffect(() => { + listenToSocketEvents(); + const interval = setInterval(() => { + scrollToBottom(); + }, 100); + return () => clearInterval(interval); + }, []); + + useEffect(() => { + if (message) { + setMessageList( + [...messageList, message].filter((data) => !data.botTyping) + ); + } + }, [message]); + + return { + message: userInput, + messageList, + messageListElem, + handleOnChange, + handleSendMessage, + scrollToBottom, + }; +} diff --git a/chatter/src/layouts/CoreLayout/components/IconBackground.js b/chatter/src/layouts/CoreLayout/components/IconBackground.js index 8caef381c..f5093f984 100644 --- a/chatter/src/layouts/CoreLayout/components/IconBackground.js +++ b/chatter/src/layouts/CoreLayout/components/IconBackground.js @@ -16,7 +16,7 @@ function getRandomIcon() { function IconRow({ numberOfIcons }) { return (
- {[...new Array(numberOfIcons)].map(() => { + {[...new Array(numberOfIcons)].map((_, i) => { const icon = getRandomIcon(); return ( @@ -29,6 +29,7 @@ function IconRow({ numberOfIcons }) { marginTop: `${getRandomNumber(-SPACING_MARGIN, SPACING_MARGIN)}px`, marginLeft: `${getRandomNumber(-SPACING_MARGIN, SPACING_MARGIN)}px`, }} + key={i} /> ); })} @@ -43,8 +44,8 @@ export default function IconBackground() { return (
- {[...new Array(numberOfRows)].map(() => ( - + {[...new Array(numberOfRows)].map((_, i) => ( + ))}
)