Skip to content

Commit 2acfdd1

Browse files
committed
Message Send with Attachment Successfully
1 parent 93ef9ba commit 2acfdd1

File tree

11 files changed

+1524
-308
lines changed

11 files changed

+1524
-308
lines changed

app.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ const mongoose = require("mongoose");
44
const dotenv = require("dotenv");
55
const path = require("path");
66
const cookieParser = require("cookie-parser");
7+
const http = require("http");
8+
const moment = require("moment");
79

810
// Internal Dependencies:
911
const {
@@ -15,8 +17,16 @@ const userRouter = require("./router/userRouter");
1517
const inboxRouter = require("./router/inboxRouter");
1618

1719
const app = express();
20+
const server = http.createServer(app);
1821
dotenv.config();
1922

23+
// socket creation
24+
const io = require("socket.io")(server);
25+
global.io = io;
26+
27+
// Moment Set as application local:
28+
app.locals.moment = moment;
29+
2030
// Database Connection
2131
mongoose
2232
.connect(process.env.DB_CONNECTION_URL, {

controller/inboxController.js

Lines changed: 66 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,72 @@ const getMessages = async (req, res, next) => {
121121

122122
// Send Message:
123123
const sendMessage = async (req, res, next) => {
124-
res.status(200).json({ msg: "File Uploaded Successfully!" });
124+
if (req.body.message || (req.files && req.files.length > 0)) {
125+
try {
126+
// save message text/attachment in database
127+
let attachments = null;
128+
129+
if (req.files && req.files.length > 0) {
130+
attachments = [];
131+
132+
req.files.forEach((file) => {
133+
attachments.push(file.filename);
134+
});
135+
}
136+
137+
const newMessage = new Message({
138+
text: req.body.message,
139+
attachment: attachments,
140+
sender: {
141+
id: req.user.userid,
142+
name: req.user.username,
143+
avatar: req.user.avatar || null,
144+
},
145+
receiver: {
146+
id: req.body.receiverId,
147+
name: req.body.receiverName,
148+
avatar: req.body.avatar || null,
149+
},
150+
conversation_id: req.body.conversationId,
151+
});
152+
153+
const result = await newMessage.save();
154+
155+
// emit socket event
156+
global.io.emit("new_message", {
157+
message: {
158+
conversation_id: req.body.conversationId,
159+
sender: {
160+
id: req.user.userid,
161+
name: req.user.username,
162+
avatar: req.user.avatar || null,
163+
},
164+
message: req.body.message,
165+
attachment: attachments,
166+
date_time: result.date_time,
167+
},
168+
});
169+
170+
res.status(200).json({
171+
message: "Successful!",
172+
data: result,
173+
});
174+
} catch (err) {
175+
res.status(500).json({
176+
errors: {
177+
common: {
178+
msg: err.message,
179+
},
180+
},
181+
});
182+
}
183+
} else {
184+
res.status(500).json({
185+
errors: {
186+
common: "message text or attachment is required!",
187+
},
188+
});
189+
}
125190
};
126191

127192
// Module Export:

package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,9 @@
2626
"express-validator": "^6.11.1",
2727
"http-errors": "^1.8.0",
2828
"jsonwebtoken": "^8.5.1",
29+
"moment": "^2.29.1",
2930
"mongoose": "^5.12.13",
30-
"multer": "^1.4.2"
31+
"multer": "^1.4.2",
32+
"socket.io": "^4.1.2"
3133
}
3234
}

public/js/socket.io.min.js

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

test.ejs

Lines changed: 0 additions & 163 deletions
Original file line numberDiff line numberDiff line change
@@ -1,163 +0,0 @@
1-
2-
<div id="chat-container">
3-
<div id="search-container">
4-
<input type="text" placeholder="Search" />
5-
</div>
6-
7-
<div class="new-message-container" onclick="openModal()">
8-
<a href="#">+</a>
9-
</div>
10-
11-
<div id="chat-title">
12-
<span id="conversation-partner"></span>
13-
<img src="./images/trash.png" alt="Delete Conversation" />
14-
</div>
15-
16-
<!-- placeholder div if no messages are in messages area -->
17-
<div id="chat-message-list">
18-
<div class="nothing">select a conversation</div>
19-
</div>
20-
21-
<!-- send message form -->
22-
<form id="chat-form" method="post" enctype="multipart/form-data">
23-
<label for="attachment"><img src="./images/attachment.png" alt="Add Attachment" /></label>
24-
<input type="file" multiple name="attachment" class="hide" id="attachment" />
25-
<input type="text" name="message" placeholder="Type a message" autocomplete="off" />
26-
</form>
27-
28-
</div>
29-
<%- include('./partials/add-conversation-modal.ejs'); %>
30-
31-
<!-- import socket io client from cdn -->
32-
<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/3.1.3/socket.io.min.js"></script>
33-
34-
<script>
35-
const form = document.querySelector('#chat-form');
36-
const messageContainer = document.querySelector('#chat-message-list');
37-
const chatTitleContainer = document.querySelector('#conversation-partner');
38-
const loggedinUserId = '<%= loggedInUser.userid %>';
39-
const loggedinUserName = '<%= loggedInUser.username %>';
40-
let participant = null; // selected conversation participant object
41-
let current_conversation_id; // selected conversation id
42-
43-
// socket initialization
44-
const socket = io('<%= process.env.APP_URL %>');
45-
46-
// handle new/live incoming message from socket
47-
socket.on("new_message", data => {
48-
// only respond if current conversation is open in any client
49-
if(data.message.conversation_id == current_conversation_id) {
50-
// message class
51-
const messageClass = data.message.sender.id === loggedinUserId ? 'you-message' : 'other-message';
52-
53-
const senderAvatar = data.message.sender.avatar ? `<img src="./uploads/avatars/${data.message.sender.avatar}" alt="${data.message.sender.name}" />` : `<img src="./images/nophoto.png" alt="${data.message.sender.name}" />`;
54-
// message attachments
55-
let attachments = '<div class="attachments">';
56-
if(data.message.attachment && data.message.attachment.length > 0) {
57-
data.message.attachment.forEach(attachment => {
58-
attachments += `<img src="./uploads/attachments/${attachment}" /> `;
59-
});
60-
}
61-
attachments += '</div>';
62-
let messageHTML;
63-
// do not show avatar for loggedin user
64-
if(data.message.sender.id == loggedinUserId) {
65-
messageHTML = `<div class="message-row ${messageClass}"><div class="message-content">
66-
<div class="message-text">${data.message.message}</div>
67-
${attachments}
68-
<div class="message-time">${moment(data.message.date_time).fromNow()}</div>
69-
</div></div>`;
70-
} else {
71-
messageHTML = `<div class="message-row ${messageClass}"><div class="message-content">
72-
${senderAvatar}
73-
<div class="message-text">${data.message.message}</div>
74-
${attachments}
75-
<div class="message-time">${moment(data.message.date_time).fromNow()}</div>
76-
</div></div>`;
77-
}
78-
// append the inoming message to message area as last item
79-
document.querySelector('#chat-message-list > .message-row:first-child').insertAdjacentHTML('beforeBegin', messageHTML);
80-
}
81-
});
82-
83-
// get messages of a conversation
84-
async function getMessages(conversation_id, current_conversation_name){
85-
// messages failure toast
86-
const messagesFailureToast = Toastify({
87-
text: "Error loading messages!",
88-
duration: 1000,
89-
});
90-
let response = await fetch(`/inbox/messages/${conversation_id}`);
91-
const result= await response.json();
92-
if(!result.errors && result.data) {
93-
form.style.visibility = 'visible';
94-
95-
const {data, user, conversation_id} = result;
96-
participant = data.participant;
97-
current_conversation_id = conversation_id;
98-
if(data.messages) {
99-
let allMessages = '';
100-
if(data.messages.length > 0) {
101-
data.messages.forEach((message) => {
102-
let senderAvatar = message.sender.avatar ? `./uploads/avatars/${message.sender.avatar}` : './images/nophoto.png';
103-
const messageClass = message.sender.id === loggedinUserId ? 'you-message' : 'other-message';
104-
const showAvatar = message.sender.id === loggedinUserId ? '' : `<img src="${senderAvatar}" alt="${message.sender.name}" />`;
105-
// message attachments
106-
let attachments = '<div class="attachments">';
107-
if(message.attachment && message.attachment.length > 0) {
108-
message.attachment.forEach(attachment => {
109-
attachments += `<img src="./uploads/attachments/${attachment}" /> `;
110-
});
111-
}
112-
attachments += '</div>';
113-
// final message html
114-
let messageHTML = `<div class="message-row ${messageClass}"><div class="message-content">
115-
${showAvatar}
116-
<div class="message-text">${message.text}</div>
117-
${attachments}
118-
<div class="message-time">${moment(message.date_time).fromNow()}</div>
119-
</div></div>`;
120-
allMessages += messageHTML;
121-
messageContainer.innerHTML = allMessages;
122-
});
123-
} else if(data.messages.length === 0) {
124-
messageContainer.innerHTML = '<div class="message-row"></div>';
125-
}
126-
chatTitleContainer.textContent = current_conversation_name;
127-
}
128-
} else {
129-
messagesFailureToast.showToast();
130-
}
131-
}
132-
133-
// message sending
134-
form.onsubmit = async function (event) {
135-
event.preventDefault();
136-
const sendMessageFailureToast = Toastify({
137-
text: "Error sending message",
138-
duration: 1000,
139-
});
140-
// prepare the form data
141-
const formData = new FormData(form);
142-
formData.append('receiverId', participant.id);
143-
formData.append('receiverName', participant.name);
144-
formData.append('avatar', participant.avatar || '');
145-
formData.append('conversationId', current_conversation_id);
146-
147-
// send the request to server
148-
let response = await fetch("/inbox/message", {
149-
method: "POST",
150-
body: formData,
151-
});
152-
// get response
153-
let result = await response.json();
154-
if (!result.errors) {
155-
form.reset(); // reset the form
156-
} else {
157-
sendMessageFailureToast.showToast();
158-
}
159-
}
160-
161-
</script>
162-
</body>
163-
</html>

test.js

Lines changed: 0 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -1,62 +0,0 @@
1-
// external imports
2-
const multer = require("multer");
3-
const path = require("path");
4-
const createError = require("http-errors");
5-
6-
function uploader(
7-
subfolder_path,
8-
allowed_file_types,
9-
max_file_size,
10-
max_number_of_files,
11-
error_msg
12-
) {
13-
// File upload folder
14-
const UPLOADS_FOLDER = `${__dirname}/../public/uploads/${subfolder_path}/`;
15-
16-
// define the storage
17-
const storage = multer.diskStorage({
18-
destination: (req, file, cb) => {
19-
cb(null, UPLOADS_FOLDER);
20-
},
21-
filename: (req, file, cb) => {
22-
const fileExt = path.extname(file.originalname);
23-
const fileName =
24-
file.originalname
25-
.replace(fileExt, "")
26-
.toLowerCase()
27-
.split(" ")
28-
.join("-") +
29-
"-" +
30-
Date.now();
31-
32-
cb(null, fileName + fileExt);
33-
},
34-
});
35-
36-
// preapre the final multer upload object
37-
const upload = multer({
38-
storage: storage,
39-
limits: {
40-
fileSize: max_file_size,
41-
},
42-
fileFilter: (req, file, cb) => {
43-
if (req.files.length > max_number_of_files) {
44-
cb(
45-
createError(
46-
`Maximum ${max_number_of_files} files are allowed to upload!`
47-
)
48-
);
49-
} else {
50-
if (allowed_file_types.includes(file.mimetype)) {
51-
cb(null, true);
52-
} else {
53-
cb(createError(error_msg));
54-
}
55-
}
56-
},
57-
});
58-
59-
return upload;
60-
}
61-
62-
module.exports = uploader;

utilities/multipleUploader.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ const multipleUploader = (
2020
cb(null, UPLOADS_FOLDER);
2121
},
2222
filename: (req, file, cb) => {
23-
console.log(req.files);
2423
const fileExt = path.extname(file.originalname);
2524
const fileName =
2625
file.originalname

0 commit comments

Comments
 (0)