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) => (
+
))}
)