Skip to content

Commit cd63f7a

Browse files
authored
Merge branch 'master' into lastminutechanges
2 parents c05f898 + 1ea51d5 commit cd63f7a

File tree

2 files changed

+180
-92
lines changed

2 files changed

+180
-92
lines changed

app/static/js/messages.js

Lines changed: 177 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -1,124 +1,232 @@
11
import { User, Message, Conversation } from "./models/index.js";
22

3+
const timestampFormat = {
4+
sameDay: '[Today] LT', // Today, show only the time
5+
lastDay: '[Yesterday] LT', // Yesterday, show only the time
6+
lastWeek: 'dddd LT', // Within the last week, show the day and time
7+
sameElse: 'DD/MM/YYYY LT' // Older messages, show full date and time
8+
}
9+
310
const socket = io.connect(`/messages/socket`, { rememberTransport: false });
11+
const current = await User.getCurrent();
412

513
$(document).ready(async function () {
6-
// DOM element references
7-
const chatUsername = document.getElementById('chat-username');
8-
const chatMessages = document.getElementById('chat-messages');
914
const messageInput = document.getElementById('message');
10-
const sendButton = document.getElementById('send');
1115
let selectedChatItem = null;
1216

1317
const chatList = document.getElementById('chat-list');
14-
// Get all conversations and display them
18+
1519
const conversations = await Conversation.getAll();
1620
for (const conversation of conversations) {
1721
console.log(conversation);
1822
await conversation.loadHistory();
1923
displayConversation(conversation);
2024
}
2125

26+
const search1 = document.getElementById("search-input1");
27+
const result1 = document.getElementById("searchResults1");
28+
29+
search1.addEventListener("input", async function () {
30+
let limit = 0
31+
let request = await User.search(search1.value);
32+
let results = []
33+
if (request === []) {
34+
return
35+
}
36+
result1.innerHTML = '';
37+
38+
request.forEach((ele, i) => {
39+
let out = document.createElement("div");
40+
out.innerHTML += `<div class="list-group-item list-group-item-action card w-100 shadow-xss border-0 rounded-0 px-4 py-0">
41+
<div class="card-header d-flex justify-content-between align-items-center">
42+
<button aria-label="Close" class="close" type="button">
43+
<span aria-hidden="true">&times;</span>
44+
</button>
45+
<div class="card-body p-0 d-flex">
46+
<figure class="avatar me-3"><img alt="avater" class="shadow-sm rounded-circle w25"
47+
src=${request[i].pfp}></figure>
48+
<h3 class="fw-600 text-grey-900 font-xsss lh-28">${request[i].username}</h3>
49+
</div>
50+
</div>
51+
</div>`;
52+
out.addEventListener("click", async () => {
53+
await Conversation.createPrivate(ele.id)
54+
});
55+
results.push(out);
56+
});
57+
58+
results.forEach(ele => {
59+
result1.appendChild(ele);
60+
});
61+
});
62+
63+
const searchGroup = document.getElementById("search-group");
64+
const searchResultGroup = document.getElementById("searchResults2");
65+
let selectedUsers = [current]
66+
67+
searchGroup.addEventListener("input", async function () {
68+
let results = await User.search(searchGroup.value);
69+
results = results.filter(user => selectedUsers.find(u => u.id === user.id) === undefined);
70+
searchResultGroup.innerHTML = '';
71+
72+
results.forEach((ele, i) => {
73+
let out = document.createElement("div");
74+
out.innerHTML = `<div class="list-group-item list-group-item-action card w-100 shadow-xss border-0 rounded-0 px-4 py-0" style="z-index: 10;">
75+
<div class="card-header d-flex justify-content-between align-items-center">
76+
<button aria-label="Close" class="close" type="button">
77+
<span aria-hidden="true">&times;</span>
78+
</button>
79+
<div class="card-body p-0 d-flex">
80+
<figure class="avatar me-3"><img alt="avater" class="shadow-sm rounded-circle w25"
81+
src=${results[i].pfp}></figure>
82+
<h3 class="fw-600 text-grey-900 font-xsss lh-28">${results[i].username}</h3>
83+
</div>
84+
</div>
85+
</div>`;
86+
searchResultGroup.appendChild(out);
87+
out.addEventListener("click", async () => {
88+
selectedUsers.push(ele);
89+
out.remove();
90+
console.log(selectedUsers);
91+
document.getElementById("pending").innerHTML += `<div class="list-group-item list-group-item-action card w-100 shadow-xss border-0 rounded-0 px-4 py-0">
92+
<div class="card-header d-flex justify-content-between align-items-center">
93+
<button aria-label="Close" class="close" type="button">
94+
<span aria-hidden="true">&times;</span>
95+
</button>
96+
<div class="card-body p-0 d-flex">
97+
<figure class="avatar me-3"><img alt="avater" class="shadow-sm rounded-circle w25"
98+
src=${ele.pfp}></figure>
99+
<h3 class="fw-600 text-grey-900 font-xsss lh-28">${ele.username}</h3>
100+
</div>
101+
</div>
102+
</div>`
103+
});
104+
});
105+
});
106+
107+
document.getElementById('close-group').onclick = async () => {
108+
selectedUsers = [current];
109+
console.log(selectedUsers)
110+
}
111+
112+
document.getElementById('submit-group').onclick = async () => {
113+
console.log(selectedUsers);
114+
const groupName = document.getElementById('group-name').value;
115+
Conversation.createGroup(groupName, selectedUsers.map(user => user.id));
116+
}
117+
22118
/**
23119
* Display a conversation in the chat list
24120
* @param {Conversation} conversation - The conversation to display
25121
*/
26122
function displayConversation(conversation) {
27-
const item = document.createElement('li');
28-
item.classList.add('chat-item');
123+
const item = document.createElement('div');
29124
item.setAttribute('data-chat-id', conversation.id);
30125

31-
const latestMessage = conversation.getLastMessage();
126+
const latestMessage = conversation.latestMessage;
32127
item.innerHTML =
33-
`<div class='chat-info'>
34-
<div class='chat-name'>${conversation.name}</div>
35-
<div class='chat-preview'>${latestMessage === undefined ? '' : latestMessage.content}</div>
36-
<div class='timestamp'>${latestMessage === undefined ? '' : latestMessage.timestamp.local().calendar()}</div>
37-
</div>`;
38-
chatList.appendChild(item);
39-
40-
item.addEventListener('click', function () {
128+
DOMPurify.sanitize(latestMessage ?
129+
`<div class="card w-100 border-0 py-2 px-3">
130+
<div class="card-body p-0 d-flex">
131+
<figure class="avatar me-3"><img src="${conversation.image}" alt="avater" class="shadow-sm rounded-circle w40"></figure>
132+
<div>
133+
<h4 class="fw-700 text-grey-900 font-xsss">${conversation.name}</h4>
134+
<span class="d-block font-xssss fw-500 lh-3 text-grey-500">${latestMessage.content}</span>
135+
</div>
136+
<span class="ms-auto font-xssss fw-600 text-grey-700">${latestMessage.timestamp.calendar(null, timestampFormat)}</span>
137+
</div>
138+
</div>` :
139+
`<div class="card w-100 border-0 py-2 px-3">
140+
<div class="card-body p-0 d-flex">
141+
<figure class="avatar me-3"><img src="${conversation.image}" alt="avater" class="shadow-sm rounded-circle w40"></figure>
142+
<div>
143+
<h4 class="fw-700 text-grey-900 font-xsss">${conversation.name}</h4>
144+
<span class="d-block font-xssss fw-500 lh-3 text-grey-500">No messages yet.</span>
145+
</div>
146+
<span class="d-none ms-auto font-xssss fw-600 text-grey-700"></span>
147+
</div>
148+
</div>`);
149+
150+
document.getElementById('conversations').appendChild(item);
151+
152+
item.addEventListener('click', async function () {
41153
if (selectedChatItem) selectedChatItem.classList.remove('selected');
42-
43154
this.classList.add('selected');
44155
selectedChatItem = this;
45-
chatUsername.innerText = this.querySelector('.chat-name').innerText;
46-
chatMessages.innerHTML = '';
47-
48-
conversation.history.forEach(message => {
49-
const li = document.createElement('li');
50-
li.id = message.id.toLocaleString();
51-
li.classList.add('message');
52-
li.innerHTML = `
53-
<span class="sender">${message.sender.username}</span>
54-
<span class="content">${message.content}</span>
55-
<span class="timestamp">(${moment.utc(message.timestamp).local().calendar()})</span>
56-
<span class="bluetick">READ: ${message.readByAll()}</span>
57-
`;
58-
chatMessages.appendChild(li);
59-
})
156+
157+
document.getElementById('chat-pfp').src = conversation.image;
158+
document.getElementById('chat-username').innerText = conversation.name;
159+
160+
await conversation.loadHistory();
161+
displayMessages(conversation);
162+
document.getElementById('chat-wrap').scrollTop = document.getElementById('chat-wrap').scrollHeight;
60163

61164
conversation.read();
62165
});
63166
}
64167

65-
// Send message button click event
66-
sendButton.addEventListener('click', sendMessage);
67-
// Message input keypress event
168+
function displayMessages(conversation) {
169+
const messagesDiv = document.getElementById('messages');
170+
messagesDiv.innerHTML = ``;
171+
conversation.history.forEach(msg => messagesDiv.prepend(renderMessage(msg)))
172+
}
173+
174+
function renderMessage(message) {
175+
const div = document.createElement('div');
176+
div.classList = `message-item ${message.sender.id === current.id ? `outgoing-message` : ``}`;
177+
div.setAttribute('data-message-id', message.id);
178+
179+
div.innerHTML = DOMPurify.sanitize(
180+
`<div class="message-user">
181+
<figure class="avatar"><img src="${message.sender.pfp}" alt="avatar"></figure>
182+
<div><h5>${message.sender.username}</h5>
183+
<div class="time">${message.timestamp.calendar(null, timestampFormat)}</div>
184+
</div>
185+
</div>
186+
<div class="message-wrap">${message.content}</div>`
187+
);
188+
189+
return div;
190+
}
191+
192+
document.getElementById('message-button').addEventListener('click', sendMessage);
68193
messageInput.addEventListener('keypress', function (event) {
69194
if (event.key === 'Enter') sendMessage();
70195
});
71196

72-
/**
73-
* Send a message
74-
*/
75197
function sendMessage() {
76-
let messageToSend = messageInput.value;
198+
const messageInput = document.getElementById('message');
77199
if (messageInput.value.trim() === '') return;
78-
messageInput.value = "";
200+
201+
const messageToSend = messageInput.value;
202+
messageInput.value = '';
203+
79204
socket.emit('message', {
80205
'conversation_id': selectedChatItem.getAttribute('data-chat-id'),
81206
'content': messageToSend
82207
});
83208
}
84209

85-
/**
86-
* Create a message element
87-
* @param {Message} message - The message object
88-
* @returns {HTMLElement} - The created message element
89-
*/
90-
function createMessageElement(message) {
91-
const li = document.createElement("li");
92-
li.id = message.id.toLocaleString();
93-
li.classList.add('message');
94-
li.innerHTML = `
95-
<span class="sender">${message.sender.username}</span>
96-
<span class="content">${message.content}</span>
97-
<span class="timestamp">(${moment.utc(message.timestamp).local().calendar()})</span>
98-
<span class="bluetick">READ: ${message.readByAll()}</span>
99-
`;
100-
return li;
101-
}
102-
103-
// Event listener for new messages
104210
socket.on('new_message', async function (data) {
105211
const message = await Message.fromJson(data);
106-
console.log(message);
212+
message.conversation.history.push(message);
107213
if (selectedChatItem && selectedChatItem.getAttribute('data-chat-id') === message.conversation.id) {
108-
chatMessages.appendChild(createMessageElement(message));
214+
console.log(message)
215+
document.getElementById('messages').appendChild(renderMessage(message));
216+
document.getElementById('chat-wrap').scrollTop = document.getElementById('chat-wrap').scrollHeight;
109217
socket.emit('read_message', {'message_id': message.id});
110218
}
111219
});
112220

113221
// Event listener for bluetick (read confirmation)
114-
socket.on('bluetick', data => {
115-
console.log(data);
116-
data.forEach(messageId => {
117-
const element = document.getElementById(messageId.toLocaleString());
118-
if (element === undefined) return;
119-
element.querySelector('.bluetick').innerText = `READ: True`;
120-
});
121-
});
222+
// socket.on('bluetick', data => {
223+
// console.log(data);
224+
// data.forEach(messageId => {
225+
// const element = document.getElementById(messageId.toLocaleString());
226+
// if (element === undefined) return;
227+
// element.querySelector('.bluetick').innerText = `READ: True`;
228+
// });
229+
// });
122230

123231
// Event listener for message edits
124232
socket.on('edit', data => {
@@ -140,27 +248,4 @@ $(document).ready(async function () {
140248

141249
socket.on('new_conversation', async data => displayConversation(await Conversation.fromJson(data)));
142250

143-
// Create conversation button click event
144-
const createConversationButton = document.getElementById('create-conversation');
145-
createConversationButton.addEventListener('click', function () {
146-
const targetUserId = prompt('Enter the user ID to create a conversation with:');
147-
if (!targetUserId) return;
148-
149-
Conversation.createPrivate(targetUserId);
150-
});
151-
152-
// Create group conversation button click event
153-
const createGroupConversationButton = document.getElementById('create-group-conversation');
154-
createGroupConversationButton.addEventListener('click', function () {
155-
const groupName = prompt('Enter the group name:');
156-
if (!groupName) return;
157-
158-
const userIDsString = prompt('Enter the user IDs (comma-separated) for the group members:');
159-
if (!userIDsString) return;
160-
161-
const userIDs = userIDsString.split(',').map(id => id.trim());
162-
163-
Conversation.createGroup(groupName, userIDs);
164-
});
165-
166251
});

app/templates/messages.html

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,9 @@ <h5 class="modal-title" id="group-create">Add Users</h5>
5959
<div class="modal-body">
6060
<form action="#" class="float-left header-search ms-3">
6161
<div class="form-group mb-0 icon-input">
62+
<input class="mb-3 bg-grey border-0 lh-32 pt-2 pb-2 ps-5 pe-3 font-xsss fw-600 rounded-xl w350 theme-dark-bg"
63+
id="group-name" placeholder="Group name"
64+
type="text" required>
6265
<input class="bg-grey border-0 lh-32 pt-2 pb-2 ps-5 pe-3 font-xsss fw-600 rounded-xl w350 theme-dark-bg"
6366
id="search-group" placeholder="Start typing to search.."
6467
type="text">

0 commit comments

Comments
 (0)