Rev 5933 | Autoría | Comparar con el anterior | Ultima modificación | Ver Log |
import React, { useEffect, useRef, useState } from 'react'import { axios, scrollToBottom } from '../../utils'import { useDispatch } from 'react-redux'import { addNotification } from '../../redux/notification/notification.actions'import IconButton from '@mui/material/IconButton'import MoreVertIcon from '@mui/icons-material/MoreVert'import ArrowBackIcon from '@mui/icons-material/ArrowBack'import QuestionAnswerRoundedIcon from '@mui/icons-material/QuestionAnswerRounded'import MessageBox from './MessageBox'import MessageTemplate from './MessageTemplate'import EmptySection from '../../shared/empty-section/EmptySection'import ConfirmModal from '../../shared/confirm-modal/ConfirmModal'import LoaderContainer from '../../app/components/UI/LoaderContainer'import Spinner from '../../shared/loading-spinner/Spinner'import { getMessagesDifferences } from '../../services/chat'const DEFAULT_PAGES = { current: 1, last: 1 }const Chatmail = ({messagesUrl = '',selectedConversation = null,setConversation = () => null,}) => {const [oldMessages, setOldMessages] = useState([])const [messages, setMessages] = useState([])const [pages, setPages] = useState(DEFAULT_PAGES)const [loading, setLoading] = useState(false)const [isGetting, setIsGetting] = useState(false)const [isShowConfirm, setIsShowConfirm] = useState(false)const messagesList = useRef(null)const dispatch = useDispatch()const getMessages = () => {try {const controller = new AbortController()const query = (url = '', page = pages.current) =>axios.get(`${url}?page=${page}`, {signal: controller.signal,}).then((response) => response.data)return { query, controller }} catch (error) {dispatch(addNotification({ style: 'danger', message: 'Error: ' + error }))throw new Error(error)}}const getMoreMessages = () => {setLoading(true)const { query } = getMessages()query(messagesUrl, pages.current).then(({ success, data, pagination }) => {if (!success) {const errorMessage = data ?? 'Ha ocurrido un error'dispatch(addNotification({ style: 'danger', msg: errorMessage }))return}if (pagination.current > 1) {setOldMessages((prevOldMessages) => [...data.reverse(),...prevOldMessages,])}setPages((prevPages) => ({...prevPages,last: pagination.last,}))}).catch((error) => {const errorMessage = 'Error: ' + errordispatch(addNotification({ style: 'danger', message: errorMessage }))throw new Error(error)}).finally(() => setLoading(false))}const hearbeat = () => {setIsGetting(true)const { query } = getMessages()query(messagesUrl, 1).then(({ success, data, pagination }) => {if (!success) {const errorMessage = data ?? 'Ha ocurrido un error'dispatch(addNotification({ style: 'danger', msg: errorMessage }))return}const newMessages = getMessagesDifferences(messages, data)console.log(newMessages)if (newMessages.length) {setMessages((prevMessages) => [...prevMessages,...newMessages.reverse(),])}setPages((prevPages) => ({...prevPages,last: pagination.last,}))}).catch((err) => {const errorMessage = 'Error: ' + errdispatch(addNotification({ style: 'danger', message: errorMessage }))throw new Error(err)}).finally(() => setIsGetting(false))}const loadMore = () => {setLoading(true)setPages((prevPages) => ({ ...prevPages, current: prevPages.current + 1 }))}const toggleConfirmModal = () => {setIsShowConfirm(!isShowConfirm)}const sendMessage = async (sendUrl = '', message = {}) => {try {const formData = new FormData()Object.entries(message).forEach(([key, value]) =>formData.append(key, value))await axios.post(sendUrl, formData)scrollToBottom(messagesList)} catch (error) {const errorMessage = new Error(error)dispatch(addNotification({ style: 'danger', msg: errorMessage.message }))}}const deleteConversation = () => {axios.post(selectedConversation.delete_link).then(({ data: response }) => {const { success, data } = responseif (!success) {dispatch(addNotification({ style: 'danger', msg: data }))return}dispatch(addNotification({ style: 'success', msg: data }))toggleConfirmModal()setConversation(null)})}useEffect(() => {getMoreMessages()}, [pages.current])useEffect(() => {setMessages([])setOldMessages([])setPages(DEFAULT_PAGES)}, [selectedConversation])useEffect(() => {if (isGetting) returnconst messagesInterval = setTimeout(() => {hearbeat()}, 2000)return () => {clearTimeout(messagesInterval)}}, [isGetting])return (<><div className="chat"><Chatmail.Headername={selectedConversation.name}profile={selectedConversation.profile}options={[{url: selectedConversation.delete_link,label: 'Borrar convesación',action: toggleConfirmModal,},]}changeTab={() => setConversation(null)}/>{![...messages, ...oldMessages].length ? (<EmptySectionmessage="No hay mensajes en esta conversación"Icon={<QuestionAnswerRoundedIcon />}/>) : (<Chatmail.ListisLastPage={pages.current >= pages.last}messages={[...oldMessages, ...messages]}onIntersection={loadMore}scrollRef={messagesList}isLoading={loading}/>)}<MessageBoxonSend={sendMessage}sendUrl={selectedConversation.send_link}/></div><ConfirmModalshow={isShowConfirm}onClose={toggleConfirmModal}onAccept={deleteConversation}acceptLabel="Aceptar"/></>)}const Header = ({ name, profile, options, changeTab }) => {return (<><div className="chat_header"><IconButton onClick={changeTab}><ArrowBackIcon /></IconButton><a href={profile}><h2>{name}</h2></a><Header.Options options={options} /></div></>)}const List = ({messages,onIntersection,isLastPage,scrollRef,isLoading,}) => {const loadMoreEl = useRef(null)useEffect(() => {const observer = new IntersectionObserver((entries) => {if (entries[0].isIntersecting && !isLoading) {onIntersection()}})if (loadMoreEl.current === null) {return}observer.observe(loadMoreEl.current)return () => {observer.disconnect()}}, [isLoading, onIntersection])return (<div className="messages_container" ref={scrollRef}><div className="message_wrapper">{!isLastPage && <p ref={loadMoreEl}>Cargando...</p>}{isLoading && (<LoaderContainer><Spinner /></LoaderContainer>)}{messages.map((message, index) => (<MessageTemplate message={message} key={JSON.stringify(message)} />))}</div></div>)}const Options = ({ options }) => {const [isShowMenu, setIsShowMenu] = useState(false)const toggleOptions = () => {setIsShowMenu(!isShowMenu)}return (<div className="header-options"><IconButton onClick={toggleOptions}><MoreVertIcon /></IconButton><div className="position-relative"><div className={`feed-options ${isShowMenu ? 'active' : ''}`}><ul>{options.map((option, index) => {if (!option.url) {return null}return (<li key={index}><buttonclassName="btn option-btn"onClick={() => {option.action()toggleOptions()}}>{option.label}</button></li>)})}</ul></div></div></div>)}Chatmail.Header = HeaderChatmail.List = ListHeader.Options = Optionsexport default Chatmail