diff --git a/backend/controllers/message.js b/backend/controllers/message.js index 08a6651..fac431b 100644 --- a/backend/controllers/message.js +++ b/backend/controllers/message.js @@ -35,6 +35,10 @@ exports.getContactMessages = async (req, res, next) => { const io = getIO(); const { contactId } = req.params; + const { search, page = 1 } = req.query; + + let limit = 5; + let offset = limit * (page - 1); try { const contact = await Contact.findByPk(contactId); @@ -45,9 +49,14 @@ exports.getContactMessages = async (req, res, next) => { } setMessagesAsRead(contactId); - const contactMessages = await contact.getMessages(); - return res.json(contactMessages); + const contactMessages = await contact.getMessages({ + limit, + offset, + order: [["createdAt", "DESC"]], + }); + + return res.json(contactMessages.reverse()); } catch (err) { next(err); } diff --git a/backend/controllers/session.json b/backend/controllers/session.json index 6d94970..38138e9 100644 --- a/backend/controllers/session.json +++ b/backend/controllers/session.json @@ -1 +1 @@ -{"WABrowserId":"\"W5pw0Llb60mSeV7WOHnk8A==\"","WASecretBundle":"{\"key\":\"alDLbPjonDFzCh5PEPql9cy59LNh1HFG/AZJVoucuYI=\",\"encKey\":\"FQ1MZ2eIH9hKV4dqFoBYTv1/89aopcMAa4CXgh/9csM=\",\"macKey\":\"alDLbPjonDFzCh5PEPql9cy59LNh1HFG/AZJVoucuYI=\"}","WAToken1":"\"cCkFiHM1JR5ck9OMV+WCiBG6QLCjsCjifdel3jRQ56k=\"","WAToken2":"\"1@0Y7HKBGDZWNuaYV7LiEwm0Y8xzlGoUGoyI8OD/KWjEH7UqVxJWfeT+pMNF4XBO7LeyEjpXVljC6rvQ==\""} \ No newline at end of file +{"WABrowserId":"\"W5pw0Llb60mSeV7WOHnk8A==\"","WASecretBundle":"{\"key\":\"alDLbPjonDFzCh5PEPql9cy59LNh1HFG/AZJVoucuYI=\",\"encKey\":\"FQ1MZ2eIH9hKV4dqFoBYTv1/89aopcMAa4CXgh/9csM=\",\"macKey\":\"alDLbPjonDFzCh5PEPql9cy59LNh1HFG/AZJVoucuYI=\"}","WAToken1":"\"GvHbBS6HkZD8f5nYpOxt5qsna4XaDx3JPsjoxwKiM6g=\"","WAToken2":"\"1@P1h9eDyW0m0ILzNfgs6sEF35E66OndfdlOjm3oYzeV13cg82FjBbRvhsma9+p5+/R7zjMUv6L+l/gg==\""} \ No newline at end of file diff --git a/backend/controllers/wbotMessageListener.js b/backend/controllers/wbotMessageListener.js index 0cd0cdf..69212fe 100644 --- a/backend/controllers/wbotMessageListener.js +++ b/backend/controllers/wbotMessageListener.js @@ -84,23 +84,27 @@ const wbotMessageListener = () => { } }); - wbot.on("message_ack", (msg, ack) => { - // console.log("alteração do ack da msg", msg); + wbot.on("message_ack", async (msg, ack) => { try { - const result = Message.update({ ack: ack }, { where: { id: msg.id.id } }); - - if (!result) { + const messageToUpdate = await Message.findOne({ + where: { id: msg.id.id }, + }); + if (!messageToUpdate) { const error = new Error( - "Erro ao definir as mensagens como lidas no banco de dados" + "Erro ao alterar o ack da mensagem no banco de dados" ); error.satusCode = 501; throw error; } + await messageToUpdate.update({ ack: ack }); + + io.to(messageToUpdate.contactId).emit("appMessage", { + action: "update", + message: messageToUpdate, + }); } catch (err) { console.log(err); } - - console.log("ack da msg", ack); }); }; diff --git a/backend/middleware/is-auth.js b/backend/middleware/is-auth.js index 7841141..c4800a1 100644 --- a/backend/middleware/is-auth.js +++ b/backend/middleware/is-auth.js @@ -7,6 +7,7 @@ module.exports = (req, res, next) => { decodedToken = jwt.verify(token, "mysecret"); } catch (err) { err.statusCode = 401; + err.message = "invalidToken"; next(err); } diff --git a/backend/util/database.js b/backend/util/database.js index 0123800..7651be1 100644 --- a/backend/util/database.js +++ b/backend/util/database.js @@ -6,6 +6,7 @@ const sequelize = new Sequelize("econo_whatsbot", "root", "nodecomplete", { collate: "utf8mb4_bin", }, dialect: "mysql", + timezone: "-03:00", host: "localhost", logging: false, }); diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 9d961e4..acea429 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -8691,6 +8691,14 @@ "resolved": "https://registry.npmjs.org/moment/-/moment-2.26.0.tgz", "integrity": "sha512-oIixUO+OamkUkwjhAVE18rAMfRJNsNe/Stid/gwHSOfHrOtw9EhAY2AHvdKZ/k/MggcYELFCJz/Sn2pL8b8JMw==" }, + "moment-timezone": { + "version": "0.5.31", + "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.31.tgz", + "integrity": "sha512-+GgHNg8xRhMXfEbv81iDtrVeTcWt0kWmTEY1XQK14dICTXnWJnT0dxdlPspwqF3keKMVPXwayEsk1DI0AA/jdA==", + "requires": { + "moment": ">= 2.9.0" + } + }, "move-concurrently": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz", diff --git a/frontend/package.json b/frontend/package.json index b3befac..308b5af 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -11,6 +11,7 @@ "bootstrap": "^4.5.0", "emoji-mart": "^3.0.0", "moment": "^2.26.0", + "moment-timezone": "^0.5.31", "react": "^16.13.1", "react-audio-player": "^0.13.0", "react-bootstrap": "^1.0.1", diff --git a/frontend/src/components/MessageSearch/useMessageSerach.js b/frontend/src/components/MessageSearch/useMessageSerach.js new file mode 100644 index 0000000..aa44e04 --- /dev/null +++ b/frontend/src/components/MessageSearch/useMessageSerach.js @@ -0,0 +1,22 @@ +import { useEffect, useState } from "react"; +import api from "../../util/api"; + +const useMessageSerach = (query, pageNumber) => { + useEffect(() => { + const fetchMessages = async () => { + try { + const res = await api.get("/messages/" + contactId, { + headers: { Authorization: "Bearer " + token }, + params: { serach: query, page: pageNumber }, + }); + setListMessages(res.data); + } catch (err) { + alert(err); + } + }; + }, [query, pageNumber]); + + return null; +}; + +export default useMessageSerach; diff --git a/frontend/src/pages/Chat/Chat.js b/frontend/src/pages/Chat/Chat.js index f59fa69..0f71c8f 100644 --- a/frontend/src/pages/Chat/Chat.js +++ b/frontend/src/pages/Chat/Chat.js @@ -1,5 +1,5 @@ import React, { useState, useEffect } from "react"; -import "./Chat.css"; +import { useHistory } from "react-router-dom"; import api from "../../util/api"; import profilePic from "../../Images/canove.png"; import profileDefaultPic from "../../Images/profile_default.png"; @@ -10,12 +10,15 @@ import LogedinNavbar from "../../components/Navbar/LogedinNavbar"; import DefaultNavbar from "../../components/Navbar/DefaultNavbar"; import ChatBox from "../ChatBox/ChatBox"; +import "./Chat.css"; // let socket; const Chat = ({ showToast }) => { const token = localStorage.getItem("token"); const username = localStorage.getItem("username"); + const history = useHistory(); + const [currentPeerContact, setCurrentPeerContact] = useState(null); const [contacts, setContacts] = useState([]); const [displayedContacts, setDisplayedContacts] = useState([]); @@ -30,11 +33,18 @@ const Chat = ({ showToast }) => { setContacts(res.data); setDisplayedContacts(res.data); } catch (err) { - alert(err.response.data.message); + if (err.response.data.message === "invalidToken") { + localStorage.removeItem("token"); + localStorage.removeItem("username"); + localStorage.removeItem("userId"); + history.push("/login"); + alert("Sessão expirada, por favor, faça login novamente."); + } + console.log(err); } }; fetchContacts(); - }, [currentPeerContact, token, notification]); + }, [currentPeerContact, token, notification, history]); useEffect(() => { const socket = openSocket("http://localhost:8080"); @@ -42,7 +52,6 @@ const Chat = ({ showToast }) => { socket.emit("joinNotification"); socket.on("contact", data => { - console.log("mensagem de um novo contato"); if (data.action === "create") { addContact(data.contact); setNotification(prevState => !prevState); @@ -50,7 +59,6 @@ const Chat = ({ showToast }) => { }); socket.on("appMessage", data => { - console.log("mensagem de contato existente"); setNotification(prevState => !prevState); // handleUnreadMessages(data.message.contactId); }); diff --git a/frontend/src/pages/ChatBox/ChatBox.css b/frontend/src/pages/ChatBox/ChatBox.css index b3a391c..491581a 100644 --- a/frontend/src/pages/ChatBox/ChatBox.css +++ b/frontend/src/pages/ChatBox/ChatBox.css @@ -171,40 +171,43 @@ input::placeholder { /* View item message */ .viewItemRight { + margin-right: 20px; + margin-top: 5px; min-width: 100px; max-width: 600px; height: auto; - - margin-right: 20px; - margin-top: 5px; - background-color: #dcf8c6; + display: block; + position: relative; align-self: flex-end; + text-align: left; + background-color: #dcf8c6; border-top-left-radius: 8px; border-top-right-radius: 8px; border-bottom-left-radius: 8px; border-bottom-right-radius: 0px; - padding-left: 20px; - padding-right: 20px; - padding-top: 10px; - padding-bottom: 10px; - color: #203152; box-shadow: 0 5px 5px #808888; + padding-left: 10px; + padding-right: 40px; + padding-top: 5px; + padding-bottom: 0px; + color: #203152; } .viewItemRight2 { - width: 300px; - padding: 4px; + width: 315px; + padding: 8px; height: auto; background-color: #dcf8c6; align-self: flex-end; margin-right: 20px; margin-top: 10px; + display: block; + position: relative; border-top-left-radius: 8px; border-top-right-radius: 8px; border-bottom-left-radius: 8px; border-bottom-right-radius: 0px; color: #203152; - /* text-align: left; */ box-shadow: 0 5px 5px #808888; } @@ -214,6 +217,8 @@ input::placeholder { align-self: flex-end; margin-right: 20px; margin-top: 10px; + display: block; + position: relative; border-top-left-radius: 8px; border-top-right-radius: 8px; border-bottom-left-radius: 8px; @@ -247,33 +252,37 @@ input::placeholder { } .viewItemLeft { + margin-left: 20px; + margin-top: 5px; min-width: 100px; max-width: 600px; height: auto; + display: block; + position: relative; - margin-left: 20px; - margin-top: 5px; background-color: #ffffff; align-self: flex-start; border-top-left-radius: 8px; border-top-right-radius: 8px; border-bottom-left-radius: 0px; border-bottom-right-radius: 8px; - padding-left: 20px; - padding-right: 20px; - padding-top: 10px; - padding-bottom: 10px; + padding-left: 10px; + padding-right: 40px; + padding-top: 5px; + padding-bottom: 0px; color: #303030; box-shadow: 0 5px 5px #808888; } .viewItemLeft2 { - width: 300px; - padding: 4px; + width: 315px; + padding: 8px; height: auto; + background-color: #ffffff; margin-left: 10px; margin-top: 10px; - background-color: #ffffff; + display: block; + position: relative; border-top-left-radius: 8px; border-top-right-radius: 8px; border-bottom-left-radius: 0px; @@ -287,6 +296,8 @@ input::placeholder { margin-top: 10px; width: 300px; height: auto; + display: block; + position: relative; border-top-left-radius: 8px; border-top-right-radius: 8px; border-bottom-left-radius: 0px; @@ -330,10 +341,16 @@ input::placeholder { .textContentItem { font-size: 16px; overflow-wrap: break-word; + padding: 0px 50px 0px 0px; } -.messageAck { - font-size: 10px; +.timestamp { + font-size: 11px; + position: absolute; + bottom: 5px; + right: 10px; + text-transform: uppercase; + color: #999; } .viewListContentChat div:last-child { @@ -354,7 +371,7 @@ input::placeholder { .imgItemLeft { object-fit: cover; - width: 300px; + width: 290px; height: 300px; border-top-left-radius: 8px; border-top-right-radius: 8px; diff --git a/frontend/src/pages/ChatBox/ChatBox.js b/frontend/src/pages/ChatBox/ChatBox.js index cf619b8..580d546 100644 --- a/frontend/src/pages/ChatBox/ChatBox.js +++ b/frontend/src/pages/ChatBox/ChatBox.js @@ -1,10 +1,13 @@ -import React, { useState, useEffect } from "react"; -import { Card } from "react-bootstrap"; -import { FiPaperclip, FiSend, FiX, FiSmile, FiDownload } from "react-icons/fi"; +import React, { useState, useEffect, useRef } from "react"; +// import { Card } from "react-bootstrap"; alterei pra DIV, remover caso não dê problemas +import { FiPaperclip, FiSend, FiX, FiSmile } from "react-icons/fi"; import { RiSendPlane2Line } from "react-icons/ri"; +import { BsCheck, BsCheckAll, BsClock } from "react-icons/bs"; +import { FaFileDownload } from "react-icons/fa"; import "emoji-mart/css/emoji-mart.css"; import { Picker } from "emoji-mart"; import ModalImage from "react-modal-image"; +import moment from "moment-timezone"; import api from "../../util/api"; import openSocket from "socket.io-client"; @@ -15,7 +18,12 @@ import ScrollToBottom from "react-scroll-to-bottom"; import "react-toastify/dist/ReactToastify.css"; import "./ChatBox.css"; -// let socket; + +// const executeScroll = myRef => +// myRef.current.scrollIntoView({ +// // behavior: "smooth", +// block: "end", +// }); const ChatBox = ({ currentPeerContact }) => { const SERVER_URL = "http://localhost:8080/"; @@ -25,19 +33,29 @@ const ChatBox = ({ currentPeerContact }) => { const username = localStorage.getItem("username"); const token = localStorage.getItem("token"); const mediaInitialState = { preview: "", raw: "", name: "" }; + // const [isLoading, setIsLoading] = useState(true); const [listMessages, setListMessages] = useState([]); const [inputMessage, setInputMessage] = useState(""); const [media, setMedia] = useState(mediaInitialState); const [showEmoji, setShowEmoji] = useState(false); + // let lastMessageRef = useRef(); + + // useEffect(() => { + // executeScroll(lastMessageRef); + // }, [isLoading]); + useEffect(() => { const fetchMessages = async () => { + // setIsLoading(true); try { const res = await api.get("/messages/" + contactId, { headers: { Authorization: "Bearer " + token }, + params: { page: 3 }, }); setListMessages(res.data); + // setIsLoading(false); } catch (err) { alert(err); } @@ -58,9 +76,13 @@ const ChatBox = ({ currentPeerContact }) => { socket.emit("joinChatBox", contactId, () => {}); socket.on("appMessage", data => { + // setIsLoading(true); if (data.action === "create") { addMessage(data.message); + } else if (data.action === "update") { + updateMessageAck(data.message); } + // setIsLoading(false); }); return () => { @@ -68,6 +90,18 @@ const ChatBox = ({ currentPeerContact }) => { }; }, [contactId]); + const updateMessageAck = message => { + let id = message.id; + setListMessages(prevState => { + console.log("mudando o ack da mensagem"); + let aux = [...prevState]; + let messageIndex = aux.findIndex(message => message.id === id); + aux[messageIndex].ack = message.ack; + + return aux; + }); + }; + const addMessage = message => { setListMessages(prevState => [...prevState, message]); }; @@ -140,10 +174,24 @@ const ChatBox = ({ currentPeerContact }) => { console.log(listMessages); + const renderMsgAck = message => { + //todo remove timestamp logic from main return and adopt moment to timestamps + if (message.ack === 0) { + return ; + } else if (message.ack === 1) { + return ; + } else if (message.ack === 2) { + return ; + } else if (message.ack === 3) { + return ; + } + }; + const renderMessages = () => { if (listMessages.length > 0) { let viewListMessages = []; listMessages.forEach((message, index) => { + // mensagens recebidas if (message.userId === 0) { if (message.mediaUrl && message.mediaType === "image") { viewListMessages.push( @@ -155,36 +203,80 @@ const ChatBox = ({ currentPeerContact }) => { large={`${SERVER_URL}${message.mediaUrl}`} alt="" /> - {message.messageBody} +
+ {message.messageBody} + + {moment(message.createdAt) + .tz("America/Sao_Paulo") + .format("HH:mm")} + +
); } else if (message.mediaUrl && message.mediaType === "audio") { viewListMessages.push( -
- {message.messageBody} +
+
+ + {moment(message.createdAt) + .tz("America/Sao_Paulo") + .format("HH:mm")} + +
+
+ ); + } else if (message.mediaUrl && message.mediaType === "video") { + viewListMessages.push( +
+
); } else if (message.mediaUrl) { viewListMessages.push(
- - {message.messageBody} -
- Download -
+
+ + {message.messageBody} + + + + {moment(message.createdAt) + .tz("America/Sao_Paulo") + .format("HH:mm")} + +
); } else { viewListMessages.push(
- {message.messageBody} +
+ {message.messageBody} + + {moment(message.createdAt) + .tz("America/Sao_Paulo") + .format("HH:mm")} + +
); } @@ -193,43 +285,86 @@ const ChatBox = ({ currentPeerContact }) => { viewListMessages.push(
- {message.messageBody} +
+ {message.messageBody} + + {moment(message.createdAt) + .tz("America/Sao_Paulo") + .format("HH:mm")}{" "} + {renderMsgAck(message)} + +
); + // mensagens enviadas } else if (message.mediaUrl && message.mediaType === "audio") { viewListMessages.push( -
- {message.messageBody} +
+
+ + {moment(message.createdAt) + .tz("America/Sao_Paulo") + .format("HH:mm")}{" "} + {renderMsgAck(message)} + +
+
+ ); + } else if (message.mediaUrl && message.mediaType === "video") { + viewListMessages.push( +
+
); } else if (message.mediaUrl) { viewListMessages.push(
- - {message.messageBody} -
- Download -
+
+ + {message.messageBody} + + + + {moment(message.createdAt) + .tz("America/Sao_Paulo") + .format("HH:mm")}{" "} + {renderMsgAck(message)} + +
); } else { viewListMessages.push(
- {message.messageBody} - {message.ack} +
+ {message.messageBody} + + {moment(message.createdAt) + .tz("America/Sao_Paulo") + .format("HH:mm")}{" "} + {renderMsgAck(message)} + +
); } @@ -246,7 +381,7 @@ const ChatBox = ({ currentPeerContact }) => { }; return ( - +
{

{currentPeerContact.name}

- -

Status do contato

-
+ Status do contato
-
{renderMessages()}
+
+ {renderMessages()} + {/*
*/} +
{media.preview ? (
@@ -326,7 +462,7 @@ const ChatBox = ({ currentPeerContact }) => {
)} - +
); };