import { memo, useCallback, useState, useEffect, useRef } from 'react'; import PropTypes from 'prop-types'; import { AnimatePresence } from 'framer-motion'; import { BREAKPOINTS, MODERATOR_PILL_TIMEOUT } from '../../../constants'; import { channel as $channelContent } from '../../../content'; import { CHAT_USER_ROLE } from './useChatConnection/utils'; import { clsm, extractChannelIdfromChannelArn } from '../../../utils'; import { useChannel } from '../../../contexts/Channel'; import { useChat } from '../../../contexts/Chat'; import { useNotif } from '../../../contexts/Notification'; import { useResponsiveDevice } from '../../../contexts/ResponsiveDevice'; import { useUser } from '../../../contexts/User'; import CelebrationViewerStreamAction from '../ViewerStreamActions/Celebration'; import ChatPopup from './ChatPopup'; import Composer from './Composer'; import ConnectingOverlay from './ConnectingOverlay'; import Messages from './Messages'; import Notification from '../../../components/Notification'; import useResizeObserver from '../../../hooks/useResizeObserver'; const $content = $channelContent.chat; const Chat = ({ shouldRunCelebration }) => { const [chatContainerDimensions, setChatContainerDimensions] = useState(); const { channelData, isChannelLoading } = useChannel(); const { color: channelColor } = channelData || {}; const { isSessionValid, userData } = useUser(); const { notifyError, notifySuccess, notifyInfo } = useNotif(); const { isLandscape, isMobileView, currentBreakpoint, mainRef, isProfileMenuOpen } = useResponsiveDevice(); const isSplitView = isMobileView && isLandscape; const isStackedView = currentBreakpoint < BREAKPOINTS.lg; const chatSectionRef = useRef(); let chatPopupParentEl = chatSectionRef.current; if (isSplitView) chatPopupParentEl = document.body; else if (isStackedView) chatPopupParentEl = mainRef.current; const { actions, chatUserRole, hasConnectionError, isConnecting, sendAttemptError, deletedMessageIds, deletedMessage, setDeletedMessage, messages } = useChat(); const isLoading = isConnecting || isChannelLoading; /** * Chat Moderation State and Actions */ const isModerator = chatUserRole === CHAT_USER_ROLE.MODERATOR; const [isChatPopupOpen, setIsChatPopupOpen] = useState(false); const [selectedMessage, setSelectedMessage] = useState({}); const openChatPopup = useCallback( (messageData) => { setIsChatPopupOpen(true); setSelectedMessage(messageData); }, [setSelectedMessage] ); // Show moderation pill if user role is moderator useEffect(() => { if (isModerator && !isLoading) { setTimeout( () => notifyInfo($content.notifications.moderating, { timeout: MODERATOR_PILL_TIMEOUT }), 300 // Wait for the loading spinner exit transition to complete ); } }, [isModerator, notifyInfo, isLoading]); const updateChatContainerDimensions = useCallback((elem) => { const { clientWidth, clientHeight } = elem; setChatContainerDimensions({ width: clientWidth, height: clientHeight }); }, []); const handleDeleteMessage = useCallback(() => { const { id } = selectedMessage; actions.deleteMessage(id); deletedMessageIds.current.push(id); notifySuccess($content.notifications.success.message_removed); }, [selectedMessage, actions, deletedMessageIds, notifySuccess]); useResizeObserver(chatSectionRef, (entry) => { if (entry) updateChatContainerDimensions(entry.target); }); useEffect(() => { if (chatSectionRef.current) { updateChatContainerDimensions(chatSectionRef.current); } }, [chatSectionRef, updateChatContainerDimensions]); useEffect(() => { if (deletedMessage && !isModerator) { const message = messages?.find(({ id }) => id === deletedMessage); if (message) { const { sender: { attributes: { channelArn: deletedMessageOwner } } } = message; if ( extractChannelIdfromChannelArn(deletedMessageOwner.toLowerCase()) === userData?.trackingId ) notifyError($content.notifications.error.your_message_was_removed); } setDeletedMessage(undefined); } }, [ deletedMessage, deletedMessageIds, isModerator, messages, notifyError, notifySuccess, selectedMessage.id, setDeletedMessage, userData ]); return ( <>