From 4a4813bd9c1677805e7926650ef7a498ebc214ad Mon Sep 17 00:00:00 2001 From: Georgi 7DIGIT Date: Fri, 28 Jul 2023 14:35:52 +0300 Subject: [PATCH] Fix: scrolling of the messages container in the Consultation page --- src/blocks/VideoRoom/VideoRoom.jsx | 2 + src/pages/Consultation/Consultation.jsx | 189 ++++++++++++++++++----- src/pages/Consultation/consultation.scss | 14 +- 3 files changed, 160 insertions(+), 45 deletions(-) diff --git a/src/blocks/VideoRoom/VideoRoom.jsx b/src/blocks/VideoRoom/VideoRoom.jsx index 284934f..88cb86f 100644 --- a/src/blocks/VideoRoom/VideoRoom.jsx +++ b/src/blocks/VideoRoom/VideoRoom.jsx @@ -15,6 +15,7 @@ export function VideoRoom({ leaveConsultation, handleSendMessage, hasUnreadMessages, + isClientInSession, token, t, }) { @@ -66,6 +67,7 @@ export function VideoRoom({ isMicrophoneOn={isMicrophoneOn} isRoomConnecting={!localParticipant} hasUnreadMessages={hasUnreadMessages} + isInSession={isClientInSession} t={t} /> diff --git a/src/pages/Consultation/Consultation.jsx b/src/pages/Consultation/Consultation.jsx index 3b9840e..69acb9a 100644 --- a/src/pages/Consultation/Consultation.jsx +++ b/src/pages/Consultation/Consultation.jsx @@ -4,6 +4,7 @@ import React, { useRef, useCallback, useContext, + useMemo, } from "react"; import { useTranslation } from "react-i18next"; import { Navigate, useLocation, useNavigate } from "react-router-dom"; @@ -21,9 +22,11 @@ import { Toggle, TypingIndicator, } from "@USupport-components-library/src"; + import { useWindowDimensions, ONE_HOUR, + getDateView, } from "@USupport-components-library/utils"; import { @@ -33,7 +36,9 @@ import { useDebounce, useGetAllChatHistoryData, } from "#hooks"; + import { Page, VideoRoom } from "#blocks"; + import { RootContext } from "#routes"; import { Logger } from "twilio-video"; @@ -44,6 +49,21 @@ import "./consultation.scss"; const SOCKET_IO_URL = `${import.meta.env.VITE_SOCKET_IO_URL}`; +const systemMessageTypes = [ + "client_joined", + "client_left", + "client_microphone_on", + "client_microphone_off", + "client_camera_on", + "client_camera_off", + "provider_joined", + "provider_left", + "provider_microphone_on", + "provider_microphone_off", + "provider_camera_on", + "provider_camera_off", +]; + /** * Consultation * @@ -84,15 +104,31 @@ export const Consultation = () => { const [showOptions, setShowOptions] = useState(false); const [search, setSearch] = useState(""); const [hasUnreadMessages, setHasUnreadMessages] = useState(true); + const [isClientInSession, setIsClientInSession] = useState(false); const [isClientTyping, setIsClientTyping] = useState(false); + const [isChatShownOnTablet, setIsChatShownOnTablet] = useState(true); const debouncedSearch = useDebounce(search, 500); + const checkHasClientJoined = (messages) => { + // Sort the messages by time descending so the latest messages are first + // Then check which one of the following two cases is true: + const joinMessages = messages + .filter( + (x) => x.content === "client_joined" || x.content === "client_left" + ) + .sort((a, b) => new Date(Number(b.time)) - new Date(Number(a.time))); + return joinMessages[0].content === "client_joined"; + }; + const chatDataQuery = useGetChatData(consultation?.chatId, (data) => { - setMessages((prev) => ({ - ...prev, - currentSession: data.messages, - })); + { + setIsClientInSession(checkHasClientJoined(data.messages)); + setMessages((prev) => ({ + ...prev, + currentSession: data.messages, + })); + } }); const clientId = chatDataQuery.data?.clientDetailId; @@ -103,6 +139,16 @@ export const Consultation = () => { true ); + const [areMessagesHidden, setAreMessagesHidden] = useState(true); + + const hasMessages = useMemo(() => { + return ( + messages?.currentSession?.length > 0 || + messages?.previousSessions?.length > 0 + ); + }, [messages]); + + // End of session alerts useEffect(() => { const endTime = new Date(consultation.timestamp + ONE_HOUR); let isTenMinAlertShown, @@ -134,6 +180,7 @@ export const Consultation = () => { }; }, []); + // Calculate all chat history useEffect(() => { if ( allChatHistoryQuery.data?.messages && @@ -203,13 +250,38 @@ export const Consultation = () => { // eslint-disable-next-line react-hooks/exhaustive-deps }, []); + useEffect(() => { + let timeout, timeoutTwo; + if (hasMessages && areMessagesHidden && isChatShownOnMobile) { + timeout = setTimeout(() => { + if (backdropMessagesContainerRef.current) { + backdropMessagesContainerRef.current.scrollTop = + backdropMessagesContainerRef.current.scrollHeight; + } + setAreMessagesHidden(false); + }, 1000); + } + + if (!areMessagesHidden && !isChatShownOnMobile) { + timeoutTwo = setTimeout(() => { + setAreMessagesHidden(true); + }, 300); + } + + return () => { + if (timeout) clearTimeout(timeout); + if (timeoutTwo) clearTimeout(timeoutTwo); + }; + }, [messages, isChatShownOnMobile]); + // Scroll the messages container to the bottom when a new message is received useEffect(() => { if ( (messages.currentSession?.length > 0 || messages.previousSessions?.length > 0) && backdropMessagesContainerRef.current && - backdropMessagesContainerRef.current.scrollHeight > 0 + backdropMessagesContainerRef.current.scrollHeight > 0 && + !areMessagesHidden ) { backdropMessagesContainerRef.current.scrollTo({ top: backdropMessagesContainerRef.current?.scrollHeight, @@ -220,6 +292,7 @@ export const Consultation = () => { messages, backdropMessagesContainerRef.current?.scrollHeight, debouncedSearch, + areMessagesHidden, ]); const onSendSuccess = (newMessage) => { @@ -237,6 +310,11 @@ export const Consultation = () => { const leaveConsultationMutation = useLeaveConsultation(); const receiveMessage = (message) => { + if (message.content === "client_left") { + setIsClientInSession(false); + } else if (message.content === "client_joined") { + setIsClientInSession(true); + } setHasUnreadMessages(true); setMessages((messages) => { return { @@ -246,21 +324,6 @@ export const Consultation = () => { }); }; - const systemMessageTypes = [ - "client_joined", - "client_left", - "client_microphone_on", - "client_microphone_off", - "client_camera_on", - "client_camera_off", - "provider_joined", - "provider_left", - "provider_microphone_on", - "provider_microphone_off", - "provider_camera_on", - "provider_camera_off", - ]; - const renderAllMessages = useCallback(() => { if (chatDataQuery.isLoading) return ; @@ -273,8 +336,15 @@ export const Consultation = () => { message.content?.toLowerCase().includes(debouncedSearch.toLowerCase()) ); } + let lastDate; + const messagesToReturn = messagesToShow.map((message, index) => { + let shouldShowDate = false; + const currentMessageDate = getDateView(new Date(Number(message.time))); + if (currentMessageDate !== lastDate) { + shouldShowDate = true; + lastDate = currentMessageDate; + } - const messagesToReturn = messagesToShow?.map((message, index) => { if (message.type === "system") { if (!areSystemMessagesShown) return null; return ( @@ -286,6 +356,7 @@ export const Consultation = () => { : message.content } date={new Date(Number(message.time))} + showDate={shouldShowDate} /> ); } else { @@ -296,6 +367,7 @@ export const Consultation = () => { message={message.content} sent date={new Date(Number(message.time))} + showDate={shouldShowDate} /> ); } else { @@ -305,6 +377,7 @@ export const Consultation = () => { message={message.content} received date={new Date(Number(message.time))} + showDate={shouldShowDate} /> ); } @@ -349,7 +422,6 @@ export const Consultation = () => { }); }; - const [isChatShownOnTablet, setIsChatShownOnTablet] = useState(true); const toggleChat = () => { if (!isChatShownOnMobile) { setTimeout(() => { @@ -436,10 +508,11 @@ export const Consultation = () => { leaveConsultation={leaveConsultation} handleSendMessage={handleSendMessage} hasUnreadMessages={hasUnreadMessages} + isClientInSession={isClientInSession} token={token} t={t} /> - {isChatShownOnTablet && ( + {isChatShownOnTablet && width >= 1366 && ( { setIsChatShownOnMobile(false)} - reference={width < 768 ? backdropMessagesContainerRef : null} + // reference={width < 768 ? backdropMessagesContainerRef : null} headingComponent={ { } >
+ {areMessagesHidden && }
= 768 ? backdropMessagesContainerRef : null} + ref={backdropMessagesContainerRef} className={`page__consultation__container__messages__messages-container ${ showOptions ? "page__consultation__container__messages__messages-container--show-options" : "" }`} + style={{ + visibility: areMessagesHidden ? "hidden" : "visible", + }} > {renderAllMessages()} -
= 768 ? "100px" : "50px", - }} - />
{(isChatShownOnMobile || width >= 768) && ( { + let timeout; + if ( + messages?.currentSession?.length > 0 || + messages?.previousSessions?.length > 0 + ) { + timeout = setTimeout(() => { + if (messagesContainerRef.current) { + messagesContainerRef.current.scrollTop = + messagesContainerRef.current.scrollHeight; + } + setIsHidden(false); + }, 1000); + } + + return () => clearTimeout(timeout); + }, [messages]); + useEffect(() => { + let timeout; if ( (messages.currentSession?.length > 0 || messages.previousSessions?.length > 0) && - messagesContainerRef.current && - messagesContainerRef.current.scrollHeight > 0 && - showMessages + messagesContainerRef.current?.scrollHeight > 0 && + showMessages && + !isHidden ) { - messagesContainerRef.current.scrollTo({ - top: messagesContainerRef.current?.scrollHeight, - behavior: "smooth", - }); + timeout = setTimeout(() => { + messagesContainerRef.current.scrollTo({ + top: messagesContainerRef.current?.scrollHeight, + behavior: "smooth", + }); + }, 300); } - }, [messages, messagesContainerRef.current?.scrollHeight, showMessages]); + return () => { + if (timeout) clearTimeout(timeout); + }; + }, [ + messages, + messagesContainerRef.current?.scrollHeight, + showMessages, + isHidden, + ]); return width >= 1024 ? (
@@ -568,6 +671,8 @@ const MessageList = ({ isAbsolute t={t} /> + {isHidden && } +
{showMessages && renderAllMessages()}
+