Rev 230 | Rev 378 | Ir a la última revisión | Autoría | Comparar con el anterior | Ultima modificación | Ver Log |
import React, { memo, useEffect, useRef, useState } from "react";import { axios } from "../../utils";import { useForm } from "react-hook-form";import { useDispatch } from "react-redux";import { addNotification } from "../../redux/notification/notification.actions";import parse from "html-react-parser";import styled, { css } from "styled-components";import SendIcon from "@mui/icons-material/Send";import IconButton from "@mui/material/IconButton";import ArrowBackIcon from "@mui/icons-material/ArrowBack";import AttachFileIcon from "@mui/icons-material/AttachFile";import InsertEmoticonIcon from "@mui/icons-material/InsertEmoticon";import Options from "../UI/Option";import Emojione from "./emojione/Emojione";import FileModal from "../modals/FileModal";import LoaderContainer from "../UI/LoaderContainer";import Spinner from "../UI/Spinner";const StyledChatContainer = styled.div`background-color: var(--bg-color);border-radius: var(--border-radius);border: 1px solid var(--border-primary);height: 80vh;display: flex;flex-direction: column;flex-grow: 1;padding: 0px 0px !important;`;const StyledChatHeader = styled.div`align-items: center;border-bottom: 1px solid var(--border-primary);display: flex;justify-content: center;padding: 1rem 0.5rem;position: relative;& > button:first-child {position: absolute;left: 1rem;top: 50%;transform: translateY(-50%);display: inline-flex;@media (min-width: 768px) {display: none;}}`;const StyledTitle = styled.h2`font-size: 1.5rem;font-weight: 500;width: fit-content;max-width: 20ch;text-align: center;@media (min-width: 768px) {max-width: 30ch;}`;const StyledMessageList = styled.div`gap: 0.5rem;display: flex;flex-direction: column-reverse;height: -webkit-fill-available;padding: 0.5rem 0;overflow: auto;`;const StyledMessage = styled.div`box-shadow: var(--light-shadow);display: inline-flex;flex-direction: column;gap: 0.5rem;margin-bottom: 0.5rem;max-width: 70%;min-width: 4rem;padding: 0.5rem;position: relative;& > p {color: var(--chat-color);max-width: 100%;overflow: hidden;text-overflow: ellipsis;word-break: break-word;}& > img,& > video {max-width: 250px;max-height: 250px;object-fit: contain;}&:first-child {margin-top: 0.5rem;}.time {color: var(--subtitle-color);font-size: 0.8rem;}.emojione {width: 1rem;height: 1rem;}${(props) => {if (props.send) {return css`align-self: flex-end;background-color: var(--chat-send);border-radius: 10px 0 10px 10px;margin-right: 0.5rem;`;}return css`align-self: flex-start;background-color: var(--chat-received);border-radius: 0 10px 10px 10px;margin-left: 0.5rem;`;}}`;const StyledForm = styled.form`border-top: 1px solid var(--border-primary);padding: 0.5rem;position: relative;display: flex;justify-content: center;align-items: center;gap: 0.5rem;`;const StyledInput = styled.input`border: none;outline: none;width: 100%;padding: 0.5rem 1rem;border-radius: 30px;background: var(--bg-color-secondary);&:focus {background: var(--bg-color-secondary);}`;const StyledLoader = styled(LoaderContainer)`max-height: 50px;max-width: 50px;`;function messageAreEqual(oldProps, newProps) {return oldProps.message.id? oldProps.message.id === newProps.message.id: oldProps.message.uuid === newProps.message.uuid;}const Chat = ({ children }) => {return <StyledChatContainer>{children}</StyledChatContainer>;};const Header = ({ children, options = [], onClose }) => {return (<StyledChatHeader><IconButton onClick={onClose}><ArrowBackIcon /></IconButton>{children}{!!options.length && <Options options={options} right="1rem" />}</StyledChatHeader>);};const Title = ({ children, url }) => {if (!url) {return <StyledTitle>{children}</StyledTitle>;}return (<a href={url} style={{ width: "fit-content" }}><StyledTitle>{children}</StyledTitle></a>);};const List = ({ messages = [], onPagination, scrollRef, loading }) => {const loadMoreEl = useRef(null);useEffect(() => {const observer = new IntersectionObserver(onPagination);if (loadMoreEl.current) {observer.observe(loadMoreEl.current);}return () => {observer.disconnect();};}, [messages]);return (<StyledMessageList ref={scrollRef}>{messages.map((message) => (<List.Message message={message} key={message.id} />))}<span ref={loadMoreEl}>.</span>{loading && (<StyledLoader><Spinner /></StyledLoader>)}</StyledMessageList>);};// eslint-disable-next-line react/display-nameconst Message = memo(({ message }) => {const senderName = (message) => {if (message.type === "group" && !message.u === 1) {return <span className="user_name">{message.user_name}</span>;}};const messagesContent = {text: (// eslint-disable-next-line no-undef<p>{parse(emojione.shortnameToImage(message.m || message.message))}</p>),image: <img src={message.m || message.filename} alt="chat_image" />,video: (<video src={message.m || message.filename} preload="none" controls />),document: (<a href={message.m || message.filename} download><img className="pdf" src="/images/extension/pdf.png" alt="pdf" /></a>),};return (<>{senderName(message)}<StyledMessagesend={message.u ? message.u === 1 : message.side === "left"}>{messagesContent[message.mtype || message.type]}<label className="time">{!message.not_received && (<iclassName="fa fa-check"style={message.seen ? { color: "blue" } : { color: "gray" }}/>)}{message.time || message.date}</label></StyledMessage></>);}, messageAreEqual);const SubmitForm = ({ sendUrl, uploadUrl, onSubmit: onComplete }) => {const [showEmojione, setShowEmojione] = useState(false);const [isShowFileModal, setIsShowFileModal] = useState(false);const [isSending, setIsSending] = useState(false);const dispatch = useDispatch();const { handleSubmit, setValue, register, reset, getValues } = useForm();const onSubmit = handleSubmit(({ message }) => {const formData = new FormData();// eslint-disable-next-line no-undefformData.append("message", emojione.toShort(message));axios.post(sendUrl, formData).then((response) => {const { success, data } = response.data;if (!success) {const errorMessage =typeof data === "string" ? data : "Ha ocurrido un error";setShowEmojione(false);dispatch(addNotification({ style: "danger", msg: errorMessage }));return;}setShowEmojione(false);onComplete();reset();});});const sendFile = (file) => {setIsSending(true);const formData = new FormData();formData.append("file", file);axios.post(uploadUrl, formData).then(({ data: response }) => {const { success, data } = response;if (!success) {const errorMessage =typeof data === "string" ? data : "Ha ocurrido un error";dispatch(addNotification({ style: "success", msg: errorMessage }));return;}toggleFileModal();onComplete();}).finally(() => setIsSending(false));};const toggleEmojione = () => {setShowEmojione(!showEmojione);};const toggleFileModal = () => {setIsShowFileModal(!isShowFileModal);};const onClickEmoji = (event) => {const shortname = event.currentTarget.dataset.shortname;const currentMessage = getValues("message");// eslint-disable-next-line no-undefconst unicode = emojione.shortnameToUnicode(shortname);setValue("message", `${currentMessage}${unicode}`);};return (<><StyledForm onSubmit={onSubmit}>{showEmojione && <Emojione onClickEmoji={onClickEmoji} />}<IconButton onClick={toggleFileModal}><AttachFileIcon /></IconButton><IconButton onClick={toggleEmojione}><InsertEmoticonIcon /></IconButton><StyledInputtype="text"name="message"placeholder="Escribe un mensaje"ref={register({ required: true })}/><IconButton type="submit"><SendIcon /></IconButton></StyledForm><FileModalisShow={isShowFileModal}onHide={toggleFileModal}onComplete={sendFile}loading={isSending}/></>);};Chat.Header = Header;Chat.Title = Title;Chat.List = List;List.Message = Message;Chat.SubmitForm = SubmitForm;export default Chat;