diff --git a/backend/src/controllers/MessageController.ts b/backend/src/controllers/MessageController.ts index 210790d..fd2091d 100644 --- a/backend/src/controllers/MessageController.ts +++ b/backend/src/controllers/MessageController.ts @@ -2,6 +2,7 @@ import { Request, Response } from "express"; import SetTicketMessagesAsRead from "../helpers/SetTicketMessagesAsRead"; import { getIO } from "../libs/socket"; +import Message from "../models/Message"; import ListMessagesService from "../services/MessageServices/ListMessagesService"; import ShowTicketService from "../services/TicketServices/ShowTicketService"; @@ -17,6 +18,7 @@ type MessageData = { body: string; fromMe: boolean; read: boolean; + quotedMsg?: Message; }; export const index = async (req: Request, res: Response): Promise => { @@ -35,7 +37,7 @@ export const index = async (req: Request, res: Response): Promise => { export const store = async (req: Request, res: Response): Promise => { const { ticketId } = req.params; - const { body }: MessageData = req.body; + const { body, quotedMsg }: MessageData = req.body; const medias = req.files as Express.Multer.File[]; const ticket = await ShowTicketService(ticketId); @@ -47,7 +49,7 @@ export const store = async (req: Request, res: Response): Promise => { }) ); } else { - await SendWhatsAppMessage({ body, ticket }); + await SendWhatsAppMessage({ body, ticket, quotedMsg }); } await SetTicketMessagesAsRead(ticket); diff --git a/backend/src/helpers/GetWbotMessage.ts b/backend/src/helpers/GetWbotMessage.ts index 70aaafb..b230965 100644 --- a/backend/src/helpers/GetWbotMessage.ts +++ b/backend/src/helpers/GetWbotMessage.ts @@ -13,19 +13,32 @@ export const GetWbotMessage = async ( `${ticket.contact.number}@${ticket.isGroup ? "g" : "c"}.us` ); - try { - const chatMessages = await wbotChat.fetchMessages({ limit: 20 }); + let limit = 20; - const msgToDelete = chatMessages.find(msg => msg.id.id === messageId); + const fetchWbotMessagesGradually = async (): Promise => { + const chatMessages = await wbotChat.fetchMessages({ limit }); - if (!msgToDelete) { - throw new Error(); + const msgFound = chatMessages.find(msg => msg.id.id === messageId); + + if (!msgFound && limit < 100) { + limit += 20; + return fetchWbotMessagesGradually(); } - return msgToDelete; + return msgFound; + }; + + try { + const msgFound = await fetchWbotMessagesGradually(); + + if (!msgFound) { + throw new Error("Cannot found message within 100 last messages"); + } + + return msgFound; } catch (err) { console.log(err); - throw new AppError("ERR_DELETE_WAPP_MSG"); + throw new AppError("ERR_FETCH_WAPP_MSG"); } }; diff --git a/backend/src/helpers/SerializeWbotMsgId.ts b/backend/src/helpers/SerializeWbotMsgId.ts new file mode 100644 index 0000000..4b5886e --- /dev/null +++ b/backend/src/helpers/SerializeWbotMsgId.ts @@ -0,0 +1,12 @@ +import Message from "../models/Message"; +import Ticket from "../models/Ticket"; + +const SerializeWbotMsgId = (ticket: Ticket, message: Message): string => { + const serializedMsgId = `${message.fromMe}_${ticket.contact.number}@${ + ticket.isGroup ? "g" : "c" + }.us_${message.id}`; + + return serializedMsgId; +}; + +export default SerializeWbotMsgId; diff --git a/backend/src/services/MessageServices/CreateMessageService.ts b/backend/src/services/MessageServices/CreateMessageService.ts index cbdd5bb..a98c1c6 100644 --- a/backend/src/services/MessageServices/CreateMessageService.ts +++ b/backend/src/services/MessageServices/CreateMessageService.ts @@ -28,7 +28,14 @@ const CreateMessageService = async ({ await Message.upsert(messageData); const message = await Message.findByPk(messageData.id, { - include: ["contact", "quotedMsg"] + include: [ + "contact", + { + model: Message, + as: "quotedMsg", + include: ["contact"] + } + ] }); if (!message) { diff --git a/backend/src/services/MessageServices/ListMessagesService.ts b/backend/src/services/MessageServices/ListMessagesService.ts index 6824b3c..459f2a6 100644 --- a/backend/src/services/MessageServices/ListMessagesService.ts +++ b/backend/src/services/MessageServices/ListMessagesService.ts @@ -32,7 +32,14 @@ const ListMessagesService = async ({ const { count, rows: messages } = await Message.findAndCountAll({ where: { ticketId }, limit, - include: ["contact", "quotedMsg"], + include: [ + "contact", + { + model: Message, + as: "quotedMsg", + include: ["contact"] + } + ], offset, order: [["createdAt", "DESC"]] }); diff --git a/backend/src/services/WbotServices/SendWhatsAppMessage.ts b/backend/src/services/WbotServices/SendWhatsAppMessage.ts index f14e96b..8b28b75 100644 --- a/backend/src/services/WbotServices/SendWhatsAppMessage.ts +++ b/backend/src/services/WbotServices/SendWhatsAppMessage.ts @@ -1,23 +1,37 @@ import { Message as WbotMessage } from "whatsapp-web.js"; import AppError from "../../errors/AppError"; import GetTicketWbot from "../../helpers/GetTicketWbot"; +import GetWbotMessage from "../../helpers/GetWbotMessage"; +import SerializeWbotMsgId from "../../helpers/SerializeWbotMsgId"; +import Message from "../../models/Message"; import Ticket from "../../models/Ticket"; interface Request { body: string; ticket: Ticket; + quotedMsg?: Message; } const SendWhatsAppMessage = async ({ body, - ticket + ticket, + quotedMsg }: Request): Promise => { - try { - const wbot = await GetTicketWbot(ticket); + let quotedMsgSerializedId: string | undefined; + if (quotedMsg) { + await GetWbotMessage(ticket, quotedMsg.id); + quotedMsgSerializedId = SerializeWbotMsgId(ticket, quotedMsg); + } + const wbot = await GetTicketWbot(ticket); + + try { const sentMessage = await wbot.sendMessage( `${ticket.contact.number}@${ticket.isGroup ? "g" : "c"}.us`, - body + body, + { + quotedMessageId: quotedMsgSerializedId + } ); await ticket.update({ lastMessage: body }); diff --git a/backend/src/services/WbotServices/wbotMessageListener.ts b/backend/src/services/WbotServices/wbotMessageListener.ts index 7860d03..ea6a93b 100644 --- a/backend/src/services/WbotServices/wbotMessageListener.ts +++ b/backend/src/services/WbotServices/wbotMessageListener.ts @@ -344,7 +344,14 @@ const wbotMessageListener = (wbot: Session): void => { try { const messageToUpdate = await Message.findByPk(msg.id.id, { - include: ["contact"] + include: [ + "contact", + { + model: Message, + as: "quotedMsg", + include: ["contact"] + } + ] }); if (!messageToUpdate) { return; diff --git a/frontend/src/components/MessageInput/index.js b/frontend/src/components/MessageInput/index.js index 95de524..eb8779d 100644 --- a/frontend/src/components/MessageInput/index.js +++ b/frontend/src/components/MessageInput/index.js @@ -1,4 +1,4 @@ -import React, { useState, useEffect, useContext } from "react"; +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"; @@ -170,17 +170,24 @@ const MessageInput = ({ ticketStatus }) => { const [showEmoji, setShowEmoji] = useState(false); const [loading, setLoading] = useState(false); const [recording, setRecording] = useState(false); + const inputRef = useRef(); const { setReplyingMessage, replyingMessage } = useContext( ReplyMessageContext ); useEffect(() => { + inputRef.current.focus(); + }, [replyingMessage]); + + useEffect(() => { + inputRef.current.focus(); return () => { setInputMessage(""); setShowEmoji(false); setMedias([]); + setReplyingMessage(null); }; - }, [ticketId]); + }, [ticketId, setReplyingMessage]); const handleChangeInput = e => { setInputMessage(e.target.value); @@ -245,6 +252,7 @@ const MessageInput = ({ ticketStatus }) => { fromMe: true, mediaUrl: "", body: `${username}: ${inputMessage.trim()}`, + quotedMsg: replyingMessage, }; try { await api.post(`/messages/${ticketId}`, message); @@ -264,6 +272,7 @@ const MessageInput = ({ ticketStatus }) => { setInputMessage(""); setShowEmoji(false); setLoading(false); + setReplyingMessage(null); }; const handleStartRecording = async () => { @@ -344,7 +353,7 @@ const MessageInput = ({ ticketStatus }) => { aria-label="showRecorder" component="span" disabled={loading || ticketStatus !== "open"} - onClick={() => setReplyingMessage({})} + onClick={() => setReplyingMessage(null)} > @@ -386,7 +395,7 @@ const MessageInput = ({ ticketStatus }) => { else { return ( - {replyingMessage.id && renderReplyingMessage(replyingMessage)} + {replyingMessage && renderReplyingMessage(replyingMessage)}
{
input && input.focus()} + inputRef={input => { + input && input.focus(); + input && (inputRef.current = input); + }} className={classes.messageInput} placeholder={ ticketStatus === "open" diff --git a/frontend/src/components/MessageOptionsMenu/index.js b/frontend/src/components/MessageOptionsMenu/index.js index eb737e8..773feac 100644 --- a/frontend/src/components/MessageOptionsMenu/index.js +++ b/frontend/src/components/MessageOptionsMenu/index.js @@ -65,9 +65,11 @@ const MessageOptionsMenu = ({ message, menuOpen, handleClose, anchorEl }) => { open={menuOpen} onClose={handleClose} > - - {i18n.t("messageOptionsMenu.delete")} - + {message.fromMe && ( + + {i18n.t("messageOptionsMenu.delete")} + + )} {i18n.t("messageOptionsMenu.reply")} diff --git a/frontend/src/components/MessagesList/index.js b/frontend/src/components/MessagesList/index.js index 2c42300..943db07 100644 --- a/frontend/src/components/MessagesList/index.js +++ b/frontend/src/components/MessagesList/index.js @@ -106,7 +106,7 @@ const useStyles = makeStyles(theme => ({ overflow: "hidden", }, - quotedSideLeft: { + quotedSideColorLeft: { flex: "none", width: "4px", backgroundColor: "#6bcbef", @@ -158,7 +158,7 @@ const useStyles = makeStyles(theme => ({ whiteSpace: "pre-wrap", }, - quotedSideRight: { + quotedSideColorRight: { flex: "none", width: "4px", backgroundColor: "#35cd96", @@ -170,6 +170,7 @@ const useStyles = makeStyles(theme => ({ color: "#999", zIndex: 1, backgroundColor: "inherit", + opacity: "90%", "&:hover, &.Mui-focusVisible": { backgroundColor: "inherit" }, }, @@ -534,14 +535,14 @@ const MessagesList = ({ ticketId, isGroup, setReplyingMessage }) => { })} >
{!message.quotedMsg?.fromMe && ( - {message.contact?.name} + {message.quotedMsg?.contact?.name} )} {message.quotedMsg?.body} diff --git a/frontend/src/context/ReplyingMessage/ReplyingMessageContext.js b/frontend/src/context/ReplyingMessage/ReplyingMessageContext.js index 6199f93..5e82bf5 100644 --- a/frontend/src/context/ReplyingMessage/ReplyingMessageContext.js +++ b/frontend/src/context/ReplyingMessage/ReplyingMessageContext.js @@ -3,7 +3,7 @@ import React, { useState, createContext } from "react"; const ReplyMessageContext = createContext(); const ReplyMessageProvider = ({ children }) => { - const [replyingMessage, setReplyingMessage] = useState({}); + const [replyingMessage, setReplyingMessage] = useState(null); return (