Rev 16288 | AutorÃa | Comparar con el anterior | Ultima modificación | Ver Log |
import React, { useEffect, useRef, useState } from "react";
import { axios } from "../../../utils";
import { useForm } from "react-hook-form";
import Emojione from "./emojione/Emojione";
import FileModal from "./fileModal/FileModal";
import MessagesList from "./messages/MessagesList";
import ConferenceModal from "./components/ConferenceModal";
import IconButton from "@mui/material/IconButton";
import AttachFileIcon from "@mui/icons-material/AttachFile";
import InsertEmoticonIcon from "@mui/icons-material/InsertEmoticon";
import SendIcon from "@mui/icons-material/Send";
import MoreVertIcon from "@mui/icons-material/MoreVert";
import styles from "./chat.module.scss";
const permittedFiles =
"video/mp4, video/mpeg, video/webm, application/pdf, image/jpeg, image/png, image/jpg";
const Chat = ({ entity, timezones }) => {
const [oldMessages, setOldMessages] = useState([]);
const [messages, setMessages] = useState([]);
const [totalPages, setTotalPages] = useState(1);
const [currentPage, setCurrentPage] = useState(1);
const [loading, setLoading] = useState(false);
const [isGettingMessages, setIsGettingMessages] = useState(false);
const [showEmojione, setShowEmojione] = useState(false);
const [selectedFile, setSelectedFile] = useState("");
const { handleSubmit } = useForm();
const scrollList = useRef(null);
const inputTextEl = useRef(null);
const fileInputEl = useRef(null);
const {
url_get_all_messages,
url_send,
url_upload,
url_close,
url_mark_seen,
url_zoom,
type,
} = entity;
// Get messages
const getMessages = () => {
setLoading(true);
axios
.get(url_get_all_messages)
.then(({ data: response }) => {
const { data, success } = response;
if (!success) {
return console.log("Ha ocurrido un error");
}
const messageResponse = [...data.items].reverse();
const updatedMessages = messageResponse.reduce(
(acum, updatedMessage) => {
if (
messages.findIndex(
(message) => message.id === updatedMessage.id
) === -1
) {
acum = [...acum, updatedMessage];
}
return acum;
},
[]
);
if (updatedMessages.length > 0) {
setMessages((prevMessages) => [...prevMessages, ...updatedMessages]);
setTotalPages(data.pages);
scrollTo(scrollList);
}
})
.finally(() => setLoading(false));
};
const onIntersection = (entities) => {
const target = entities[0];
if (target.isIntersecting && currentPage < totalPages) {
setIsGettingMessages(true);
setCurrentPage((prevState) => prevState + 1);
scrollTo(scrollList, 200);
}
};
const getOldMessages = () => {
setIsGettingMessages(true);
axios
.get(`${url_get_all_messages}?page=${currentPage}`)
.then(({ data: response }) => {
const { data, success } = response;
if (success && data.page > 1) {
setOldMessages([...data.items.slice().reverse(), ...oldMessages]);
}
})
.finally(() => setIsGettingMessages(false));
};
//Utilitys
const scrollTo = (element, distance) => {
const divToScrollEl = element.current;
const options = {
top: distance,
behavior: "smooth",
};
if (!distance) {
divToScrollEl.scrollBy({ ...options, top: divToScrollEl.scrollHeight });
return;
}
divToScrollEl.scrollBy(options);
};
const onClickEmoji = (event) => {
const shortname = event.currentTarget.dataset.shortname;
const currentText = inputTextEl.current.value;
const cursorPosition = inputTextEl.current.selectionStart;
const textBehind = currentText.substring(0, cursorPosition);
const textForward = currentText.substring(cursorPosition);
const unicode = emojione.shortnameToUnicode(shortname);
inputTextEl.current.value = `${textBehind}${unicode}${textForward}`;
inputTextEl.current.focus();
inputTextEl.current.setSelectionRange(
cursorPosition + unicode.length,
cursorPosition + unicode.length
);
};
const handleUploadFile = ({ target }) => {
const file = target.files[0];
if (!file) return;
setSelectedFile(file);
};
const removeSelectedFile = () => {
setSelectedFile("");
};
// On send
const handleKeyDown = (e) => {
if (e.key !== "Enter") return false;
e.preventDefault();
onHandleSubmit();
};
const onHandleSubmit = () => {
const formData = new FormData();
formData.append("message", emojione.toShort(inputTextEl.current.value));
axios.post(url_send, formData).then(({ data: response }) => {
const { data, success } = response;
if (!success) {
console.log("Ha ocurrido un error: " + data);
return;
}
inputTextEl.current.value = "";
setShowEmojione(false);
scrollTo(scrollList);
});
};
const handleSendFile = () => {
const formData = new FormData();
formData.append("file", selectedFile);
axios.post(url_upload, formData).then(({ data: response }) => {
const { success, data } = response;
if (!success) {
console.log("Ha ocurrido un error: " + data);
return;
}
setSelectedFile("");
setShowEmojione(false);
scrollTo(scrollList);
});
};
useEffect(() => {
let timeInterval;
if (loading) return;
timeInterval = setTimeout(() => getMessages(), 2000);
return () => {
clearTimeout(timeInterval);
};
}, [loading]);
useEffect(() => {
setMessages([]);
setOldMessages([]);
setTotalPages(1);
setCurrentPage(1);
}, [entity]);
useEffect(() => getOldMessages(), [currentPage]);
useEffect(() => axios.post(url_mark_seen), []);
return (
<div className={styles.chat}>
<Chat.Header
name={entity.name}
conferenceUrl={url_zoom}
timezones={timezones}
/>
<MessagesList
isLastPage={currentPage >= totalPages}
messages={[...oldMessages, ...messages]}
onIntersection={onIntersection}
scrollRef={scrollList}
isLoading={isGettingMessages}
/>
<div className={styles.chat__input__container}>
{showEmojione && <Emojione onClickEmoji={onClickEmoji} />}
<form
onSubmit={handleSubmit(onHandleSubmit)}
encType="multipart/form-data"
>
<button
type="button"
className={"btn " + styles.icon_btn}
onClick={() => fileInputEl.current.click()}
>
<AttachFileIcon />
</button>
<button
type="button"
className={"btn " + styles.icon_btn}
onClick={() => setShowEmojione(!showEmojione)}
>
<InsertEmoticonIcon />
</button>
<input
type="file"
ref={(e) => (fileInputEl.current = e)}
accept={permittedFiles}
onChange={handleUploadFile}
hidden
/>
<textarea
className={styles.chatInput}
placeholder="Escribe un mensaje"
onKeyDown={handleKeyDown}
ref={inputTextEl}
rows="1"
/>
<button type="submit" className={"btn " + styles.send_btn}>
<SendIcon />
</button>
</form>
</div>
{selectedFile && (
<FileModal
file={selectedFile}
onCancel={removeSelectedFile}
onSend={handleSendFile}
/>
)}
</div>
);
};
const Header = ({ name, conferenceUrl, timezones }) => {
const [isShowConferenceModal, setisShowConferenceModal] = useState(false);
const toggleConferenceModal = () =>
setisShowConferenceModal(!isShowConferenceModal);
const options = [
{ label: "Crear Conferencia", action: toggleConferenceModal },
];
return (
<div className={styles.chat_header}>
<h2>{name}</h2>
<Header.Options options={options} />
<ConferenceModal
isShow={isShowConferenceModal}
onClose={toggleConferenceModal}
timezones={timezones}
zoomUrl={conferenceUrl}
onCreate={toggleConferenceModal}
/>
</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) => (
<li key={index}>
<button
className="btn option-btn"
onClick={() => {
toggleOptions();
option.action();
}}
>
{option.label}
</button>
</li>
))}
</ul>
</div>
</div>
</div>
);
};
Chat.Header = Header;
Header.Options = Options;
export default Chat;