mirror of
https://github.com/cheveguerra/whaticket-community.git
synced 2026-04-20 04:39:20 +00:00
new chat with material ui
This commit is contained in:
Binary file not shown.
@@ -19,7 +19,7 @@ const fileStorage = multer.diskStorage({
|
|||||||
cb(null, "public");
|
cb(null, "public");
|
||||||
},
|
},
|
||||||
filename: (req, file, cb) => {
|
filename: (req, file, cb) => {
|
||||||
cb(null, new Date().getTime() + "-" + file.originalname);
|
cb(null, new Date().getTime() + "-" + file.originalname.replace(/\s/g, ""));
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -5,19 +5,22 @@ const Sequelize = require("sequelize");
|
|||||||
exports.getContacts = async (req, res) => {
|
exports.getContacts = async (req, res) => {
|
||||||
try {
|
try {
|
||||||
const contacts = await Contact.findAll({
|
const contacts = await Contact.findAll({
|
||||||
include: { model: Message, attributes: [] },
|
include: {
|
||||||
|
model: Message,
|
||||||
|
attributes: ["messageBody"],
|
||||||
|
},
|
||||||
attributes: {
|
attributes: {
|
||||||
include: [
|
include: [
|
||||||
[
|
[
|
||||||
Sequelize.literal(`(
|
Sequelize.literal(`(
|
||||||
SELECT COUNT(*)
|
SELECT COUNT(*)
|
||||||
FROM messages AS message
|
FROM messages AS message
|
||||||
WHERE
|
WHERE
|
||||||
message.contactId = contact.id
|
message.contactId = contact.id
|
||||||
AND
|
AND
|
||||||
message.read = 0
|
message.read = 0
|
||||||
|
|
||||||
)`),
|
)`),
|
||||||
"unreadMessages",
|
"unreadMessages",
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -81,15 +81,7 @@ exports.getContactMessages = async (req, res, next) => {
|
|||||||
|
|
||||||
const serializedMessages = contactMessages.map(message => {
|
const serializedMessages = contactMessages.map(message => {
|
||||||
return {
|
return {
|
||||||
id: message.id,
|
...message.dataValues,
|
||||||
createdAt: message.createdAt,
|
|
||||||
updatedAt: message.updatedAt,
|
|
||||||
messageBody: message.messageBody,
|
|
||||||
userId: message.userId,
|
|
||||||
ack: message.ack,
|
|
||||||
read: message.read,
|
|
||||||
mediaType: message.mediaType,
|
|
||||||
contactId: message.contactId,
|
|
||||||
mediaUrl: `${
|
mediaUrl: `${
|
||||||
message.mediaUrl
|
message.mediaUrl
|
||||||
? `http://localhost:8080/public/${message.mediaUrl}`
|
? `http://localhost:8080/public/${message.mediaUrl}`
|
||||||
@@ -117,7 +109,7 @@ exports.postCreateContactMessage = async (req, res, next) => {
|
|||||||
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;
|
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 {
|
||||||
@@ -143,7 +135,14 @@ exports.postCreateContactMessage = async (req, res, next) => {
|
|||||||
|
|
||||||
io.to(contactId).emit("appMessage", {
|
io.to(contactId).emit("appMessage", {
|
||||||
action: "create",
|
action: "create",
|
||||||
message: newMessage,
|
message: {
|
||||||
|
...newMessage.dataValues,
|
||||||
|
mediaUrl: `${
|
||||||
|
message.mediaUrl
|
||||||
|
? `http://localhost:8080/public/${message.mediaUrl}`
|
||||||
|
: ""
|
||||||
|
}`,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
await setMessagesAsRead(contactId);
|
await setMessagesAsRead(contactId);
|
||||||
|
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
{"WABrowserId":"\"E9dnt9Mm/JiFDCMJQHkXBw==\"","WASecretBundle":"{\"key\":\"fHTKtoDcxidboASIs5WV5JKyRrF+SfMktqHXmq/KBJU=\",\"encKey\":\"FrM3OnnEbuEr1JrtKypw5CPSc6rSD5bjbOGstv8ijk4=\",\"macKey\":\"fHTKtoDcxidboASIs5WV5JKyRrF+SfMktqHXmq/KBJU=\"}","WAToken1":"\"Q+xBrzEsm9SAVo3hRyKwC3L/kvNfieq45Jt6/pFePbw=\"","WAToken2":"\"1@WlcfjMFEzc0c9JcHHHAK5BfaDDxigchRdN+GrHmVWsix+b7Od5d9Ls21/JC5ojCqbFBwnzQ5zZ/Fbg==\""}
|
{"WABrowserId":"\"E9dnt9Mm/JiFDCMJQHkXBw==\"","WASecretBundle":"{\"key\":\"fHTKtoDcxidboASIs5WV5JKyRrF+SfMktqHXmq/KBJU=\",\"encKey\":\"FrM3OnnEbuEr1JrtKypw5CPSc6rSD5bjbOGstv8ijk4=\",\"macKey\":\"fHTKtoDcxidboASIs5WV5JKyRrF+SfMktqHXmq/KBJU=\"}","WAToken1":"\"9leF1nILpG4UaO0PWCoKA48h8dSn8UdIPNYZg3bpZv4=\"","WAToken2":"\"1@6OzeSH6s5Y8ozcOu4+l2N7wfaSk6C5Aop8h2BN08xAvvuGCfcuMzctLlOMNDaSw1j2qfFNM4CGjKdA==\""}
|
||||||
@@ -184,7 +184,7 @@ const ContactsList = ({ selectedContact, setSelectedContact }) => {
|
|||||||
</ListItemAvatar>
|
</ListItemAvatar>
|
||||||
<ListItemText
|
<ListItemText
|
||||||
primary={contact.name}
|
primary={contact.name}
|
||||||
secondary="last contact message..."
|
secondary={contact.messages[0].messageBody || "oio"}
|
||||||
/>
|
/>
|
||||||
<ListItemSecondaryAction>
|
<ListItemSecondaryAction>
|
||||||
{contact.unreadMessages > 0 && (
|
{contact.unreadMessages > 0 && (
|
||||||
|
|||||||
@@ -1,21 +1,23 @@
|
|||||||
import React from "react";
|
import React, { useState, useEffect } from "react";
|
||||||
|
import api from "../../../../util/api";
|
||||||
|
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 { green } from "@material-ui/core/colors";
|
||||||
|
|
||||||
import AttachFileIcon from "@material-ui/icons/AttachFile";
|
import AttachFileIcon from "@material-ui/icons/AttachFile";
|
||||||
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";
|
||||||
|
|
||||||
const useStyles = makeStyles(theme => ({
|
const useStyles = makeStyles(theme => ({
|
||||||
root: {
|
|
||||||
flexGrow: 1,
|
|
||||||
},
|
|
||||||
|
|
||||||
newMessageBox: {
|
newMessageBox: {
|
||||||
background: "#eee",
|
background: "#eee",
|
||||||
display: "flex",
|
display: "flex",
|
||||||
|
position: "relative",
|
||||||
padding: "10px 13px",
|
padding: "10px 13px",
|
||||||
borderTopLeftRadius: 0,
|
borderTopLeftRadius: 0,
|
||||||
borderTopRightRadius: 0,
|
borderTopRightRadius: 0,
|
||||||
@@ -35,33 +37,201 @@ const useStyles = makeStyles(theme => ({
|
|||||||
width: "80%",
|
width: "80%",
|
||||||
},
|
},
|
||||||
|
|
||||||
sendMessageIcon: {
|
sendMessageIcons: {
|
||||||
opacity: "80%",
|
color: "grey",
|
||||||
margin: 4,
|
margin: 4,
|
||||||
alignSelf: "middle",
|
|
||||||
cursor: "pointer",
|
cursor: "pointer",
|
||||||
"&:hover": {
|
"&:hover": {
|
||||||
opacity: "70%",
|
opacity: "70%",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
viewMediaInputWrapper: {
|
||||||
|
display: "flex",
|
||||||
|
padding: "10px 13px",
|
||||||
|
position: "relative",
|
||||||
|
borderTopLeftRadius: 0,
|
||||||
|
borderTopRightRadius: 0,
|
||||||
|
borderBottomLeftRadius: 0,
|
||||||
|
justifyContent: "space-between",
|
||||||
|
backgroundColor: "#eee",
|
||||||
|
},
|
||||||
|
|
||||||
|
emojiBox: {
|
||||||
|
position: "absolute",
|
||||||
|
bottom: 50,
|
||||||
|
borderTop: "1px solid #e8e8e8",
|
||||||
|
},
|
||||||
|
|
||||||
|
circleLoading: {
|
||||||
|
color: green[500],
|
||||||
|
position: "absolute",
|
||||||
|
top: 0,
|
||||||
|
left: "50%",
|
||||||
|
marginTop: 6,
|
||||||
|
marginBottom: 6,
|
||||||
|
marginLeft: -12,
|
||||||
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const MessagesInput = () => {
|
const MessagesInput = ({ selectedContact }) => {
|
||||||
const classes = useStyles();
|
const classes = useStyles();
|
||||||
|
const contactId = selectedContact.id;
|
||||||
|
const userId = localStorage.getItem("userId");
|
||||||
|
const username = localStorage.getItem("username");
|
||||||
|
const token = localStorage.getItem("token");
|
||||||
|
|
||||||
return (
|
const mediaInitialState = { preview: "", raw: "", name: "" };
|
||||||
<Paper variant="outlined" className={classes.newMessageBox}>
|
const [media, setMedia] = useState(mediaInitialState);
|
||||||
<MoodIcon className={classes.sendMessageIcon} />
|
const [inputMessage, setInputMessage] = useState("");
|
||||||
<AttachFileIcon className={classes.sendMessageIcon} />
|
const [showEmoji, setShowEmoji] = useState(false);
|
||||||
<div className={classes.messageInputWrapper}>
|
const [loading, setLoading] = useState(false);
|
||||||
<InputBase
|
|
||||||
className={classes.messageInput}
|
useEffect(() => {
|
||||||
placeholder="Escreva uma mensagem"
|
return () => {
|
||||||
|
setInputMessage("");
|
||||||
|
setShowEmoji(false);
|
||||||
|
setMedia({});
|
||||||
|
};
|
||||||
|
}, [selectedContact]);
|
||||||
|
|
||||||
|
const handleChangeInput = e => {
|
||||||
|
setInputMessage(e.target.value);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleAddEmoji = e => {
|
||||||
|
let emoji = e.native;
|
||||||
|
setInputMessage(prevState => prevState + emoji);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleChangeMedia = e => {
|
||||||
|
if (e.target.files.length) {
|
||||||
|
setMedia({
|
||||||
|
preview: URL.createObjectURL(e.target.files[0]),
|
||||||
|
raw: e.target.files[0],
|
||||||
|
name: e.target.files[0].name,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleInputPaste = e => {
|
||||||
|
if (e.clipboardData.files[0]) {
|
||||||
|
setMedia({
|
||||||
|
preview: URL.createObjectURL(e.clipboardData.files[0]),
|
||||||
|
raw: e.clipboardData.files[0],
|
||||||
|
name: e.clipboardData.files[0].name,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleUploadMedia = async e => {
|
||||||
|
setLoading(true);
|
||||||
|
e.preventDefault();
|
||||||
|
const formData = new FormData();
|
||||||
|
formData.append("media", media.raw);
|
||||||
|
formData.append("userId", userId);
|
||||||
|
formData.append("messageBody", media.name);
|
||||||
|
|
||||||
|
try {
|
||||||
|
await api.post(`/messages/${contactId}`, formData, {
|
||||||
|
headers: { Authorization: "Bearer " + token },
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
console.log(err);
|
||||||
|
alert(err);
|
||||||
|
}
|
||||||
|
setLoading(false);
|
||||||
|
setMedia(mediaInitialState);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSendMessage = async () => {
|
||||||
|
if (inputMessage.trim() === "") return;
|
||||||
|
const message = {
|
||||||
|
read: 1,
|
||||||
|
userId: userId,
|
||||||
|
mediaUrl: "",
|
||||||
|
messageBody: `${username}: ${inputMessage.trim()}`,
|
||||||
|
};
|
||||||
|
try {
|
||||||
|
await api.post(`/messages/${contactId}`, message, {
|
||||||
|
headers: { Authorization: "Bearer " + token },
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
alert(err);
|
||||||
|
}
|
||||||
|
setInputMessage("");
|
||||||
|
setShowEmoji(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
if (media.preview)
|
||||||
|
return (
|
||||||
|
<Paper variant="outlined" className={classes.viewMediaInputWrapper}>
|
||||||
|
<CancelIcon
|
||||||
|
className={classes.sendMessageIcons}
|
||||||
|
onClick={e => setMedia(mediaInitialState)}
|
||||||
/>
|
/>
|
||||||
</div>
|
<span>
|
||||||
<SendIcon className={classes.sendMessageIcon} />
|
{media.name}
|
||||||
</Paper>
|
{/* <img src={media.preview} alt=""></img> */}
|
||||||
);
|
</span>
|
||||||
|
{loading ? (
|
||||||
|
<div>
|
||||||
|
<CircularProgress className={classes.circleLoading} />
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
|
<SendIcon
|
||||||
|
className={classes.sendMessageIcons}
|
||||||
|
onClick={handleUploadMedia}
|
||||||
|
/>
|
||||||
|
</Paper>
|
||||||
|
);
|
||||||
|
else {
|
||||||
|
return (
|
||||||
|
<Paper variant="outlined" className={classes.newMessageBox}>
|
||||||
|
<MoodIcon
|
||||||
|
className={classes.sendMessageIcons}
|
||||||
|
onClick={e => setShowEmoji(prevState => !prevState)}
|
||||||
|
/>
|
||||||
|
{showEmoji ? (
|
||||||
|
<div className={classes.emojiBox}>
|
||||||
|
<Picker
|
||||||
|
perLine={16}
|
||||||
|
showPreview={false}
|
||||||
|
showSkinTones={false}
|
||||||
|
onSelect={handleAddEmoji}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
|
<label htmlFor="upload-button" className={classes.sendMessageIcons}>
|
||||||
|
<AttachFileIcon />
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type="file"
|
||||||
|
id="upload-button"
|
||||||
|
style={{ display: "none" }}
|
||||||
|
onChange={handleChangeMedia}
|
||||||
|
/>
|
||||||
|
<div className={classes.messageInputWrapper}>
|
||||||
|
<InputBase
|
||||||
|
className={classes.messageInput}
|
||||||
|
placeholder="Escreva uma mensagem"
|
||||||
|
value={inputMessage}
|
||||||
|
onChange={handleChangeInput}
|
||||||
|
onPaste={handleInputPaste}
|
||||||
|
onKeyPress={e => {
|
||||||
|
if (e.key === "Enter") {
|
||||||
|
handleSendMessage();
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<SendIcon
|
||||||
|
className={classes.sendMessageIcons}
|
||||||
|
onClick={handleSendMessage}
|
||||||
|
/>
|
||||||
|
</Paper>
|
||||||
|
);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export default MessagesInput;
|
export default MessagesInput;
|
||||||
|
|||||||
@@ -129,6 +129,12 @@ const useStyles = makeStyles(theme => ({
|
|||||||
boxShadow: "0 2px 2px #808888",
|
boxShadow: "0 2px 2px #808888",
|
||||||
},
|
},
|
||||||
|
|
||||||
|
textContentItem: {
|
||||||
|
alignSelf: "middle",
|
||||||
|
overflowWrap: "break-word",
|
||||||
|
padding: "3px 80px 6px 6px",
|
||||||
|
},
|
||||||
|
|
||||||
messageMedia: {
|
messageMedia: {
|
||||||
objectFit: "cover",
|
objectFit: "cover",
|
||||||
width: 250,
|
width: 250,
|
||||||
@@ -139,11 +145,6 @@ const useStyles = makeStyles(theme => ({
|
|||||||
borderBottomRightRadius: 8,
|
borderBottomRightRadius: 8,
|
||||||
},
|
},
|
||||||
|
|
||||||
textContentItem: {
|
|
||||||
overflowWrap: "break-word",
|
|
||||||
padding: "0px 60px 10px 0px",
|
|
||||||
},
|
|
||||||
|
|
||||||
timestamp: {
|
timestamp: {
|
||||||
fontSize: 11,
|
fontSize: 11,
|
||||||
position: "absolute",
|
position: "absolute",
|
||||||
@@ -168,6 +169,15 @@ const useStyles = makeStyles(theme => ({
|
|||||||
alignSelf: "center",
|
alignSelf: "center",
|
||||||
marginLeft: "0px",
|
marginLeft: "0px",
|
||||||
},
|
},
|
||||||
|
|
||||||
|
ackIcons: {
|
||||||
|
fontSize: 18,
|
||||||
|
},
|
||||||
|
|
||||||
|
ackDoneAllIcon: {
|
||||||
|
color: green[500],
|
||||||
|
fontSize: 18,
|
||||||
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const MessagesList = ({ selectedContact }) => {
|
const MessagesList = ({ selectedContact }) => {
|
||||||
@@ -264,8 +274,9 @@ const MessagesList = ({ selectedContact }) => {
|
|||||||
setMessagesList(prevState => {
|
setMessagesList(prevState => {
|
||||||
let aux = [...prevState];
|
let aux = [...prevState];
|
||||||
let messageIndex = aux.findIndex(message => message.id === id);
|
let messageIndex = aux.findIndex(message => message.id === id);
|
||||||
aux[messageIndex].ack = message.ack;
|
if (messageIndex) {
|
||||||
|
aux[messageIndex].ack = message.ack;
|
||||||
|
}
|
||||||
return aux;
|
return aux;
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@@ -307,16 +318,18 @@ const MessagesList = ({ selectedContact }) => {
|
|||||||
|
|
||||||
const renderMessageAck = message => {
|
const renderMessageAck = message => {
|
||||||
if (message.ack === 0) {
|
if (message.ack === 0) {
|
||||||
return <AccessTimeIcon fontSize="small" />;
|
return <AccessTimeIcon fontSize="small" className={classes.ackIcons} />;
|
||||||
}
|
}
|
||||||
if (message.ack === 1) {
|
if (message.ack === 1) {
|
||||||
return <DoneIcon fontSize="small" />;
|
return <DoneIcon fontSize="small" className={classes.ackIcons} />;
|
||||||
}
|
}
|
||||||
if (message.ack === 2) {
|
if (message.ack === 2) {
|
||||||
return <DoneAllIcon fontSize="small" />;
|
return <DoneAllIcon fontSize="small" className={classes.ackIcons} />;
|
||||||
}
|
}
|
||||||
if (message.ack === 3) {
|
if (message.ack === 3) {
|
||||||
return <DoneAllIcon fontSize="small" color="primary" />;
|
return (
|
||||||
|
<DoneAllIcon fontSize="small" className={classes.ackDoneAllIcon} />
|
||||||
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -372,10 +385,9 @@ const MessagesList = ({ selectedContact }) => {
|
|||||||
|
|
||||||
const renderMessages = () => {
|
const renderMessages = () => {
|
||||||
if (messagesList.length > 0) {
|
if (messagesList.length > 0) {
|
||||||
let viewMessagesList = [];
|
const viewMessagesList = messagesList.map((message, index) => {
|
||||||
messagesList.forEach((message, index) => {
|
|
||||||
if (message.userId === 0) {
|
if (message.userId === 0) {
|
||||||
viewMessagesList.push(
|
return [
|
||||||
renderDailyTimestamps(message, index),
|
renderDailyTimestamps(message, index),
|
||||||
<div className={classes.messageLeft} key={message.id}>
|
<div className={classes.messageLeft} key={message.id}>
|
||||||
{message.mediaUrl && checkMessaageMedia(message)}
|
{message.mediaUrl && checkMessaageMedia(message)}
|
||||||
@@ -387,10 +399,10 @@ const MessagesList = ({ selectedContact }) => {
|
|||||||
.format("HH:mm")}
|
.format("HH:mm")}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>,
|
||||||
);
|
];
|
||||||
} else {
|
} else {
|
||||||
viewMessagesList.push(
|
return [
|
||||||
renderDailyTimestamps(message, index),
|
renderDailyTimestamps(message, index),
|
||||||
<div className={classes.messageRight} key={message.id}>
|
<div className={classes.messageRight} key={message.id}>
|
||||||
{message.mediaUrl && checkMessaageMedia(message)}
|
{message.mediaUrl && checkMessaageMedia(message)}
|
||||||
@@ -403,8 +415,8 @@ const MessagesList = ({ selectedContact }) => {
|
|||||||
{renderMessageAck(message)}
|
{renderMessageAck(message)}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>,
|
||||||
);
|
];
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return viewMessagesList;
|
return viewMessagesList;
|
||||||
|
|||||||
Reference in New Issue
Block a user