1
1
import { User , Message , Conversation } from "./models/index.js" ;
2
2
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
+
3
10
const socket = io . connect ( `/messages/socket` , { rememberTransport : false } ) ;
11
+ const current = await User . getCurrent ( ) ;
4
12
5
13
$ ( document ) . ready ( async function ( ) {
6
- // DOM element references
7
- const chatUsername = document . getElementById ( 'chat-username' ) ;
8
- const chatMessages = document . getElementById ( 'chat-messages' ) ;
9
14
const messageInput = document . getElementById ( 'message' ) ;
10
- const sendButton = document . getElementById ( 'send' ) ;
11
15
let selectedChatItem = null ;
12
16
13
17
const chatList = document . getElementById ( 'chat-list' ) ;
14
- // Get all conversations and display them
18
+
15
19
const conversations = await Conversation . getAll ( ) ;
16
20
for ( const conversation of conversations ) {
17
21
console . log ( conversation ) ;
18
22
await conversation . loadHistory ( ) ;
19
23
displayConversation ( conversation ) ;
20
24
}
21
25
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">×</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">×</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">×</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
+
22
118
/**
23
119
* Display a conversation in the chat list
24
120
* @param {Conversation } conversation - The conversation to display
25
121
*/
26
122
function displayConversation ( conversation ) {
27
- const item = document . createElement ( 'li' ) ;
28
- item . classList . add ( 'chat-item' ) ;
123
+ const item = document . createElement ( 'div' ) ;
29
124
item . setAttribute ( 'data-chat-id' , conversation . id ) ;
30
125
31
- const latestMessage = conversation . getLastMessage ( ) ;
126
+ const latestMessage = conversation . latestMessage ;
32
127
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 ( ) {
41
153
if ( selectedChatItem ) selectedChatItem . classList . remove ( 'selected' ) ;
42
-
43
154
this . classList . add ( 'selected' ) ;
44
155
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 ;
60
163
61
164
conversation . read ( ) ;
62
165
} ) ;
63
166
}
64
167
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 ) ;
68
193
messageInput . addEventListener ( 'keypress' , function ( event ) {
69
194
if ( event . key === 'Enter' ) sendMessage ( ) ;
70
195
} ) ;
71
196
72
- /**
73
- * Send a message
74
- */
75
197
function sendMessage ( ) {
76
- let messageToSend = messageInput . value ;
198
+ const messageInput = document . getElementById ( 'message' ) ;
77
199
if ( messageInput . value . trim ( ) === '' ) return ;
78
- messageInput . value = "" ;
200
+
201
+ const messageToSend = messageInput . value ;
202
+ messageInput . value = '' ;
203
+
79
204
socket . emit ( 'message' , {
80
205
'conversation_id' : selectedChatItem . getAttribute ( 'data-chat-id' ) ,
81
206
'content' : messageToSend
82
207
} ) ;
83
208
}
84
209
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
104
210
socket . on ( 'new_message' , async function ( data ) {
105
211
const message = await Message . fromJson ( data ) ;
106
- console . log ( message ) ;
212
+ message . conversation . history . push ( message ) ;
107
213
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 ;
109
217
socket . emit ( 'read_message' , { 'message_id' : message . id } ) ;
110
218
}
111
219
} ) ;
112
220
113
221
// 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
+ // });
122
230
123
231
// Event listener for message edits
124
232
socket . on ( 'edit' , data => {
@@ -140,27 +248,4 @@ $(document).ready(async function () {
140
248
141
249
socket . on ( 'new_conversation' , async data => displayConversation ( await Conversation . fromJson ( data ) ) ) ;
142
250
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
-
166
251
} ) ;
0 commit comments