diff --git a/backend/src/services/wbotMessageListener.js b/backend/src/services/wbotMessageListener.js index bf860d2..99dab36 100644 --- a/backend/src/services/wbotMessageListener.js +++ b/backend/src/services/wbotMessageListener.js @@ -9,7 +9,7 @@ const Ticket = require("../models/Ticket"); const Message = require("../models/Message"); const { getIO } = require("../libs/socket"); -const { getWbot } = require("../libs/wbot"); +const { getWbot, init } = require("../libs/wbot"); const verifyContact = async (msgContact, profilePicUrl) => { let contact = await Contact.findOne({ @@ -66,7 +66,7 @@ const verifyTicket = async contact => { const handlMedia = async (msg, ticket) => { const media = await msg.downloadMedia(); let newMessage; - + console.log("criando midia"); if (media) { if (!media.filename) { let ext = media.mimetype.split("/")[1].split(";")[0]; @@ -150,14 +150,6 @@ const wbotMessageListener = () => { let msgContact; if (msg.fromMe) { - const alreadyExists = await Message.findOne({ - where: { id: msg.id.id }, - }); - // return if message was already created by messagesController - if (alreadyExists) { - return; - } - msgContact = await wbot.getContactById(msg.to); } else { msgContact = await msg.getContact(); @@ -167,6 +159,17 @@ const wbotMessageListener = () => { const contact = await verifyContact(msgContact, profilePicUrl); const ticket = await verifyTicket(contact); + //return if message was already created by messageController + if (msg.fromMe) { + const alreadyExists = await Message.findOne({ + where: { id: msg.id.id }, + }); + + if (alreadyExists) { + return; + } + } + await handleMessage(msg, ticket, contact); } catch (err) { Sentry.captureException(err); @@ -179,11 +182,9 @@ const wbotMessageListener = () => { const messageToUpdate = await Message.findOne({ where: { id: msg.id.id }, }); - if (!messageToUpdate) { return; } - await messageToUpdate.update({ ack: ack }); io.to(messageToUpdate.ticketId).emit("appMessage", { diff --git a/frontend/src/components/_layout/MainListItems.js b/frontend/src/components/_layout/MainListItems.js new file mode 100644 index 0000000..41dd8c7 --- /dev/null +++ b/frontend/src/components/_layout/MainListItems.js @@ -0,0 +1,60 @@ +import React from "react"; +import { Link as RouterLink } from "react-router-dom"; + +import ListItem from "@material-ui/core/ListItem"; +import ListItemIcon from "@material-ui/core/ListItemIcon"; +import ListItemText from "@material-ui/core/ListItemText"; +import DashboardIcon from "@material-ui/icons/Dashboard"; +import WhatsAppIcon from "@material-ui/icons/WhatsApp"; +import SyncAltIcon from "@material-ui/icons/SyncAlt"; + +import ContactPhoneIcon from "@material-ui/icons/ContactPhone"; + +import { i18n } from "../../translate/i18n"; + +function ListItemLink(props) { + const { icon, primary, to, className } = props; + + const renderLink = React.useMemo( + () => + React.forwardRef((itemProps, ref) => ( + + )), + [to] + ); + + return ( +
  • + + {icon ? {icon} : null} + + +
  • + ); +} + +const MainListItems = () => { + return ( +
    + } /> + } + /> + } + /> + + } + /> +
    + ); +}; + +export default MainListItems; diff --git a/frontend/src/components/_layout/index.js b/frontend/src/components/_layout/index.js new file mode 100644 index 0000000..abbd9ae --- /dev/null +++ b/frontend/src/components/_layout/index.js @@ -0,0 +1,302 @@ +import React, { useState, useContext, useEffect } from "react"; +import clsx from "clsx"; +import { makeStyles } from "@material-ui/core/styles"; + +import Drawer from "@material-ui/core/Drawer"; + +import AppBar from "@material-ui/core/AppBar"; +import Toolbar from "@material-ui/core/Toolbar"; +import List from "@material-ui/core/List"; +import Typography from "@material-ui/core/Typography"; +import Divider from "@material-ui/core/Divider"; +import IconButton from "@material-ui/core/IconButton"; +import Badge from "@material-ui/core/Badge"; + +import MenuIcon from "@material-ui/icons/Menu"; +import ChevronLeftIcon from "@material-ui/icons/ChevronLeft"; +import NotificationsIcon from "@material-ui/icons/Notifications"; +import MainListItems from "./MainListItems"; +import AccountCircle from "@material-ui/icons/AccountCircle"; + +import MenuItem from "@material-ui/core/MenuItem"; +import Menu from "@material-ui/core/Menu"; + +import openSocket from "socket.io-client"; +import { format } from "date-fns"; +import { toast } from "react-toastify"; +import { useHistory, useParams } from "react-router-dom"; +import { i18n } from "../../translate/i18n"; + +import { AuthContext } from "../../context/Auth/AuthContext"; + +const drawerWidth = 240; + +const useStyles = makeStyles(theme => ({ + root: { + display: "flex", + height: "100vh", + }, + + toolbar: { + paddingRight: 24, // keep right padding when drawer closed + }, + toolbarIcon: { + display: "flex", + alignItems: "center", + justifyContent: "flex-end", + padding: "0 8px", + minHeight: "48px", + }, + appBar: { + zIndex: theme.zIndex.drawer + 1, + transition: theme.transitions.create(["width", "margin"], { + easing: theme.transitions.easing.sharp, + duration: theme.transitions.duration.leavingScreen, + }), + }, + appBarShift: { + marginLeft: drawerWidth, + width: `calc(100% - ${drawerWidth}px)`, + transition: theme.transitions.create(["width", "margin"], { + easing: theme.transitions.easing.sharp, + duration: theme.transitions.duration.enteringScreen, + }), + }, + menuButton: { + marginRight: 36, + }, + menuButtonHidden: { + display: "none", + }, + title: { + flexGrow: 1, + }, + drawerPaper: { + position: "relative", + whiteSpace: "nowrap", + width: drawerWidth, + transition: theme.transitions.create("width", { + easing: theme.transitions.easing.sharp, + duration: theme.transitions.duration.enteringScreen, + }), + }, + drawerPaperClose: { + overflowX: "hidden", + transition: theme.transitions.create("width", { + easing: theme.transitions.easing.sharp, + duration: theme.transitions.duration.leavingScreen, + }), + width: theme.spacing(7), + [theme.breakpoints.up("sm")]: { + width: theme.spacing(9), + }, + }, + appBarSpacer: { + minHeight: "48px", + }, + content: { + flex: 1, + overflow: "auto", + }, + container: { + paddingTop: theme.spacing(4), + paddingBottom: theme.spacing(4), + }, + paper: { + padding: theme.spacing(2), + display: "flex", + overflow: "auto", + flexDirection: "column", + }, +})); + +const MainDrawer = ({ appTitle, children }) => { + const { handleLogout } = useContext(AuthContext); + const classes = useStyles(); + const [open, setOpen] = useState(true); + const [anchorEl, setAnchorEl] = React.useState(null); + const menuOpen = Boolean(anchorEl); + const drawerState = localStorage.getItem("drawerOpen"); + + const history = useHistory(); + const userId = +localStorage.getItem("userId"); + const { ticketId } = useParams(); + const [notifications, setNotifications] = useState([]); + + useEffect(() => { + if (!("Notification" in window)) { + console.log("This browser doesn't support notifications"); + } else { + Notification.requestPermission(); + } + }, []); + + useEffect(() => { + if (drawerState === "0") { + setOpen(false); + } + }, [drawerState]); + + useEffect(() => { + const socket = openSocket(process.env.REACT_APP_BACKEND_URL); + socket.emit("joinNotification"); + + socket.on("appMessage", data => { + if (data.action === "create") { + if ( + (ticketId && + data.message.ticketId === +ticketId && + document.visibilityState === "visible") || + (data.ticket.userId !== userId && data.ticket.userId) + ) + return; + showDesktopNotification(data); + } + }); + + return () => { + socket.disconnect(); + }; + }, [history, ticketId, userId]); + + const showDesktopNotification = ({ message, contact, ticket }) => { + const options = { + body: `${message.body} - ${format(new Date(), "HH:mm")}`, + icon: contact.profilePicUrl, + tag: ticket.id, + }; + let notification = new Notification( + `${i18n.t("tickets.notification.message")} ${contact.name}`, + options + ); + + notification.onclick = function (event) { + event.preventDefault(); // + window.open(`/chat/${ticket.id}`, "_self"); + }; + + document.addEventListener("visibilitychange", () => { + if (document.visibilityState === "visible") { + notification.close(); + } + }); + + document.getElementById("sound").play(); + }; + + const handleDrawerOpen = () => { + setOpen(true); + localStorage.setItem("drawerOpen", 1); + }; + const handleDrawerClose = () => { + setOpen(false); + localStorage.setItem("drawerOpen", 0); + }; + + const handleMenu = event => { + setAnchorEl(event.currentTarget); + }; + + const handleClose = () => { + setAnchorEl(null); + }; + + return ( +
    + +
    + + + +
    + + + + + +
    + + + + + + + {appTitle} + + + + + + + +
    + + + + + Profile + Logout + +
    +
    +
    +
    +
    + + {children ? children : null} +
    + +
    + ); +}; + +export default MainDrawer;