new chat with material ui

This commit is contained in:
Cassio Santos
2020-06-11 09:26:12 -03:00
parent d91c0c5155
commit d30e61033b
8 changed files with 247 additions and 63 deletions

View File

@@ -19,7 +19,7 @@ const fileStorage = multer.diskStorage({
cb(null, "public");
},
filename: (req, file, cb) => {
cb(null, new Date().getTime() + "-" + file.originalname);
cb(null, new Date().getTime() + "-" + file.originalname.replace(/\s/g, ""));
},
});

View File

@@ -5,19 +5,22 @@ const Sequelize = require("sequelize");
exports.getContacts = async (req, res) => {
try {
const contacts = await Contact.findAll({
include: { model: Message, attributes: [] },
include: {
model: Message,
attributes: ["messageBody"],
},
attributes: {
include: [
[
Sequelize.literal(`(
SELECT COUNT(*)
FROM messages AS message
WHERE
message.contactId = contact.id
AND
message.read = 0
)`),
SELECT COUNT(*)
FROM messages AS message
WHERE
message.contactId = contact.id
AND
message.read = 0
)`),
"unreadMessages",
],
],

View File

@@ -81,15 +81,7 @@ exports.getContactMessages = async (req, res, next) => {
const serializedMessages = contactMessages.map(message => {
return {
id: message.id,
createdAt: message.createdAt,
updatedAt: message.updatedAt,
messageBody: message.messageBody,
userId: message.userId,
ack: message.ack,
read: message.read,
mediaType: message.mediaType,
contactId: message.contactId,
...message.dataValues,
mediaUrl: `${
message.mediaUrl
? `http://localhost:8080/public/${message.mediaUrl}`
@@ -117,7 +109,7 @@ exports.postCreateContactMessage = async (req, res, next) => {
const contact = await Contact.findByPk(contactId);
if (media) {
const newMedia = MessageMedia.fromFilePath(req.file.path);
message.mediaUrl = req.file.filename;
message.mediaUrl = req.file.filename.replace(/\s/g, "");
if (newMedia.mimetype) {
message.mediaType = newMedia.mimetype.split("/")[0];
} else {
@@ -143,7 +135,14 @@ exports.postCreateContactMessage = async (req, res, next) => {
io.to(contactId).emit("appMessage", {
action: "create",
message: newMessage,
message: {
...newMessage.dataValues,
mediaUrl: `${
message.mediaUrl
? `http://localhost:8080/public/${message.mediaUrl}`
: ""
}`,
},
});
await setMessagesAsRead(contactId);

View File

@@ -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==\""}

View File

@@ -184,7 +184,7 @@ const ContactsList = ({ selectedContact, setSelectedContact }) => {
</ListItemAvatar>
<ListItemText
primary={contact.name}
secondary="last contact message..."
secondary={contact.messages[0].messageBody || "oio"}
/>
<ListItemSecondaryAction>
{contact.unreadMessages > 0 && (

View File

@@ -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 Paper from "@material-ui/core/Paper";
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 MoodIcon from "@material-ui/icons/Mood";
import SendIcon from "@material-ui/icons/Send";
import CancelIcon from "@material-ui/icons/Cancel";
const useStyles = makeStyles(theme => ({
root: {
flexGrow: 1,
},
newMessageBox: {
background: "#eee",
display: "flex",
position: "relative",
padding: "10px 13px",
borderTopLeftRadius: 0,
borderTopRightRadius: 0,
@@ -35,33 +37,201 @@ const useStyles = makeStyles(theme => ({
width: "80%",
},
sendMessageIcon: {
opacity: "80%",
sendMessageIcons: {
color: "grey",
margin: 4,
alignSelf: "middle",
cursor: "pointer",
"&:hover": {
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 contactId = selectedContact.id;
const userId = localStorage.getItem("userId");
const username = localStorage.getItem("username");
const token = localStorage.getItem("token");
return (
<Paper variant="outlined" className={classes.newMessageBox}>
<MoodIcon className={classes.sendMessageIcon} />
<AttachFileIcon className={classes.sendMessageIcon} />
<div className={classes.messageInputWrapper}>
<InputBase
className={classes.messageInput}
placeholder="Escreva uma mensagem"
const mediaInitialState = { preview: "", raw: "", name: "" };
const [media, setMedia] = useState(mediaInitialState);
const [inputMessage, setInputMessage] = useState("");
const [showEmoji, setShowEmoji] = useState(false);
const [loading, setLoading] = useState(false);
useEffect(() => {
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>
<SendIcon className={classes.sendMessageIcon} />
</Paper>
);
<span>
{media.name}
{/* <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;

View File

@@ -129,6 +129,12 @@ const useStyles = makeStyles(theme => ({
boxShadow: "0 2px 2px #808888",
},
textContentItem: {
alignSelf: "middle",
overflowWrap: "break-word",
padding: "3px 80px 6px 6px",
},
messageMedia: {
objectFit: "cover",
width: 250,
@@ -139,11 +145,6 @@ const useStyles = makeStyles(theme => ({
borderBottomRightRadius: 8,
},
textContentItem: {
overflowWrap: "break-word",
padding: "0px 60px 10px 0px",
},
timestamp: {
fontSize: 11,
position: "absolute",
@@ -168,6 +169,15 @@ const useStyles = makeStyles(theme => ({
alignSelf: "center",
marginLeft: "0px",
},
ackIcons: {
fontSize: 18,
},
ackDoneAllIcon: {
color: green[500],
fontSize: 18,
},
}));
const MessagesList = ({ selectedContact }) => {
@@ -264,8 +274,9 @@ const MessagesList = ({ selectedContact }) => {
setMessagesList(prevState => {
let aux = [...prevState];
let messageIndex = aux.findIndex(message => message.id === id);
aux[messageIndex].ack = message.ack;
if (messageIndex) {
aux[messageIndex].ack = message.ack;
}
return aux;
});
};
@@ -307,16 +318,18 @@ const MessagesList = ({ selectedContact }) => {
const renderMessageAck = message => {
if (message.ack === 0) {
return <AccessTimeIcon fontSize="small" />;
return <AccessTimeIcon fontSize="small" className={classes.ackIcons} />;
}
if (message.ack === 1) {
return <DoneIcon fontSize="small" />;
return <DoneIcon fontSize="small" className={classes.ackIcons} />;
}
if (message.ack === 2) {
return <DoneAllIcon fontSize="small" />;
return <DoneAllIcon fontSize="small" className={classes.ackIcons} />;
}
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 = () => {
if (messagesList.length > 0) {
let viewMessagesList = [];
messagesList.forEach((message, index) => {
const viewMessagesList = messagesList.map((message, index) => {
if (message.userId === 0) {
viewMessagesList.push(
return [
renderDailyTimestamps(message, index),
<div className={classes.messageLeft} key={message.id}>
{message.mediaUrl && checkMessaageMedia(message)}
@@ -387,10 +399,10 @@ const MessagesList = ({ selectedContact }) => {
.format("HH:mm")}
</span>
</div>
</div>
);
</div>,
];
} else {
viewMessagesList.push(
return [
renderDailyTimestamps(message, index),
<div className={classes.messageRight} key={message.id}>
{message.mediaUrl && checkMessaageMedia(message)}
@@ -403,8 +415,8 @@ const MessagesList = ({ selectedContact }) => {
{renderMessageAck(message)}
</span>
</div>
</div>
);
</div>,
];
}
});
return viewMessagesList;