import { useEffect, useRef, useState } from 'react'; import PropTypes from 'prop-types'; import { channel as $channelContent } from '../../../../content'; import { clsm } from '../../../../utils'; import { getAvatarSrc } from '../../../../helpers'; import { useChannel } from '../../../../contexts/Channel'; import { useChat } from '../../../../contexts/Chat'; import { useResponsiveDevice } from '../../../../contexts/ResponsiveDevice'; import ChatLine from './ChatLine'; import StickScrollButton from './StickScrollButton'; import useStickyScroll from '../../../../hooks/useStickyScroll'; import useResize from '../../../../hooks/useResize'; const $content = $channelContent.chat; const Messages = ({ isChatPopupOpen, isModerator, openChatPopup }) => { const { channelData } = useChannel(); const { username: chatRoomOwnerUsername } = channelData || {}; const chatRef = useRef(); const { messages, initMessages } = useChat(); const [hasInitMessages, setHasInitMessages] = useState(false); const { isSticky, scrollToBottom } = useStickyScroll(chatRef, messages); const { isMobileView, isLandscape } = useResponsiveDevice(); const isSplitView = isMobileView && isLandscape; useEffect(() => { if (chatRoomOwnerUsername) { initMessages(chatRoomOwnerUsername); setHasInitMessages(true); } return () => setHasInitMessages(false); }, [chatRoomOwnerUsername, initMessages]); useEffect(() => { // Reset the sticky scroll when entering/exiting split view // as the scroll position will change between layouts setTimeout(scrollToBottom, 10); }, [isSplitView, scrollToBottom]); useResize(() => { // Reset the sticky scroll when resizing browser setTimeout(scrollToBottom, 10); }); return ( <div className={clsm([ 'flex', 'h-full', 'items-end', 'justify-center', 'relative', 'w-full' ])} > <div className={clsm([ 'no-scrollbar', 'absolute', 'h-full', 'w-full', 'flex', 'flex-col', 'items-start', 'space-y-3', 'px-[18px]', 'pt-5', 'overflow-x-hidden', 'overflow-y-auto', 'supports-overlay:overflow-y-overlay' ])} role="log" ref={chatRef} > {(hasInitMessages ? messages : []).map( ({ content: message, id: messageId, isDeleted, isOwnMessage, isPreloaded, sender: { attributes: senderAttributes }, wasDeletedByUser }) => { const { channelAssetUrls: channelAssetUrlsStr, avatar: avatarName, ...restSenderAttributes } = senderAttributes; const avatarSrc = getAvatarSrc({ avatar: avatarName, channelAssetUrls: channelAssetUrlsStr ? JSON.parse(channelAssetUrlsStr) : {} }); const selectMessageToModerate = () => openChatPopup({ id: messageId, message, avatarSrc, isOwnMessage, ...restSenderAttributes }); if (isDeleted && (isOwnMessage || wasDeletedByUser)) return ( <p data-testid="chatline-message-removed" className={clsm([ 'dark:text-darkMode-gray-light', 'text-lightMode-gray-dark', 'font-bold', 'py-3' ])} key={messageId} > {$content.message_removed} </p> ); else if (isDeleted) return null; return ( <ChatLine {...(isModerator ? { onClick: selectMessageToModerate } : {})} {...senderAttributes} avatarSrc={avatarSrc} isFocusable={isModerator && !isChatPopupOpen} key={messageId} message={message} shouldAnimateIn={!isPreloaded} /> ); } )} </div> <StickScrollButton isSticky={isSticky} scrollToBottom={scrollToBottom} /> </div> ); }; Messages.defaultProps = { isChatPopupOpen: false, isModerator: false }; Messages.propTypes = { isChatPopupOpen: PropTypes.bool, isModerator: PropTypes.bool, openChatPopup: PropTypes.func.isRequired }; export default Messages;