diff --git a/backend/app.js b/backend/app.js index ef4de1d..6dca422 100644 --- a/backend/app.js +++ b/backend/app.js @@ -52,7 +52,7 @@ sequelize }); socket.on("joinNotification", () => { - console.log("chat entro no canal de notificações"); + console.log("chat entrou no canal de notificações"); socket.join("notification"); }); diff --git a/backend/controllers/contact.js b/backend/controllers/contact.js index c40b590..332893e 100644 --- a/backend/controllers/contact.js +++ b/backend/controllers/contact.js @@ -7,7 +7,7 @@ exports.getContacts = async (req, res) => { const contacts = await Contact.findAll({ include: { model: Message, - attributes: ["messageBody"], + attributes: ["messageBody", "createdAt"], }, attributes: { include: [ diff --git a/backend/controllers/session.json b/backend/controllers/session.json index eb89948..06803dd 100644 --- a/backend/controllers/session.json +++ b/backend/controllers/session.json @@ -1 +1 @@ -{"WABrowserId":"\"E9dnt9Mm/JiFDCMJQHkXBw==\"","WASecretBundle":"{\"key\":\"fHTKtoDcxidboASIs5WV5JKyRrF+SfMktqHXmq/KBJU=\",\"encKey\":\"FrM3OnnEbuEr1JrtKypw5CPSc6rSD5bjbOGstv8ijk4=\",\"macKey\":\"fHTKtoDcxidboASIs5WV5JKyRrF+SfMktqHXmq/KBJU=\"}","WAToken1":"\"6UDP2DUC5ZZ99Iqy9CHRg53e2ZyeiVXEbcSnnxzuGWo=\"","WAToken2":"\"1@RByirgRpxu+HWxlX4oTADTz2yDLH4v2KNmYVektVG8dS9vCyaAukGYQG5/7Nvl82IuLiCrKtTffJLw==\""} \ No newline at end of file +{"WABrowserId":"\"E9dnt9Mm/JiFDCMJQHkXBw==\"","WASecretBundle":"{\"key\":\"fHTKtoDcxidboASIs5WV5JKyRrF+SfMktqHXmq/KBJU=\",\"encKey\":\"FrM3OnnEbuEr1JrtKypw5CPSc6rSD5bjbOGstv8ijk4=\",\"macKey\":\"fHTKtoDcxidboASIs5WV5JKyRrF+SfMktqHXmq/KBJU=\"}","WAToken1":"\"tjwS2T/ux5P+nyxYcBEj+gRIUS/XqREEEPqap787yzg=\"","WAToken2":"\"1@781NE8OipW/PigfWCI6tiz8fEpUOVVhyNEePCZMuEyC7aXG0cy1I75liSVz8z6DxVjXiw4iGI7c4Hg==\""} \ No newline at end of file diff --git a/backend/middleware/is-auth.js b/backend/middleware/is-auth.js index c4800a1..0f67299 100644 --- a/backend/middleware/is-auth.js +++ b/backend/middleware/is-auth.js @@ -3,7 +3,7 @@ const jwt = require("jsonwebtoken"); module.exports = (req, res, next) => { let decodedToken; try { - const token = req.get("Authorization").split(" ")[1]; + const [, token] = req.get("Authorization").split(" "); decodedToken = jwt.verify(token, "mysecret"); } catch (err) { err.statusCode = 401; diff --git a/frontend/src/App.js b/frontend/src/App.js index 849229e..26af3bd 100644 --- a/frontend/src/App.js +++ b/frontend/src/App.js @@ -1,60 +1,10 @@ import React from "react"; -import { BrowserRouter, Route, Switch } from "react-router-dom"; -import { toast, ToastContainer } from "react-toastify"; +import Routes from "./routes"; -import Dashboard from "./pages/Home/Dashboard"; -import Chat from "./pages/Chat/Chat"; -import Profile from "./pages/Profile/Profile"; -import Signup from "./pages/Signup/Signup"; -import Login from "./pages/Login/Login"; import "./App.css"; const App = () => { - const showToast = (type, message) => { - switch (type) { - case 0: - toast.warning(message); - break; - case 1: - toast.success(message); - break; - default: - break; - } - }; - - return ( - - - - } /> - } - /> - } - /> - } - /> - } - /> - - - ); + return ; }; export default App; diff --git a/frontend/src/Context/Auth/AuthContext.js b/frontend/src/Context/Auth/AuthContext.js new file mode 100644 index 0000000..09a9230 --- /dev/null +++ b/frontend/src/Context/Auth/AuthContext.js @@ -0,0 +1,19 @@ +import React, { createContext } from "react"; + +import useAuth from "./useAuth"; + +const AuthContext = createContext(); + +const AuthProvider = ({ children }) => { + const { isAuth, loading, handleLogin, handleLogout } = useAuth(); + + return ( + + {children} + + ); +}; + +export { AuthContext, AuthProvider }; diff --git a/frontend/src/Context/Auth/useAuth.js b/frontend/src/Context/Auth/useAuth.js new file mode 100644 index 0000000..e4fde5f --- /dev/null +++ b/frontend/src/Context/Auth/useAuth.js @@ -0,0 +1,50 @@ +import { useState, useEffect } from "react"; +import { useHistory } from "react-router-dom"; + +import api from "../../util/api"; + +const useAuth = () => { + const history = useHistory(); + const [isAuth, setIsAuth] = useState(false); + const [loading, setLoading] = useState(true); + + useEffect(() => { + const token = localStorage.getItem("token"); + + if (token) { + api.defaults.headers.Authorization = `Bearer ${JSON.parse(token)}`; + setIsAuth(true); + } + + setLoading(false); + }, []); + + const handleLogin = async (e, user) => { + e.preventDefault(); + try { + const res = await api.post("/auth/login", user); + localStorage.setItem("token", JSON.stringify(res.data.token)); + localStorage.setItem("username", res.data.username); + localStorage.setItem("userId", res.data.userId); + api.defaults.headers.Authorization = `Bearer ${res.data.token}`; + setIsAuth(true); + history.push("/chat"); + } catch (err) { + console.log(err); + } + }; + + const handleLogout = e => { + e.preventDefault(); + setIsAuth(false); + localStorage.removeItem("token"); + localStorage.removeItem("username"); + localStorage.removeItem("userId"); + api.defaults.headers.Authorization = undefined; + history.push("/login"); + }; + + return { isAuth, loading, handleLogin, handleLogout }; +}; + +export default useAuth; diff --git a/frontend/src/components/Layout/MainDrawer.js b/frontend/src/components/Layout/MainDrawer.js index 1351841..ffed0d5 100644 --- a/frontend/src/components/Layout/MainDrawer.js +++ b/frontend/src/components/Layout/MainDrawer.js @@ -1,4 +1,4 @@ -import React, { useState } from "react"; +import React, { useState, useContext, useEffect } from "react"; import clsx from "clsx"; import { makeStyles } from "@material-ui/core/styles"; @@ -16,6 +16,12 @@ 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 { AuthContext } from "../../Context/Auth/AuthContext"; const drawerWidth = 240; @@ -98,14 +104,34 @@ const useStyles = makeStyles(theme => ({ })); 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"); + + useEffect(() => { + if (drawerState === "0") { + setOpen(false); + } + }, [drawerState]); 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 ( @@ -159,6 +185,36 @@ const MainDrawer = ({ appTitle, children }) => { + +
+ + + + + Profile + Logout + +
diff --git a/frontend/src/components/Layout/MainListItems.js b/frontend/src/components/Layout/MainListItems.js index 03db7f9..5005051 100644 --- a/frontend/src/components/Layout/MainListItems.js +++ b/frontend/src/components/Layout/MainListItems.js @@ -10,7 +10,9 @@ import ListItemText from "@material-ui/core/ListItemText"; import Collapse from "@material-ui/core/Collapse"; import DashboardIcon from "@material-ui/icons/Dashboard"; -import PeopleIcon from "@material-ui/icons/People"; +import WhatsAppIcon from "@material-ui/icons/WhatsApp"; +// import PeopleIcon from "@material-ui/icons/People"; +import BorderOuterIcon from "@material-ui/icons/BorderOuter"; import ChatIcon from "@material-ui/icons/Chat"; import BarChartIcon from "@material-ui/icons/BarChart"; import LayersIcon from "@material-ui/icons/Layers"; @@ -25,7 +27,7 @@ const useStyles = makeStyles(theme => ({ })); function ListItemLink(props) { - const { icon, primary, to } = props; + const { icon, primary, to, className } = props; const renderLink = React.useMemo( () => @@ -37,7 +39,7 @@ function ListItemLink(props) { return (
  • - + {icon ? {icon} : null} @@ -56,33 +58,38 @@ const MainListItems = () => { return (
    } /> - } /> - + - + {open ? : } - - - - - - + } + /> + } + /> - + - + diff --git a/frontend/src/index.js b/frontend/src/index.js index e993252..a7eef3a 100644 --- a/frontend/src/index.js +++ b/frontend/src/index.js @@ -6,10 +6,18 @@ import CssBaseline from "@material-ui/core/CssBaseline"; import App from "./App"; ReactDOM.render( - - - - - , + + + , document.getElementById("root") ); + +// ReactDOM.render( +// +// +// +// , +// + +// document.getElementById("root") +// ); diff --git a/frontend/src/pages/Chat/components/ContactsList/ContactsList.js b/frontend/src/pages/Chat/components/ContactsList/ContactsList.js index 3760c12..93bcdb0 100644 --- a/frontend/src/pages/Chat/components/ContactsList/ContactsList.js +++ b/frontend/src/pages/Chat/components/ContactsList/ContactsList.js @@ -2,6 +2,7 @@ import React, { useState, useEffect } from "react"; import { useHistory } from "react-router-dom"; import api from "../../../../util/api"; import openSocket from "socket.io-client"; +import moment from "moment-timezone"; import profileDefaultPic from "../../../../Images/profile_default.png"; @@ -13,12 +14,12 @@ import ListItem from "@material-ui/core/ListItem"; import ListItemText from "@material-ui/core/ListItemText"; import ListItemAvatar from "@material-ui/core/ListItemAvatar"; import ListItemSecondaryAction from "@material-ui/core/ListItemSecondaryAction"; +import Typography from "@material-ui/core/Typography"; import Avatar from "@material-ui/core/Avatar"; import Divider from "@material-ui/core/Divider"; import Badge from "@material-ui/core/Badge"; import SearchIcon from "@material-ui/icons/Search"; import InputBase from "@material-ui/core/InputBase"; -import Typography from "@material-ui/core/Typography"; import ContactsHeader from "../ContactsHeader/ContactsHeader"; @@ -87,6 +88,19 @@ const useStyles = makeStyles(theme => ({ border: "none", borderRadius: 30, }, + + contactNameWrapper: { + display: "flex", + // display: "inline", + }, + + contactName: { + // flex: 1, + }, + + lastMessageTime: { + marginLeft: "auto", + }, })); const ContactsList = ({ selectedContact, setSelectedContact }) => { @@ -102,18 +116,16 @@ const ContactsList = ({ selectedContact, setSelectedContact }) => { useEffect(() => { const fetchContacts = async () => { try { - const res = await api.get("/contacts", { - headers: { Authorization: "Bearer " + token }, - }); + const res = await api.get( + "/contacts" + // { headers: { Authorization: "Bearer " + token }, } + ); setContacts(res.data); setDisplayedContacts(res.data); } catch (err) { - 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."); + if (err) { + console.log(err); + alert(err); } console.log(err); } @@ -206,9 +218,35 @@ const ContactsList = ({ selectedContact, setSelectedContact }) => { > + + {contact.name} + + {contact.messages && contact.messages[0] && ( + + {moment(contact.messages[0].createdAt) + .tz("America/Sao_Paulo") + .format("HH:mm")} + + )} +
    + } secondary={ (contact.messages && contact.messages[0] && diff --git a/frontend/src/pages/Chat/components/MessagesInput/MessagesInput.js b/frontend/src/pages/Chat/components/MessagesInput/MessagesInput.js index ba24d4c..1d90d03 100644 --- a/frontend/src/pages/Chat/components/MessagesInput/MessagesInput.js +++ b/frontend/src/pages/Chat/components/MessagesInput/MessagesInput.js @@ -77,7 +77,6 @@ const MessagesInput = ({ selectedContact, searchParam }) => { const contactId = selectedContact.id; const userId = localStorage.getItem("userId"); const username = localStorage.getItem("username"); - const token = localStorage.getItem("token"); const mediaInitialState = { preview: "", raw: "", name: "" }; const [media, setMedia] = useState(mediaInitialState); @@ -131,9 +130,7 @@ const MessagesInput = ({ selectedContact, searchParam }) => { formData.append("messageBody", media.name); try { - await api.post(`/messages/${contactId}`, formData, { - headers: { Authorization: "Bearer " + token }, - }); + await api.post(`/messages/${contactId}`, formData); } catch (err) { console.log(err); alert(err); @@ -151,9 +148,7 @@ const MessagesInput = ({ selectedContact, searchParam }) => { messageBody: `${username}: ${inputMessage.trim()}`, }; try { - await api.post(`/messages/${contactId}`, message, { - headers: { Authorization: "Bearer " + token }, - }); + await api.post(`/messages/${contactId}`, message); } catch (err) { alert(err); } diff --git a/frontend/src/pages/Chat/components/MessagesList/MessagesList.js b/frontend/src/pages/Chat/components/MessagesList/MessagesList.js index 15ac1f6..bc54cf5 100644 --- a/frontend/src/pages/Chat/components/MessagesList/MessagesList.js +++ b/frontend/src/pages/Chat/components/MessagesList/MessagesList.js @@ -218,7 +218,6 @@ const MessagesList = ({ selectedContact }) => { const fetchMessages = async () => { try { const res = await api.get("/messages/" + contactId, { - headers: { Authorization: "Bearer " + token }, params: { searchParam, pageNumber }, }); setMessagesList(prevMessages => { diff --git a/frontend/src/pages/ChatBox/ChatBox.css b/frontend/src/pages/ChatBox/ChatBox.css deleted file mode 100644 index f2bddc8..0000000 --- a/frontend/src/pages/ChatBox/ChatBox.css +++ /dev/null @@ -1,464 +0,0 @@ -/* Chat board */ - -.viewChatBoard { - display: flex; - flex: 1; - flex-direction: column; - position: relative; - height: 92vh; - background-image: url("../../Images/wa-background.png"); - - backface-visibility: hidden; - border-left: 1px solid #ededed; - /* max-height: 104.3%; */ - /* min-height: 104.3%; */ - /* margin-right: 20px; */ - /* border-radius: 10px; */ - /* box-shadow: 0 5px 5px #808888; */ -} -.viewAvatarItem { - margin-left: 20px; - /* background-image: url('../../images/nopic.jpg'); */ -} - -.headerChatBoard { - border-bottom: 1px solid #e8e8e8; - padding: 5px; - display: flex; - - background-color: #ededed; -} -.aboutme { - padding-top: 20px; - margin-left: 80px; - - align-items: center; - display: flex; - position: absolute; -} -.textHeaderChatBoard { - font-weight: bold; - color: #203152; - margin-left: 10px; -} - -.viewListContentChat { - display: flex; - flex: 1; - flex-direction: column; - overflow-y: scroll; - padding-top: 10px; - padding-bottom: 20px; -} - -.viewListContentChat::-webkit-scrollbar-track { - padding: 2px 0; -} - -.viewListContentChat::-webkit-scrollbar { - width: 15px; -} - -.viewListContentChat::-webkit-scrollbar-thumb { - border-radius: 10px; - box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.3); - background-color: #e8e8e8; -} - -.viewBottom { - display: flex; - flex-direction: row; - align-items: center; - justify-content: center; - height: 100px; - - background-color: #f0f0f0; -} - -.viewMediaBottom { - display: flex; - flex-direction: row; - align-items: center; - justify-content: center; - height: 100px; - - background-color: #f0f0f0; -} - -.viewInputGallery { - opacity: 0; - position: absolute; - z-index: -1; - left: 10px; - width: 30px; -} - -.icOpenGallery { - width: 20px; - height: 20px; - margin-left: 10px; - margin-right: 10px; - cursor: pointer; -} - -.icOpenGallery:hover { - opacity: 70%; - width: 20px; - height: 20px; - margin-left: 10px; - margin-right: 10px; - cursor: pointer; -} - -.icOpenEmojis { - width: 20px; - height: 20px; - margin-left: 10px; - cursor: pointer; -} - -.icOpenEmojis:hover { - opacity: 70%; - width: 20px; - height: 20px; - margin-left: 10px; - cursor: pointer; -} - -.icSend { - width: 20px; - height: 20px; - margin-left: 5px; - margin-right: 30px; - cursor: pointer; -} - -.icSend:hover { - opacity: 70%; - width: 20px; - height: 20px; - margin-left: 5px; - margin-right: 30px; - cursor: pointer; -} - -.viewMediaInput { - flex: 1; - width: 100%; - margin-right: 10px; -} - -.viewInput { - flex: 1; - border-radius: 30px; - padding-left: 10px; - padding-right: 10px; - border: 0px; - height: 50px; - margin-right: 10px; -} - -.viewInput:focus { - outline: 0; -} - -input::placeholder { - color: rgb(199, 199, 199); -} - -/* serach messages */ - -.searchMessageBar { - padding: 10px; - width: 300px; - margin-bottom: 10px; - background-color: #f7f7f7; -} - -.search-input-field { - width: 300px; - - border-radius: 10px; - - /* margin-right: 85%; */ - align-self: center; - border: none !important; - box-shadow: none !important; - outline: none !important; - - padding: 10px; - text-align: center; -} - -.search-input-container { - /* IE10 */ - display: flex; - width: 50%; - margin-left: 50%; - align-content: flex-end; - margin-bottom: 0px; -} - -/* View item message */ - -.viewItemRight { - margin-right: 20px; - margin-top: 5px; - min-width: 100px; - max-width: 600px; - height: auto; - 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; - box-shadow: 0 5px 5px #808888; - padding-left: 10px; - padding-right: 40px; - padding-top: 5px; - padding-bottom: 0px; - color: #203152; -} - -.viewItemRight2 { - 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; - box-shadow: 0 5px 5px #808888; -} - -.viewItemRight3 { - width: 300px; - height: auto; - 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; - text-align: left; -} - -.viewWrapItemLeft { - /* width: 300px; */ - text-align: left; - align-self: flex-start; - margin-left: 20px; - margin-top: 10px; - margin-bottom: -15px; -} - -.viewWrapItemRight { - width: 300px; - align-self: flex-end; - text-align: left; - /* margin-left: 20px; - margin-top: 10px; - margin-bottom: -15px; */ -} - -.viewWrapItemLeft3 { - flex-direction: row; - display: flex; - width: 340px; - margin-bottom: -15px; -} - -.viewItemLeft { - margin-left: 20px; - margin-top: 5px; - min-width: 100px; - max-width: 600px; - height: auto; - display: block; - position: relative; - - 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: 10px; - padding-right: 40px; - padding-top: 5px; - padding-bottom: 0px; - color: #303030; - box-shadow: 0 5px 5px #808888; -} - -.viewItemLeft2 { - width: 315px; - padding: 8px; - height: auto; - background-color: #ffffff; - margin-left: 10px; - margin-top: 10px; - display: block; - position: relative; - border-top-left-radius: 8px; - border-top-right-radius: 8px; - border-bottom-left-radius: 0px; - border-bottom-right-radius: 8px; - color: #203152; - box-shadow: 0 5px 5px #808888; -} - -.viewItemLeft3 { - margin-left: 10px; - 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; - border-bottom-right-radius: 8px; - color: white; -} - -.timesetup { - align-items: center; -} - -.peerAvatarLeft { - width: 30px; - height: 30px; - border-radius: 15px; - margin-right: 10px; - margin-bottom: 10px; - object-fit: cover; - align-self: flex-end; -} - -.viewPaddingLeft { - width: 40px; -} - -/* Item text */ - -.textContentItem { - font-size: 16px; - overflow-wrap: break-word; - padding: 0px 50px 0px 0px; -} - -.timestamp { - font-size: 11px; - position: absolute; - bottom: 5px; - right: 10px; - text-transform: uppercase; - color: #999; -} - -.viewListContentChat div:last-child { - margin-bottom: 10px; -} - -/* Item image */ - -.imgItemRight { - object-fit: cover; - width: 300px; - height: 300px; - border-top-left-radius: 8px; - border-top-right-radius: 8px; - border-bottom-left-radius: 8px; - border-bottom-right-radius: 0px; -} - -.imgItemLeft { - object-fit: cover; - width: 290px; - height: 300px; - border-top-left-radius: 8px; - border-top-right-radius: 8px; - border-bottom-left-radius: 0px; - border-bottom-right-radius: 8px; -} - -.time { - align-items: center; - text-align: center; - align-self: center; - font: italic small-caps bold 15px/30px Georgia, serif; - width: 110px; - background-color: #e1f3fb; - margin: 10px; - padding-top: 5px; - padding-bottom: 5px; - padding-left: 5px; - padding-right: 5px; - border-radius: 10px; -} - -.textTime { - color: #808888; - font-size: 12px; - font-style: italic; - align-self: center; - margin-left: 0px; -} - -/* Stickers */ - -.viewStickers { - position: absolute; - bottom: 90px; - border-top: 1px solid #e8e8e8; - overflow-x: scroll; -} -.viewStickers::-webkit-scrollbar-track { - padding: 2px 0; -} - -.viewStickers::-webkit-scrollbar { - width: 15px; -} - -.viewStickers::-webkit-scrollbar-thumb { - border-radius: 10px; - box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.3); - background-color: #e8e8e8; -} - -.imgSticker { - width: 80px; - height: 80px; - object-fit: contain; -} - -/* Say hi */ - -.viewWrapSayHi { - margin-top: 20px; - display: flex; - flex-direction: row; - justify-content: center; - align-items: center; -} - -.textSayHi { - color: #808888; - font-size: 14px; -} diff --git a/frontend/src/pages/ChatBox/ChatBox.js b/frontend/src/pages/ChatBox/ChatBox.js deleted file mode 100644 index 88346cd..0000000 --- a/frontend/src/pages/ChatBox/ChatBox.js +++ /dev/null @@ -1,566 +0,0 @@ -import React, { useState, useEffect, useRef } from "react"; -import InfiniteScrollReverse from "react-infinite-scroll-reverse"; -import { Spinner } from "react-bootstrap"; -import { - FiPaperclip, - FiSend, - FiTrash2, - FiSmile, - FiFastForward, - FiFile, -} from "react-icons/fi"; -import { BsCheck, BsCheckAll, BsClock } from "react-icons/bs"; - -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"; -import profileDefaultPic from "../../Images/profile_default.png"; -import ReactAudioPlayer from "react-audio-player"; - -import "react-toastify/dist/ReactToastify.css"; -import "emoji-mart/css/emoji-mart.css"; -import "./ChatBox.css"; - -const ChatBox = ({ currentPeerContact }) => { - const SERVER_URL = "http://localhost:8080/"; - const contactId = currentPeerContact.id; - - const userId = localStorage.getItem("userId"); - const username = localStorage.getItem("username"); - const token = localStorage.getItem("token"); - - const mediaInitialState = { preview: "", raw: "", name: "" }; - const [inputMessage, setInputMessage] = useState(""); - const [media, setMedia] = useState(mediaInitialState); - const [showEmoji, setShowEmoji] = useState(false); - const [loading, setLoading] = useState(true); - const [listMessages, setListMessages] = useState([]); - const [hasMore, setHasMore] = useState(false); - const [searchParam, setSearchParam] = useState(""); - const [pageNumber, setPageNumber] = useState(0); - const lastMessageRef = useRef(); - - useEffect(() => { - setListMessages([]); - }, [searchParam]); - - useEffect(() => { - setLoading(true); - const delayDebounceFn = setTimeout(() => { - console.log(searchParam); - const fetchMessages = async () => { - try { - const res = await api.get("/messages/" + contactId, { - headers: { Authorization: "Bearer " + token }, - params: { searchParam, pageNumber }, - }); - setListMessages(prevMessages => { - return [...res.data.messages, ...prevMessages]; - }); - setHasMore(res.data.messages.length > 0); - setLoading(false); - console.log(res.data); - if (pageNumber === 1 && res.data.messages.length > 0) { - scrollToBottom(); - } - } catch (err) { - console.log(err); - alert(err); - } - }; - fetchMessages(); - }, 1000); - return () => clearTimeout(delayDebounceFn); - }, [searchParam, pageNumber, contactId, token]); - - useEffect(() => { - const socket = openSocket(SERVER_URL); - - socket.emit("joinChatBox", contactId, () => {}); - - socket.on("appMessage", data => { - if (data.action === "create") { - addMessage(data.message); - scrollToBottom(); - } else if (data.action === "update") { - updateMessageAck(data.message); - } - }); - - return () => { - socket.disconnect(); - setInputMessage(""); - setSearchParam(""); - setShowEmoji(false); - setPageNumber(1); - setMedia({}); - setListMessages([]); - }; - }, [contactId]); - - const loadMore = () => { - setPageNumber(prevPageNumber => prevPageNumber + 1); - }; - - const updateMessageAck = message => { - let id = message.id; - setListMessages(prevState => { - let aux = [...prevState]; - let messageIndex = aux.findIndex(message => message.id === id); - aux[messageIndex].ack = message.ack; - - return aux; - }); - }; - - const scrollToBottom = () => { - if (lastMessageRef) { - lastMessageRef.current.scrollIntoView({}); - } - }; - - const addMessage = message => { - setListMessages(prevState => { - if (prevState.length >= 20) { - let aux = [...prevState]; - aux.shift(); - aux.push(message); - return aux; - } else { - return [...prevState, message]; - } - }); - }; - - const handleChangeInput = e => { - setInputMessage(e.target.value); - }; - - const handleAddEmoji = e => { - let emoji = e.native; - setInputMessage(prevState => prevState + emoji); - }; - - const handleChangeMedia = e => { - if (e.target.files.length) { - setMedia({ - preview: URL.createObjectURL(e.target.files[0]), - raw: e.target.files[0], - name: e.target.files[0].name, - }); - } - }; - - const handleInputPaste = e => { - if (e.clipboardData.files[0]) { - setMedia({ - preview: URL.createObjectURL(e.clipboardData.files[0]), - raw: e.clipboardData.files[0], - name: e.clipboardData.files[0].name, - }); - } - }; - - const handleUploadMedia = async e => { - e.preventDefault(); - const formData = new FormData(); - formData.append("media", media.raw); - formData.append("userId", userId); - formData.append("messageBody", media.name); - - try { - await api.post(`/messages/${contactId}`, formData, { - headers: { Authorization: "Bearer " + token }, - }); - } catch (err) { - console.log(err); - alert(err); - } - setMedia(mediaInitialState); - }; - - const handleSendMessage = async () => { - if (inputMessage.trim() === "") return; - const message = { - read: 1, - userId: userId, - mediaUrl: "", - messageBody: `${username}: ${inputMessage.trim()}`, - }; - try { - await api.post(`/messages/${contactId}`, message, { - headers: { Authorization: "Bearer " + token }, - }); - } catch (err) { - alert(err); - } - setInputMessage(""); - setShowEmoji(false); - }; - - const handleSearch = e => { - setSearchParam(e.target.value); - setPageNumber(1); - }; - - const checkMessageDay = (message, index) => { - if (index < listMessages.length - 1) { - let messageDay = moment(listMessages[index].createdAt) - .tz("America/Sao_Paulo") - .format("DD"); - - let nextMessageDay = moment(listMessages[index + 1].createdAt) - .tz("America/Sao_Paulo") - .format("DD"); - - if (messageDay < nextMessageDay) { - return ( - -
    - {moment(listMessages[index + 1].createdAt) - .tz("America/Sao_Paulo") - .format("DD/MM/YY")} -
    -
    - ); - } - } - if (index + 1 === listMessages.length) { - return ( -
    - ); - } - }; - - 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( -
    - -
    - {message.messageBody} - - {moment(message.createdAt) - .tz("America/Sao_Paulo") - .format("HH:mm")} - -
    -
    , - checkMessageDay(message, index) - ); - } else if (message.mediaUrl && message.mediaType === "audio") { - viewListMessages.push( -
    - -
    - - {moment(message.createdAt) - .tz("America/Sao_Paulo") - .format("HH:mm")} - -
    -
    , - checkMessageDay(message, index) - ); - } else if (message.mediaUrl && message.mediaType === "video") { - viewListMessages.push( -
    -
    , - checkMessageDay(message, index) - ); - } else if (message.mediaUrl) { - viewListMessages.push( -
    -
    - - {message.messageBody} - - - - {moment(message.createdAt) - .tz("America/Sao_Paulo") - .format("HH:mm")} - -
    -
    , - checkMessageDay(message, index) - ); - } else { - viewListMessages.push( -
    -
    - {message.messageBody} - - {moment(message.createdAt) - .tz("America/Sao_Paulo") - .format("HH:mm")} - -
    -
    , - checkMessageDay(message, index) - ); - } - } // mensagens enviadas - else { - if (message.mediaUrl && message.mediaType === "image") { - viewListMessages.push( -
    - -
    - {message.messageBody} - - {moment(message.createdAt) - .tz("America/Sao_Paulo") - .format("HH:mm")}{" "} - {renderMsgAck(message)} - -
    -
    , - checkMessageDay(message, index) - ); - } else if (message.mediaUrl && message.mediaType === "audio") { - viewListMessages.push( -
    - -
    - - {moment(message.createdAt) - .tz("America/Sao_Paulo") - .format("HH:mm")}{" "} - {renderMsgAck(message)} - -
    -
    , - checkMessageDay(message, index) - ); - } else if (message.mediaUrl && message.mediaType === "video") { - viewListMessages.push( -
    -
    , - checkMessageDay(message, index) - ); - } else if (message.mediaUrl) { - viewListMessages.push( -
    -
    - - {message.messageBody} - - - - {moment(message.createdAt) - .tz("America/Sao_Paulo") - .format("HH:mm")}{" "} - {renderMsgAck(message)} - -
    -
    , - checkMessageDay(message, index) - ); - } else { - viewListMessages.push( -
    -
    - {message.messageBody} - - {moment(message.createdAt) - .tz("America/Sao_Paulo") - .format("HH:mm")}{" "} - {renderMsgAck(message)} - -
    -
    , - checkMessageDay(message, index) - ); - } - } - }); - return viewListMessages; - } else { - return ( -
    - Diga olá para o seu novo contato -
    - ); - } - }; - - console.log(listMessages); - - return ( -
    -
    - - -

    {currentPeerContact.name}

    -
    -
    - -

    Status do contato

    -
    -
    -
    - -
    -
    - - {listMessages.length > 0 ? renderMessages() : []} - - - {media.preview ? ( -
    - setMedia(mediaInitialState)} - /> - - {media.name} - {/* */} - - -
    - ) : ( -
    -
    - {showEmoji ? ( -
    - -
    - ) : null} - - setShowEmoji(prevState => !prevState)} - /> - - -
    - - inputMessage && inputMessage.focus()} - name="inputMessage" - autoComplete="off" - className="viewInput" - placeholder="Digite uma mensagem" - onPaste={handleInputPaste} - value={inputMessage} - onChange={handleChangeInput} - onKeyPress={e => { - if (e.key === "Enter") { - handleSendMessage(); - } - }} - /> - - -
    - )} - {loading ? ( -
    - -
    - ) : null} -
    - ); -}; - -export default ChatBox; diff --git a/frontend/src/pages/Login/Login.js b/frontend/src/pages/Login/Login.js index b457cc6..9bd0b14 100644 --- a/frontend/src/pages/Login/Login.js +++ b/frontend/src/pages/Login/Login.js @@ -1,6 +1,4 @@ -import React, { useState } from "react"; -import { useHistory } from "react-router-dom"; -import api from "../../util/api"; +import React, { useState, useContext } from "react"; import { Link as RouterLink } from "react-router-dom"; import Avatar from "@material-ui/core/Avatar"; @@ -17,6 +15,8 @@ import Typography from "@material-ui/core/Typography"; import { makeStyles } from "@material-ui/core/styles"; import Container from "@material-ui/core/Container"; +import { AuthContext } from "../../Context/Auth/AuthContext"; + const Copyright = () => { return ( @@ -51,27 +51,11 @@ const useStyles = makeStyles(theme => ({ })); const Login = ({ showToast }) => { - const [user, setUser] = useState({ email: "", password: "" }); - const history = useHistory(); - const classes = useStyles(); - const handleLogin = async e => { - e.preventDefault(); - try { - const res = await api.post("/auth/login", user); - localStorage.setItem("token", res.data.token); - localStorage.setItem("username", res.data.username); - localStorage.setItem("userId", res.data.userId); - // const remainingMilliseconds = 60 * 60 * 1000; - // const expiryDate = new Date(new Date().getTime() + remainingMilliseconds); - // localStorage.setItem("expiryDate", expiryDate.toISOString()); - showToast(1, "Login efetuado com sucesso"); - history.push("/chat"); - } catch (err) { - console.log(err); - } - }; + const [user, setUser] = useState({ email: "", password: "" }); + + const { handleLogin } = useContext(AuthContext); const handleChangeInput = e => { setUser({ ...user, [e.target.name]: e.target.value }); diff --git a/frontend/src/pages/Profile/Profile.css b/frontend/src/pages/Profile/Profile.css deleted file mode 100644 index e69de29..0000000 diff --git a/frontend/src/pages/Signup/Signup.js b/frontend/src/pages/Signup/Signup.js index 6bbe458..4303a77 100644 --- a/frontend/src/pages/Signup/Signup.js +++ b/frontend/src/pages/Signup/Signup.js @@ -54,6 +54,7 @@ const useStyles = makeStyles(theme => ({ const SignUp = () => { const classes = useStyles(); const history = useHistory(); + const [user, setUser] = useState({ name: "", email: "", password: "" }); const handleChangeInput = e => { diff --git a/frontend/src/pages/WhatsAuth/WhatsAuth.js b/frontend/src/pages/WhatsAuth/WhatsAuth.js new file mode 100644 index 0000000..91d83a4 --- /dev/null +++ b/frontend/src/pages/WhatsAuth/WhatsAuth.js @@ -0,0 +1,79 @@ +import React from "react"; +import clsx from "clsx"; +import MainDrawer from "../../components/Layout/MainDrawer"; + +import { makeStyles } from "@material-ui/core/styles"; + +import Container from "@material-ui/core/Container"; +import Grid from "@material-ui/core/Grid"; +import Paper from "@material-ui/core/Paper"; + +const useStyles = makeStyles(theme => ({ + root: { + display: "flex", + }, + + title: { + flexGrow: 1, + }, + + appBarSpacer: theme.mixins.toolbar, + content: { + flexGrow: 1, + + overflow: "auto", + }, + container: { + // paddingTop: theme.spacing(4), + // paddingBottom: theme.spacing(4), + height: `calc(100% - 64px)`, + }, + paper: { + padding: theme.spacing(2), + display: "flex", + overflow: "auto", + flexDirection: "column", + }, + fixedHeight: { + height: 640, + }, +})); + +const WhatsAuth = () => { + const classes = useStyles(); + + const fixedHeightPaper = clsx(classes.paper, classes.fixedHeight); + + return ( +
    + +
    +
    +
    + + + + +

    Status da conexão

    +
    +
    + + +

    QR Code

    +
    +
    + {/* + +

    paper2

    +
    +
    */} +
    +
    +
    +
    +
    +
    + ); +}; + +export default WhatsAuth; diff --git a/frontend/src/routes.js b/frontend/src/routes.js new file mode 100644 index 0000000..627d7c4 --- /dev/null +++ b/frontend/src/routes.js @@ -0,0 +1,70 @@ +import React, { useContext } from "react"; +import { BrowserRouter, Route, Switch, Redirect } from "react-router-dom"; + +import { AuthContext, AuthProvider } from "./Context/Auth/AuthContext"; + +import Dashboard from "./pages/Home/Dashboard"; +import Chat from "./pages/Chat/Chat"; +import Profile from "./pages/Profile/Profile"; +import Signup from "./pages/Signup/Signup"; +import Login from "./pages/Login/Login"; +import WhatsAuth from "./pages/WhatsAuth/WhatsAuth"; + +const PrivateRoute = ({ component: Component, ...rest }) => { + const { isAuth, loading } = useContext(AuthContext); + + if (loading) return

    Loading...

    ; + + return ( + + isAuth ? ( + + ) : ( + + ) + } + /> + ); +}; + +const PublicRoute = ({ component: Component, ...rest }) => { + const { isAuth, loading } = useContext(AuthContext); + + if (loading) return

    Loading...

    ; + + return ( + + !isAuth ? ( + + ) : ( + + ) + } + /> + ); +}; + +const Routes = () => { + return ( + + + + + + + + + + + + + ); +}; + +export default Routes;