migration to WSL

This commit is contained in:
canove
2020-06-24 09:40:51 -03:00
parent 0270ae09e6
commit c60c0f44fc
47 changed files with 17939 additions and 17696 deletions

14
.vscode/launch.json vendored Normal file
View File

@@ -0,0 +1,14 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"type": "node",
"request": "launch",
"name": "Launch Program",
"program": "${workspaceFolder}\\backend\\app.js"
}
]
}

View File

@@ -1,7 +1,8 @@
NODE_ENV=development NODE_ENV=development
PORT=8080 HOST=localhost
PORT=8080
DB_HOST=
DB_USER= DB_HOST=
DB_PASS= DB_USER=
DB_PASS=
DB_NAME= DB_NAME=

6
backend/.gitignore vendored
View File

@@ -1,4 +1,4 @@
node_modules node_modules
public/* public/*
!public/.gitkeep !public/.gitkeep
.env .env

View File

@@ -1,76 +1,76 @@
require("dotenv/config"); require("dotenv/config");
const express = require("express"); const express = require("express");
const path = require("path"); const path = require("path");
const cors = require("cors"); const cors = require("cors");
const sequelize = require("./util/database"); const sequelize = require("./util/database");
const multer = require("multer"); const multer = require("multer");
const wBot = require("./controllers/wbot"); const wBot = require("./controllers/wbot");
const Contact = require("./models/Contact"); const Contact = require("./models/Contact");
const wbotMessageListener = require("./controllers/wbotMessageListener"); const wbotMessageListener = require("./controllers/wbotMessageListener");
const wbotMonitor = require("./controllers/wbotMonitor"); const wbotMonitor = require("./controllers/wbotMonitor");
const messageRoutes = require("./routes/message"); const messageRoutes = require("./routes/message");
const ContactRoutes = require("./routes/contacts"); const ContactRoutes = require("./routes/contacts");
const AuthRoutes = require("./routes/auth"); const AuthRoutes = require("./routes/auth");
const WhatsRoutes = require("./routes/whatsapp"); const WhatsRoutes = require("./routes/whatsapp");
const app = express(); const app = express();
const fileStorage = multer.diskStorage({ const fileStorage = multer.diskStorage({
destination: (req, file, cb) => { destination: (req, file, cb) => {
cb(null, "public"); cb(null, "public");
}, },
filename: (req, file, cb) => { filename: (req, file, cb) => {
cb(null, new Date().getTime() + "-" + file.originalname.replace(/\s/g, "")); cb(null, new Date().getTime() + "-" + file.originalname.replace(/\s/g, ""));
}, },
}); });
app.use(cors()); app.use(cors());
app.use(express.json()); app.use(express.json());
app.use(multer({ storage: fileStorage }).single("media")); app.use(multer({ storage: fileStorage }).single("media"));
app.use("/public", express.static(path.join(__dirname, "public"))); app.use("/public", express.static(path.join(__dirname, "public")));
app.use(messageRoutes); app.use(messageRoutes);
app.use(ContactRoutes); app.use(ContactRoutes);
app.use(WhatsRoutes); app.use(WhatsRoutes);
app.use("/auth", AuthRoutes); app.use("/auth", AuthRoutes);
app.use((error, req, res, next) => { app.use((error, req, res, next) => {
console.log(error); // console.log(error);
const status = error.statusCode || 500; const status = error.statusCode || 500;
const message = error.message; const message = error.message;
const data = error.data; const data = error.data;
res.status(status).json({ message: message, data: data }); res.status(status).json({ message: message, data: data });
}); });
sequelize sequelize
.sync() .sync()
.then(() => { .then(() => {
const server = app.listen(process.env.PORT); const server = app.listen(process.env.PORT);
const io = require("./socket").init(server); const io = require("./socket").init(server);
io.on("connection", socket => { io.on("connection", socket => {
console.log("Client Connected"); console.log("Client Connected");
socket.on("joinChatBox", contactId => { socket.on("joinChatBox", contactId => {
socket.join(contactId); socket.join(contactId);
}); });
socket.on("joinNotification", () => { socket.on("joinNotification", () => {
console.log("chat entrou no canal de notificações"); console.log("chat entrou no canal de notificações");
socket.join("notification"); socket.join("notification");
}); });
socket.on("disconnect", () => { socket.on("disconnect", () => {
console.log("Client disconnected"); console.log("Client disconnected");
}); });
}); });
wBot.init().then(() => { wBot.init().then(() => {
wbotMessageListener(); wbotMessageListener();
wbotMonitor(); wbotMonitor();
}); });
console.log("Server started on", process.env.PORT); console.log("Server started on", process.env.PORT);
}) })
.catch(err => { .catch(err => {
console.log(err); console.log(err);
}); });

View File

@@ -1,60 +1,60 @@
const { validationResult } = require("express-validator"); const { validationResult } = require("express-validator");
const bcrypt = require("bcryptjs"); const bcrypt = require("bcryptjs");
const jwt = require("jsonwebtoken"); const jwt = require("jsonwebtoken");
const User = require("../models/User"); const User = require("../models/User");
exports.signup = async (req, res, next) => { exports.signup = async (req, res, next) => {
const errors = validationResult(req); const errors = validationResult(req);
if (!errors.isEmpty()) { if (!errors.isEmpty()) {
const error = new Error("Validation failed"); const error = new Error("Validation failed");
error.statusCode = 422; error.statusCode = 422;
error.data = errors.array(); error.data = errors.array();
throw error; throw error;
} }
const { name, password, email } = req.body; const { name, password, email } = req.body;
try { try {
const hashedPw = await bcrypt.hash(password, 12); const hashedPw = await bcrypt.hash(password, 12);
const user = User.build({ const user = User.build({
email: email, email: email,
password: hashedPw, password: hashedPw,
name: name, name: name,
}); });
const result = await user.save(); const result = await user.save();
res.status(201).json({ message: "User created!", userId: result.id }); res.status(201).json({ message: "User created!", userId: result.id });
} catch (err) { } catch (err) {
next(err); next(err);
} }
}; };
exports.login = async (req, res, next) => { exports.login = async (req, res, next) => {
const { email, password } = req.body; const { email, password } = req.body;
let loadedUser; let loadedUser;
try { try {
const user = await User.findOne({ where: { email: email } }); const user = await User.findOne({ where: { email: email } });
if (!user) { if (!user) {
const error = new Error("Usuário não encontrado"); const error = new Error("Usuário não encontrado");
error.statusCode = 401; error.statusCode = 401;
throw error; throw error;
} }
loadedUser = user; loadedUser = user;
const isEqual = await bcrypt.compare(password, user.password); const isEqual = await bcrypt.compare(password, user.password);
if (!isEqual) { if (!isEqual) {
const error = new Error("Senha incorreta"); const error = new Error("Senha incorreta");
error.statusCode = 401; error.statusCode = 401;
throw error; throw error;
} }
const token = jwt.sign( const token = jwt.sign(
{ email: loadedUser.email, userId: loadedUser.id }, { email: loadedUser.email, userId: loadedUser.id },
"mysecret", "mysecret",
{ expiresIn: "24h" } { expiresIn: "24h" }
); );
return res return res
.status(200) .status(200)
.json({ token: token, username: loadedUser.name, userId: loadedUser.id }); .json({ token: token, username: loadedUser.name, userId: loadedUser.id });
} catch (err) { } catch (err) {
next(err); next(err);
} }
}; };

View File

@@ -1,46 +1,75 @@
const Contact = require("../models/Contact"); const Contact = require("../models/Contact");
const Message = require("../models/Message"); const Message = require("../models/Message");
const Sequelize = require("sequelize"); const Sequelize = require("sequelize");
const { getIO } = require("../socket");
exports.getContacts = async (req, res) => { const { getWbot } = require("./wbot");
const { searchParam } = req.query;
exports.getContacts = async (req, res) => {
const lowerSerachParam = searchParam.toLowerCase(); const { searchParam } = req.query;
const whereCondition = { const lowerSerachParam = searchParam.toLowerCase();
name: Sequelize.where(
Sequelize.fn("LOWER", Sequelize.col("name")), const whereCondition = {
"LIKE", name: Sequelize.where(
"%" + lowerSerachParam + "%" Sequelize.fn("LOWER", Sequelize.col("name")),
), "LIKE",
}; "%" + lowerSerachParam + "%"
),
//todo >> add contact number to search where condition };
try { //todo >> add contact number to search where condition
const contacts = await Contact.findAll({
where: whereCondition, try {
attributes: { const contacts = await Contact.findAll({
include: [ where: whereCondition,
[ attributes: {
Sequelize.literal(`( include: [
SELECT COUNT(*) [
FROM messages AS message Sequelize.literal(`(
WHERE SELECT COUNT(*)
message.contactId = contact.id FROM messages AS message
AND WHERE
message.read = 0 message.contactId = contact.id
AND
)`), message.read = 0
"unreadMessages",
], )`),
], "unreadMessages",
}, ],
order: [["updatedAt", "DESC"]], ],
}); },
order: [["updatedAt", "DESC"]],
return res.json(contacts); });
} catch (err) {
console.log(err); return res.json(contacts);
} } catch (err) {
}; console.log(err);
}
};
exports.createContact = async (req, res, next) => {
const wbot = getWbot();
const io = getIO();
const { number, name } = req.body;
try {
const res = await wbot.isRegisteredUser(`55${number}@c.us`);
if (!res) {
const error = new Error("O número informado não é um Whatsapp Válido");
error.statusCode = 422;
throw error;
}
const profilePicUrl = await wbot.getProfilePicUrl(`55${number}@c.us`);
const contact = await Contact.create({
name,
number: `55${number}`,
profilePicUrl,
});
res.status(200).json(contact);
} catch (err) {
next(err);
}
};

View File

@@ -1,155 +1,155 @@
const fs = require("fs"); const fs = require("fs");
const Message = require("../models/Message"); const Message = require("../models/Message");
const Contact = require("../models/Contact"); const Contact = require("../models/Contact");
const { getIO } = require("../socket"); const { getIO } = require("../socket");
const { getWbot } = require("./wbot"); const { getWbot } = require("./wbot");
const Sequelize = require("sequelize"); const Sequelize = require("sequelize");
const { MessageMedia } = require("whatsapp-web.js"); const { MessageMedia } = require("whatsapp-web.js");
const setMessagesAsRead = async contactId => { const setMessagesAsRead = async contactId => {
const io = getIO(); const io = getIO();
try { try {
const result = await Message.update( const result = await Message.update(
{ read: true }, { read: true },
{ {
where: { where: {
contactId: contactId, contactId: contactId,
read: false, read: false,
}, },
} }
); );
if (!result) { if (!result) {
const error = new Error( const error = new Error(
"Erro ao definir as mensagens como lidas no banco de dados" "Erro ao definir as mensagens como lidas no banco de dados"
); );
error.satusCode = 501; error.statusCode = 501;
throw error; throw error;
} }
io.to("notification").emit("contact", { io.to("notification").emit("contact", {
action: "updateUnread", action: "updateUnread",
contactId: contactId, contactId: contactId,
}); });
} catch (err) { } catch (err) {
console.log(err); console.log(err);
} }
}; };
exports.getContactMessages = async (req, res, next) => { exports.getContactMessages = async (req, res, next) => {
const wbot = getWbot(); const wbot = getWbot();
const io = getIO(); const io = getIO();
const { contactId } = req.params; const { contactId } = req.params;
const { searchParam, pageNumber = 1 } = req.query; const { searchParam, pageNumber = 1 } = req.query;
const lowerSerachParam = searchParam.toLowerCase(); const lowerSerachParam = searchParam.toLowerCase();
const whereCondition = { const whereCondition = {
messageBody: Sequelize.where( messageBody: Sequelize.where(
Sequelize.fn("LOWER", Sequelize.col("messageBody")), Sequelize.fn("LOWER", Sequelize.col("messageBody")),
"LIKE", "LIKE",
"%" + lowerSerachParam + "%" "%" + lowerSerachParam + "%"
), ),
}; };
let limit = 20; let limit = 20;
let offset = limit * (pageNumber - 1); let offset = limit * (pageNumber - 1);
try { try {
const contact = await Contact.findByPk(contactId); const contact = await Contact.findByPk(contactId);
if (!contact) { if (!contact) {
const error = new Error("Erro ao localizar o contato no banco de dados"); const error = new Error("Erro ao localizar o contato no banco de dados");
error.satusCode = 501; error.statusCode = 501;
throw error; throw error;
} }
await setMessagesAsRead(contactId); await setMessagesAsRead(contactId);
const messagesFound = await contact.countMessages({ const messagesFound = await contact.countMessages({
where: whereCondition, where: whereCondition,
}); });
const contactMessages = await contact.getMessages({ const contactMessages = await contact.getMessages({
where: whereCondition, where: whereCondition,
limit, limit,
offset, offset,
order: [["createdAt", "DESC"]], order: [["createdAt", "DESC"]],
}); });
const serializedMessages = contactMessages.map(message => { const serializedMessages = contactMessages.map(message => {
return { return {
...message.dataValues, ...message.dataValues,
mediaUrl: `${ mediaUrl: `${
message.mediaUrl message.mediaUrl
? `http://${process.env.HOST}:${process.env.PORT}/public/${message.mediaUrl}` ? `http://${process.env.HOST}:${process.env.PORT}/public/${message.mediaUrl}`
: "" : ""
}`, }`,
}; };
}); });
return res.json({ return res.json({
messages: serializedMessages.reverse(), messages: serializedMessages.reverse(),
contact: contact, contact: contact,
messagesFound, messagesFound,
}); });
} catch (err) { } catch (err) {
next(err); next(err);
} }
}; };
exports.postCreateContactMessage = async (req, res, next) => { exports.postCreateContactMessage = async (req, res, next) => {
const wbot = getWbot(); const wbot = getWbot();
const io = getIO(); const io = getIO();
const { contactId } = req.params; const { contactId } = req.params;
const message = req.body; const message = req.body;
const media = req.file; const media = req.file;
let sentMessage; let sentMessage;
try { try {
const contact = await Contact.findByPk(contactId); const contact = await Contact.findByPk(contactId);
if (media) { if (media) {
const newMedia = MessageMedia.fromFilePath(req.file.path); const newMedia = MessageMedia.fromFilePath(req.file.path);
message.mediaUrl = req.file.filename.replace(/\s/g, ""); message.mediaUrl = req.file.filename.replace(/\s/g, "");
if (newMedia.mimetype) { if (newMedia.mimetype) {
message.mediaType = newMedia.mimetype.split("/")[0]; message.mediaType = newMedia.mimetype.split("/")[0];
} else { } else {
message.mediaType = "other"; message.mediaType = "other";
} }
sentMessage = await wbot.sendMessage(`${contact.number}@c.us`, newMedia); sentMessage = await wbot.sendMessage(`${contact.number}@c.us`, newMedia);
} else { } else {
sentMessage = await wbot.sendMessage( sentMessage = await wbot.sendMessage(
`${contact.number}@c.us`, `${contact.number}@c.us`,
message.messageBody message.messageBody
); );
} }
message.id = sentMessage.id.id; message.id = sentMessage.id.id;
const newMessage = await contact.createMessage(message); const newMessage = await contact.createMessage(message);
if (!newMessage) { if (!newMessage) {
const error = new Error("Erro ao inserir a mensagem no banco de dados"); const error = new Error("Erro ao inserir a mensagem no banco de dados");
error.satusCode = 501; error.statusCode = 501;
throw error; throw error;
} }
io.to(contactId).emit("appMessage", { io.to(contactId).emit("appMessage", {
action: "create", action: "create",
message: { message: {
...newMessage.dataValues, ...newMessage.dataValues,
mediaUrl: `${ mediaUrl: `${
message.mediaUrl message.mediaUrl
? `http://${process.env.HOST}:${process.env.PORT}/public/${message.mediaUrl}` ? `http://${process.env.HOST}:${process.env.PORT}/public/${message.mediaUrl}`
: "" : ""
}`, }`,
}, },
}); });
await setMessagesAsRead(contactId); await setMessagesAsRead(contactId);
return res.json({ message: "Mensagem enviada" }); return res.json({ message: "Mensagem enviada" });
} catch (err) { } catch (err) {
next(err); next(err);
} }
}; };

View File

@@ -1,78 +1,78 @@
const qrCode = require("qrcode-terminal"); const qrCode = require("qrcode-terminal");
const { Client } = require("whatsapp-web.js"); const { Client } = require("whatsapp-web.js");
const Whatsapp = require("../models/Whatsapp"); const Whatsapp = require("../models/Whatsapp");
const { getIO } = require("../socket"); const { getIO } = require("../socket");
let wbot; let wbot;
module.exports = { module.exports = {
init: async () => { init: async () => {
try { try {
let sessionCfg; let sessionCfg;
const dbSession = await Whatsapp.findOne({ where: { id: 1 } }); const dbSession = await Whatsapp.findOne({ where: { id: 1 } });
if (dbSession && dbSession.session) { if (dbSession && dbSession.session) {
sessionCfg = JSON.parse(dbSession.session); sessionCfg = JSON.parse(dbSession.session);
} }
wbot = new Client({ wbot = new Client({
session: sessionCfg, session: sessionCfg,
restartOnAuthFail: true, restartOnAuthFail: true,
}); });
wbot.initialize(); wbot.initialize();
wbot.on("qr", async qr => { wbot.on("qr", async qr => {
qrCode.generate(qr, { small: true }); qrCode.generate(qr, { small: true });
await Whatsapp.upsert({ id: 1, qrcode: qr, status: "pending" }); await Whatsapp.upsert({ id: 1, qrcode: qr, status: "pending" });
getIO().emit("qrcode", { getIO().emit("qrcode", {
action: "update", action: "update",
qr: qr, qr: qr,
}); });
}); });
wbot.on("authenticated", async session => { wbot.on("authenticated", async session => {
console.log("AUTHENTICATED"); console.log("AUTHENTICATED");
await Whatsapp.upsert({ await Whatsapp.upsert({
id: 1, id: 1,
session: JSON.stringify(session), session: JSON.stringify(session),
status: "authenticated", status: "authenticated",
}); });
getIO().emit("whats_auth", { getIO().emit("whats_auth", {
action: "authentication", action: "authentication",
session: dbSession, session: dbSession,
}); });
}); });
wbot.on("auth_failure", async msg => { wbot.on("auth_failure", async msg => {
console.error("AUTHENTICATION FAILURE", msg); console.error("AUTHENTICATION FAILURE", msg);
await Whatsapp.update({ session: "" }, { where: { id: 1 } }); await Whatsapp.update({ session: "" }, { where: { id: 1 } });
}); });
wbot.on("ready", async () => { wbot.on("ready", async () => {
console.log("READY"); console.log("READY");
await Whatsapp.update( await Whatsapp.update(
{ status: "online", qrcode: "" }, { status: "online", qrcode: "" },
{ where: { id: 1 } } { where: { id: 1 } }
); );
// const chats = await wbot.getChats(); // pega as mensagens nao lidas (recebidas quando o bot estava offline) // const chats = await wbot.getChats(); // pega as mensagens nao lidas (recebidas quando o bot estava offline)
// let unreadMessages; // todo > salvar isso no DB pra mostrar no frontend // let unreadMessages; // todo > salvar isso no DB pra mostrar no frontend
// for (let chat of chats) { // for (let chat of chats) {
// if (chat.unreadCount > 0) { // if (chat.unreadCount > 0) {
// unreadMessages = await chat.fetchMessages({ // unreadMessages = await chat.fetchMessages({
// limit: chat.unreadCount, // limit: chat.unreadCount,
// }); // });
// } // }
// } // }
// console.log(unreadMessages); // console.log(unreadMessages);
wbot.sendPresenceAvailable(); wbot.sendPresenceAvailable();
}); });
return wbot; return wbot;
} catch (err) { } catch (err) {
console.log(err); console.log(err);
} }
}, },
getWbot: () => { getWbot: () => {
if (!wbot) { if (!wbot) {
throw new Error("Wbot not initialized"); throw new Error("Wbot not initialized");
} }
return wbot; return wbot;
}, },
}; };

View File

@@ -1,131 +1,131 @@
const Contact = require("../models/Contact"); const Contact = require("../models/Contact");
const Message = require("../models/Message"); const Message = require("../models/Message");
const path = require("path"); const path = require("path");
const fs = require("fs"); const fs = require("fs");
const { getIO } = require("../socket"); const { getIO } = require("../socket");
const { getWbot, init } = require("./wbot"); const { getWbot, init } = require("./wbot");
const wbotMessageListener = () => { const wbotMessageListener = () => {
const io = getIO(); const io = getIO();
const wbot = getWbot(); const wbot = getWbot();
wbot.on("message", async msg => { wbot.on("message", async msg => {
let newMessage; let newMessage;
// console.log(msg); // console.log(msg);
if (msg.from === "status@broadcast") { if (msg.from === "status@broadcast") {
return; return;
} }
try { try {
const msgContact = await msg.getContact(); const msgContact = await msg.getContact();
const imageUrl = await msgContact.getProfilePicUrl(); const imageUrl = await msgContact.getProfilePicUrl();
try { try {
let contact = await Contact.findOne({ let contact = await Contact.findOne({
where: { number: msgContact.number }, where: { number: msgContact.number },
}); });
if (contact) { if (contact) {
await contact.update({ imageURL: imageUrl }); await contact.update({ profilePicUrl: imageUrl });
} }
if (!contact) { if (!contact) {
try { try {
contact = await Contact.create({ contact = await Contact.create({
name: msgContact.pushname || msgContact.number.toString(), name: msgContact.pushname || msgContact.number.toString(),
number: msgContact.number, number: msgContact.number,
imageURL: imageUrl, profilePicUrl: imageUrl,
}); });
} catch (err) { } catch (err) {
console.log(err); console.log(err);
} }
} }
if (msg.hasMedia) { if (msg.hasMedia) {
const media = await msg.downloadMedia(); const media = await msg.downloadMedia();
if (media) { if (media) {
if (!media.filename) { if (!media.filename) {
let ext = media.mimetype.split("/")[1].split(";")[0]; let ext = media.mimetype.split("/")[1].split(";")[0];
media.filename = `${new Date().getTime()}.${ext}`; media.filename = `${new Date().getTime()}.${ext}`;
} }
fs.writeFile( fs.writeFile(
path.join(__dirname, "..", "public", media.filename), path.join(__dirname, "..", "public", media.filename),
media.data, media.data,
"base64", "base64",
err => { err => {
console.log(err); console.log(err);
} }
); );
newMessage = await contact.createMessage({ newMessage = await contact.createMessage({
id: msg.id.id, id: msg.id.id,
messageBody: msg.body || media.filename, messageBody: msg.body || media.filename,
mediaUrl: media.filename, mediaUrl: media.filename,
mediaType: media.mimetype.split("/")[0], mediaType: media.mimetype.split("/")[0],
}); });
await contact.update({ lastMessage: msg.body || media.filename }); await contact.update({ lastMessage: msg.body || media.filename });
} }
} else { } else {
newMessage = await contact.createMessage({ newMessage = await contact.createMessage({
id: msg.id.id, id: msg.id.id,
messageBody: msg.body, messageBody: msg.body,
}); });
await contact.update({ lastMessage: msg.body }); await contact.update({ lastMessage: msg.body });
} }
io.to(contact.id) io.to(contact.id)
.to("notification") .to("notification")
.emit("appMessage", { .emit("appMessage", {
action: "create", action: "create",
message: { message: {
...newMessage.dataValues, ...newMessage.dataValues,
mediaUrl: `${ mediaUrl: `${
newMessage.mediaUrl newMessage.mediaUrl
? `http://${process.env.HOST}:${process.env.PORT}/public/${newMessage.mediaUrl}` ? `http://${process.env.HOST}:${process.env.PORT}/public/${newMessage.mediaUrl}`
: "" : ""
}`, }`,
}, },
contact: { contact: {
...contact.dataValues, ...contact.dataValues,
unreadMessages: 1, unreadMessages: 1,
lastMessage: newMessage.messageBody, lastMessage: newMessage.messageBody,
}, },
}); });
let chat = await msg.getChat(); let chat = await msg.getChat();
chat.sendSeen(); chat.sendSeen();
} catch (err) { } catch (err) {
console.log(err); console.log(err);
} }
} catch (err) { } catch (err) {
console.log(err); console.log(err);
} }
}); });
wbot.on("message_ack", async (msg, ack) => { wbot.on("message_ack", async (msg, ack) => {
try { try {
const messageToUpdate = await Message.findOne({ const messageToUpdate = await Message.findOne({
where: { id: msg.id.id }, where: { id: msg.id.id },
}); });
if (!messageToUpdate) { if (!messageToUpdate) {
// will throw an error is msg wasn't sent from app // will throw an error is msg wasn't sent from app
const error = new Error( const error = new Error(
"Erro ao alterar o ack da mensagem no banco de dados" "Erro ao alterar o ack da mensagem no banco de dados"
); );
error.satusCode = 501; error.statusCode = 501;
throw error; throw error;
} }
await messageToUpdate.update({ ack: ack }); await messageToUpdate.update({ ack: ack });
io.to(messageToUpdate.contactId).emit("appMessage", { io.to(messageToUpdate.contactId).emit("appMessage", {
action: "update", action: "update",
message: messageToUpdate, message: messageToUpdate,
}); });
} catch (err) { } catch (err) {
console.log(err); console.log(err);
} }
}); });
}; };
module.exports = wbotMessageListener; module.exports = wbotMessageListener;

View File

@@ -1,50 +1,50 @@
const Whatsapp = require("../models/Whatsapp"); const Whatsapp = require("../models/Whatsapp");
const wbotMessageListener = require("./wbotMessageListener"); const wbotMessageListener = require("./wbotMessageListener");
const { getIO } = require("../socket"); const { getIO } = require("../socket");
const { getWbot, init } = require("./wbot"); const { getWbot, init } = require("./wbot");
const wbotMonitor = () => { const wbotMonitor = () => {
const io = getIO(); const io = getIO();
const wbot = getWbot(); const wbot = getWbot();
try { try {
wbot.on("change_state", newState => { wbot.on("change_state", newState => {
console.log("monitor", newState); console.log("monitor", newState);
}); });
wbot.on("change_battery", async batteryInfo => { wbot.on("change_battery", async batteryInfo => {
// Battery percentage for attached device has changed // Battery percentage for attached device has changed
const { battery, plugged } = batteryInfo; const { battery, plugged } = batteryInfo;
try { try {
await Whatsapp.update({ battery, plugged }, { where: { id: 1 } }); await Whatsapp.update({ battery, plugged }, { where: { id: 1 } });
} catch (err) { } catch (err) {
console.log(err); console.log(err);
} }
console.log(`Battery: ${battery}% - Charging? ${plugged}`); //todo> save batery state to db console.log(`Battery: ${battery}% - Charging? ${plugged}`); //todo> save batery state to db
}); });
wbot.on("disconnected", reason => { wbot.on("disconnected", reason => {
console.log("disconnected", reason); //todo> save connection status to DB console.log("disconnected", reason); //todo> save connection status to DB
setTimeout( setTimeout(
() => () =>
init() init()
.then(res => { .then(res => {
wbotMessageListener(); wbotMessageListener();
wbotMonitor(); wbotMonitor();
}) })
.catch(err => console.log(err)), .catch(err => console.log(err)),
2000 2000
); );
}); });
// setInterval(() => { // setInterval(() => {
// wbot.resetState(); // wbot.resetState();
// }, 20000); // }, 20000);
} catch (err) { } catch (err) {
console.log(err); console.log(err);
} }
}; };
module.exports = wbotMonitor; module.exports = wbotMonitor;

View File

@@ -1,15 +1,30 @@
const Whatsapp = require("../models/Whatsapp"); const Whatsapp = require("../models/Whatsapp");
const { getIO } = require("../socket");
exports.getSession = async (req, res, next) => { const { getWbot, init } = require("./wbot");
try {
const dbSession = await Whatsapp.findOne({ where: { id: 1 } }); exports.getSession = async (req, res, next) => {
try {
if (!dbSession) { const dbSession = await Whatsapp.findOne({ where: { id: 1 } });
return res.status(200).json({ message: "Session not found" });
} if (!dbSession) {
return res.status(200).json({ message: "Session not found" });
return res.status(200).json(dbSession); }
} catch (err) {
next(err); return res.status(200).json(dbSession);
} } catch (err) {
}; next(err);
}
};
exports.getContacts = async (req, res, next) => {
const io = getIO();
const wbot = getWbot();
try {
const phoneContacts = await wbot.getContacts();
return res.status(200).json(phoneContacts);
} catch (err) {
next(err);
}
};

View File

@@ -1,22 +1,22 @@
const jwt = require("jsonwebtoken"); const jwt = require("jsonwebtoken");
module.exports = (req, res, next) => { module.exports = (req, res, next) => {
let decodedToken; let decodedToken;
try { try {
const [, token] = req.get("Authorization").split(" "); const [, token] = req.get("Authorization").split(" ");
decodedToken = jwt.verify(token, "mysecret"); decodedToken = jwt.verify(token, "mysecret");
} catch (err) { req.userId = decodedToken.userId;
err.statusCode = 401; } catch (err) {
err.message = "invalidToken"; err.statusCode = 401;
next(err); err.message = "invalidToken";
} next(err);
}
if (!decodedToken) {
const error = new Error("Falha na autenticação"); if (!decodedToken) {
error.statusCode = 401; const error = new Error("Falha na autenticação");
next(error); error.statusCode = 401;
} next(error);
}
req.userId = decodedToken.userId;
next(); next();
}; };

View File

@@ -1,19 +1,19 @@
const Sequelize = require("sequelize"); const Sequelize = require("sequelize");
const sequelize = require("../util/database"); const sequelize = require("../util/database");
const Message = require("./Message"); const Message = require("./Message");
const Contact = sequelize.define("contact", { const Contact = sequelize.define("contact", {
name: { type: Sequelize.STRING(100), allowNull: false }, name: { type: Sequelize.STRING(100), allowNull: false },
number: { type: Sequelize.STRING(15), allowNull: false }, number: { type: Sequelize.STRING(15), allowNull: false },
imageURL: { type: Sequelize.STRING(200) }, profilePicUrl: { type: Sequelize.STRING(200) },
lastMessage: { type: Sequelize.TEXT }, lastMessage: { type: Sequelize.TEXT },
}); });
Contact.hasMany(Message, { Contact.hasMany(Message, {
onDelete: "CASCADE", onDelete: "CASCADE",
onUpdate: "RESTRICT", onUpdate: "RESTRICT",
}); });
module.exports = Contact; module.exports = Contact;

View File

@@ -1,22 +1,22 @@
const Sequelize = require("sequelize"); const Sequelize = require("sequelize");
const sequelize = require("../util/database"); const sequelize = require("../util/database");
const Message = sequelize.define("message", { const Message = sequelize.define("message", {
id: { id: {
type: Sequelize.STRING(50), type: Sequelize.STRING(50),
allowNull: false, allowNull: false,
primaryKey: true, primaryKey: true,
}, },
createdAt: { createdAt: {
allowNull: false, allowNull: false,
type: Sequelize.DATE(6), type: Sequelize.DATE(6),
}, },
userId: { type: Sequelize.INTEGER, defaultValue: 0 }, userId: { type: Sequelize.INTEGER, defaultValue: 0 },
ack: { type: Sequelize.INTEGER, defaultValue: 0 }, ack: { type: Sequelize.INTEGER, defaultValue: 0 },
messageBody: { type: Sequelize.TEXT, allowNull: false }, messageBody: { type: Sequelize.TEXT, allowNull: false },
read: { type: Sequelize.BOOLEAN, defaultValue: false }, read: { type: Sequelize.BOOLEAN, defaultValue: false },
mediaUrl: { type: Sequelize.STRING(250) }, mediaUrl: { type: Sequelize.STRING(250) },
mediaType: { type: Sequelize.STRING(250) }, mediaType: { type: Sequelize.STRING(250) },
}); });
module.exports = Message; module.exports = Message;

View File

@@ -1,17 +1,17 @@
const Sequelize = require("sequelize"); const Sequelize = require("sequelize");
const sequelize = require("../util/database"); const sequelize = require("../util/database");
const User = sequelize.define("user", { const User = sequelize.define("user", {
id: { id: {
type: Sequelize.INTEGER, type: Sequelize.INTEGER,
allowNull: false, allowNull: false,
primaryKey: true, primaryKey: true,
autoIncrement: true, autoIncrement: true,
}, },
name: { type: Sequelize.STRING(100), allowNull: false }, name: { type: Sequelize.STRING(100), allowNull: false },
password: { type: Sequelize.STRING(100), allowNull: false }, password: { type: Sequelize.STRING(100), allowNull: false },
email: { type: Sequelize.STRING(100), allowNull: false }, email: { type: Sequelize.STRING(100), allowNull: false },
}); });
module.exports = User; module.exports = User;

View File

@@ -1,13 +1,13 @@
const Sequelize = require("sequelize"); const Sequelize = require("sequelize");
const sequelize = require("../util/database"); const sequelize = require("../util/database");
const Whatsapp = sequelize.define("whatsapp", { const Whatsapp = sequelize.define("whatsapp", {
session: { type: Sequelize.TEXT() }, session: { type: Sequelize.TEXT() },
qrcode: { type: Sequelize.TEXT() }, qrcode: { type: Sequelize.TEXT() },
status: { type: Sequelize.STRING(60) }, status: { type: Sequelize.STRING(60) },
battery: { type: Sequelize.STRING(20) }, battery: { type: Sequelize.STRING(20) },
plugged: { type: Sequelize.BOOLEAN() }, plugged: { type: Sequelize.BOOLEAN() },
}); });
module.exports = Whatsapp; module.exports = Whatsapp;

View File

@@ -1,30 +1,35 @@
const express = require("express"); const express = require("express");
const { body } = require("express-validator"); const { body } = require("express-validator");
const User = require("../models/User"); const User = require("../models/User");
const authController = require("../controllers/auth"); const authController = require("../controllers/auth");
const isAuth = require("../middleware/is-auth");
const routes = express.Router();
const routes = express.Router();
routes.put(
"/signup", routes.put(
[ "/signup",
body("email") [
.isEmail() body("email")
.withMessage("Email inválido") .isEmail()
.custom((value, { req }) => { .withMessage("Email inválido")
return User.findOne({ where: { email: value } }).then(user => { .custom((value, { req }) => {
if (user) { return User.findOne({ where: { email: value } }).then(user => {
return Promise.reject("Um cadastro com este email já existe!"); if (user) {
} return Promise.reject("Um cadastro com este email já existe!");
}); }
}) });
.normalizeEmail(), })
body("password").trim().isLength({ min: 5 }), .normalizeEmail(),
body("name").trim().not().isEmpty(), body("password").trim().isLength({ min: 5 }),
], body("name").trim().not().isEmpty(),
authController.signup ],
); authController.signup
);
routes.post("/login", authController.login);
routes.post("/login", authController.login);
module.exports = routes;
routes.get("/check", isAuth, (req, res) => {
res.status(200).json({ authenticated: true });
});
module.exports = routes;

View File

@@ -1,11 +1,13 @@
const express = require("express"); const express = require("express");
const isAuth = require("../middleware/is-auth"); const isAuth = require("../middleware/is-auth");
const ContactController = require("../controllers/contact"); const ContactController = require("../controllers/contact");
const routes = express.Router(); const routes = express.Router();
routes.get("/contacts", isAuth, ContactController.getContacts); routes.get("/contacts", isAuth, ContactController.getContacts);
// routes.post(ContactController.postCreateContact); // routes.post(ContactController.postCreateContact);
module.exports = routes; routes.post("/contacts", isAuth, ContactController.createContact);
module.exports = routes;

View File

@@ -1,19 +1,19 @@
const express = require("express"); const express = require("express");
const isAuth = require("../middleware/is-auth"); const isAuth = require("../middleware/is-auth");
const MessangeController = require("../controllers/message"); const MessangeController = require("../controllers/message");
const routes = express.Router(); const routes = express.Router();
routes.get( routes.get(
"/messages/:contactId", "/messages/:contactId",
isAuth, isAuth,
MessangeController.getContactMessages MessangeController.getContactMessages
); );
routes.post( routes.post(
"/messages/:contactId", "/messages/:contactId",
isAuth, isAuth,
MessangeController.postCreateContactMessage MessangeController.postCreateContactMessage
); );
module.exports = routes; module.exports = routes;

View File

@@ -1,15 +1,22 @@
const express = require("express"); const express = require("express");
const isAuth = require("../middleware/is-auth"); const isAuth = require("../middleware/is-auth");
const WhatsappController = require("../controllers/whatsapp"); const WhatsappController = require("../controllers/whatsapp");
const routes = express.Router(); const routes = express.Router();
routes.get("/whatsapp/session", isAuth, WhatsappController.getSession); routes.get("/whatsapp/session", isAuth, WhatsappController.getSession);
// routes.post( // routes.post(
// "/messages/:contactId", // "/messages/:contactId",
// isAuth, // isAuth,
// WhatsappController.postCreateContactMessage // WhatsappController.postCreateContactMessage
// ); // );
module.exports = routes; routes.get("/whatsapp/contacts", isAuth, WhatsappController.getContacts);
// routes.post(
// "/messages/:contactId",
// isAuth,
// WhatsappController.postCreateContactMessage
// );
module.exports = routes;

View File

@@ -1,14 +1,14 @@
let io; let io;
module.exports = { module.exports = {
init: httpServer => { init: httpServer => {
io = require("socket.io")(httpServer); io = require("socket.io")(httpServer);
return io; return io;
}, },
getIO: () => { getIO: () => {
if (!io) { if (!io) {
throw new Error("Socket IO not initialized"); throw new Error("Socket IO not initialized");
} }
return io; return io;
}, },
}; };

View File

@@ -1,18 +1,18 @@
require("dotenv/config"); require("dotenv/config");
const Sequelize = require("sequelize"); const Sequelize = require("sequelize");
const sequelize = new Sequelize({ const sequelize = new Sequelize({
define: { define: {
charset: "utf8mb4", charset: "utf8mb4",
collate: "utf8mb4_bin", collate: "utf8mb4_bin",
}, },
dialect: "mysql", dialect: "mysql",
timezone: "-03:00", timezone: "-03:00",
host: process.env.DB_HOST, host: process.env.DB_HOST,
database: process.env.DB_NAME, database: process.env.DB_NAME,
username: process.env.DB_USER, username: process.env.DB_USER,
password: process.env.DB_PASS, password: process.env.DB_PASS,
logging: false, logging: false,
}); });
module.exports = sequelize; module.exports = sequelize;

29026
frontend/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,48 +1,48 @@
{ {
"name": "frontend", "name": "frontend",
"version": "0.1.0", "version": "0.1.0",
"private": true, "private": true,
"dependencies": { "dependencies": {
"@material-ui/core": "^4.9.14", "@material-ui/core": "^4.9.14",
"@material-ui/icons": "^4.9.1", "@material-ui/icons": "^4.9.1",
"@testing-library/jest-dom": "^4.2.4", "@testing-library/jest-dom": "^4.2.4",
"@testing-library/react": "^9.5.0", "@testing-library/react": "^9.5.0",
"@testing-library/user-event": "^7.2.1", "@testing-library/user-event": "^7.2.1",
"axios": "^0.19.2", "axios": "^0.19.2",
"dotenv": "^8.2.0", "dotenv": "^8.2.0",
"emoji-mart": "^3.0.0", "emoji-mart": "^3.0.0",
"moment": "^2.26.0", "moment": "^2.26.0",
"moment-timezone": "^0.5.31", "moment-timezone": "^0.5.31",
"qrcode.react": "^1.0.0", "qrcode.react": "^1.0.0",
"react": "^16.13.1", "react": "^16.13.1",
"react-dom": "^16.13.1", "react-dom": "^16.13.1",
"react-infinite-scroll-reverse": "^1.0.3", "react-infinite-scroll-reverse": "^1.0.3",
"react-modal-image": "^2.5.0", "react-modal-image": "^2.5.0",
"react-moment": "^0.9.7", "react-moment": "^0.9.7",
"react-router-dom": "^5.2.0", "react-router-dom": "^5.2.0",
"react-scripts": "3.4.1", "react-scripts": "3.4.1",
"react-toastify": "^6.0.4", "react-toastify": "^6.0.4",
"socket.io-client": "^2.3.0" "socket.io-client": "^2.3.0"
}, },
"scripts": { "scripts": {
"start": "react-scripts start", "start": "react-scripts start",
"build": "react-scripts build", "build": "react-scripts build",
"test": "react-scripts test", "test": "react-scripts test",
"eject": "react-scripts eject" "eject": "react-scripts eject"
}, },
"eslintConfig": { "eslintConfig": {
"extends": "react-app" "extends": "react-app"
}, },
"browserslist": { "browserslist": {
"production": [ "production": [
">0.2%", ">0.2%",
"not dead", "not dead",
"not op_mini all" "not op_mini all"
], ],
"development": [ "development": [
"last 1 chrome version", "last 1 chrome version",
"last 1 firefox version", "last 1 firefox version",
"last 1 safari version" "last 1 safari version"
] ]
} }
} }

View File

@@ -1,11 +1,11 @@
button:active { button:active {
opacity: 0.5; opacity: 0.5;
} }
button:focus { button:focus {
outline: 0; outline: 0;
} }
img:active { img:active {
opacity: 0.5; opacity: 0.5;
} }

View File

@@ -1,19 +1,19 @@
import React, { createContext } from "react"; import React, { createContext } from "react";
import useAuth from "./useAuth"; import useAuth from "./useAuth";
const AuthContext = createContext(); const AuthContext = createContext();
const AuthProvider = ({ children }) => { const AuthProvider = ({ children }) => {
const { isAuth, loading, handleLogin, handleLogout } = useAuth(); const { isAuth, loading, handleLogin, handleLogout } = useAuth();
return ( return (
<AuthContext.Provider <AuthContext.Provider
value={{ loading, isAuth, handleLogin, handleLogout }} value={{ loading, isAuth, handleLogin, handleLogout }}
> >
{children} {children}
</AuthContext.Provider> </AuthContext.Provider>
); );
}; };
export { AuthContext, AuthProvider }; export { AuthContext, AuthProvider };

View File

@@ -1,50 +1,70 @@
import { useState, useEffect } from "react"; import { useState, useEffect } from "react";
import { useHistory } from "react-router-dom"; import { useHistory } from "react-router-dom";
import api from "../../util/api"; import api from "../../util/api";
const useAuth = () => { const useAuth = () => {
const history = useHistory(); const history = useHistory();
const [isAuth, setIsAuth] = useState(false); const [isAuth, setIsAuth] = useState(false);
const [loading, setLoading] = useState(true); const [loading, setLoading] = useState(true);
useEffect(() => { useEffect(() => {
const token = localStorage.getItem("token"); const token = localStorage.getItem("token");
if (token) { if (token) {
api.defaults.headers.Authorization = `Bearer ${JSON.parse(token)}`; api.defaults.headers.Authorization = `Bearer ${JSON.parse(token)}`;
setIsAuth(true); setIsAuth(true);
} }
const checkAuth = async () => {
setLoading(false); if (
}, []); history.location.pathname === "/login" ||
history.location.pathname === "/signup"
const handleLogin = async (e, user) => { ) {
e.preventDefault(); setLoading(false);
try {
const res = await api.post("/auth/login", user); return;
localStorage.setItem("token", JSON.stringify(res.data.token)); }
localStorage.setItem("username", res.data.username); try {
localStorage.setItem("userId", res.data.userId); const res = await api.get("/auth/check");
api.defaults.headers.Authorization = `Bearer ${res.data.token}`; if (res.status === 200) {
setIsAuth(true); setIsAuth(true);
history.push("/chat"); setLoading(false);
} catch (err) { }
alert(err); } catch (err) {
} setLoading(false);
}; setIsAuth(false);
alert("Erro de autenticação. Por favor, faça login novamente");
const handleLogout = e => { }
e.preventDefault(); };
setIsAuth(false); checkAuth();
localStorage.removeItem("token"); }, [history.location.pathname]);
localStorage.removeItem("username");
localStorage.removeItem("userId"); const handleLogin = async (e, user) => {
api.defaults.headers.Authorization = undefined; e.preventDefault();
history.push("/login"); try {
}; const res = await api.post("/auth/login", user);
localStorage.setItem("token", JSON.stringify(res.data.token));
return { isAuth, loading, handleLogin, handleLogout }; localStorage.setItem("username", res.data.username);
}; localStorage.setItem("userId", res.data.userId);
api.defaults.headers.Authorization = `Bearer ${res.data.token}`;
export default useAuth; setIsAuth(true);
history.push("/chat");
} catch (err) {
alert(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;

View File

@@ -1,229 +1,229 @@
import React, { useState, useContext, useEffect } from "react"; import React, { useState, useContext, useEffect } from "react";
import clsx from "clsx"; import clsx from "clsx";
import { makeStyles } from "@material-ui/core/styles"; import { makeStyles } from "@material-ui/core/styles";
import Drawer from "@material-ui/core/Drawer"; import Drawer from "@material-ui/core/Drawer";
import AppBar from "@material-ui/core/AppBar"; import AppBar from "@material-ui/core/AppBar";
import Toolbar from "@material-ui/core/Toolbar"; import Toolbar from "@material-ui/core/Toolbar";
import List from "@material-ui/core/List"; import List from "@material-ui/core/List";
import Typography from "@material-ui/core/Typography"; import Typography from "@material-ui/core/Typography";
import Divider from "@material-ui/core/Divider"; import Divider from "@material-ui/core/Divider";
import IconButton from "@material-ui/core/IconButton"; import IconButton from "@material-ui/core/IconButton";
import Badge from "@material-ui/core/Badge"; import Badge from "@material-ui/core/Badge";
import MenuIcon from "@material-ui/icons/Menu"; import MenuIcon from "@material-ui/icons/Menu";
import ChevronLeftIcon from "@material-ui/icons/ChevronLeft"; import ChevronLeftIcon from "@material-ui/icons/ChevronLeft";
import NotificationsIcon from "@material-ui/icons/Notifications"; import NotificationsIcon from "@material-ui/icons/Notifications";
import MainListItems from "./MainListItems"; import MainListItems from "./MainListItems";
import AccountCircle from "@material-ui/icons/AccountCircle"; import AccountCircle from "@material-ui/icons/AccountCircle";
import MenuItem from "@material-ui/core/MenuItem"; import MenuItem from "@material-ui/core/MenuItem";
import Menu from "@material-ui/core/Menu"; import Menu from "@material-ui/core/Menu";
import { AuthContext } from "../../Context/Auth/AuthContext"; import { AuthContext } from "../../Context/Auth/AuthContext";
const drawerWidth = 240; const drawerWidth = 240;
const useStyles = makeStyles(theme => ({ const useStyles = makeStyles(theme => ({
root: { root: {
display: "flex", display: "flex",
height: "100vh", height: "100vh",
}, },
toolbar: { toolbar: {
paddingRight: 24, // keep right padding when drawer closed paddingRight: 24, // keep right padding when drawer closed
}, },
toolbarIcon: { toolbarIcon: {
display: "flex", display: "flex",
alignItems: "center", alignItems: "center",
justifyContent: "flex-end", justifyContent: "flex-end",
padding: "0 8px", padding: "0 8px",
...theme.mixins.toolbar, ...theme.mixins.toolbar,
}, },
appBar: { appBar: {
zIndex: theme.zIndex.drawer + 1, zIndex: theme.zIndex.drawer + 1,
transition: theme.transitions.create(["width", "margin"], { transition: theme.transitions.create(["width", "margin"], {
easing: theme.transitions.easing.sharp, easing: theme.transitions.easing.sharp,
duration: theme.transitions.duration.leavingScreen, duration: theme.transitions.duration.leavingScreen,
}), }),
}, },
appBarShift: { appBarShift: {
marginLeft: drawerWidth, marginLeft: drawerWidth,
width: `calc(100% - ${drawerWidth}px)`, width: `calc(100% - ${drawerWidth}px)`,
transition: theme.transitions.create(["width", "margin"], { transition: theme.transitions.create(["width", "margin"], {
easing: theme.transitions.easing.sharp, easing: theme.transitions.easing.sharp,
duration: theme.transitions.duration.enteringScreen, duration: theme.transitions.duration.enteringScreen,
}), }),
}, },
menuButton: { menuButton: {
marginRight: 36, marginRight: 36,
}, },
menuButtonHidden: { menuButtonHidden: {
display: "none", display: "none",
}, },
title: { title: {
flexGrow: 1, flexGrow: 1,
}, },
drawerPaper: { drawerPaper: {
position: "relative", position: "relative",
whiteSpace: "nowrap", whiteSpace: "nowrap",
width: drawerWidth, width: drawerWidth,
transition: theme.transitions.create("width", { transition: theme.transitions.create("width", {
easing: theme.transitions.easing.sharp, easing: theme.transitions.easing.sharp,
duration: theme.transitions.duration.enteringScreen, duration: theme.transitions.duration.enteringScreen,
}), }),
}, },
drawerPaperClose: { drawerPaperClose: {
overflowX: "hidden", overflowX: "hidden",
transition: theme.transitions.create("width", { transition: theme.transitions.create("width", {
easing: theme.transitions.easing.sharp, easing: theme.transitions.easing.sharp,
duration: theme.transitions.duration.leavingScreen, duration: theme.transitions.duration.leavingScreen,
}), }),
width: theme.spacing(7), width: theme.spacing(7),
[theme.breakpoints.up("sm")]: { [theme.breakpoints.up("sm")]: {
width: theme.spacing(9), width: theme.spacing(9),
}, },
}, },
appBarSpacer: theme.mixins.toolbar, appBarSpacer: theme.mixins.toolbar,
content: { content: {
flex: 1, flex: 1,
// height: "100%", // height: "100%",
overflow: "auto", overflow: "auto",
}, },
container: { container: {
paddingTop: theme.spacing(4), paddingTop: theme.spacing(4),
paddingBottom: theme.spacing(4), paddingBottom: theme.spacing(4),
}, },
paper: { paper: {
padding: theme.spacing(2), padding: theme.spacing(2),
display: "flex", display: "flex",
overflow: "auto", overflow: "auto",
flexDirection: "column", flexDirection: "column",
}, },
})); }));
const MainDrawer = ({ appTitle, children }) => { const MainDrawer = ({ appTitle, children }) => {
const { handleLogout } = useContext(AuthContext); const { handleLogout } = useContext(AuthContext);
const classes = useStyles(); const classes = useStyles();
const [open, setOpen] = useState(true); const [open, setOpen] = useState(true);
const [anchorEl, setAnchorEl] = React.useState(null); const [anchorEl, setAnchorEl] = React.useState(null);
const menuOpen = Boolean(anchorEl); const menuOpen = Boolean(anchorEl);
const drawerState = localStorage.getItem("drawerOpen"); const drawerState = localStorage.getItem("drawerOpen");
useEffect(() => { useEffect(() => {
if (drawerState === "0") { if (drawerState === "0") {
setOpen(false); setOpen(false);
} }
}, [drawerState]); }, [drawerState]);
const handleDrawerOpen = () => { const handleDrawerOpen = () => {
setOpen(true); setOpen(true);
localStorage.setItem("drawerOpen", 1); localStorage.setItem("drawerOpen", 1);
}; };
const handleDrawerClose = () => { const handleDrawerClose = () => {
setOpen(false); setOpen(false);
localStorage.setItem("drawerOpen", 0); localStorage.setItem("drawerOpen", 0);
}; };
const handleMenu = event => { const handleMenu = event => {
setAnchorEl(event.currentTarget); setAnchorEl(event.currentTarget);
}; };
const handleClose = () => { const handleClose = () => {
setAnchorEl(null); setAnchorEl(null);
}; };
return ( return (
<div className={classes.root}> <div className={classes.root}>
<Drawer <Drawer
variant="permanent" variant="permanent"
classes={{ classes={{
paper: clsx(classes.drawerPaper, !open && classes.drawerPaperClose), paper: clsx(classes.drawerPaper, !open && classes.drawerPaperClose),
}} }}
open={open} open={open}
> >
<div className={classes.toolbarIcon}> <div className={classes.toolbarIcon}>
<IconButton onClick={handleDrawerClose}> <IconButton onClick={handleDrawerClose}>
<ChevronLeftIcon /> <ChevronLeftIcon />
</IconButton> </IconButton>
</div> </div>
<Divider /> <Divider />
<List> <List>
<MainListItems /> <MainListItems />
</List> </List>
<Divider /> <Divider />
</Drawer> </Drawer>
<AppBar <AppBar
position="absolute" position="absolute"
className={clsx(classes.appBar, open && classes.appBarShift)} className={clsx(classes.appBar, open && classes.appBarShift)}
> >
<Toolbar className={classes.toolbar}> <Toolbar className={classes.toolbar}>
<IconButton <IconButton
edge="start" edge="start"
color="inherit" color="inherit"
aria-label="open drawer" aria-label="open drawer"
onClick={handleDrawerOpen} onClick={handleDrawerOpen}
className={clsx( className={clsx(
classes.menuButton, classes.menuButton,
open && classes.menuButtonHidden open && classes.menuButtonHidden
)} )}
> >
<MenuIcon /> <MenuIcon />
</IconButton> </IconButton>
<Typography <Typography
component="h1" component="h1"
variant="h6" variant="h6"
color="inherit" color="inherit"
noWrap noWrap
className={classes.title} className={classes.title}
> >
{appTitle} {appTitle}
</Typography> </Typography>
<IconButton color="inherit"> <IconButton color="inherit">
<Badge badgeContent={0} color="secondary"> <Badge badgeContent={0} color="secondary">
<NotificationsIcon /> <NotificationsIcon />
</Badge> </Badge>
</IconButton> </IconButton>
<div> <div>
<IconButton <IconButton
aria-label="account of current user" aria-label="account of current user"
aria-controls="menu-appbar" aria-controls="menu-appbar"
aria-haspopup="true" aria-haspopup="true"
onClick={handleMenu} onClick={handleMenu}
color="inherit" color="inherit"
> >
<AccountCircle /> <AccountCircle />
</IconButton> </IconButton>
<Menu <Menu
id="menu-appbar" id="menu-appbar"
anchorEl={anchorEl} anchorEl={anchorEl}
anchorOrigin={{ anchorOrigin={{
vertical: "top", vertical: "top",
horizontal: "right", horizontal: "right",
}} }}
keepMounted keepMounted
transformOrigin={{ transformOrigin={{
vertical: "top", vertical: "top",
horizontal: "right", horizontal: "right",
}} }}
open={menuOpen} open={menuOpen}
onClose={handleClose} onClose={handleClose}
> >
<MenuItem onClick={handleClose}>Profile</MenuItem> <MenuItem onClick={handleClose}>Profile</MenuItem>
<MenuItem onClick={handleLogout}>Logout</MenuItem> <MenuItem onClick={handleLogout}>Logout</MenuItem>
</Menu> </Menu>
</div> </div>
</Toolbar> </Toolbar>
</AppBar> </AppBar>
<main className={classes.content}> <main className={classes.content}>
<div className={classes.appBarSpacer} /> <div className={classes.appBarSpacer} />
{children ? children : <h1>Dashboard</h1>} {children ? children : <h1>Dashboard</h1>}
</main> </main>
</div> </div>
); );
}; };
export default MainDrawer; export default MainDrawer;

View File

@@ -1,125 +1,125 @@
import React from "react"; import React from "react";
import { Link as RouterLink } from "react-router-dom"; import { Link as RouterLink } from "react-router-dom";
import { makeStyles } from "@material-ui/core/styles"; import { makeStyles } from "@material-ui/core/styles";
import List from "@material-ui/core/List"; import List from "@material-ui/core/List";
import ListItem from "@material-ui/core/ListItem"; import ListItem from "@material-ui/core/ListItem";
import ListItemIcon from "@material-ui/core/ListItemIcon"; import ListItemIcon from "@material-ui/core/ListItemIcon";
import ListItemText from "@material-ui/core/ListItemText"; import ListItemText from "@material-ui/core/ListItemText";
// import ListSubheader from "@material-ui/core/ListSubheader"; // import ListSubheader from "@material-ui/core/ListSubheader";
import Collapse from "@material-ui/core/Collapse"; import Collapse from "@material-ui/core/Collapse";
import DashboardIcon from "@material-ui/icons/Dashboard"; import DashboardIcon from "@material-ui/icons/Dashboard";
import WhatsAppIcon from "@material-ui/icons/WhatsApp"; import WhatsAppIcon from "@material-ui/icons/WhatsApp";
// import PeopleIcon from "@material-ui/icons/People"; // import PeopleIcon from "@material-ui/icons/People";
import SyncAltIcon from "@material-ui/icons/SyncAlt"; import SyncAltIcon from "@material-ui/icons/SyncAlt";
import ChatIcon from "@material-ui/icons/Chat"; import ChatIcon from "@material-ui/icons/Chat";
import BarChartIcon from "@material-ui/icons/BarChart"; import BarChartIcon from "@material-ui/icons/BarChart";
import LayersIcon from "@material-ui/icons/Layers"; import LayersIcon from "@material-ui/icons/Layers";
// import AssignmentIcon from "@material-ui/icons/Assignment"; // import AssignmentIcon from "@material-ui/icons/Assignment";
import ExpandLess from "@material-ui/icons/ExpandLess"; import ExpandLess from "@material-ui/icons/ExpandLess";
import ExpandMore from "@material-ui/icons/ExpandMore"; import ExpandMore from "@material-ui/icons/ExpandMore";
const useStyles = makeStyles(theme => ({ const useStyles = makeStyles(theme => ({
nested: { nested: {
paddingLeft: theme.spacing(4), paddingLeft: theme.spacing(4),
}, },
})); }));
function ListItemLink(props) { function ListItemLink(props) {
const { icon, primary, to, className } = props; const { icon, primary, to, className } = props;
const renderLink = React.useMemo( const renderLink = React.useMemo(
() => () =>
React.forwardRef((itemProps, ref) => ( React.forwardRef((itemProps, ref) => (
<RouterLink to={to} ref={ref} {...itemProps} /> <RouterLink to={to} ref={ref} {...itemProps} />
)), )),
[to] [to]
); );
return ( return (
<li> <li>
<ListItem button component={renderLink} className={className}> <ListItem button component={renderLink} className={className}>
{icon ? <ListItemIcon>{icon}</ListItemIcon> : null} {icon ? <ListItemIcon>{icon}</ListItemIcon> : null}
<ListItemText primary={primary} /> <ListItemText primary={primary} />
</ListItem> </ListItem>
</li> </li>
); );
} }
const MainListItems = () => { const MainListItems = () => {
const classes = useStyles(); const classes = useStyles();
const [open, setOpen] = React.useState(false); const [open, setOpen] = React.useState(false);
const handleClick = () => { const handleClick = () => {
setOpen(!open); setOpen(!open);
}; };
return ( return (
<div> <div>
<ListItemLink to="/" primary="Dashboard" icon={<DashboardIcon />} /> <ListItemLink to="/" primary="Dashboard" icon={<DashboardIcon />} />
<ListItem button onClick={handleClick}> <ListItem button onClick={handleClick}>
<ListItemIcon> <ListItemIcon>
<WhatsAppIcon /> <WhatsAppIcon />
</ListItemIcon> </ListItemIcon>
<ListItemText primary="WhatsApp" /> <ListItemText primary="WhatsApp" />
{open ? <ExpandLess /> : <ExpandMore />} {open ? <ExpandLess /> : <ExpandMore />}
</ListItem> </ListItem>
<Collapse in={open} timeout="auto" unmountOnExit> <Collapse in={open} timeout="auto" unmountOnExit>
<List component="div" disablePadding> <List component="div" disablePadding>
<ListItemLink <ListItemLink
className={classes.nested} className={classes.nested}
to="/whats-auth" to="/whats-auth"
primary="Conexão" primary="Conexão"
icon={<SyncAltIcon />} icon={<SyncAltIcon />}
/> />
<ListItemLink <ListItemLink
className={classes.nested} className={classes.nested}
to="/chat" to="/chat"
primary="Chat" primary="Chat"
icon={<ChatIcon />} icon={<ChatIcon />}
/> />
</List> </List>
</Collapse> </Collapse>
<ListItem button disabled> <ListItem button disabled>
<ListItemIcon> <ListItemIcon>
<BarChartIcon /> <BarChartIcon />
</ListItemIcon> </ListItemIcon>
<ListItemText primary="Relatórios" /> <ListItemText primary="Relatórios" />
</ListItem> </ListItem>
<ListItem button disabled> <ListItem button disabled>
<ListItemIcon> <ListItemIcon>
<LayersIcon /> <LayersIcon />
</ListItemIcon> </ListItemIcon>
<ListItemText primary="Integrações" /> <ListItemText primary="Integrações" />
</ListItem> </ListItem>
</div> </div>
); );
}; };
// export const secondaryListItems = ( // export const secondaryListItems = (
// <div> // <div>
// <ListSubheader inset>Saved reports</ListSubheader> // <ListSubheader inset>Saved reports</ListSubheader>
// <ListItem button> // <ListItem button>
// <ListItemIcon> // <ListItemIcon>
// <AssignmentIcon /> // <AssignmentIcon />
// </ListItemIcon> // </ListItemIcon>
// <ListItemText primary="Current month" /> // <ListItemText primary="Current month" />
// </ListItem> // </ListItem>
// <ListItem button> // <ListItem button>
// <ListItemIcon> // <ListItemIcon>
// <AssignmentIcon /> // <AssignmentIcon />
// </ListItemIcon> // </ListItemIcon>
// <ListItemText primary="Last quarter" /> // <ListItemText primary="Last quarter" />
// </ListItem> // </ListItem>
// <ListItem button> // <ListItem button>
// <ListItemIcon> // <ListItemIcon>
// <AssignmentIcon /> // <AssignmentIcon />
// </ListItemIcon> // </ListItemIcon>
// <ListItemText primary="Year-end sale" /> // <ListItemText primary="Year-end sale" />
// </ListItem> // </ListItem>
// </div> // </div>
// ); // );
export default MainListItems; export default MainListItems;

View File

@@ -1,37 +1,37 @@
import React from "react"; import React from "react";
import { Navbar, Nav, Container } from "react-bootstrap"; import { Navbar, Nav, Container } from "react-bootstrap";
import { LinkContainer } from "react-router-bootstrap"; import { LinkContainer } from "react-router-bootstrap";
const LogedinNavbar = () => { const LogedinNavbar = () => {
return ( return (
<div> <div>
<Navbar variant="dark" bg="dark" expand="lg"> <Navbar variant="dark" bg="dark" expand="lg">
<Container> <Container>
<LinkContainer to="/" style={{ color: "#519032" }}> <LinkContainer to="/" style={{ color: "#519032" }}>
<Navbar.Brand>EconoWhatsBot</Navbar.Brand> <Navbar.Brand>EconoWhatsBot</Navbar.Brand>
</LinkContainer> </LinkContainer>
<Navbar.Toggle aria-controls="responsive-navbar-nav" /> <Navbar.Toggle aria-controls="responsive-navbar-nav" />
<Navbar.Collapse id="responsive-navbar-nav"> <Navbar.Collapse id="responsive-navbar-nav">
<Nav className="mr-auto"> <Nav className="mr-auto">
<LinkContainer to="/"> <LinkContainer to="/">
<Nav.Link href="#home">Home</Nav.Link> <Nav.Link href="#home">Home</Nav.Link>
</LinkContainer> </LinkContainer>
<LinkContainer to="/chat"> <LinkContainer to="/chat">
<Nav.Link href="#link">Chat</Nav.Link> <Nav.Link href="#link">Chat</Nav.Link>
</LinkContainer> </LinkContainer>
</Nav> </Nav>
<LinkContainer to="/login"> <LinkContainer to="/login">
<Nav.Link href="#login">Login</Nav.Link> <Nav.Link href="#login">Login</Nav.Link>
</LinkContainer> </LinkContainer>
<LinkContainer to="/signup"> <LinkContainer to="/signup">
<Nav.Link href="#signup">Signup</Nav.Link> <Nav.Link href="#signup">Signup</Nav.Link>
</LinkContainer> </LinkContainer>
</Navbar.Collapse> </Navbar.Collapse>
</Container> </Container>
</Navbar> </Navbar>
</div> </div>
); );
}; };
export default LogedinNavbar; export default LogedinNavbar;

View File

@@ -1,52 +1,52 @@
import React from "react"; import React from "react";
import { useHistory } from "react-router-dom"; import { useHistory } from "react-router-dom";
import { Navbar, Nav, Container } from "react-bootstrap"; import { Navbar, Nav, Container } from "react-bootstrap";
import { LinkContainer } from "react-router-bootstrap"; import { LinkContainer } from "react-router-bootstrap";
import "./Navbar.css"; import "./Navbar.css";
const DefaultNavbar = () => { const DefaultNavbar = () => {
const username = localStorage.getItem("username"); const username = localStorage.getItem("username");
const history = useHistory(); const history = useHistory();
const handleLogout = e => { const handleLogout = e => {
e.preventDefault(); e.preventDefault();
localStorage.removeItem("token"); localStorage.removeItem("token");
localStorage.removeItem("userName"); localStorage.removeItem("userName");
localStorage.removeItem("userId"); localStorage.removeItem("userId");
history.push("/"); history.push("/");
}; };
return ( return (
<div> <div>
<Navbar variant="dark" bg="dark" expand="lg"> <Navbar variant="dark" bg="dark" expand="lg">
<Container> <Container>
<LinkContainer to="/" style={{ color: "#519032" }}> <LinkContainer to="/" style={{ color: "#519032" }}>
<Navbar.Brand>EconoWhatsBot</Navbar.Brand> <Navbar.Brand>EconoWhatsBot</Navbar.Brand>
</LinkContainer> </LinkContainer>
<Navbar.Toggle aria-controls="responsive-navbar-nav" /> <Navbar.Toggle aria-controls="responsive-navbar-nav" />
<Navbar.Collapse id="responsive-navbar-nav"> <Navbar.Collapse id="responsive-navbar-nav">
<Nav className="mr-auto"> <Nav className="mr-auto">
<LinkContainer to="/"> <LinkContainer to="/">
<Nav.Link href="#home">Home</Nav.Link> <Nav.Link href="#home">Home</Nav.Link>
</LinkContainer> </LinkContainer>
<LinkContainer to="/chat"> <LinkContainer to="/chat">
<Nav.Link href="#link">Chat</Nav.Link> <Nav.Link href="#link">Chat</Nav.Link>
</LinkContainer> </LinkContainer>
<LinkContainer to="/chat2"> <LinkContainer to="/chat2">
<Nav.Link href="#link">Chat MaterialUi</Nav.Link> <Nav.Link href="#link">Chat MaterialUi</Nav.Link>
</LinkContainer> </LinkContainer>
</Nav> </Nav>
<Navbar.Text> <Navbar.Text>
Logado como: <a href="#login">{username}</a> Logado como: <a href="#login">{username}</a>
</Navbar.Text> </Navbar.Text>
<Nav.Link href="#logout" onClick={handleLogout}> <Nav.Link href="#logout" onClick={handleLogout}>
Logout Logout
</Nav.Link> </Nav.Link>
</Navbar.Collapse> </Navbar.Collapse>
</Container> </Container>
</Navbar> </Navbar>
</div> </div>
); );
}; };
export default DefaultNavbar; export default DefaultNavbar;

View File

@@ -1,85 +1,85 @@
import React from "react"; import React from "react";
import { useParams } from "react-router-dom"; import { useParams } from "react-router-dom";
import Grid from "@material-ui/core/Grid"; import Grid from "@material-ui/core/Grid";
import Paper from "@material-ui/core/Paper"; import Paper from "@material-ui/core/Paper";
import { makeStyles } from "@material-ui/core/styles"; import { makeStyles } from "@material-ui/core/styles";
import ContactsList from "./components/ContactsList/ContactsList"; import ContactsList from "./components/ContactsList/ContactsList";
import MessagesList from "./components/MessagesList/MessagesList"; import MessagesList from "./components/MessagesList/MessagesList";
import MainDrawer from "../../components/Layout/MainDrawer"; import MainDrawer from "../../components/Layout/MainDrawer";
const useStyles = makeStyles(theme => ({ const useStyles = makeStyles(theme => ({
chatContainer: { chatContainer: {
flex: 1, flex: 1,
backgroundColor: "#eee", backgroundColor: "#eee",
// padding: 20, // padding: 20,
height: `calc(100% - 64px)`, height: `calc(100% - 64px)`,
overflowY: "hidden", overflowY: "hidden",
}, },
chatPapper: { chatPapper: {
backgroundColor: "#eee", backgroundColor: "#eee",
display: "flex", display: "flex",
height: "100%", height: "100%",
overflowY: "hidden", overflowY: "hidden",
}, },
contactsWrapper: { contactsWrapper: {
display: "flex", display: "flex",
height: "100%", height: "100%",
flexDirection: "column", flexDirection: "column",
overflow: "hidden", overflow: "hidden",
}, },
messagessWrapper: { messagessWrapper: {
display: "flex", display: "flex",
height: "100%", height: "100%",
flexDirection: "column", flexDirection: "column",
overflow: "hidden", overflow: "hidden",
}, },
welcomeMsg: { welcomeMsg: {
backgroundColor: "#eee", backgroundColor: "#eee",
display: "flex", display: "flex",
justifyContent: "space-evenly", justifyContent: "space-evenly",
alignItems: "center", alignItems: "center",
height: "100%", height: "100%",
textAlign: "center", textAlign: "center",
}, },
})); }));
const Chat = () => { const Chat = () => {
const classes = useStyles(); const classes = useStyles();
const { contactId } = useParams(); const { contactId } = useParams();
return ( return (
<div> <div>
<MainDrawer appTitle="Chat"> <MainDrawer appTitle="Chat">
<div className={classes.chatContainer}> <div className={classes.chatContainer}>
<Paper square className={classes.chatPapper}> <Paper square className={classes.chatPapper}>
<Grid container spacing={0}> <Grid container spacing={0}>
<Grid item xs={4} className={classes.contactsWrapper}> <Grid item xs={4} className={classes.contactsWrapper}>
<ContactsList /> <ContactsList />
</Grid> </Grid>
<Grid item xs={8} className={classes.messagessWrapper}> <Grid item xs={8} className={classes.messagessWrapper}>
{contactId ? ( {contactId ? (
<> <>
<MessagesList /> <MessagesList />
</> </>
) : ( ) : (
<Paper <Paper
square square
variant="outlined" variant="outlined"
className={classes.welcomeMsg} className={classes.welcomeMsg}
> >
<span>Selecione um contato para começar a conversar</span> <span>Selecione um contato para começar a conversar</span>
</Paper> </Paper>
)} )}
</Grid> </Grid>
</Grid> </Grid>
</Paper> </Paper>
</div> </div>
</MainDrawer> </MainDrawer>
</div> </div>
); );
}; };
export default Chat; export default Chat;

View File

@@ -0,0 +1,72 @@
import React, { useState } from "react";
import Button from "@material-ui/core/Button";
import TextField from "@material-ui/core/TextField";
import Dialog from "@material-ui/core/Dialog";
import DialogActions from "@material-ui/core/DialogActions";
import DialogContent from "@material-ui/core/DialogContent";
import DialogTitle from "@material-ui/core/DialogTitle";
const AddContactModal = ({ modalOpen, setModalOpen, handleAddContact }) => {
const initialState = { name: "", number: "" };
const [contact, setContact] = useState(initialState);
const handleClose = () => {
setModalOpen(false);
};
const handleChangeInput = e => {
setContact({ ...contact, [e.target.name]: e.target.value });
};
return (
<div>
<Dialog
open={modalOpen}
onClose={handleClose}
aria-labelledby="form-dialog-title"
>
<DialogTitle id="form-dialog-title">Adicionar contato</DialogTitle>
<DialogContent>
<TextField
autoComplete="false"
autoFocus
margin="dense"
name="number"
id="contactNumber"
label="Número"
type="text"
value={contact.number}
onChange={handleChangeInput}
fullWidth
/>
<TextField
margin="dense"
name="name"
id="contactName"
label="Nome do contato"
type="text"
value={contact.name}
onChange={handleChangeInput}
fullWidth
/>
</DialogContent>
<DialogActions>
<Button onClick={handleClose} color="primary">
Cancelar
</Button>
<Button
onClick={e => {
handleAddContact(contact);
setContact(initialState);
}}
color="primary"
>
Adicionar
</Button>
</DialogActions>
</Dialog>
</div>
);
};
export default AddContactModal;

View File

@@ -1,47 +1,47 @@
import React from "react"; import React from "react";
import { makeStyles } from "@material-ui/core/styles"; import { makeStyles } from "@material-ui/core/styles";
import Card from "@material-ui/core/Card"; import Card from "@material-ui/core/Card";
import CardHeader from "@material-ui/core/CardHeader"; import CardHeader from "@material-ui/core/CardHeader";
import IconButton from "@material-ui/core/IconButton"; import IconButton from "@material-ui/core/IconButton";
import MoreVertIcon from "@material-ui/icons/MoreVert"; import MoreVertIcon from "@material-ui/icons/MoreVert";
import Avatar from "@material-ui/core/Avatar"; import Avatar from "@material-ui/core/Avatar";
import profileDefaultPic from "../../../../Images/profile_default.png"; import profileDefaultPic from "../../../../Images/profile_default.png";
const useStyles = makeStyles(theme => ({ const useStyles = makeStyles(theme => ({
contactsHeader: { contactsHeader: {
display: "flex", display: "flex",
flex: "none", flex: "none",
// height: 80, // height: 80,
backgroundColor: "#eee", backgroundColor: "#eee",
borderBottomLeftRadius: 0, borderBottomLeftRadius: 0,
borderBottomRightRadius: 0, borderBottomRightRadius: 0,
borderTopRightRadius: 0, borderTopRightRadius: 0,
}, },
settingsIcon: { settingsIcon: {
alignSelf: "center", alignSelf: "center",
marginLeft: "auto", marginLeft: "auto",
padding: 8, padding: 8,
}, },
})); }));
const ContactsHeader = () => { const ContactsHeader = () => {
const classes = useStyles(); const classes = useStyles();
const username = localStorage.getItem("username"); const username = localStorage.getItem("username");
return ( return (
<Card variant="outlined" square className={classes.contactsHeader}> <Card variant="outlined" square className={classes.contactsHeader}>
<CardHeader <CardHeader
avatar={<Avatar alt="logged_user" src={profileDefaultPic} />} avatar={<Avatar alt="logged_user" src={profileDefaultPic} />}
title={username} title={username}
/> />
<IconButton className={classes.settingsIcon} aria-label="settings"> <IconButton className={classes.settingsIcon} aria-label="settings">
<MoreVertIcon /> <MoreVertIcon />
</IconButton> </IconButton>
</Card> </Card>
); );
}; };
export default ContactsHeader; export default ContactsHeader;

View File

@@ -1,333 +1,381 @@
import React, { useState, useEffect } from "react"; import React, { useState, useEffect } from "react";
import { useHistory, useParams } from "react-router-dom"; import { useHistory, useParams } from "react-router-dom";
import api from "../../../../util/api"; import api from "../../../../util/api";
import openSocket from "socket.io-client"; import openSocket from "socket.io-client";
import moment from "moment-timezone"; import moment from "moment-timezone";
import profileDefaultPic from "../../../../Images/profile_default.png"; import profileDefaultPic from "../../../../Images/profile_default.png";
import { makeStyles } from "@material-ui/core/styles"; import { makeStyles } from "@material-ui/core/styles";
import { green } from "@material-ui/core/colors"; import { green } from "@material-ui/core/colors";
import CircularProgress from "@material-ui/core/CircularProgress"; import CircularProgress from "@material-ui/core/CircularProgress";
import Paper from "@material-ui/core/Paper"; import Paper from "@material-ui/core/Paper";
import List from "@material-ui/core/List"; import List from "@material-ui/core/List";
import ListItem from "@material-ui/core/ListItem"; import ListItem from "@material-ui/core/ListItem";
import ListItemText from "@material-ui/core/ListItemText"; import ListItemText from "@material-ui/core/ListItemText";
import ListItemAvatar from "@material-ui/core/ListItemAvatar"; import ListItemAvatar from "@material-ui/core/ListItemAvatar";
import Typography from "@material-ui/core/Typography"; import Typography from "@material-ui/core/Typography";
import Avatar from "@material-ui/core/Avatar"; import Avatar from "@material-ui/core/Avatar";
import Divider from "@material-ui/core/Divider"; import AddIcon from "@material-ui/icons/Add";
import Badge from "@material-ui/core/Badge"; import Divider from "@material-ui/core/Divider";
import SearchIcon from "@material-ui/icons/Search"; import Badge from "@material-ui/core/Badge";
import InputBase from "@material-ui/core/InputBase"; import SearchIcon from "@material-ui/icons/Search";
import InputBase from "@material-ui/core/InputBase";
import ContactsHeader from "../ContactsHeader/ContactsHeader"; import Fab from "@material-ui/core/Fab";
import AddContactModal from "../AddContact/AddContactModal";
const useStyles = makeStyles(theme => ({
contactsWrapper: { import ContactsHeader from "../ContactsHeader/ContactsHeader";
display: "flex",
height: "100%", const useStyles = makeStyles(theme => ({
flexDirection: "column", contactsWrapper: {
overflow: "hidden", display: "flex",
}, height: "100%",
flexDirection: "column",
contactsHeader: { overflow: "hidden",
display: "flex", },
backgroundColor: "#eee",
borderBottomLeftRadius: 0, contactsHeader: {
borderBottomRightRadius: 0, display: "flex",
borderTopRightRadius: 0, backgroundColor: "#eee",
}, borderBottomLeftRadius: 0,
borderBottomRightRadius: 0,
settingsIcon: { borderTopRightRadius: 0,
alignSelf: "center", },
marginLeft: "auto",
padding: 8, settingsIcon: {
}, alignSelf: "center",
marginLeft: "auto",
contactsList: { padding: 8,
position: "relative", },
borderTopLeftRadius: 0,
borderTopRightRadius: 0, contactsList: {
borderBottomRightRadius: 0, position: "relative",
flexGrow: 1, borderTopLeftRadius: 0,
overflowY: "scroll", borderTopRightRadius: 0,
"&::-webkit-scrollbar": { borderBottomRightRadius: 0,
width: "8px", flexGrow: 1,
}, overflowY: "scroll",
"&::-webkit-scrollbar-thumb": { "&::-webkit-scrollbar": {
boxShadow: "inset 0 0 6px rgba(0, 0, 0, 0.3)", width: "8px",
backgroundColor: "#e8e8e8", },
}, "&::-webkit-scrollbar-thumb": {
}, boxShadow: "inset 0 0 6px rgba(0, 0, 0, 0.3)",
contactsSearchBox: { backgroundColor: "#e8e8e8",
position: "relative", },
background: "#fafafa", },
padding: "10px 13px", contactsSearchBox: {
}, position: "relative",
background: "#fafafa",
serachInputWrapper: { padding: "10px 13px",
background: "#fff", },
display: "flex",
borderRadius: 40, serachInputWrapper: {
padding: 4, background: "#fff",
}, display: "flex",
borderRadius: 40,
searchIcon: { padding: 4,
color: "grey", },
marginLeft: 6,
marginRight: 6, searchIcon: {
alignSelf: "center", color: "grey",
}, marginLeft: 6,
marginRight: 6,
contactsSearchInput: { alignSelf: "center",
flex: 1, },
border: "none",
borderRadius: 30, contactsSearchInput: {
}, flex: 1,
border: "none",
contactNameWrapper: { borderRadius: 30,
display: "flex", },
// display: "inline",
}, contactNameWrapper: {
display: "flex",
lastMessageTime: { // display: "inline",
marginLeft: "auto", },
},
lastMessageTime: {
contactLastMessage: { marginLeft: "auto",
paddingRight: 20, },
},
contactLastMessage: {
newMessagesCount: { paddingRight: 20,
alignSelf: "center", },
marginRight: 8,
marginLeft: "auto", newMessagesCount: {
}, alignSelf: "center",
marginRight: 8,
badgeStyle: { marginLeft: "auto",
color: "white", },
backgroundColor: green[500],
}, badgeStyle: {
circleLoading: { color: "white",
color: green[500], backgroundColor: green[500],
opacity: "70%", },
position: "absolute", circleLoading: {
top: 0, color: green[500],
left: "50%", opacity: "70%",
marginTop: 12, position: "absolute",
// marginLeft: -12, top: 0,
}, left: "50%",
})); marginTop: 12,
// marginLeft: -12,
const ContactsList = () => { },
const classes = useStyles(); fabButton: {
const token = localStorage.getItem("token"); position: "absolute",
const { contactId } = useParams(); zIndex: 1,
const [contacts, setContacts] = useState([]); bottom: 20,
const [loading, setLoading] = useState(); left: 0,
const [searchParam, setSearchParam] = useState(""); right: 0,
margin: "0 auto",
const history = useHistory(); },
}));
useEffect(() => {
if (!("Notification" in window)) { const ContactsList = () => {
console.log("Esse navegador não suporte notificações"); const classes = useStyles();
} else { const token = localStorage.getItem("token");
Notification.requestPermission(); const { contactId } = useParams();
} const [contacts, setContacts] = useState([]);
}, []); const [loading, setLoading] = useState();
const [searchParam, setSearchParam] = useState("");
useEffect(() => {
setLoading(true); const [modalOpen, setModalOpen] = useState(false);
const delayDebounceFn = setTimeout(() => {
const fetchContacts = async () => { const history = useHistory();
try {
const res = await api.get("/contacts", { useEffect(() => {
params: { searchParam }, if (!("Notification" in window)) {
}); console.log("Esse navegador não suporte notificações");
setContacts(res.data); } else {
setLoading(false); Notification.requestPermission();
} catch (err) { }
console.log(err); }, []);
}
}; useEffect(() => {
fetchContacts(); setLoading(true);
}, 1000); const delayDebounceFn = setTimeout(() => {
return () => clearTimeout(delayDebounceFn); const fetchContacts = async () => {
}, [searchParam, token]); try {
const res = await api.get("/contacts", {
useEffect(() => { params: { searchParam },
const socket = openSocket(process.env.REACT_APP_BACKEND_URL); });
setContacts(res.data);
socket.emit("joinNotification"); setLoading(false);
} catch (err) {
socket.on("contact", data => { console.log(err);
if (data.action === "updateUnread") { }
resetUnreadMessages(data.contactId); };
} fetchContacts();
}); }, 1000);
return () => clearTimeout(delayDebounceFn);
socket.on("appMessage", data => { }, [searchParam, token]);
if (data.action === "create") {
updateUnreadMessagesCount(data); useEffect(() => {
if ( const socket = openSocket(process.env.REACT_APP_BACKEND_URL);
contactId &&
data.message.contactId === +contactId && socket.emit("joinNotification");
document.visibilityState === "visible"
) socket.on("contact", data => {
return; if (data.action === "updateUnread") {
showDesktopNotification(data); resetUnreadMessages(data.contactId);
} }
}); });
return () => { socket.on("appMessage", data => {
socket.disconnect(); if (data.action === "create") {
}; updateUnreadMessagesCount(data);
}, [contactId]); if (
contactId &&
const updateUnreadMessagesCount = data => { data.message.contactId === +contactId &&
setContacts(prevState => { document.visibilityState === "visible"
const contactIndex = prevState.findIndex( )
contact => contact.id === data.message.contactId return;
); showDesktopNotification(data);
}
if (contactIndex !== -1) { });
let aux = [...prevState];
aux[contactIndex].unreadMessages++; return () => {
aux[contactIndex].lastMessage = data.message.messageBody; socket.disconnect();
aux.unshift(aux.splice(contactIndex, 1)[0]); };
return aux; }, [contactId]);
} else {
return [data.contact, ...prevState]; const updateUnreadMessagesCount = data => {
} setContacts(prevState => {
}); const contactIndex = prevState.findIndex(
}; contact => contact.id === data.message.contactId
);
const showDesktopNotification = data => {
const options = { if (contactIndex !== -1) {
body: `${data.message.messageBody} - ${moment(new Date()) let aux = [...prevState];
.tz("America/Sao_Paulo") aux[contactIndex].unreadMessages++;
.format("DD/MM/YY - HH:mm")}`, aux[contactIndex].lastMessage = data.message.messageBody;
icon: data.contact.imageURL, aux.unshift(aux.splice(contactIndex, 1)[0]);
}; return aux;
new Notification(`Mensagem de ${data.contact.name}`, options); } else {
document.getElementById("sound").play(); return [data.contact, ...prevState];
}; }
});
const resetUnreadMessages = contactId => { };
setContacts(prevState => {
let aux = [...prevState]; const showDesktopNotification = data => {
let contactIndex = aux.findIndex(contact => contact.id === +contactId); const options = {
aux[contactIndex].unreadMessages = 0; body: `${data.message.messageBody} - ${moment(new Date())
.tz("America/Sao_Paulo")
return aux; .format("DD/MM/YY - HH:mm")}`,
}); icon: data.contact.profilePicUrl,
}; };
new Notification(`Mensagem de ${data.contact.name}`, options);
const handleSelectContact = (e, contact) => { document.getElementById("sound").play();
history.push(`/chat/${contact.id}`); };
};
const resetUnreadMessages = contactId => {
const handleSearchContact = e => { setContacts(prevState => {
// let searchTerm = e.target.value.toLowerCase(); let aux = [...prevState];
setSearchParam(e.target.value.toLowerCase()); let contactIndex = aux.findIndex(contact => contact.id === +contactId);
}; aux[contactIndex].unreadMessages = 0;
return ( return aux;
<div className={classes.contactsWrapper}> });
<ContactsHeader /> };
<Paper variant="outlined" square className={classes.contactsSearchBox}>
<div className={classes.serachInputWrapper}> const handleSelectContact = (e, contact) => {
<SearchIcon className={classes.searchIcon} /> history.push(`/chat/${contact.id}`);
<InputBase };
className={classes.contactsSearchInput}
placeholder="Buscar contatos" const handleSearchContact = e => {
type="search" // let searchTerm = e.target.value.toLowerCase();
onChange={handleSearchContact} setSearchParam(e.target.value.toLowerCase());
/> };
</div>
</Paper> const handleShowContactModal = e => {
<Paper variant="outlined" className={classes.contactsList}> setModalOpen(true);
<List> };
{contacts.map((contact, index) => (
<React.Fragment key={contact.id}> const handleAddContact = async contact => {
<ListItem try {
button const res = await api.post("/contacts", contact);
onClick={e => handleSelectContact(e, contact)} setContacts(prevState => [res.data, ...prevState]);
selected={contactId && +contactId === contact.id} setModalOpen(false);
> console.log(res.data);
<ListItemAvatar> } catch (err) {
<Avatar if (err.response.status === 422) {
src={ console.log("deu erro", err.response);
contact.imageURL ? contact.imageURL : profileDefaultPic alert(err.response.data.message);
} } else {
></Avatar> alert(err);
</ListItemAvatar> }
<ListItemText }
primary={ };
<span className={classes.contactNameWrapper}>
<Typography return (
noWrap <div className={classes.contactsWrapper}>
component="span" <ContactsHeader />
variant="body2" <AddContactModal
color="textPrimary" setModalOpen={setModalOpen}
> modalOpen={modalOpen}
{contact.name} handleAddContact={handleAddContact}
</Typography> />
{contact.lastMessage && ( <Paper variant="outlined" square className={classes.contactsSearchBox}>
<Typography <div className={classes.serachInputWrapper}>
className={classes.lastMessageTime} <SearchIcon className={classes.searchIcon} />
component="span" <InputBase
variant="body2" className={classes.contactsSearchInput}
color="textSecondary" placeholder="Buscar contatos"
> type="search"
{moment(contact.updatedAt) onChange={handleSearchContact}
.tz("America/Sao_Paulo") />
.format("HH:mm")} </div>
</Typography> </Paper>
)} <Paper variant="outlined" className={classes.contactsList}>
</span> <List>
} {contacts.map((contact, index) => (
secondary={ <React.Fragment key={contact.id}>
<span className={classes.contactNameWrapper}> <ListItem
<Typography button
className={classes.contactLastMessage} onClick={e => handleSelectContact(e, contact)}
noWrap selected={contactId && +contactId === contact.id}
component="span" >
variant="body2" <ListItemAvatar>
color="textSecondary" <Avatar
> src={
{contact.lastMessage || <br />} contact.profilePicUrl
</Typography> ? contact.profilePicUrl
<Badge : profileDefaultPic
className={classes.newMessagesCount} }
badgeContent={contact.unreadMessages} ></Avatar>
classes={{ </ListItemAvatar>
badge: classes.badgeStyle, <ListItemText
}} primary={
/> <span className={classes.contactNameWrapper}>
</span> <Typography
} noWrap
/> component="span"
</ListItem> variant="body2"
<Divider variant="inset" component="li" /> color="textPrimary"
</React.Fragment> >
))} {contact.name}
</List> </Typography>
{loading ? ( {contact.lastMessage && (
<div> <Typography
<CircularProgress className={classes.circleLoading} /> className={classes.lastMessageTime}
</div> component="span"
) : null} variant="body2"
</Paper> color="textSecondary"
<audio id="sound" preload="auto"> >
<source src={require("../../../../util/sound.mp3")} type="audio/mpeg" /> {moment(contact.updatedAt)
<source src={require("../../../../util/sound.ogg")} type="audio/ogg" /> .tz("America/Sao_Paulo")
<embed hidden={true} autostart="false" loop={false} src="./sound.mp3" /> .format("HH:mm")}
</audio> </Typography>
</div> )}
); </span>
}; }
secondary={
export default ContactsList; <span className={classes.contactNameWrapper}>
<Typography
className={classes.contactLastMessage}
noWrap
component="span"
variant="body2"
color="textSecondary"
>
{contact.lastMessage || <br />}
</Typography>
<Badge
className={classes.newMessagesCount}
badgeContent={contact.unreadMessages}
classes={{
badge: classes.badgeStyle,
}}
/>
</span>
}
/>
</ListItem>
<Divider variant="inset" component="li" />
</React.Fragment>
))}
</List>
{loading ? (
<div>
<CircularProgress className={classes.circleLoading} />
</div>
) : null}
<Fab
color="secondary"
aria-label="add"
className={classes.fabButton}
onClick={handleShowContactModal}
>
<AddIcon />
</Fab>
</Paper>
<audio id="sound" preload="auto">
<source src={require("../../../../util/sound.mp3")} type="audio/mpeg" />
<source src={require("../../../../util/sound.ogg")} type="audio/ogg" />
<embed hidden={true} autostart="false" loop={false} src="./sound.mp3" />
</audio>
</div>
);
};
export default ContactsList;

View File

@@ -1,254 +1,254 @@
import React, { useState, useEffect } from "react"; import React, { useState, useEffect } from "react";
import { useParams } from "react-router-dom"; import { useParams } from "react-router-dom";
import api from "../../../../util/api"; import api from "../../../../util/api";
import "emoji-mart/css/emoji-mart.css"; import "emoji-mart/css/emoji-mart.css";
import { Picker } from "emoji-mart"; import { Picker } from "emoji-mart";
import { makeStyles } from "@material-ui/core/styles"; import { makeStyles } from "@material-ui/core/styles";
import Paper from "@material-ui/core/Paper"; import Paper from "@material-ui/core/Paper";
import InputBase from "@material-ui/core/InputBase"; import InputBase from "@material-ui/core/InputBase";
import CircularProgress from "@material-ui/core/CircularProgress"; import CircularProgress from "@material-ui/core/CircularProgress";
import { green } from "@material-ui/core/colors"; import { green } from "@material-ui/core/colors";
import AttachFileIcon from "@material-ui/icons/AttachFile"; import AttachFileIcon from "@material-ui/icons/AttachFile";
import IconButton from "@material-ui/core/IconButton"; import IconButton from "@material-ui/core/IconButton";
import MoodIcon from "@material-ui/icons/Mood"; import MoodIcon from "@material-ui/icons/Mood";
import SendIcon from "@material-ui/icons/Send"; import SendIcon from "@material-ui/icons/Send";
import CancelIcon from "@material-ui/icons/Cancel"; import CancelIcon from "@material-ui/icons/Cancel";
const useStyles = makeStyles(theme => ({ const useStyles = makeStyles(theme => ({
newMessageBox: { newMessageBox: {
background: "#eee", background: "#eee",
display: "flex", display: "flex",
padding: "10px", padding: "10px",
alignItems: "center", alignItems: "center",
}, },
messageInputWrapper: { messageInputWrapper: {
padding: 6, padding: 6,
background: "#fff", background: "#fff",
display: "flex", display: "flex",
borderRadius: 40, borderRadius: 40,
flex: 1, flex: 1,
}, },
messageInput: { messageInput: {
paddingLeft: 10, paddingLeft: 10,
flex: 1, flex: 1,
border: "none", border: "none",
}, },
sendMessageIcons: { sendMessageIcons: {
color: "grey", color: "grey",
}, },
uploadInput: { uploadInput: {
display: "none", display: "none",
}, },
viewMediaInputWrapper: { viewMediaInputWrapper: {
display: "flex", display: "flex",
padding: "10px 13px", padding: "10px 13px",
position: "relative", position: "relative",
justifyContent: "space-between", justifyContent: "space-between",
alignItems: "center", alignItems: "center",
backgroundColor: "#eee", backgroundColor: "#eee",
}, },
emojiBox: { emojiBox: {
position: "absolute", position: "absolute",
bottom: 63, bottom: 63,
width: 40, width: 40,
borderTop: "1px solid #e8e8e8", borderTop: "1px solid #e8e8e8",
}, },
circleLoading: { circleLoading: {
color: green[500], color: green[500],
opacity: "70%", opacity: "70%",
position: "absolute", position: "absolute",
top: "20%", top: "20%",
left: "50%", left: "50%",
// marginTop: 8, // marginTop: 8,
// marginBottom: 6, // marginBottom: 6,
marginLeft: -12, marginLeft: -12,
}, },
})); }));
const MessagesInput = ({ searchParam }) => { const MessagesInput = ({ searchParam }) => {
const classes = useStyles(); const classes = useStyles();
const { contactId } = useParams(); const { contactId } = useParams();
const userId = localStorage.getItem("userId"); const userId = localStorage.getItem("userId");
const username = localStorage.getItem("username"); const username = localStorage.getItem("username");
const mediaInitialState = { preview: "", raw: "", name: "" }; const mediaInitialState = { preview: "", raw: "", name: "" };
const [media, setMedia] = useState(mediaInitialState); const [media, setMedia] = useState(mediaInitialState);
const [inputMessage, setInputMessage] = useState(""); const [inputMessage, setInputMessage] = useState("");
const [showEmoji, setShowEmoji] = useState(false); const [showEmoji, setShowEmoji] = useState(false);
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
useEffect(() => { useEffect(() => {
return () => { return () => {
setInputMessage(""); setInputMessage("");
setShowEmoji(false); setShowEmoji(false);
setMedia({}); setMedia({});
}; };
}, [contactId]); }, [contactId]);
const handleChangeInput = e => { const handleChangeInput = e => {
setInputMessage(e.target.value); setInputMessage(e.target.value);
}; };
const handleAddEmoji = e => { const handleAddEmoji = e => {
let emoji = e.native; let emoji = e.native;
setInputMessage(prevState => prevState + emoji); setInputMessage(prevState => prevState + emoji);
}; };
const handleChangeMedia = e => { const handleChangeMedia = e => {
if (e.target.files.length) { if (e.target.files.length) {
setMedia({ setMedia({
preview: URL.createObjectURL(e.target.files[0]), preview: URL.createObjectURL(e.target.files[0]),
raw: e.target.files[0], raw: e.target.files[0],
name: e.target.files[0].name, name: e.target.files[0].name,
}); });
} }
}; };
const handleInputPaste = e => { const handleInputPaste = e => {
if (e.clipboardData.files[0]) { if (e.clipboardData.files[0]) {
setMedia({ setMedia({
preview: URL.createObjectURL(e.clipboardData.files[0]), preview: URL.createObjectURL(e.clipboardData.files[0]),
raw: e.clipboardData.files[0], raw: e.clipboardData.files[0],
name: e.clipboardData.files[0].name, name: e.clipboardData.files[0].name,
}); });
} }
}; };
const handleUploadMedia = async e => { const handleUploadMedia = async e => {
setLoading(true); setLoading(true);
e.preventDefault(); e.preventDefault();
const formData = new FormData(); const formData = new FormData();
formData.append("media", media.raw); formData.append("media", media.raw);
formData.append("userId", userId); formData.append("userId", userId);
formData.append("messageBody", media.name); formData.append("messageBody", media.name);
try { try {
await api.post(`/messages/${contactId}`, formData); await api.post(`/messages/${contactId}`, formData);
} catch (err) { } catch (err) {
console.log(err); console.log(err);
alert(err); alert(err);
} }
setLoading(false); setLoading(false);
setMedia(mediaInitialState); setMedia(mediaInitialState);
}; };
const handleSendMessage = async () => { const handleSendMessage = async () => {
if (inputMessage.trim() === "") return; if (inputMessage.trim() === "") return;
const message = { const message = {
read: 1, read: 1,
userId: userId, userId: userId,
mediaUrl: "", mediaUrl: "",
messageBody: `${username}: ${inputMessage.trim()}`, messageBody: `${username}: ${inputMessage.trim()}`,
}; };
try { try {
await api.post(`/messages/${contactId}`, message); await api.post(`/messages/${contactId}`, message);
} catch (err) { } catch (err) {
alert(err); alert(err);
} }
setInputMessage(""); setInputMessage("");
setShowEmoji(false); setShowEmoji(false);
}; };
if (media.preview) if (media.preview)
return ( return (
<Paper <Paper
variant="outlined" variant="outlined"
square square
className={classes.viewMediaInputWrapper} className={classes.viewMediaInputWrapper}
> >
<IconButton <IconButton
aria-label="cancel-upload" aria-label="cancel-upload"
component="span" component="span"
onClick={e => setMedia(mediaInitialState)} onClick={e => setMedia(mediaInitialState)}
> >
<CancelIcon className={classes.sendMessageIcons} /> <CancelIcon className={classes.sendMessageIcons} />
</IconButton> </IconButton>
{loading ? ( {loading ? (
<div> <div>
<CircularProgress className={classes.circleLoading} /> <CircularProgress className={classes.circleLoading} />
</div> </div>
) : ( ) : (
<span> <span>
{media.name} {media.name}
{/* <img src={media.preview} alt=""></img> */} {/* <img src={media.preview} alt=""></img> */}
</span> </span>
)} )}
<IconButton <IconButton
aria-label="send-upload" aria-label="send-upload"
component="span" component="span"
onClick={handleUploadMedia} onClick={handleUploadMedia}
> >
<SendIcon className={classes.sendMessageIcons} /> <SendIcon className={classes.sendMessageIcons} />
</IconButton> </IconButton>
</Paper> </Paper>
); );
else { else {
return ( return (
<Paper variant="outlined" square className={classes.newMessageBox}> <Paper variant="outlined" square className={classes.newMessageBox}>
<IconButton <IconButton
aria-label="emojiPicker" aria-label="emojiPicker"
component="span" component="span"
onClick={e => setShowEmoji(prevState => !prevState)} onClick={e => setShowEmoji(prevState => !prevState)}
> >
<MoodIcon className={classes.sendMessageIcons} /> <MoodIcon className={classes.sendMessageIcons} />
</IconButton> </IconButton>
{showEmoji ? ( {showEmoji ? (
<div className={classes.emojiBox}> <div className={classes.emojiBox}>
<Picker <Picker
perLine={16} perLine={16}
showPreview={false} showPreview={false}
showSkinTones={false} showSkinTones={false}
onSelect={handleAddEmoji} onSelect={handleAddEmoji}
/> />
</div> </div>
) : null} ) : null}
<input <input
type="file" type="file"
id="upload-button" id="upload-button"
className={classes.uploadInput} className={classes.uploadInput}
onChange={handleChangeMedia} onChange={handleChangeMedia}
/> />
<label htmlFor="upload-button"> <label htmlFor="upload-button">
<IconButton aria-label="upload" component="span"> <IconButton aria-label="upload" component="span">
<AttachFileIcon className={classes.sendMessageIcons} /> <AttachFileIcon className={classes.sendMessageIcons} />
</IconButton> </IconButton>
</label> </label>
<div className={classes.messageInputWrapper}> <div className={classes.messageInputWrapper}>
<InputBase <InputBase
inputRef={input => input && !searchParam && input.focus()} inputRef={input => input && !searchParam && input.focus()}
className={classes.messageInput} className={classes.messageInput}
placeholder="Escreva uma mensagem" placeholder="Escreva uma mensagem"
value={inputMessage} value={inputMessage}
onChange={handleChangeInput} onChange={handleChangeInput}
onPaste={handleInputPaste} onPaste={handleInputPaste}
onKeyPress={e => { onKeyPress={e => {
if (e.key === "Enter") { if (e.key === "Enter") {
handleSendMessage(); handleSendMessage();
} }
}} }}
/> />
</div> </div>
<IconButton <IconButton
aria-label="emojiPicker" aria-label="emojiPicker"
component="span" component="span"
onClick={handleSendMessage} onClick={handleSendMessage}
> >
<SendIcon className={classes.sendMessageIcons} /> <SendIcon className={classes.sendMessageIcons} />
</IconButton> </IconButton>
</Paper> </Paper>
); );
} }
}; };
export default MessagesInput; export default MessagesInput;

File diff suppressed because it is too large Load Diff

View File

@@ -1,14 +1,14 @@
import React from "react"; import React from "react";
import MainDrawer from "../../components/Layout/MainDrawer"; import MainDrawer from "../../components/Layout/MainDrawer";
const Dashboard = () => { const Dashboard = () => {
return ( return (
<div> <div>
<MainDrawer appTitle="Dashboard"> <MainDrawer appTitle="Dashboard">
<h1>Todo Dashboard</h1> <h1>Todo Dashboard</h1>
</MainDrawer> </MainDrawer>
</div> </div>
); );
}; };
export default Dashboard; export default Dashboard;

View File

@@ -1,144 +1,144 @@
import React, { useState, useContext } from "react"; import React, { useState, useContext } from "react";
import { Link as RouterLink } from "react-router-dom"; import { Link as RouterLink } from "react-router-dom";
import Avatar from "@material-ui/core/Avatar"; import Avatar from "@material-ui/core/Avatar";
import Button from "@material-ui/core/Button"; import Button from "@material-ui/core/Button";
import CssBaseline from "@material-ui/core/CssBaseline"; import CssBaseline from "@material-ui/core/CssBaseline";
import TextField from "@material-ui/core/TextField"; import TextField from "@material-ui/core/TextField";
// import FormControlLabel from "@material-ui/core/FormControlLabel"; // import FormControlLabel from "@material-ui/core/FormControlLabel";
// import Checkbox from "@material-ui/core/Checkbox"; // import Checkbox from "@material-ui/core/Checkbox";
import Link from "@material-ui/core/Link"; import Link from "@material-ui/core/Link";
import Grid from "@material-ui/core/Grid"; import Grid from "@material-ui/core/Grid";
import Box from "@material-ui/core/Box"; import Box from "@material-ui/core/Box";
import LockOutlinedIcon from "@material-ui/icons/LockOutlined"; import LockOutlinedIcon from "@material-ui/icons/LockOutlined";
import Typography from "@material-ui/core/Typography"; import Typography from "@material-ui/core/Typography";
import { makeStyles } from "@material-ui/core/styles"; import { makeStyles } from "@material-ui/core/styles";
import Container from "@material-ui/core/Container"; import Container from "@material-ui/core/Container";
import { AuthContext } from "../../Context/Auth/AuthContext"; import { AuthContext } from "../../Context/Auth/AuthContext";
const Copyright = () => { const Copyright = () => {
return ( return (
<Typography variant="body2" color="textSecondary" align="center"> <Typography variant="body2" color="textSecondary" align="center">
{"Copyright © "} {"Copyright © "}
<Link color="inherit" href="https://material-ui.com/"> <Link color="inherit" href="https://material-ui.com/">
Canove Canove
</Link>{" "} </Link>{" "}
{new Date().getFullYear()} {new Date().getFullYear()}
{"."} {"."}
</Typography> </Typography>
); );
}; };
const useStyles = makeStyles(theme => ({ const useStyles = makeStyles(theme => ({
paper: { paper: {
marginTop: theme.spacing(8), marginTop: theme.spacing(8),
display: "flex", display: "flex",
flexDirection: "column", flexDirection: "column",
alignItems: "center", alignItems: "center",
}, },
avatar: { avatar: {
margin: theme.spacing(1), margin: theme.spacing(1),
backgroundColor: theme.palette.secondary.main, backgroundColor: theme.palette.secondary.main,
}, },
form: { form: {
width: "100%", // Fix IE 11 issue. width: "100%", // Fix IE 11 issue.
marginTop: theme.spacing(1), marginTop: theme.spacing(1),
}, },
submit: { submit: {
margin: theme.spacing(3, 0, 2), margin: theme.spacing(3, 0, 2),
}, },
})); }));
const Login = ({ showToast }) => { const Login = ({ showToast }) => {
const classes = useStyles(); const classes = useStyles();
const [user, setUser] = useState({ email: "", password: "" }); const [user, setUser] = useState({ email: "", password: "" });
const { handleLogin } = useContext(AuthContext); const { handleLogin } = useContext(AuthContext);
const handleChangeInput = e => { const handleChangeInput = e => {
setUser({ ...user, [e.target.name]: e.target.value }); setUser({ ...user, [e.target.name]: e.target.value });
}; };
return ( return (
<Container component="main" maxWidth="xs"> <Container component="main" maxWidth="xs">
<CssBaseline /> <CssBaseline />
<div className={classes.paper}> <div className={classes.paper}>
<Avatar className={classes.avatar}> <Avatar className={classes.avatar}>
<LockOutlinedIcon /> <LockOutlinedIcon />
</Avatar> </Avatar>
<Typography component="h1" variant="h5"> <Typography component="h1" variant="h5">
Login Login
</Typography> </Typography>
<form <form
className={classes.form} className={classes.form}
noValidate noValidate
onSubmit={e => handleLogin(e, user)} onSubmit={e => handleLogin(e, user)}
> >
<TextField <TextField
variant="outlined" variant="outlined"
margin="normal" margin="normal"
required required
fullWidth fullWidth
id="email" id="email"
label="Email" label="Email"
name="email" name="email"
value={user.email} value={user.email}
onChange={handleChangeInput} onChange={handleChangeInput}
autoComplete="email" autoComplete="email"
autoFocus autoFocus
/> />
<TextField <TextField
variant="outlined" variant="outlined"
margin="normal" margin="normal"
required required
fullWidth fullWidth
name="password" name="password"
label="Senha" label="Senha"
type="password" type="password"
id="password" id="password"
value={user.password} value={user.password}
onChange={handleChangeInput} onChange={handleChangeInput}
autoComplete="current-password" autoComplete="current-password"
/> />
{/* <FormControlLabel {/* <FormControlLabel
control={<Checkbox value="remember" color="primary" />} control={<Checkbox value="remember" color="primary" />}
label="Lembrar" label="Lembrar"
/> */} /> */}
<Button <Button
type="submit" type="submit"
fullWidth fullWidth
variant="contained" variant="contained"
color="primary" color="primary"
className={classes.submit} className={classes.submit}
> >
Entrar Entrar
</Button> </Button>
<Grid container> <Grid container>
{/* <Grid item xs> {/* <Grid item xs>
<Link href="#" variant="body2"> <Link href="#" variant="body2">
Forgot password? Forgot password?
</Link> </Link>
</Grid> */} </Grid> */}
<Grid item> <Grid item>
<Link <Link
href="#" href="#"
variant="body2" variant="body2"
component={RouterLink} component={RouterLink}
to="/signup" to="/signup"
> >
{"Não tem uma conta? Cadastre-se!"} {"Não tem uma conta? Cadastre-se!"}
</Link> </Link>
</Grid> </Grid>
</Grid> </Grid>
</form> </form>
</div> </div>
<Box mt={8}> <Box mt={8}>
<Copyright /> <Copyright />
</Box> </Box>
</Container> </Container>
); );
}; };
export default Login; export default Login;

View File

@@ -1,154 +1,154 @@
import React, { useState } from "react"; import React, { useState } from "react";
import { useHistory } from "react-router-dom"; import { useHistory } from "react-router-dom";
import api from "../../util/api"; import api from "../../util/api";
import { Link as RouterLink } from "react-router-dom"; import { Link as RouterLink } from "react-router-dom";
import Avatar from "@material-ui/core/Avatar"; import Avatar from "@material-ui/core/Avatar";
import Button from "@material-ui/core/Button"; import Button from "@material-ui/core/Button";
import CssBaseline from "@material-ui/core/CssBaseline"; import CssBaseline from "@material-ui/core/CssBaseline";
import TextField from "@material-ui/core/TextField"; import TextField from "@material-ui/core/TextField";
// import FormControlLabel from "@material-ui/core/FormControlLabel"; // import FormControlLabel from "@material-ui/core/FormControlLabel";
// import Checkbox from "@material-ui/core/Checkbox"; // import Checkbox from "@material-ui/core/Checkbox";
import Link from "@material-ui/core/Link"; import Link from "@material-ui/core/Link";
import Grid from "@material-ui/core/Grid"; import Grid from "@material-ui/core/Grid";
import Box from "@material-ui/core/Box"; import Box from "@material-ui/core/Box";
import LockOutlinedIcon from "@material-ui/icons/LockOutlined"; import LockOutlinedIcon from "@material-ui/icons/LockOutlined";
import Typography from "@material-ui/core/Typography"; import Typography from "@material-ui/core/Typography";
import { makeStyles } from "@material-ui/core/styles"; import { makeStyles } from "@material-ui/core/styles";
import Container from "@material-ui/core/Container"; import Container from "@material-ui/core/Container";
function Copyright() { function Copyright() {
return ( return (
<Typography variant="body2" color="textSecondary" align="center"> <Typography variant="body2" color="textSecondary" align="center">
{"Copyright © "} {"Copyright © "}
<Link color="inherit" href="https://material-ui.com/"> <Link color="inherit" href="https://material-ui.com/">
Canove Canove
</Link>{" "} </Link>{" "}
{new Date().getFullYear()} {new Date().getFullYear()}
{"."} {"."}
</Typography> </Typography>
); );
} }
const useStyles = makeStyles(theme => ({ const useStyles = makeStyles(theme => ({
paper: { paper: {
marginTop: theme.spacing(8), marginTop: theme.spacing(8),
display: "flex", display: "flex",
flexDirection: "column", flexDirection: "column",
alignItems: "center", alignItems: "center",
}, },
avatar: { avatar: {
margin: theme.spacing(1), margin: theme.spacing(1),
backgroundColor: theme.palette.secondary.main, backgroundColor: theme.palette.secondary.main,
}, },
form: { form: {
width: "100%", // Fix IE 11 issue. width: "100%", // Fix IE 11 issue.
marginTop: theme.spacing(3), marginTop: theme.spacing(3),
}, },
submit: { submit: {
margin: theme.spacing(3, 0, 2), margin: theme.spacing(3, 0, 2),
}, },
})); }));
const SignUp = () => { const SignUp = () => {
const classes = useStyles(); const classes = useStyles();
const history = useHistory(); const history = useHistory();
const [user, setUser] = useState({ name: "", email: "", password: "" }); const [user, setUser] = useState({ name: "", email: "", password: "" });
const handleChangeInput = e => { const handleChangeInput = e => {
setUser({ ...user, [e.target.name]: e.target.value }); setUser({ ...user, [e.target.name]: e.target.value });
}; };
const handleSignUp = async e => { const handleSignUp = async e => {
e.preventDefault(); e.preventDefault();
try { try {
await api.put("/auth/signup", user); await api.put("/auth/signup", user);
} catch (err) { } catch (err) {
alert(err); alert(err);
} }
history.push("/login"); history.push("/login");
}; };
return ( return (
<Container component="main" maxWidth="xs"> <Container component="main" maxWidth="xs">
<CssBaseline /> <CssBaseline />
<div className={classes.paper}> <div className={classes.paper}>
<Avatar className={classes.avatar}> <Avatar className={classes.avatar}>
<LockOutlinedIcon /> <LockOutlinedIcon />
</Avatar> </Avatar>
<Typography component="h1" variant="h5"> <Typography component="h1" variant="h5">
Cadastre-se Cadastre-se
</Typography> </Typography>
<form className={classes.form} noValidate onSubmit={handleSignUp}> <form className={classes.form} noValidate onSubmit={handleSignUp}>
<Grid container spacing={2}> <Grid container spacing={2}>
<Grid item xs={12}> <Grid item xs={12}>
<TextField <TextField
autoComplete="name" autoComplete="name"
name="name" name="name"
variant="outlined" variant="outlined"
required required
fullWidth fullWidth
id="name" id="name"
label="Nome" label="Nome"
value={user.name} value={user.name}
onChange={handleChangeInput} onChange={handleChangeInput}
autoFocus autoFocus
/> />
</Grid> </Grid>
<Grid item xs={12}> <Grid item xs={12}>
<TextField <TextField
variant="outlined" variant="outlined"
required required
fullWidth fullWidth
id="email" id="email"
label="Email" label="Email"
name="email" name="email"
autoComplete="email" autoComplete="email"
value={user.email} value={user.email}
onChange={handleChangeInput} onChange={handleChangeInput}
/> />
</Grid> </Grid>
<Grid item xs={12}> <Grid item xs={12}>
<TextField <TextField
variant="outlined" variant="outlined"
required required
fullWidth fullWidth
name="password" name="password"
label="Senha" label="Senha"
type="password" type="password"
id="password" id="password"
autoComplete="current-password" autoComplete="current-password"
value={user.password} value={user.password}
onChange={handleChangeInput} onChange={handleChangeInput}
/> />
</Grid> </Grid>
</Grid> </Grid>
<Button <Button
type="submit" type="submit"
fullWidth fullWidth
variant="contained" variant="contained"
color="primary" color="primary"
className={classes.submit} className={classes.submit}
> >
Cadastrar Cadastrar
</Button> </Button>
<Grid container justify="flex-end"> <Grid container justify="flex-end">
<Grid item> <Grid item>
<Link href="#" variant="body2" component={RouterLink} to="/login"> <Link href="#" variant="body2" component={RouterLink} to="/login">
tem uma conta? Entre! tem uma conta? Entre!
</Link> </Link>
</Grid> </Grid>
</Grid> </Grid>
</form> </form>
</div> </div>
<Box mt={5}> <Box mt={5}>
<Copyright /> <Copyright />
</Box> </Box>
</Container> </Container>
); );
}; };
export default SignUp; export default SignUp;

View File

@@ -1,126 +1,126 @@
import React, { useState, useEffect } from "react"; import React, { useState, useEffect } from "react";
import { useHistory } from "react-router-dom"; import { useHistory } from "react-router-dom";
import api from "../../util/api"; import api from "../../util/api";
import MainDrawer from "../../components/Layout/MainDrawer"; import MainDrawer from "../../components/Layout/MainDrawer";
import openSocket from "socket.io-client"; import openSocket from "socket.io-client";
import { makeStyles } from "@material-ui/core/styles"; import { makeStyles } from "@material-ui/core/styles";
import Container from "@material-ui/core/Container"; import Container from "@material-ui/core/Container";
import Grid from "@material-ui/core/Grid"; import Grid from "@material-ui/core/Grid";
import Paper from "@material-ui/core/Paper"; import Paper from "@material-ui/core/Paper";
import Bateryinfo from "./components/Bateryinfo"; import Bateryinfo from "./components/Bateryinfo";
import Qrcode from "./components/Qrcode"; import Qrcode from "./components/Qrcode";
const useStyles = makeStyles(theme => ({ const useStyles = makeStyles(theme => ({
root: { root: {
display: "flex", display: "flex",
}, },
title: { title: {
flexGrow: 1, flexGrow: 1,
}, },
appBarSpacer: theme.mixins.toolbar, appBarSpacer: theme.mixins.toolbar,
content: { content: {
flexGrow: 1, flexGrow: 1,
overflow: "auto", overflow: "auto",
}, },
container: { container: {
// paddingTop: theme.spacing(4), // paddingTop: theme.spacing(4),
// paddingBottom: theme.spacing(4), // paddingBottom: theme.spacing(4),
height: `calc(100% - 64px)`, height: `calc(100% - 64px)`,
}, },
paper: { paper: {
padding: theme.spacing(2), padding: theme.spacing(2),
display: "flex", display: "flex",
overflow: "auto", overflow: "auto",
flexDirection: "column", flexDirection: "column",
}, },
fixedHeight: { fixedHeight: {
height: 640, height: 640,
}, },
})); }));
const WhatsAuth = () => { const WhatsAuth = () => {
const classes = useStyles(); const classes = useStyles();
const history = useHistory(); const history = useHistory();
const [qrCode, setQrCode] = useState(""); const [qrCode, setQrCode] = useState("");
const [session, setSession] = useState({}); const [session, setSession] = useState({});
useEffect(() => { useEffect(() => {
const fetchSession = async () => { const fetchSession = async () => {
try { try {
const res = await api.get("/whatsapp/session"); const res = await api.get("/whatsapp/session");
setQrCode(res.data.qrcode); setQrCode(res.data.qrcode);
setSession(res.data); setSession(res.data);
} catch (err) { } catch (err) {
console.log(err); console.log(err);
} }
}; };
fetchSession(); fetchSession();
}, []); }, []);
useEffect(() => { useEffect(() => {
const socket = openSocket(process.env.REACT_APP_BACKEND_URL); const socket = openSocket(process.env.REACT_APP_BACKEND_URL);
socket.on("qrcode", data => { socket.on("qrcode", data => {
if (data.action === "update") { if (data.action === "update") {
setQrCode(data.qr); setQrCode(data.qr);
} }
}); });
socket.on("whats_auth", data => { socket.on("whats_auth", data => {
if (data.action === "authentication") { if (data.action === "authentication") {
history.push("/chat"); history.push("/chat");
setQrCode(""); setQrCode("");
setSession(data.session); setSession(data.session);
} }
}); });
return () => { return () => {
socket.disconnect(); socket.disconnect();
}; };
}, [history]); }, [history]);
console.log(session); console.log(session);
return ( return (
<div> <div>
<MainDrawer appTitle="QR Code"> <MainDrawer appTitle="QR Code">
<div className={classes.root}> <div className={classes.root}>
<main className={classes.content}> <main className={classes.content}>
<div className={classes.appBarSpacer} /> <div className={classes.appBarSpacer} />
<Container maxWidth="lg" className={classes.container}> <Container maxWidth="lg" className={classes.container}>
<Grid container spacing={3}> <Grid container spacing={3}>
{session.status === "pending" ? ( {session.status === "pending" ? (
<Grid item xs={6}> <Grid item xs={6}>
<Paper className={classes.paper}> <Paper className={classes.paper}>
<Qrcode qrCode={qrCode} /> <Qrcode qrCode={qrCode} />
</Paper> </Paper>
</Grid> </Grid>
) : ( ) : (
<Grid item xs={6}> <Grid item xs={6}>
<Paper className={classes.paper}> <Paper className={classes.paper}>
<Bateryinfo sessio={session} /> <Bateryinfo sessio={session} />
</Paper> </Paper>
</Grid> </Grid>
)} )}
{/* <Grid item xs={12} md={4} lg={3}> {/* <Grid item xs={12} md={4} lg={3}>
<Paper className={fixedHeightPaper}> <Paper className={fixedHeightPaper}>
<h1>paper2</h1> <h1>paper2</h1>
</Paper> </Paper>
</Grid> */} </Grid> */}
</Grid> </Grid>
</Container> </Container>
</main> </main>
</div> </div>
</MainDrawer> </MainDrawer>
</div> </div>
); );
}; };
export default WhatsAuth; export default WhatsAuth;

View File

@@ -1,34 +1,34 @@
import React from "react"; import React from "react";
import Link from "@material-ui/core/Link"; import Link from "@material-ui/core/Link";
import { makeStyles } from "@material-ui/core/styles"; import { makeStyles } from "@material-ui/core/styles";
import Typography from "@material-ui/core/Typography"; import Typography from "@material-ui/core/Typography";
const useStyles = makeStyles({ const useStyles = makeStyles({
main: { main: {
flex: 1, flex: 1,
}, },
}); });
const Bateryinfo = ({ session }) => { const Bateryinfo = ({ session }) => {
const classes = useStyles(); const classes = useStyles();
return ( return (
<React.Fragment> <React.Fragment>
<Typography component="h2" variant="h6" color="primary" gutterBottom> <Typography component="h2" variant="h6" color="primary" gutterBottom>
Bateria Bateria
</Typography> </Typography>
<Typography component="p" variant="h6"> <Typography component="p" variant="h6">
{(session && session.baterry) || "Não disponível"} {(session && session.baterry) || "Não disponível"}
</Typography> </Typography>
<Typography color="textSecondary" className={classes.main}> <Typography color="textSecondary" className={classes.main}>
Carregando: {(session && session.plugged) || "Não disponível"} Carregando: {(session && session.plugged) || "Não disponível"}
</Typography> </Typography>
<div> <div>
<Link color="primary" href="#"> <Link color="primary" href="#">
Verificar bateria Verificar bateria
</Link> </Link>
</div> </div>
</React.Fragment> </React.Fragment>
); );
}; };
export default Bateryinfo; export default Bateryinfo;

View File

@@ -1,16 +1,16 @@
import React from "react"; import React from "react";
import QRCode from "qrcode.react"; import QRCode from "qrcode.react";
import Typography from "@material-ui/core/Typography"; import Typography from "@material-ui/core/Typography";
const Qrcode = ({ qrCode }) => { const Qrcode = ({ qrCode }) => {
return ( return (
<div> <div>
<Typography component="h2" variant="h6" color="primary" gutterBottom> <Typography component="h2" variant="h6" color="primary" gutterBottom>
Leia o QrCode para iniciar a sessão Leia o QrCode para iniciar a sessão
</Typography> </Typography>
<QRCode value={qrCode} size={256} /> <QRCode value={qrCode} size={256} />
</div> </div>
); );
}; };
export default Qrcode; export default Qrcode;

View File

@@ -1,71 +1,93 @@
import React, { useContext } from "react"; import React, { useContext } from "react";
import { BrowserRouter, Route, Switch, Redirect } from "react-router-dom"; import { BrowserRouter, Route, Switch, Redirect } from "react-router-dom";
import { AuthContext, AuthProvider } from "./Context/Auth/AuthContext"; import { AuthContext, AuthProvider } from "./Context/Auth/AuthContext";
import Dashboard from "./pages/Home/Dashboard"; import Dashboard from "./pages/Home/Dashboard";
import Chat from "./pages/Chat/Chat"; import Chat from "./pages/Chat/Chat";
import Profile from "./pages/Profile/Profile"; import Profile from "./pages/Profile/Profile";
import Signup from "./pages/Signup/Signup"; import Signup from "./pages/Signup/Signup";
import Login from "./pages/Login/Login"; import Login from "./pages/Login/Login";
import WhatsAuth from "./pages/WhatsAuth/WhatsAuth"; import WhatsAuth from "./pages/WhatsAuth/WhatsAuth";
import Backdrop from "@material-ui/core/Backdrop";
const PrivateRoute = ({ component: Component, ...rest }) => { import CircularProgress from "@material-ui/core/CircularProgress";
const { isAuth, loading } = useContext(AuthContext); import { makeStyles } from "@material-ui/core/styles";
if (loading) return <h1>Loading...</h1>; const useStyles = makeStyles(theme => ({
backdrop: {
return ( zIndex: theme.zIndex.drawer + 1,
<Route color: "#fff",
{...rest} },
render={props => }));
isAuth ? (
<Component {...props} /> const PrivateRoute = ({ component: Component, ...rest }) => {
) : ( const classes = useStyles();
<Redirect const { isAuth, loading } = useContext(AuthContext);
to={{ pathname: "/login", state: { from: props.location } }}
/> if (loading)
) return (
} <Backdrop className={classes.backdrop} open={loading}>
/> <CircularProgress color="inherit" />
); </Backdrop>
}; );
const PublicRoute = ({ component: Component, ...rest }) => { return (
const { isAuth, loading } = useContext(AuthContext); <Route
{...rest}
if (loading) return <h1>Loading...</h1>; render={props =>
isAuth ? (
return ( <Component {...props} />
<Route ) : (
{...rest} <Redirect
render={props => to={{ pathname: "/login", state: { from: props.location } }}
!isAuth ? ( />
<Component {...props} /> )
) : ( }
<Redirect to={{ pathname: "/", state: { from: props.location } }} /> />
) );
} };
/>
); const PublicRoute = ({ component: Component, ...rest }) => {
}; const classes = useStyles();
const { isAuth, loading } = useContext(AuthContext);
const Routes = () => {
return ( if (loading)
<BrowserRouter> return (
<AuthProvider> <Backdrop className={classes.backdrop} open={loading}>
<Switch> <CircularProgress color="inherit" />
<PrivateRoute exact path="/" component={Dashboard} /> </Backdrop>
<PrivateRoute exact path="/chat" component={Chat} /> );
<PrivateRoute exact path="/chat/:contactId" component={Chat} />
<PrivateRoute exact path="/profile" component={Profile} /> return (
<PrivateRoute exact path="/whats-auth" component={WhatsAuth} /> <Route
<PublicRoute exact path="/login" component={Login} /> {...rest}
<PublicRoute exact path="/signup" component={Signup} /> render={props =>
</Switch> !isAuth ? (
</AuthProvider> <Component {...props} />
</BrowserRouter> ) : (
); <Redirect to={{ pathname: "/", state: { from: props.location } }} />
}; )
}
export default Routes; />
);
};
const Routes = () => {
return (
<BrowserRouter>
<AuthProvider>
<Switch>
<PrivateRoute exact path="/" component={Dashboard} />
<PrivateRoute exact path="/chat" component={Chat} />
<PrivateRoute exact path="/chat/:contactId" component={Chat} />
<PrivateRoute exact path="/profile" component={Profile} />
<PrivateRoute exact path="/whats-auth" component={WhatsAuth} />
<PublicRoute exact path="/login" component={Login} />
<PublicRoute exact path="/signup" component={Signup} />
</Switch>
</AuthProvider>
</BrowserRouter>
);
};
export default Routes;

View File

@@ -1,7 +1,7 @@
import axios from "axios"; import axios from "axios";
const api = axios.create({ const api = axios.create({
baseURL: process.env.REACT_APP_BACKEND_URL, baseURL: process.env.REACT_APP_BACKEND_URL,
}); });
export default api; export default api;

View File

@@ -0,0 +1,4 @@
[ZoneTransfer]
ZoneId=3
ReferrerUrl=https://notificationsounds.com/message-tones/juntos-607
HostUrl=https://notificationsounds.com/message-tones/juntos-607/download/mp3

View File

@@ -0,0 +1,4 @@
[ZoneTransfer]
ZoneId=3
ReferrerUrl=https://notificationsounds.com/message-tones/juntos-607
HostUrl=https://notificationsounds.com/message-tones/juntos-607/download/ogg