import React, { useState, useEffect, useContext, useRef } from "react" import "emoji-mart/css/emoji-mart.css" import { useParams } from "react-router-dom" import { Picker } from "emoji-mart" import MicRecorder from "mic-recorder-to-mp3" import clsx from "clsx" import { makeStyles } from "@material-ui/core/styles" import Paper from "@material-ui/core/Paper" import InputBase from "@material-ui/core/InputBase" import CircularProgress from "@material-ui/core/CircularProgress" import { green } from "@material-ui/core/colors" import AttachFileIcon from "@material-ui/icons/AttachFile" import IconButton from "@material-ui/core/IconButton" import MoreVert from "@material-ui/icons/MoreVert" import MoodIcon from "@material-ui/icons/Mood" import SendIcon from "@material-ui/icons/Send" import CancelIcon from "@material-ui/icons/Cancel" import ClearIcon from "@material-ui/icons/Clear" import MicIcon from "@material-ui/icons/Mic" import CheckCircleOutlineIcon from "@material-ui/icons/CheckCircleOutline" import HighlightOffIcon from "@material-ui/icons/HighlightOff" import { FormControlLabel, Hidden, Menu, MenuItem, Switch, } from "@material-ui/core" import ClickAwayListener from "@material-ui/core/ClickAwayListener" import { i18n } from "../../translate/i18n" import api from "../../services/api" import RecordingTimer from "./RecordingTimer" import { ReplyMessageContext } from "../../context/ReplyingMessage/ReplyingMessageContext" import { AuthContext } from "../../context/Auth/AuthContext" import { useLocalStorage } from "../../hooks/useLocalStorage" import toastError from "../../errors/toastError" // import TicketsManager from "../../components/TicketsManager/"; import { TabTicketContext } from "../../context/TabTicketHeaderOption/TabTicketHeaderOption" import ModalTemplate from "../ModalTemplate" import { render } from '@testing-library/react' import { countTicketMsgContext } from "../../context/CountTicketMsgProvider/CountTicketMsgProvider" const Mp3Recorder = new MicRecorder({ bitRate: 128 }) const useStyles = makeStyles((theme) => ({ mainWrapper: { background: "#eee", display: "flex", flexDirection: "column", alignItems: "center", borderTop: "1px solid rgba(0, 0, 0, 0.12)", [theme.breakpoints.down("sm")]: { position: "fixed", bottom: 0, width: "100%", }, }, newMessageBox: { background: "#eee", width: "100%", display: "flex", padding: "7px", alignItems: "center", }, messageInputWrapper: { padding: 6, marginRight: 7, background: "#fff", display: "flex", borderRadius: 20, flex: 1, position: "relative", }, messageInput: { paddingLeft: 10, flex: 1, border: "none", }, sendMessageIcons: { color: "grey", }, uploadInput: { display: "none", }, viewMediaInputWrapper: { display: "flex", padding: "10px 13px", position: "relative", justifyContent: "space-between", alignItems: "center", backgroundColor: "#eee", borderTop: "1px solid rgba(0, 0, 0, 0.12)", }, emojiBox: { position: "absolute", bottom: 63, width: 40, borderTop: "1px solid #e8e8e8", }, circleLoading: { color: green[500], opacity: "70%", position: "absolute", top: "20%", left: "50%", marginLeft: -12, }, audioLoading: { color: green[500], opacity: "70%", }, recorderWrapper: { display: "flex", alignItems: "center", alignContent: "middle", }, cancelAudioIcon: { color: "red", }, sendAudioIcon: { color: "green", }, replyginMsgWrapper: { display: "flex", width: "100%", alignItems: "center", justifyContent: "center", paddingTop: 8, paddingLeft: 73, paddingRight: 7, }, replyginMsgContainer: { flex: 1, marginRight: 5, overflowY: "hidden", backgroundColor: "rgba(0, 0, 0, 0.05)", borderRadius: "7.5px", display: "flex", position: "relative", }, replyginMsgBody: { padding: 10, height: "auto", display: "block", whiteSpace: "pre-wrap", overflow: "hidden", }, replyginContactMsgSideColor: { flex: "none", width: "4px", backgroundColor: "#35cd96", }, replyginSelfMsgSideColor: { flex: "none", width: "4px", backgroundColor: "#6bcbef", }, messageContactName: { display: "flex", color: "#6bcbef", fontWeight: 500, }, messageQuickAnswersWrapper: { margin: 0, position: "absolute", bottom: "50px", background: "#ffffff", padding: "2px", border: "1px solid #CCC", left: 0, width: "100%", "& li": { listStyle: "none", "& a": { display: "block", padding: "8px", textOverflow: "ellipsis", overflow: "hidden", maxHeight: "32px", "&:hover": { background: "#F1F1F1", cursor: "pointer", }, }, }, }, })) const MessageInput = ({ ticketStatus, ticketLastMessage, ticketIsRemote }) => { const { tabOption, setTabOption } = useContext(TabTicketContext) const { countTicketMsg, setCountTicketMsg } = useContext(countTicketMsgContext) const classes = useStyles() const { ticketId } = useParams() const [medias, setMedias] = useState([]) const [inputMessage, setInputMessage] = useState("") const [showEmoji, setShowEmoji] = useState(false) const [loading, setLoading] = useState(false) const [recording, setRecording] = useState(false) const [quickAnswers, setQuickAnswer] = useState([]) const [typeBar, setTypeBar] = useState(false) const inputRef = useRef() const [anchorEl, setAnchorEl] = useState(null) const { setReplyingMessage, replyingMessage } = useContext(ReplyMessageContext) const { user } = useContext(AuthContext) const [templates, setTemplates] = useState(null) const [params, setParams] = useState(null) const [signMessage, setSignMessage] = useLocalStorage("signOption", true) const isRun = useRef(false) useEffect(() => { inputRef.current.focus() }, [replyingMessage]) useEffect(() => { if (ticketIsRemote && countTicketMsg === 0 && ticketLastMessage && ticketLastMessage.trim().length > 0) { setInputMessage(ticketLastMessage) } else { setInputMessage("") } }, [countTicketMsg, ticketIsRemote, ticketLastMessage]) useEffect(() => { inputRef.current.focus() return () => { setInputMessage("") setShowEmoji(false) setMedias([]) setReplyingMessage(null) } }, [ticketId, setReplyingMessage]) const handleChangeInput = (e) => { setInputMessage(e.target.value) handleLoadQuickAnswer(e.target.value) } const handleQuickAnswersClick = (value) => { setInputMessage(value) setTypeBar(false) } const handleAddEmoji = (e) => { let emoji = e.native setInputMessage((prevState) => prevState + emoji) } const handleChangeMedias = (e) => { if (!e.target.files) { return } const selectedMedias = Array.from(e.target.files) setMedias(selectedMedias) } const handleInputPaste = (e) => { if (e.clipboardData.files[0]) { console.log('clipboardData: ', e.clipboardData.files[0]) setMedias([e.clipboardData.files[0]]) } } const handleUploadMedia = async (e) => { setLoading(true) e.preventDefault() if (tabOption === 'search') { setTabOption('open') } const formData = new FormData() formData.append("fromMe", true) medias.forEach((media) => { formData.append("medias", media) formData.append("body", media.name) }) try { const { data } = await api.post(`/messages/${ticketId}`, formData) console.log('DATA FROM SEND MESSAGE MEDIA: ', data) } catch (err) { toastError(err) } setLoading(false) setMedias([]) } const handleSendMessage = async (templateParams = null) => { if (inputMessage.trim() === "") return setLoading(true) if (tabOption === 'search') { setTabOption('open') } if (templateParams) { for (let key in templateParams) { if (templateParams.hasOwnProperty(key)) { if (key === '_reactName') { templateParams = null break } } } } let message = { read: 1, fromMe: true, mediaUrl: "", body: (signMessage && !templateParams) ? `*${user?.name}:*\n${inputMessage.trim()}` : inputMessage.trim(), quotedMsg: replyingMessage } if (templateParams) { message = { ...message, params: templateParams } } try { const { data } = await api.post(`/messages/${ticketId}`, message) setParams(null) if (data && data?.data && Array.isArray(data.data)) { setTemplates(data.data) } setCountTicketMsg(1) } catch (err) { toastError(err) } setInputMessage("") setShowEmoji(false) setLoading(false) setReplyingMessage(null) } useEffect(() => { if (!params) return const body_params = params?.find(p => p?.type === 'BODY') console.log('------------> body_params: ', body_params) if (!body_params) return let { text } = body_params console.log('PARAMS FROM MESSAGE INPUT: ', params, ' | text: ', text) let body = text.match(/{{\d+}}/g) if (body && body.length > 0) { const { parameters } = body_params for (const key in parameters) { if (!isNaN(key)) { const { index, text: body_text } = parameters[key] text = text.replace(`{{${index}}}`, body_text) } } } console.log('NEW TEXT: ', text) setInputMessage(text) }, [params]) useEffect(() => { if (params) { handleSendMessage(params) } }, [inputMessage, params]) useEffect(() => { if (!templates) return return render( { return { id, name, components, language, } })} ticketId={ticketId} />) }, [templates]) const handleStartRecording = async () => { setLoading(true) try { await navigator.mediaDevices.getUserMedia({ audio: true }) await Mp3Recorder.start() setRecording(true) setLoading(false) } catch (err) { toastError(err) setLoading(false) } } const handleLoadQuickAnswer = async (value) => { if (value && value.indexOf("/") === 0) { try { console.log('{ searchParam: inputMessage.substring(1) },: ', { searchParam: inputMessage.substring(1) },) console.log('USER ID: ', user.id) const { data } = await api.get("/quickAnswers/", { params: { searchParam: inputMessage.substring(1), userId: user.id }, }) setQuickAnswer(data.quickAnswers) if (data.quickAnswers.length > 0) { setTypeBar(true) } else { setTypeBar(false) } } catch (err) { setTypeBar(false) } } else { setTypeBar(false) } } const handleUploadAudio = async () => { setLoading(true) if (tabOption === 'search') { setTabOption('open') } try { const [, blob] = await Mp3Recorder.stop().getMp3() if (blob.size < 10000) { setLoading(false) setRecording(false) return } const formData = new FormData() const filename = `${new Date().getTime()}.mp3` formData.append("medias", blob, filename) formData.append("body", filename) formData.append("fromMe", true) formData.append("mic_audio", true) await api.post(`/messages/${ticketId}`, formData) } catch (err) { toastError(err) } setRecording(false) setLoading(false) } const handleCancelAudio = async () => { try { await Mp3Recorder.stop().getMp3() setRecording(false) } catch (err) { toastError(err) } } const handleOpenMenuClick = (event) => { setAnchorEl(event.currentTarget) } const handleMenuItemClick = (event) => { setAnchorEl(null) } const renderReplyingMessage = (message) => { return (
{!message.fromMe && ( {message.contact?.name} )} {message.body}
setReplyingMessage(null)} >
) } if (medias.length > 0) return ( setMedias([])} > {loading ? (
) : ( {medias[0]?.name} {/* */} )}
) else { return ( {replyingMessage && renderReplyingMessage(replyingMessage)}
setShowEmoji((prevState) => !prevState)} > {showEmoji ? (
setShowEmoji(false)}>
) : null} { setSignMessage(e.target.checked) }} name="showAllTickets" color="primary" /> } />
setShowEmoji((prevState) => !prevState)} > { setSignMessage(e.target.checked) }} name="showAllTickets" color="primary" /> } />
{ input && input.focus() input && (inputRef.current = input) }} className={classes.messageInput} placeholder={ ticketStatus === "open" ? i18n.t("messagesInput.placeholderOpen") : i18n.t("messagesInput.placeholderClosed") } multiline // rowsMax={5} maxRows={5} value={inputMessage} onChange={handleChangeInput} disabled={recording || loading || ticketStatus !== "open"} onPaste={(e) => { ticketStatus === "open" && handleInputPaste(e) }} onKeyPress={(e) => { if (loading || e.shiftKey) return else if (e.key === "Enter") { handleSendMessage() } }} /> {typeBar ? ( ) : (
)}
{inputMessage ? ( ) : recording ? (
{loading ? (
) : ( )}
) : ( )}
) } } export default MessageInput