@realtimexsco/live-chat 1.0.0 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +935 -148
- package/dist/index.cjs +493 -220
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.mts +72 -3
- package/dist/index.d.ts +72 -3
- package/dist/index.mjs +487 -222
- package/dist/index.mjs.map +1 -1
- package/package.json +6 -1
package/dist/index.cjs
CHANGED
|
@@ -13,6 +13,7 @@ var tailwindMerge = require('tailwind-merge');
|
|
|
13
13
|
var lucideReact = require('lucide-react');
|
|
14
14
|
var classVarianceAuthority = require('class-variance-authority');
|
|
15
15
|
var EmojiPicker = require('emoji-picker-react');
|
|
16
|
+
var reactHotToast = require('react-hot-toast');
|
|
16
17
|
|
|
17
18
|
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
18
19
|
|
|
@@ -169,11 +170,28 @@ var init_socket_events_constants = __esm({
|
|
|
169
170
|
};
|
|
170
171
|
}
|
|
171
172
|
});
|
|
172
|
-
|
|
173
|
+
|
|
174
|
+
// src/store/useSocketStore.ts
|
|
175
|
+
var useSocketStore_exports = {};
|
|
176
|
+
__export(useSocketStore_exports, {
|
|
177
|
+
default: () => useSocketStore_default,
|
|
178
|
+
useSocketStore: () => useSocketStore
|
|
179
|
+
});
|
|
180
|
+
var useSocketStore, useSocketStore_default;
|
|
181
|
+
var init_useSocketStore = __esm({
|
|
182
|
+
"src/store/useSocketStore.ts"() {
|
|
183
|
+
init_useStore();
|
|
184
|
+
useSocketStore = exports.useChatStore;
|
|
185
|
+
useSocketStore_default = useSocketStore;
|
|
186
|
+
}
|
|
187
|
+
});
|
|
188
|
+
var getStore, SocketService, socketService; exports.socketService = void 0;
|
|
173
189
|
var init_socket_service = __esm({
|
|
174
190
|
"src/services/socket.service.ts"() {
|
|
175
191
|
init_socket_events_constants();
|
|
176
|
-
|
|
192
|
+
getStore = () => {
|
|
193
|
+
return (init_useSocketStore(), __toCommonJS(useSocketStore_exports)).useSocketStore;
|
|
194
|
+
};
|
|
177
195
|
SocketService = class {
|
|
178
196
|
constructor() {
|
|
179
197
|
this.socket = null;
|
|
@@ -192,7 +210,7 @@ var init_socket_service = __esm({
|
|
|
192
210
|
}
|
|
193
211
|
this.currentConfig = config;
|
|
194
212
|
this.isConnecting = true;
|
|
195
|
-
const { setConnecting, setConnected, setDisconnected, setError } =
|
|
213
|
+
const { setConnecting, setConnected, setDisconnected, setError } = getStore().getState();
|
|
196
214
|
setConnecting();
|
|
197
215
|
this.socket = socket_ioClient.io(config.url, config.options);
|
|
198
216
|
this.socket.on("connect", () => {
|
|
@@ -210,72 +228,72 @@ var init_socket_service = __esm({
|
|
|
210
228
|
});
|
|
211
229
|
this.socket.on(SOCKET_EVENTS.ERROR, (payload) => {
|
|
212
230
|
const message = typeof payload === "string" ? payload : payload?.message ?? "app_error";
|
|
213
|
-
|
|
231
|
+
getStore().getState().handleAppError(message);
|
|
214
232
|
});
|
|
215
233
|
this.socket.on(SOCKET_EVENTS.DM_RECEIVE, (payload) => {
|
|
216
|
-
|
|
234
|
+
getStore().getState().receivedDM(payload);
|
|
217
235
|
});
|
|
218
236
|
this.socket.on(SOCKET_EVENTS.GROUP_MESSAGE_RECEIVE, (payload) => {
|
|
219
|
-
|
|
237
|
+
getStore().getState().receivedGroupMessage(payload);
|
|
220
238
|
});
|
|
221
239
|
this.socket.on(SOCKET_EVENTS.CONVERSATION_UPDATE, (payload) => {
|
|
222
240
|
if (!payload) return;
|
|
223
|
-
|
|
241
|
+
getStore().getState().updateConversation(payload);
|
|
224
242
|
});
|
|
225
243
|
this.socket.on(SOCKET_EVENTS.DM_EDIT_RECEIVE, (payload) => {
|
|
226
|
-
|
|
244
|
+
getStore().getState().receivedEditDM(payload);
|
|
227
245
|
});
|
|
228
246
|
this.socket.on(SOCKET_EVENTS.DM_DELETE_ME, (payload) => {
|
|
229
|
-
|
|
247
|
+
getStore().getState().receivedDeleteForMe(payload);
|
|
230
248
|
});
|
|
231
249
|
this.socket.on(SOCKET_EVENTS.DM_DELETE_EVERYONE, (payload) => {
|
|
232
|
-
|
|
250
|
+
getStore().getState().receivedDeleteForEveryone(payload);
|
|
233
251
|
});
|
|
234
252
|
this.socket.on(SOCKET_EVENTS.DM_CLEAR_CHAT_SUCCESS, (payload) => {
|
|
235
|
-
|
|
253
|
+
getStore().getState().receivedDmClearChat(payload);
|
|
236
254
|
});
|
|
237
255
|
this.socket.on(SOCKET_EVENTS.GROUP_CLEAR_CHAT_SUCCESS, (payload) => {
|
|
238
|
-
|
|
256
|
+
getStore().getState().receivedGroupClearChat(payload);
|
|
239
257
|
});
|
|
240
258
|
this.socket.on(SOCKET_EVENTS.DM_DELETE_CONVERSATION_SUCCESS, (payload) => {
|
|
241
|
-
|
|
259
|
+
getStore().getState().receivedDmDeleteConversation(payload);
|
|
242
260
|
});
|
|
243
261
|
this.socket.on(SOCKET_EVENTS.GROUP_DELETE_CONVERSATION_SUCCESS, (payload) => {
|
|
244
|
-
|
|
262
|
+
getStore().getState().receivedGroupDeleteConversation(payload);
|
|
245
263
|
});
|
|
246
264
|
this.socket.on(SOCKET_EVENTS.DM_REACTION_ADD_RECEIVE, (payload) => {
|
|
247
|
-
|
|
265
|
+
getStore().getState().receivedReactionAdd(payload);
|
|
248
266
|
});
|
|
249
267
|
this.socket.on(SOCKET_EVENTS.DM_REACTION_REMOVE_RECEIVE, (payload) => {
|
|
250
|
-
|
|
268
|
+
getStore().getState().receivedReactionRemove(payload);
|
|
251
269
|
});
|
|
252
270
|
this.socket.on(SOCKET_EVENTS.DM_PIN_UPDATE, (payload) => {
|
|
253
|
-
|
|
271
|
+
getStore().getState().receivedPinUpdate(payload);
|
|
254
272
|
});
|
|
255
273
|
this.socket.on(SOCKET_EVENTS.DM_STAR_UPDATE, (payload) => {
|
|
256
|
-
|
|
274
|
+
getStore().getState().receivedStarUpdate(payload);
|
|
257
275
|
});
|
|
258
276
|
this.socket.on(SOCKET_EVENTS.DM_STATUS_UPDATE, (payload) => {
|
|
259
|
-
|
|
277
|
+
getStore().getState().receivedStatusUpdate(payload);
|
|
260
278
|
});
|
|
261
279
|
this.socket.on(SOCKET_EVENTS.DM_MARK_READ, (payload) => {
|
|
262
|
-
|
|
280
|
+
getStore().getState().receivedDMMarkRead(payload);
|
|
263
281
|
});
|
|
264
282
|
this.socket.on(SOCKET_EVENTS.GROUP_MESSAGE_READ, (payload) => {
|
|
265
|
-
|
|
283
|
+
getStore().getState().receivedGroupMarkRead(payload);
|
|
266
284
|
});
|
|
267
285
|
this.socket.on(SOCKET_EVENTS.DM_FORWARD_RECEIVE, (payload) => {
|
|
268
|
-
|
|
286
|
+
getStore().getState().receivedForward(payload);
|
|
269
287
|
});
|
|
270
288
|
this.socket.on(SOCKET_EVENTS.DM_TYPING_START, (payload) => {
|
|
271
|
-
|
|
289
|
+
getStore().getState().setTypingStart(payload);
|
|
272
290
|
});
|
|
273
291
|
this.socket.on(SOCKET_EVENTS.DM_TYPING_STOP, (payload) => {
|
|
274
|
-
|
|
292
|
+
getStore().getState().setTypingStop(payload);
|
|
275
293
|
});
|
|
276
294
|
this.socket.on(SOCKET_EVENTS.ONLINE_USERS_LIST, (payload) => {
|
|
277
295
|
const users = payload?.onlineUsers || [];
|
|
278
|
-
|
|
296
|
+
getStore().getState().setOnlineUsers(users);
|
|
279
297
|
});
|
|
280
298
|
this.socket.on(SOCKET_EVENTS.USER_ONLINE, () => {
|
|
281
299
|
this.emitGetOnlineUsers();
|
|
@@ -284,49 +302,49 @@ var init_socket_service = __esm({
|
|
|
284
302
|
this.emitGetOnlineUsers();
|
|
285
303
|
});
|
|
286
304
|
this.socket.on(SOCKET_EVENTS.CONVERSATION_STARRED_PINNED_COUNT_UPDATE, (payload) => {
|
|
287
|
-
|
|
305
|
+
getStore().getState().receivedCountUpdate(payload);
|
|
288
306
|
});
|
|
289
307
|
this.socket.on(SOCKET_EVENTS.GROUP_EDIT_RECEIVE, (payload) => {
|
|
290
|
-
|
|
308
|
+
getStore().getState().receivedEditDM(payload);
|
|
291
309
|
});
|
|
292
310
|
this.socket.on(SOCKET_EVENTS.GROUP_STATUS_UPDATE, (payload) => {
|
|
293
|
-
|
|
311
|
+
getStore().getState().receivedGroupStatusUpdate(payload);
|
|
294
312
|
});
|
|
295
313
|
this.socket.on(SOCKET_EVENTS.GROUP_MESSAGE_DELIVERED, (payload) => {
|
|
296
|
-
|
|
314
|
+
getStore().getState().receivedGroupStatusUpdate({
|
|
297
315
|
...payload,
|
|
298
316
|
status: payload?.status || "delivered"
|
|
299
317
|
});
|
|
300
318
|
});
|
|
301
319
|
this.socket.on(SOCKET_EVENTS.GROUP_REACTION_ADD_RECEIVE, (payload) => {
|
|
302
|
-
|
|
320
|
+
getStore().getState().receivedGroupReactionAdd(payload);
|
|
303
321
|
});
|
|
304
322
|
this.socket.on(SOCKET_EVENTS.GROUP_TYPING_START, (payload) => {
|
|
305
|
-
|
|
323
|
+
getStore().getState().setTypingStart(payload);
|
|
306
324
|
});
|
|
307
325
|
this.socket.on(SOCKET_EVENTS.GROUP_TYPING_STOP, (payload) => {
|
|
308
|
-
|
|
326
|
+
getStore().getState().setTypingStop(payload);
|
|
309
327
|
});
|
|
310
328
|
this.socket.on(SOCKET_EVENTS.GROUP_REACTION_REMOVE_RECEIVE, (payload) => {
|
|
311
|
-
|
|
329
|
+
getStore().getState().receivedGroupReactionRemove(payload);
|
|
312
330
|
});
|
|
313
331
|
this.socket.on(SOCKET_EVENTS.GROUP_DELETE_ME, (payload) => {
|
|
314
|
-
|
|
332
|
+
getStore().getState().receivedDeleteForMe(payload);
|
|
315
333
|
});
|
|
316
334
|
this.socket.on(SOCKET_EVENTS.GROUP_DELETE_EVERYONE, (payload) => {
|
|
317
|
-
|
|
335
|
+
getStore().getState().receivedDeleteForEveryone(payload);
|
|
318
336
|
});
|
|
319
337
|
this.socket.on(SOCKET_EVENTS.GROUP_PIN_UPDATE, (payload) => {
|
|
320
|
-
|
|
338
|
+
getStore().getState().receivedPinUpdate(payload);
|
|
321
339
|
});
|
|
322
340
|
this.socket.on(SOCKET_EVENTS.GROUP_STAR_UPDATE, (payload) => {
|
|
323
|
-
|
|
341
|
+
getStore().getState().receivedStarUpdate(payload);
|
|
324
342
|
});
|
|
325
343
|
this.socket.on(SOCKET_EVENTS.GROUP_FORWARD_RECEIVE, (payload) => {
|
|
326
|
-
|
|
344
|
+
getStore().getState().receivedForward(payload);
|
|
327
345
|
});
|
|
328
346
|
this.socket.on(SOCKET_EVENTS.GROUP_LEAVE_RECEIVE, (payload) => {
|
|
329
|
-
|
|
347
|
+
getStore().getState().receivedGroupLeave(payload);
|
|
330
348
|
});
|
|
331
349
|
}
|
|
332
350
|
disconnect() {
|
|
@@ -456,6 +474,176 @@ var init_socket_service = __esm({
|
|
|
456
474
|
}
|
|
457
475
|
});
|
|
458
476
|
|
|
477
|
+
// src/store/helpers/store.helpers.ts
|
|
478
|
+
var findConversationIdByMessageId, normalizeId, normalizeUnreadCount, incrementUnreadCount, emptyMessagesMetadata, extractMessageData, mergeLastMessage, getLatestMessageFromList, resolveConversationLastMessage, syncConversationLastMessage, isGroupConversation, findConversationById, resolveIsGroupFromPayload, getOtherParticipantId, findConversationForChannel, resolveConversationIdForChannel, getMessageContent, getMessageTimestamp, isOwnMessage, STATUS_RANK, pickBetterMessageStatus, mergeChatMessages;
|
|
479
|
+
var init_store_helpers = __esm({
|
|
480
|
+
"src/store/helpers/store.helpers.ts"() {
|
|
481
|
+
findConversationIdByMessageId = (messagesByConversation, messageId) => {
|
|
482
|
+
return Object.keys(messagesByConversation).find(
|
|
483
|
+
(cid) => messagesByConversation[cid].some(
|
|
484
|
+
(m) => String(m._id || m.id) === String(messageId)
|
|
485
|
+
)
|
|
486
|
+
);
|
|
487
|
+
};
|
|
488
|
+
normalizeId = (id) => String(id?._id || id?.id || id || "");
|
|
489
|
+
normalizeUnreadCount = (unreadCount) => {
|
|
490
|
+
if (typeof unreadCount === "number") return unreadCount;
|
|
491
|
+
return 0;
|
|
492
|
+
};
|
|
493
|
+
incrementUnreadCount = (unreadCount) => normalizeUnreadCount(unreadCount) + 1;
|
|
494
|
+
emptyMessagesMetadata = () => ({
|
|
495
|
+
nextCursor: null,
|
|
496
|
+
hasMore: false,
|
|
497
|
+
isFirstPage: true
|
|
498
|
+
});
|
|
499
|
+
extractMessageData = (payload) => {
|
|
500
|
+
const messageId = payload.messageId || payload._id || payload.message?._id || payload.message?.id;
|
|
501
|
+
const content = payload.content || payload.message?.content;
|
|
502
|
+
const conversationId = payload.conversationId || payload.message?.conversationId || payload.message?.conversation_id;
|
|
503
|
+
const status = payload.status || payload.message?.status;
|
|
504
|
+
const isPinned = payload.isPinned ?? payload.message?.isPinned;
|
|
505
|
+
const isStarred = payload.isStarred ?? payload.message?.isStarred;
|
|
506
|
+
return { messageId, content, conversationId, status, isPinned, isStarred };
|
|
507
|
+
};
|
|
508
|
+
mergeLastMessage = (existing, incoming) => {
|
|
509
|
+
if (!incoming) return existing ?? null;
|
|
510
|
+
if (!existing) return incoming;
|
|
511
|
+
const content = incoming.content?.trim() ? incoming.content : existing.content;
|
|
512
|
+
const createdAt = incoming.createdAt ?? incoming.timestamp ?? existing.createdAt ?? existing.timestamp;
|
|
513
|
+
return {
|
|
514
|
+
...existing,
|
|
515
|
+
...incoming,
|
|
516
|
+
...content !== void 0 ? { content } : {},
|
|
517
|
+
...createdAt !== void 0 ? { createdAt } : {}
|
|
518
|
+
};
|
|
519
|
+
};
|
|
520
|
+
getLatestMessageFromList = (messages) => {
|
|
521
|
+
if (!messages?.length) return null;
|
|
522
|
+
return messages.reduce((latest, message) => {
|
|
523
|
+
const latestTime = new Date(latest.createdAt || latest.timestamp || 0).getTime();
|
|
524
|
+
const messageTime = new Date(message.createdAt || message.timestamp || 0).getTime();
|
|
525
|
+
return messageTime >= latestTime ? message : latest;
|
|
526
|
+
});
|
|
527
|
+
};
|
|
528
|
+
resolveConversationLastMessage = (conversation, messagesByConversation) => {
|
|
529
|
+
const conversationId = String(conversation._id || conversation.id || "");
|
|
530
|
+
const storedLast = conversation.lastMessage;
|
|
531
|
+
if (storedLast?.content?.trim()) return storedLast;
|
|
532
|
+
const latestFromMessages = getLatestMessageFromList(messagesByConversation[conversationId] || []);
|
|
533
|
+
return mergeLastMessage(storedLast, latestFromMessages);
|
|
534
|
+
};
|
|
535
|
+
syncConversationLastMessage = (conversations, conversationId, messages) => {
|
|
536
|
+
const latest = getLatestMessageFromList(messages);
|
|
537
|
+
if (!latest) return conversations;
|
|
538
|
+
return conversations.map((conversation) => {
|
|
539
|
+
if (String(conversation._id) !== String(conversationId)) return conversation;
|
|
540
|
+
return {
|
|
541
|
+
...conversation,
|
|
542
|
+
lastMessage: mergeLastMessage(conversation.lastMessage, latest)
|
|
543
|
+
};
|
|
544
|
+
});
|
|
545
|
+
};
|
|
546
|
+
isGroupConversation = (conversation) => {
|
|
547
|
+
if (!conversation) return false;
|
|
548
|
+
return conversation.conversationType === "group" || !!conversation.isGroup;
|
|
549
|
+
};
|
|
550
|
+
findConversationById = (conversations, conversationId) => {
|
|
551
|
+
if (!conversationId) return void 0;
|
|
552
|
+
return conversations.find((c) => String(c._id) === String(conversationId));
|
|
553
|
+
};
|
|
554
|
+
resolveIsGroupFromPayload = (payload, conversations) => {
|
|
555
|
+
const conversationId = payload?.conversationId || payload?.message?.conversationId || payload?.message?.conversation_id;
|
|
556
|
+
const fromStore = findConversationById(conversations, conversationId);
|
|
557
|
+
if (fromStore) return isGroupConversation(fromStore);
|
|
558
|
+
return !!payload?.isGroup || payload?.conversationType === "group" || payload?.message?.conversationType === "group";
|
|
559
|
+
};
|
|
560
|
+
getOtherParticipantId = (conversation, loggedUserId) => {
|
|
561
|
+
if (!conversation?.participants?.length) return "";
|
|
562
|
+
const other = conversation.participants.find(
|
|
563
|
+
(p) => String(p._id || p) !== String(loggedUserId)
|
|
564
|
+
) || conversation.participants[0];
|
|
565
|
+
return String(other?._id || other || "");
|
|
566
|
+
};
|
|
567
|
+
findConversationForChannel = (channel, loggedUserId, conversations) => {
|
|
568
|
+
if (!channel || !loggedUserId) return void 0;
|
|
569
|
+
if (channel.conversationId) {
|
|
570
|
+
const byId = findConversationById(conversations, channel.conversationId);
|
|
571
|
+
if (byId) return byId;
|
|
572
|
+
}
|
|
573
|
+
if (channel.isGroup) {
|
|
574
|
+
return conversations.find(
|
|
575
|
+
(c) => isGroupConversation(c) && String(c._id) === String(channel.id)
|
|
576
|
+
);
|
|
577
|
+
}
|
|
578
|
+
return conversations.find((c) => {
|
|
579
|
+
if (isGroupConversation(c)) return false;
|
|
580
|
+
const ids = (c.participants || []).map((p) => String(p._id || p));
|
|
581
|
+
return ids.includes(String(channel.id)) && ids.includes(String(loggedUserId));
|
|
582
|
+
});
|
|
583
|
+
};
|
|
584
|
+
resolveConversationIdForChannel = (channel, loggedUserId, conversations) => {
|
|
585
|
+
const found = findConversationForChannel(channel, loggedUserId, conversations);
|
|
586
|
+
if (found?._id) return String(found._id);
|
|
587
|
+
if (channel?.conversationId) return String(channel.conversationId);
|
|
588
|
+
return null;
|
|
589
|
+
};
|
|
590
|
+
getMessageContent = (message) => String(message?.content || "").trim();
|
|
591
|
+
getMessageTimestamp = (message) => {
|
|
592
|
+
const value = message?.createdAt || message?.timestamp;
|
|
593
|
+
const time = value ? new Date(value).getTime() : NaN;
|
|
594
|
+
return Number.isFinite(time) ? time : 0;
|
|
595
|
+
};
|
|
596
|
+
isOwnMessage = (message, loggedUserId) => {
|
|
597
|
+
if (message?.isOwnMessage) return true;
|
|
598
|
+
if (!loggedUserId) return false;
|
|
599
|
+
const senderId = message?.sender?._id || message?.sender?.id || message?.sender;
|
|
600
|
+
return String(senderId) === String(loggedUserId);
|
|
601
|
+
};
|
|
602
|
+
STATUS_RANK = { sent: 1, delivered: 2, read: 3 };
|
|
603
|
+
pickBetterMessageStatus = (current, incoming) => {
|
|
604
|
+
if (!incoming) return current;
|
|
605
|
+
if (!current) return incoming;
|
|
606
|
+
return (STATUS_RANK[incoming] ?? 0) >= (STATUS_RANK[current] ?? 0) ? incoming : current;
|
|
607
|
+
};
|
|
608
|
+
mergeChatMessages = (apiMessages, localMessages, loggedUserId) => {
|
|
609
|
+
const merged = /* @__PURE__ */ new Map();
|
|
610
|
+
apiMessages.forEach((message) => {
|
|
611
|
+
const id = String(message?._id || message?.id || "");
|
|
612
|
+
if (id) merged.set(id, message);
|
|
613
|
+
});
|
|
614
|
+
localMessages.forEach((localMessage) => {
|
|
615
|
+
if (!localMessage) return;
|
|
616
|
+
const localId = String(localMessage._id || localMessage.id || "");
|
|
617
|
+
if (localId && merged.has(localId)) {
|
|
618
|
+
const existing = merged.get(localId);
|
|
619
|
+
merged.set(localId, {
|
|
620
|
+
...existing,
|
|
621
|
+
...localMessage,
|
|
622
|
+
status: pickBetterMessageStatus(existing?.status, localMessage?.status)
|
|
623
|
+
});
|
|
624
|
+
return;
|
|
625
|
+
}
|
|
626
|
+
const duplicateInApi = apiMessages.some((apiMessage) => {
|
|
627
|
+
const apiId = String(apiMessage?._id || apiMessage?.id || "");
|
|
628
|
+
if (localId && apiId && localId === apiId) return true;
|
|
629
|
+
if (!isOwnMessage(localMessage, loggedUserId)) return false;
|
|
630
|
+
if (!isOwnMessage(apiMessage, loggedUserId)) return false;
|
|
631
|
+
if (getMessageContent(localMessage) !== getMessageContent(apiMessage)) return false;
|
|
632
|
+
return Math.abs(getMessageTimestamp(localMessage) - getMessageTimestamp(apiMessage)) < 12e4;
|
|
633
|
+
});
|
|
634
|
+
if (duplicateInApi) return;
|
|
635
|
+
const key = localId || `pending-${getMessageTimestamp(localMessage)}-${getMessageContent(localMessage)}`;
|
|
636
|
+
if (!merged.has(key)) merged.set(key, localMessage);
|
|
637
|
+
});
|
|
638
|
+
return Array.from(merged.values()).filter((message, index, list) => {
|
|
639
|
+
const id = String(message?._id || message?.id || "");
|
|
640
|
+
if (!id) return true;
|
|
641
|
+
return list.findIndex((m) => String(m?._id || m?.id || "") === id) === index;
|
|
642
|
+
});
|
|
643
|
+
};
|
|
644
|
+
}
|
|
645
|
+
});
|
|
646
|
+
|
|
459
647
|
// src/constants/api.constants.ts
|
|
460
648
|
var API_ENDPOINTS;
|
|
461
649
|
var init_api_constants = __esm({
|
|
@@ -483,21 +671,6 @@ var init_api_constants = __esm({
|
|
|
483
671
|
};
|
|
484
672
|
}
|
|
485
673
|
});
|
|
486
|
-
|
|
487
|
-
// src/store/useSocketStore.ts
|
|
488
|
-
var useSocketStore_exports = {};
|
|
489
|
-
__export(useSocketStore_exports, {
|
|
490
|
-
default: () => useSocketStore_default,
|
|
491
|
-
useSocketStore: () => useSocketStore
|
|
492
|
-
});
|
|
493
|
-
var useSocketStore, useSocketStore_default;
|
|
494
|
-
var init_useSocketStore = __esm({
|
|
495
|
-
"src/store/useSocketStore.ts"() {
|
|
496
|
-
init_useStore();
|
|
497
|
-
useSocketStore = exports.useChatStore;
|
|
498
|
-
useSocketStore_default = useSocketStore;
|
|
499
|
-
}
|
|
500
|
-
});
|
|
501
674
|
var chatClientId, chatAccessToken, rateLimitResetAt; exports.setChatClientId = void 0; exports.setChatAccessToken = void 0; var isRateLimited, apiClient; exports.setChatBaseURL = void 0; var clientUserLogin, fetchUsers, fetchConversations, fetchMessages, fetchPinnedMessages, fetchStarredMessages, createGroup, fetchConversationInfo, updateConversation, updateGroupInformation, addRemoveGroupMembers, addRemoveGroupAdmins, searchMessages, searchMessagesContext, blockUnblockUser;
|
|
502
675
|
var init_api_service = __esm({
|
|
503
676
|
"src/services/api.service.ts"() {
|
|
@@ -718,138 +891,6 @@ var init_api_service = __esm({
|
|
|
718
891
|
}
|
|
719
892
|
});
|
|
720
893
|
|
|
721
|
-
// src/store/helpers/store.helpers.ts
|
|
722
|
-
var findConversationIdByMessageId, normalizeId, normalizeUnreadCount, incrementUnreadCount, emptyMessagesMetadata, extractMessageData, isGroupConversation, findConversationById, resolveIsGroupFromPayload, getOtherParticipantId, findConversationForChannel, resolveConversationIdForChannel, getMessageContent, getMessageTimestamp, isOwnMessage, STATUS_RANK, pickBetterMessageStatus, mergeChatMessages;
|
|
723
|
-
var init_store_helpers = __esm({
|
|
724
|
-
"src/store/helpers/store.helpers.ts"() {
|
|
725
|
-
findConversationIdByMessageId = (messagesByConversation, messageId) => {
|
|
726
|
-
return Object.keys(messagesByConversation).find(
|
|
727
|
-
(cid) => messagesByConversation[cid].some(
|
|
728
|
-
(m) => String(m._id || m.id) === String(messageId)
|
|
729
|
-
)
|
|
730
|
-
);
|
|
731
|
-
};
|
|
732
|
-
normalizeId = (id) => String(id?._id || id?.id || id || "");
|
|
733
|
-
normalizeUnreadCount = (unreadCount) => {
|
|
734
|
-
if (typeof unreadCount === "number") return unreadCount;
|
|
735
|
-
return 0;
|
|
736
|
-
};
|
|
737
|
-
incrementUnreadCount = (unreadCount) => normalizeUnreadCount(unreadCount) + 1;
|
|
738
|
-
emptyMessagesMetadata = () => ({
|
|
739
|
-
nextCursor: null,
|
|
740
|
-
hasMore: false,
|
|
741
|
-
isFirstPage: true
|
|
742
|
-
});
|
|
743
|
-
extractMessageData = (payload) => {
|
|
744
|
-
const messageId = payload.messageId || payload._id || payload.message?._id || payload.message?.id;
|
|
745
|
-
const content = payload.content || payload.message?.content;
|
|
746
|
-
const conversationId = payload.conversationId || payload.message?.conversationId || payload.message?.conversation_id;
|
|
747
|
-
const status = payload.status || payload.message?.status;
|
|
748
|
-
const isPinned = payload.isPinned ?? payload.message?.isPinned;
|
|
749
|
-
const isStarred = payload.isStarred ?? payload.message?.isStarred;
|
|
750
|
-
return { messageId, content, conversationId, status, isPinned, isStarred };
|
|
751
|
-
};
|
|
752
|
-
isGroupConversation = (conversation) => {
|
|
753
|
-
if (!conversation) return false;
|
|
754
|
-
return conversation.conversationType === "group" || !!conversation.isGroup;
|
|
755
|
-
};
|
|
756
|
-
findConversationById = (conversations, conversationId) => {
|
|
757
|
-
if (!conversationId) return void 0;
|
|
758
|
-
return conversations.find((c) => String(c._id) === String(conversationId));
|
|
759
|
-
};
|
|
760
|
-
resolveIsGroupFromPayload = (payload, conversations) => {
|
|
761
|
-
const conversationId = payload?.conversationId || payload?.message?.conversationId || payload?.message?.conversation_id;
|
|
762
|
-
const fromStore = findConversationById(conversations, conversationId);
|
|
763
|
-
if (fromStore) return isGroupConversation(fromStore);
|
|
764
|
-
return !!payload?.isGroup || payload?.conversationType === "group" || payload?.message?.conversationType === "group";
|
|
765
|
-
};
|
|
766
|
-
getOtherParticipantId = (conversation, loggedUserId) => {
|
|
767
|
-
if (!conversation?.participants?.length) return "";
|
|
768
|
-
const other = conversation.participants.find(
|
|
769
|
-
(p) => String(p._id || p) !== String(loggedUserId)
|
|
770
|
-
) || conversation.participants[0];
|
|
771
|
-
return String(other?._id || other || "");
|
|
772
|
-
};
|
|
773
|
-
findConversationForChannel = (channel, loggedUserId, conversations) => {
|
|
774
|
-
if (!channel || !loggedUserId) return void 0;
|
|
775
|
-
if (channel.conversationId) {
|
|
776
|
-
const byId = findConversationById(conversations, channel.conversationId);
|
|
777
|
-
if (byId) return byId;
|
|
778
|
-
}
|
|
779
|
-
if (channel.isGroup) {
|
|
780
|
-
return conversations.find(
|
|
781
|
-
(c) => isGroupConversation(c) && String(c._id) === String(channel.id)
|
|
782
|
-
);
|
|
783
|
-
}
|
|
784
|
-
return conversations.find((c) => {
|
|
785
|
-
if (isGroupConversation(c)) return false;
|
|
786
|
-
const ids = (c.participants || []).map((p) => String(p._id || p));
|
|
787
|
-
return ids.includes(String(channel.id)) && ids.includes(String(loggedUserId));
|
|
788
|
-
});
|
|
789
|
-
};
|
|
790
|
-
resolveConversationIdForChannel = (channel, loggedUserId, conversations) => {
|
|
791
|
-
const found = findConversationForChannel(channel, loggedUserId, conversations);
|
|
792
|
-
if (found?._id) return String(found._id);
|
|
793
|
-
if (channel?.conversationId) return String(channel.conversationId);
|
|
794
|
-
return null;
|
|
795
|
-
};
|
|
796
|
-
getMessageContent = (message) => String(message?.content || "").trim();
|
|
797
|
-
getMessageTimestamp = (message) => {
|
|
798
|
-
const value = message?.createdAt || message?.timestamp;
|
|
799
|
-
const time = value ? new Date(value).getTime() : NaN;
|
|
800
|
-
return Number.isFinite(time) ? time : 0;
|
|
801
|
-
};
|
|
802
|
-
isOwnMessage = (message, loggedUserId) => {
|
|
803
|
-
if (message?.isOwnMessage) return true;
|
|
804
|
-
if (!loggedUserId) return false;
|
|
805
|
-
const senderId = message?.sender?._id || message?.sender?.id || message?.sender;
|
|
806
|
-
return String(senderId) === String(loggedUserId);
|
|
807
|
-
};
|
|
808
|
-
STATUS_RANK = { sent: 1, delivered: 2, read: 3 };
|
|
809
|
-
pickBetterMessageStatus = (current, incoming) => {
|
|
810
|
-
if (!incoming) return current;
|
|
811
|
-
if (!current) return incoming;
|
|
812
|
-
return (STATUS_RANK[incoming] ?? 0) >= (STATUS_RANK[current] ?? 0) ? incoming : current;
|
|
813
|
-
};
|
|
814
|
-
mergeChatMessages = (apiMessages, localMessages, loggedUserId) => {
|
|
815
|
-
const merged = /* @__PURE__ */ new Map();
|
|
816
|
-
apiMessages.forEach((message) => {
|
|
817
|
-
const id = String(message?._id || message?.id || "");
|
|
818
|
-
if (id) merged.set(id, message);
|
|
819
|
-
});
|
|
820
|
-
localMessages.forEach((localMessage) => {
|
|
821
|
-
if (!localMessage) return;
|
|
822
|
-
const localId = String(localMessage._id || localMessage.id || "");
|
|
823
|
-
if (localId && merged.has(localId)) {
|
|
824
|
-
const existing = merged.get(localId);
|
|
825
|
-
merged.set(localId, {
|
|
826
|
-
...existing,
|
|
827
|
-
...localMessage,
|
|
828
|
-
status: pickBetterMessageStatus(existing?.status, localMessage?.status)
|
|
829
|
-
});
|
|
830
|
-
return;
|
|
831
|
-
}
|
|
832
|
-
const duplicateInApi = apiMessages.some((apiMessage) => {
|
|
833
|
-
const apiId = String(apiMessage?._id || apiMessage?.id || "");
|
|
834
|
-
if (localId && apiId && localId === apiId) return true;
|
|
835
|
-
if (!isOwnMessage(localMessage, loggedUserId)) return false;
|
|
836
|
-
if (!isOwnMessage(apiMessage, loggedUserId)) return false;
|
|
837
|
-
if (getMessageContent(localMessage) !== getMessageContent(apiMessage)) return false;
|
|
838
|
-
return Math.abs(getMessageTimestamp(localMessage) - getMessageTimestamp(apiMessage)) < 12e4;
|
|
839
|
-
});
|
|
840
|
-
if (duplicateInApi) return;
|
|
841
|
-
const key = localId || `pending-${getMessageTimestamp(localMessage)}-${getMessageContent(localMessage)}`;
|
|
842
|
-
if (!merged.has(key)) merged.set(key, localMessage);
|
|
843
|
-
});
|
|
844
|
-
return Array.from(merged.values()).filter((message, index, list) => {
|
|
845
|
-
const id = String(message?._id || message?.id || "");
|
|
846
|
-
if (!id) return true;
|
|
847
|
-
return list.findIndex((m) => String(m?._id || m?.id || "") === id) === index;
|
|
848
|
-
});
|
|
849
|
-
};
|
|
850
|
-
}
|
|
851
|
-
});
|
|
852
|
-
|
|
853
894
|
// src/store/forwardPending.store.ts
|
|
854
895
|
var pendingForwards, lastPendingMessageId, setPendingForward, peekPendingForward, peekMostRecentPendingForward, getLastPendingForwardMessageId, clearPendingForward;
|
|
855
896
|
var init_forwardPending_store = __esm({
|
|
@@ -1497,8 +1538,8 @@ var init_socket_slice = __esm({
|
|
|
1497
1538
|
"src/store/slices/socket.slice.ts"() {
|
|
1498
1539
|
init_socket_service();
|
|
1499
1540
|
init_socket_events_constants();
|
|
1500
|
-
init_api_service();
|
|
1501
1541
|
init_store_helpers();
|
|
1542
|
+
init_api_service();
|
|
1502
1543
|
init_forwardPending_store();
|
|
1503
1544
|
init_message_handlers();
|
|
1504
1545
|
init_group_handlers();
|
|
@@ -1962,6 +2003,7 @@ var init_socket_slice = __esm({
|
|
|
1962
2003
|
}));
|
|
1963
2004
|
},
|
|
1964
2005
|
// Fetching
|
|
2006
|
+
// Fetching
|
|
1965
2007
|
fetchConversations: async (limit = 10, page = 1, search = "", force = false) => {
|
|
1966
2008
|
const fetchKey = `${limit}:${page}:${search}`;
|
|
1967
2009
|
const state = get();
|
|
@@ -1974,7 +2016,15 @@ var init_socket_slice = __esm({
|
|
|
1974
2016
|
const response = await fetchConversations(limit, page, search);
|
|
1975
2017
|
const data = safelyExtractArray(response);
|
|
1976
2018
|
set((state2) => ({
|
|
1977
|
-
conversations: page === 1 ? data
|
|
2019
|
+
conversations: page === 1 ? data.map((incoming) => {
|
|
2020
|
+
const existing = state2.conversations.find(
|
|
2021
|
+
(conversation) => String(conversation._id) === String(incoming._id)
|
|
2022
|
+
);
|
|
2023
|
+
return {
|
|
2024
|
+
...incoming,
|
|
2025
|
+
lastMessage: mergeLastMessage(existing?.lastMessage, incoming.lastMessage)
|
|
2026
|
+
};
|
|
2027
|
+
}) : [...state2.conversations, ...data],
|
|
1978
2028
|
conversationsHasMore: data.length === limit,
|
|
1979
2029
|
conversationsPage: page,
|
|
1980
2030
|
isFetchingConversations: false
|
|
@@ -2026,6 +2076,7 @@ var init_socket_slice = __esm({
|
|
|
2026
2076
|
isFirstPage: isInitialLoad
|
|
2027
2077
|
}
|
|
2028
2078
|
},
|
|
2079
|
+
conversations: isInitialLoad ? syncConversationLastMessage(state2.conversations, conversationId, unique) : state2.conversations,
|
|
2029
2080
|
isFetchingMessages: false
|
|
2030
2081
|
};
|
|
2031
2082
|
});
|
|
@@ -2055,6 +2106,7 @@ var init_socket_slice = __esm({
|
|
|
2055
2106
|
const tempId = payload.tempId || `temp-${Date.now()}`;
|
|
2056
2107
|
set((state) => ({ messagePendingActions: { ...state.messagePendingActions, [tempId]: ["send"] } }));
|
|
2057
2108
|
exports.socketService.emit("dm_send", { ...payload, tempId });
|
|
2109
|
+
exports.socketService.emit("conversation_update", { conversationId: payload.conversationId });
|
|
2058
2110
|
},
|
|
2059
2111
|
emitDmEdit: (payload) => {
|
|
2060
2112
|
set((state) => ({ messagePendingActions: { ...state.messagePendingActions, [payload.messageId]: ["edit"] } }));
|
|
@@ -2193,27 +2245,22 @@ var init_socket_slice = __esm({
|
|
|
2193
2245
|
const { conversationId } = payload;
|
|
2194
2246
|
const readerId = payload.userId || payload.readerId || payload.readBy || payload.sender || payload.senderId;
|
|
2195
2247
|
if (!conversationId) return;
|
|
2196
|
-
const readStatus = payload.status || "read";
|
|
2197
2248
|
set({ lastDMMarkRead: payload });
|
|
2198
2249
|
set((state) => {
|
|
2199
2250
|
const messages = state.messagesByConversation[conversationId] || [];
|
|
2200
2251
|
const loggedUserDetails = get().loggedUserDetails || state.loggedUserDetails;
|
|
2201
2252
|
const myId = loggedUserDetails?._id || loggedUserDetails?.id;
|
|
2202
2253
|
const isReaderMe = String(readerId) === String(myId);
|
|
2203
|
-
const nextMessages = messages.map((m) => {
|
|
2204
|
-
if (isReaderMe) return m;
|
|
2205
|
-
const senderId = String(m.sender?._id || m.sender);
|
|
2206
|
-
if (senderId === String(myId)) {
|
|
2207
|
-
return { ...m, status: readStatus };
|
|
2208
|
-
}
|
|
2209
|
-
return m;
|
|
2210
|
-
});
|
|
2254
|
+
const nextMessages = messages.map((m) => ({ ...m, status: "read" }));
|
|
2211
2255
|
const nextConversations = state.conversations.map((c) => {
|
|
2212
2256
|
if (String(c._id) !== String(conversationId)) return c;
|
|
2213
2257
|
const nextUnreadCount = isReaderMe ? 0 : normalizeUnreadCount(c.unreadCount);
|
|
2214
|
-
let nextLastMessage = c.lastMessage;
|
|
2215
|
-
if (
|
|
2216
|
-
nextLastMessage =
|
|
2258
|
+
let nextLastMessage = mergeLastMessage(c.lastMessage, { status: "read" });
|
|
2259
|
+
if (!nextLastMessage?.content && messages.length) {
|
|
2260
|
+
nextLastMessage = mergeLastMessage(
|
|
2261
|
+
getLatestMessageFromList(messages),
|
|
2262
|
+
{ status: "read" }
|
|
2263
|
+
);
|
|
2217
2264
|
}
|
|
2218
2265
|
return { ...c, unreadCount: nextUnreadCount, lastMessage: nextLastMessage };
|
|
2219
2266
|
});
|
|
@@ -2375,7 +2422,7 @@ var init_socket_slice = __esm({
|
|
|
2375
2422
|
return {
|
|
2376
2423
|
...c,
|
|
2377
2424
|
...updated,
|
|
2378
|
-
lastMessage:
|
|
2425
|
+
lastMessage: mergeLastMessage(c.lastMessage, updated.lastMessage),
|
|
2379
2426
|
// Group conversation_update no longer carries per-user unread; keep local count
|
|
2380
2427
|
unreadCount: updated.unreadCount !== void 0 ? normalizeUnreadCount(updated.unreadCount) : normalizeUnreadCount(c.unreadCount)
|
|
2381
2428
|
};
|
|
@@ -3782,6 +3829,84 @@ function ScrollBar({
|
|
|
3782
3829
|
);
|
|
3783
3830
|
}
|
|
3784
3831
|
init_useStore();
|
|
3832
|
+
init_store_helpers();
|
|
3833
|
+
var conversationDrafts = /* @__PURE__ */ new Map();
|
|
3834
|
+
var DRAFT_PREVIEW_MAX_LENGTH = 50;
|
|
3835
|
+
var emptyDraft = () => ({
|
|
3836
|
+
messageInput: "",
|
|
3837
|
+
attachments: []
|
|
3838
|
+
});
|
|
3839
|
+
var draftRevision = 0;
|
|
3840
|
+
var draftListeners = /* @__PURE__ */ new Set();
|
|
3841
|
+
function notifyDraftChange() {
|
|
3842
|
+
draftRevision += 1;
|
|
3843
|
+
draftListeners.forEach((listener) => listener());
|
|
3844
|
+
}
|
|
3845
|
+
function subscribeToDrafts(listener) {
|
|
3846
|
+
draftListeners.add(listener);
|
|
3847
|
+
return () => {
|
|
3848
|
+
draftListeners.delete(listener);
|
|
3849
|
+
};
|
|
3850
|
+
}
|
|
3851
|
+
function getDraftRevision() {
|
|
3852
|
+
return draftRevision;
|
|
3853
|
+
}
|
|
3854
|
+
function useDraftRevision() {
|
|
3855
|
+
return React7.useSyncExternalStore(subscribeToDrafts, getDraftRevision, getDraftRevision);
|
|
3856
|
+
}
|
|
3857
|
+
function getConversationDraft(conversationId) {
|
|
3858
|
+
if (!conversationId) return emptyDraft();
|
|
3859
|
+
return conversationDrafts.get(conversationId) ?? emptyDraft();
|
|
3860
|
+
}
|
|
3861
|
+
function updateConversationDraft(conversationId, draft) {
|
|
3862
|
+
if (!conversationId) return;
|
|
3863
|
+
if (draft.messageInput.trim() || draft.attachments.length > 0) {
|
|
3864
|
+
conversationDrafts.set(conversationId, {
|
|
3865
|
+
messageInput: draft.messageInput,
|
|
3866
|
+
attachments: draft.attachments
|
|
3867
|
+
});
|
|
3868
|
+
} else {
|
|
3869
|
+
conversationDrafts.delete(conversationId);
|
|
3870
|
+
}
|
|
3871
|
+
notifyDraftChange();
|
|
3872
|
+
}
|
|
3873
|
+
function clearConversationDraft(conversationId) {
|
|
3874
|
+
if (!conversationId) return;
|
|
3875
|
+
if (!conversationDrafts.has(conversationId)) return;
|
|
3876
|
+
conversationDrafts.delete(conversationId);
|
|
3877
|
+
notifyDraftChange();
|
|
3878
|
+
}
|
|
3879
|
+
function getConversationDraftPreview(conversationId) {
|
|
3880
|
+
if (!conversationId) return null;
|
|
3881
|
+
const draft = conversationDrafts.get(conversationId);
|
|
3882
|
+
if (!draft) return null;
|
|
3883
|
+
const text = draft.messageInput.trim();
|
|
3884
|
+
if (text) {
|
|
3885
|
+
return text.length > DRAFT_PREVIEW_MAX_LENGTH ? `${text.slice(0, DRAFT_PREVIEW_MAX_LENGTH)}\u2026` : text;
|
|
3886
|
+
}
|
|
3887
|
+
if (draft.attachments.length > 0) {
|
|
3888
|
+
return draft.attachments.length === 1 ? "Attachment" : `${draft.attachments.length} attachments`;
|
|
3889
|
+
}
|
|
3890
|
+
return null;
|
|
3891
|
+
}
|
|
3892
|
+
function useConversationDraft(conversationId) {
|
|
3893
|
+
const composerRef = React7.useRef(emptyDraft());
|
|
3894
|
+
const syncComposerRef = React7.useCallback((draft) => {
|
|
3895
|
+
composerRef.current = draft;
|
|
3896
|
+
}, []);
|
|
3897
|
+
React7.useEffect(() => {
|
|
3898
|
+
const id = conversationId;
|
|
3899
|
+
return () => {
|
|
3900
|
+
if (!id) return;
|
|
3901
|
+
updateConversationDraft(id, composerRef.current);
|
|
3902
|
+
};
|
|
3903
|
+
}, [conversationId]);
|
|
3904
|
+
return {
|
|
3905
|
+
syncComposerRef
|
|
3906
|
+
};
|
|
3907
|
+
}
|
|
3908
|
+
|
|
3909
|
+
// src/chat/ChannelList.tsx
|
|
3785
3910
|
var ChannelList = ({ activeChannel, onChannelSelect, onNewChatClick, debouncedSearch = "" }) => {
|
|
3786
3911
|
const onlineUserIds = exports.useChatStore((s) => s.onlineUsers);
|
|
3787
3912
|
const allUsers = exports.useChatStore((s) => s.allUsers);
|
|
@@ -3789,12 +3914,14 @@ var ChannelList = ({ activeChannel, onChannelSelect, onNewChatClick, debouncedSe
|
|
|
3789
3914
|
const deletedForMeIds = exports.useChatStore((s) => s.deletedForMeIds);
|
|
3790
3915
|
const isFetchingUsers = exports.useChatStore((s) => s.isFetchingAllUsers);
|
|
3791
3916
|
const conversations = exports.useChatStore((s) => s.conversations);
|
|
3917
|
+
const messagesByConversation = exports.useChatStore((s) => s.messagesByConversation);
|
|
3792
3918
|
const isFetchingConversations = exports.useChatStore((s) => s.isFetchingConversations);
|
|
3793
3919
|
const conversationsHasMore = exports.useChatStore((s) => s.conversationsHasMore);
|
|
3794
3920
|
const conversationsPage = exports.useChatStore((s) => s.conversationsPage);
|
|
3795
3921
|
const accessToken = exports.useChatStore((s) => s.accessToken);
|
|
3796
3922
|
const loggedUserDetails = exports.useChatStore((s) => s.loggedUserDetails);
|
|
3797
3923
|
const lastFetchKeyRef = React7.useRef(null);
|
|
3924
|
+
useDraftRevision();
|
|
3798
3925
|
React7.useEffect(() => {
|
|
3799
3926
|
if (accessToken) {
|
|
3800
3927
|
exports.useChatStore.getState().fetchAllUsers();
|
|
@@ -3823,17 +3950,20 @@ var ChannelList = ({ activeChannel, onChannelSelect, onNewChatClick, debouncedSe
|
|
|
3823
3950
|
const isOnline = c.conversationType === "group" || c.isGroup ? false : onlineUserIds.includes(otherParticipantId);
|
|
3824
3951
|
const unreadCount = typeof c.unreadCount === "number" ? c.unreadCount : 0;
|
|
3825
3952
|
const isTyping = typingUsersByConv[String(c._id)]?.length > 0;
|
|
3826
|
-
|
|
3827
|
-
|
|
3828
|
-
|
|
3953
|
+
const resolvedLastMessage = resolveConversationLastMessage(c, messagesByConversation);
|
|
3954
|
+
let lastMessageContent = resolvedLastMessage?.content || "No messages yet";
|
|
3955
|
+
if (resolvedLastMessage) {
|
|
3956
|
+
if (resolvedLastMessage.isDeletedForEveryone) {
|
|
3829
3957
|
lastMessageContent = "This message has been deleted";
|
|
3830
|
-
} else if (deletedForMeIds.includes(String(
|
|
3958
|
+
} else if (deletedForMeIds.includes(String(resolvedLastMessage._id || resolvedLastMessage.id))) {
|
|
3831
3959
|
lastMessageContent = "";
|
|
3832
3960
|
}
|
|
3833
3961
|
}
|
|
3962
|
+
const conversationId = String(c._id);
|
|
3963
|
+
const draftPreview = getConversationDraftPreview(conversationId);
|
|
3834
3964
|
return {
|
|
3835
|
-
id: c.conversationType === "group" || c.isGroup ?
|
|
3836
|
-
conversationId
|
|
3965
|
+
id: c.conversationType === "group" || c.isGroup ? conversationId : otherParticipantId,
|
|
3966
|
+
conversationId,
|
|
3837
3967
|
name: name.charAt(0).toUpperCase() + name.slice(1),
|
|
3838
3968
|
image,
|
|
3839
3969
|
isOnline,
|
|
@@ -3842,16 +3972,17 @@ var ChannelList = ({ activeChannel, onChannelSelect, onNewChatClick, debouncedSe
|
|
|
3842
3972
|
isBlocked: c.isBlocked || false,
|
|
3843
3973
|
unreadCount,
|
|
3844
3974
|
isTyping,
|
|
3975
|
+
draftPreview,
|
|
3845
3976
|
pinnedCount: c.pinnedCount,
|
|
3846
3977
|
starredCount: c.starredCount,
|
|
3847
3978
|
lastMessage: {
|
|
3848
3979
|
content: lastMessageContent,
|
|
3849
|
-
timestamp:
|
|
3980
|
+
timestamp: resolvedLastMessage?.createdAt ? new Date(resolvedLastMessage.createdAt).toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" }) : "",
|
|
3850
3981
|
sender: "User"
|
|
3851
3982
|
}
|
|
3852
3983
|
};
|
|
3853
3984
|
});
|
|
3854
|
-
}, [conversations, onlineUserIds, loggedUserDetails, allUsers, typingUsersByConv, deletedForMeIds]);
|
|
3985
|
+
}, [conversations, messagesByConversation, onlineUserIds, loggedUserDetails, allUsers, typingUsersByConv, deletedForMeIds]);
|
|
3855
3986
|
const handleLoadMore = () => {
|
|
3856
3987
|
if (!isFetchingConversations && conversationsHasMore) {
|
|
3857
3988
|
exports.useChatStore.getState().fetchConversations(10, conversationsPage + 1, debouncedSearch);
|
|
@@ -3882,8 +4013,10 @@ var ChannelList = ({ activeChannel, onChannelSelect, onNewChatClick, debouncedSe
|
|
|
3882
4013
|
isActive ? "text-(--chat-theme)" : "text-[#0f172a]"
|
|
3883
4014
|
) }, channel.name), /* @__PURE__ */ React7__namespace.default.createElement("div", { className: "flex shrink-0 flex-col items-end gap-1" }, /* @__PURE__ */ React7__namespace.default.createElement("span", { className: "text-[11px] font-medium text-muted-foreground" }, channel.lastMessage?.timestamp), channel.isLeft && /* @__PURE__ */ React7__namespace.default.createElement("span", { className: "rounded-full bg-red-50 px-1.5 py-0.5 text-[9px] font-semibold text-red-600" }, "Left"))), /* @__PURE__ */ React7__namespace.default.createElement("div", { className: "flex w-full min-w-0 items-center justify-between gap-2 pr-1" }, /* @__PURE__ */ React7__namespace.default.createElement("p", { className: cn(
|
|
3884
4015
|
"min-w-0 line-clamp-1 text-[13px]",
|
|
3885
|
-
channel.isTyping ? "font-medium
|
|
3886
|
-
|
|
4016
|
+
channel.isTyping || channel.draftPreview ? "font-medium" : "text-muted-foreground",
|
|
4017
|
+
channel.isTyping && "text-(--chat-theme)",
|
|
4018
|
+
!channel.isTyping && channel.draftPreview && "text-muted-foreground"
|
|
4019
|
+
) }, channel.isTyping ? "typing..." : channel.draftPreview ? /* @__PURE__ */ React7__namespace.default.createElement(React7__namespace.default.Fragment, null, /* @__PURE__ */ React7__namespace.default.createElement("span", { className: "font-semibold text-rose-500" }, "Draft: "), /* @__PURE__ */ React7__namespace.default.createElement("span", null, channel.draftPreview)) : channel.lastMessage?.content), channel.unreadCount !== void 0 && channel.unreadCount > 0 && /* @__PURE__ */ React7__namespace.default.createElement(Badge, { className: cn(
|
|
3887
4020
|
"flex h-5 min-w-5 items-center justify-center rounded-full border-none px-1.5 text-[10px] font-semibold",
|
|
3888
4021
|
isActive ? "bg-(--chat-theme) text-white" : "bg-(--chat-theme) text-white"
|
|
3889
4022
|
) }, channel.unreadCount)))
|
|
@@ -5798,10 +5931,9 @@ var MessageReactions = ({
|
|
|
5798
5931
|
return /* @__PURE__ */ React7__namespace.default.createElement(Tooltip, { key: emoji }, /* @__PURE__ */ React7__namespace.default.createElement(TooltipTrigger, { asChild: true }, /* @__PURE__ */ React7__namespace.default.createElement(
|
|
5799
5932
|
"button",
|
|
5800
5933
|
{
|
|
5801
|
-
key: emoji,
|
|
5802
5934
|
onClick: () => onReactionClick(emoji),
|
|
5803
5935
|
className: cn(
|
|
5804
|
-
"flex items-center gap-1 px-2 py-0.5 rounded-full text-xs border shadow-sm transition-all shadow-black/5",
|
|
5936
|
+
"flex items-center gap-1 px-2 py-0.5 rounded-full text-xs border shadow-sm transition-all shadow-black/5 outline-none",
|
|
5805
5937
|
isReactedByMe ? "bg-(--chat-theme-10) border-(--chat-theme-20) text-(--chat-theme)" : "bg-white border-border/40 hover:bg-muted/50 text-[#111827]"
|
|
5806
5938
|
)
|
|
5807
5939
|
},
|
|
@@ -5864,13 +5996,9 @@ var MessageToolbar = ({
|
|
|
5864
5996
|
canEdit = true
|
|
5865
5997
|
}) => {
|
|
5866
5998
|
return /* @__PURE__ */ React7__namespace.default.createElement("div", { className: cn(
|
|
5867
|
-
"absolute -top-10 z-
|
|
5999
|
+
"absolute -top-10 z-20 flex items-center gap-1 rounded-full border bg-background/95 p-1 shadow-md backdrop-blur-sm",
|
|
5868
6000
|
isSender ? "right-0" : "left-0"
|
|
5869
|
-
) }, /* @__PURE__ */ React7__namespace.default.createElement(Button, { variant: "ghost", size: "icon", className: "size-8 rounded-full", onClick: ()
|
|
5870
|
-
onReply?.();
|
|
5871
|
-
} }, /* @__PURE__ */ React7__namespace.default.createElement(lucideReact.CornerUpLeft, { className: "size-4" })), /* @__PURE__ */ React7__namespace.default.createElement(Button, { variant: "ghost", size: "icon", className: "size-8 rounded-full", onClick: () => {
|
|
5872
|
-
onForward?.();
|
|
5873
|
-
} }, /* @__PURE__ */ React7__namespace.default.createElement(lucideReact.Forward, { className: "size-4" })), /* @__PURE__ */ React7__namespace.default.createElement(Popover, { open: isReactionOpen, onOpenChange: setIsReactionOpen }, /* @__PURE__ */ React7__namespace.default.createElement(PopoverTrigger, { asChild: true }, /* @__PURE__ */ React7__namespace.default.createElement(Button, { variant: "ghost", size: "icon", className: "size-8 rounded-full hover:bg-muted transition-all" }, /* @__PURE__ */ React7__namespace.default.createElement(lucideReact.Smile, { className: "size-4 text-muted-foreground" }))), /* @__PURE__ */ React7__namespace.default.createElement(
|
|
6001
|
+
) }, /* @__PURE__ */ React7__namespace.default.createElement(Button, { variant: "ghost", size: "icon", className: "size-8 rounded-full", onClick: onReply }, /* @__PURE__ */ React7__namespace.default.createElement(lucideReact.CornerUpLeft, { className: "size-4" })), /* @__PURE__ */ React7__namespace.default.createElement(Button, { variant: "ghost", size: "icon", className: "size-8 rounded-full", onClick: onForward }, /* @__PURE__ */ React7__namespace.default.createElement(lucideReact.Forward, { className: "size-4" })), /* @__PURE__ */ React7__namespace.default.createElement(Popover, { open: isReactionOpen, onOpenChange: setIsReactionOpen }, /* @__PURE__ */ React7__namespace.default.createElement(PopoverTrigger, { asChild: true }, /* @__PURE__ */ React7__namespace.default.createElement(Button, { variant: "ghost", size: "icon", className: "size-8 rounded-full hover:bg-muted transition-all" }, /* @__PURE__ */ React7__namespace.default.createElement(lucideReact.Smile, { className: "size-4 text-muted-foreground" }))), /* @__PURE__ */ React7__namespace.default.createElement(
|
|
5874
6002
|
PopoverContent,
|
|
5875
6003
|
{
|
|
5876
6004
|
className: "w-auto p-0 border-none shadow-2xl rounded-2xl",
|
|
@@ -6414,6 +6542,9 @@ var ChannelMessageBox = ({ activeChannel, conversationId, onSendMessage, replyTo
|
|
|
6414
6542
|
setState((prev) => ({ ...prev, ...updates }));
|
|
6415
6543
|
};
|
|
6416
6544
|
const typingTimeoutRef = React7.useRef(null);
|
|
6545
|
+
const conversationIdRef = React7.useRef(conversationId);
|
|
6546
|
+
conversationIdRef.current = conversationId;
|
|
6547
|
+
const { syncComposerRef } = useConversationDraft(conversationId);
|
|
6417
6548
|
const emitTypingStart = exports.useChatStore((s) => s.emitTypingStart);
|
|
6418
6549
|
const emitTypingStop = exports.useChatStore((s) => s.emitTypingStop);
|
|
6419
6550
|
const emitGroupTypingStart = exports.useChatStore((s) => s.emitGroupTypingStart);
|
|
@@ -6425,6 +6556,30 @@ var ChannelMessageBox = ({ activeChannel, conversationId, onSendMessage, replyTo
|
|
|
6425
6556
|
React7__namespace.default.useEffect(() => {
|
|
6426
6557
|
attachmentsCountRef.current = state.attachments.length;
|
|
6427
6558
|
}, [state.attachments.length]);
|
|
6559
|
+
React7__namespace.default.useEffect(() => {
|
|
6560
|
+
const draft = {
|
|
6561
|
+
messageInput: state.messageInput,
|
|
6562
|
+
attachments: state.attachments
|
|
6563
|
+
};
|
|
6564
|
+
syncComposerRef(draft);
|
|
6565
|
+
updateConversationDraft(conversationIdRef.current, draft);
|
|
6566
|
+
}, [state.messageInput, state.attachments, syncComposerRef]);
|
|
6567
|
+
React7__namespace.default.useEffect(() => {
|
|
6568
|
+
if (typingTimeoutRef.current) {
|
|
6569
|
+
clearTimeout(typingTimeoutRef.current);
|
|
6570
|
+
typingTimeoutRef.current = null;
|
|
6571
|
+
}
|
|
6572
|
+
const draft = getConversationDraft(conversationId);
|
|
6573
|
+
attachmentsCountRef.current = draft.attachments.length;
|
|
6574
|
+
setState((prev) => ({
|
|
6575
|
+
...prev,
|
|
6576
|
+
messageInput: draft.messageInput,
|
|
6577
|
+
attachments: draft.attachments,
|
|
6578
|
+
attachmentError: "",
|
|
6579
|
+
isTyping: false
|
|
6580
|
+
}));
|
|
6581
|
+
if (fileInputRef.current) fileInputRef.current.value = "";
|
|
6582
|
+
}, [conversationId]);
|
|
6428
6583
|
const handleInputChange = (e) => {
|
|
6429
6584
|
updateState({ messageInput: e.target.value });
|
|
6430
6585
|
if (!conversationId || !loggedUserDetails || !activeChannel) return;
|
|
@@ -6509,6 +6664,8 @@ var ChannelMessageBox = ({ activeChannel, conversationId, onSendMessage, replyTo
|
|
|
6509
6664
|
});
|
|
6510
6665
|
};
|
|
6511
6666
|
const handleSendMessage = () => {
|
|
6667
|
+
const trimmed = state.messageInput.trim();
|
|
6668
|
+
if (!trimmed && state.attachments.length === 0) return;
|
|
6512
6669
|
if (typingTimeoutRef.current) clearTimeout(typingTimeoutRef.current);
|
|
6513
6670
|
if (state.isTyping && conversationId && loggedUserDetails && activeChannel) {
|
|
6514
6671
|
updateState({ isTyping: false });
|
|
@@ -6524,7 +6681,8 @@ var ChannelMessageBox = ({ activeChannel, conversationId, onSendMessage, replyTo
|
|
|
6524
6681
|
emitTypingStop(typingPayload);
|
|
6525
6682
|
}
|
|
6526
6683
|
}
|
|
6527
|
-
onSendMessage?.(
|
|
6684
|
+
onSendMessage?.(trimmed, state.attachments, replyTo);
|
|
6685
|
+
clearConversationDraft(conversationId);
|
|
6528
6686
|
updateState({
|
|
6529
6687
|
messageInput: "",
|
|
6530
6688
|
attachments: [],
|
|
@@ -7054,6 +7212,113 @@ var ChatMain_default = ChatMain;
|
|
|
7054
7212
|
init_useStore();
|
|
7055
7213
|
init_socket_service();
|
|
7056
7214
|
init_api_service();
|
|
7215
|
+
|
|
7216
|
+
// src/config/socket.config.ts
|
|
7217
|
+
var getSocketConfig = (accessToken, tenantId, apiBaseUrl) => {
|
|
7218
|
+
const baseURL = (apiBaseUrl || "").replace("/api/v1", "");
|
|
7219
|
+
return {
|
|
7220
|
+
url: baseURL,
|
|
7221
|
+
options: {
|
|
7222
|
+
path: "/connection",
|
|
7223
|
+
auth: {
|
|
7224
|
+
accessToken
|
|
7225
|
+
},
|
|
7226
|
+
query: {
|
|
7227
|
+
platform: "web",
|
|
7228
|
+
tenantId: tenantId || "",
|
|
7229
|
+
isTenant: "true"
|
|
7230
|
+
},
|
|
7231
|
+
extraHeaders: {
|
|
7232
|
+
Authorization: `Bearer ${accessToken}`,
|
|
7233
|
+
platform: "web",
|
|
7234
|
+
tenantId: tenantId || "",
|
|
7235
|
+
isTenant: "true"
|
|
7236
|
+
},
|
|
7237
|
+
transports: ["websocket", "polling"],
|
|
7238
|
+
reconnection: true,
|
|
7239
|
+
reconnectionAttempts: Infinity,
|
|
7240
|
+
reconnectionDelay: 2e3,
|
|
7241
|
+
reconnectionDelayMax: 5e3,
|
|
7242
|
+
timeout: 2e4
|
|
7243
|
+
}
|
|
7244
|
+
};
|
|
7245
|
+
};
|
|
7246
|
+
var showNotification = (message, type = "info", icon, duration) => {
|
|
7247
|
+
const options = {
|
|
7248
|
+
icon,
|
|
7249
|
+
duration
|
|
7250
|
+
};
|
|
7251
|
+
switch (type) {
|
|
7252
|
+
case "success":
|
|
7253
|
+
reactHotToast.toast.success(message, options);
|
|
7254
|
+
break;
|
|
7255
|
+
case "error":
|
|
7256
|
+
reactHotToast.toast.error(message, options);
|
|
7257
|
+
break;
|
|
7258
|
+
case "info":
|
|
7259
|
+
reactHotToast.toast(message, options);
|
|
7260
|
+
break;
|
|
7261
|
+
case "warning":
|
|
7262
|
+
reactHotToast.toast(message, { ...options, icon: "\u26A0\uFE0F" });
|
|
7263
|
+
break;
|
|
7264
|
+
default:
|
|
7265
|
+
reactHotToast.toast(message, options);
|
|
7266
|
+
}
|
|
7267
|
+
};
|
|
7268
|
+
var downloadFile = async (src, name) => {
|
|
7269
|
+
try {
|
|
7270
|
+
const link = document.createElement("a");
|
|
7271
|
+
link.href = src;
|
|
7272
|
+
link.setAttribute("download", name);
|
|
7273
|
+
link.setAttribute("rel", "noopener noreferrer");
|
|
7274
|
+
link.click();
|
|
7275
|
+
showNotification("File Downloaded Successfully", "success");
|
|
7276
|
+
} catch (err) {
|
|
7277
|
+
console.error("File download failed:", err);
|
|
7278
|
+
showNotification("Failed to download file", "error");
|
|
7279
|
+
}
|
|
7280
|
+
};
|
|
7281
|
+
var getResponsiveWidth = (width) => {
|
|
7282
|
+
const screenWidth = window.innerWidth;
|
|
7283
|
+
if (width) {
|
|
7284
|
+
return width;
|
|
7285
|
+
} else if (screenWidth < 640) {
|
|
7286
|
+
return 350;
|
|
7287
|
+
} else if (screenWidth >= 640 && screenWidth < 1024) {
|
|
7288
|
+
return 600;
|
|
7289
|
+
} else if (screenWidth >= 1024 && screenWidth < 1280) {
|
|
7290
|
+
return 800;
|
|
7291
|
+
} else {
|
|
7292
|
+
return 1e3;
|
|
7293
|
+
}
|
|
7294
|
+
};
|
|
7295
|
+
var isEmpty = (value) => {
|
|
7296
|
+
if (value === null || value === void 0) return true;
|
|
7297
|
+
if (typeof value === "string") return value.trim().length === 0;
|
|
7298
|
+
if (Array.isArray(value)) {
|
|
7299
|
+
return value.length === 0 || value.every((item) => isEmpty(item));
|
|
7300
|
+
}
|
|
7301
|
+
if (typeof value === "object") {
|
|
7302
|
+
return Object.keys(value).length === 0;
|
|
7303
|
+
}
|
|
7304
|
+
return false;
|
|
7305
|
+
};
|
|
7306
|
+
function uniqueList(list, key) {
|
|
7307
|
+
const seen = /* @__PURE__ */ new Set();
|
|
7308
|
+
return list.filter((item) => {
|
|
7309
|
+
const value = item[key];
|
|
7310
|
+
if (seen.has(value)) return false;
|
|
7311
|
+
seen.add(value);
|
|
7312
|
+
return true;
|
|
7313
|
+
});
|
|
7314
|
+
}
|
|
7315
|
+
var capitalizeFirst = (str) => str.charAt(0).toUpperCase() + str.slice(1);
|
|
7316
|
+
var getFirstAndLastNameOneCharacter = (name) => {
|
|
7317
|
+
const names = name.split(" ");
|
|
7318
|
+
return names.map((name2) => name2.charAt(0).toUpperCase()).join("");
|
|
7319
|
+
};
|
|
7320
|
+
|
|
7321
|
+
// src/index.ts
|
|
7057
7322
|
var index_default = ChatMain_default;
|
|
7058
7323
|
|
|
7059
7324
|
exports.Channel = Channel;
|
|
@@ -7066,9 +7331,17 @@ exports.ForwardModal = ForwardModal_default;
|
|
|
7066
7331
|
exports.LoggedUserDetails = LoggedUserDetails_default;
|
|
7067
7332
|
exports.MessageInput = ChannelMessageBox_default;
|
|
7068
7333
|
exports.MessageList = ActiveChannelMessages_default;
|
|
7334
|
+
exports.capitalizeFirst = capitalizeFirst;
|
|
7069
7335
|
exports.default = index_default;
|
|
7070
7336
|
exports.defaultChatLayoutConfig = defaultChatLayoutConfig;
|
|
7337
|
+
exports.downloadFile = downloadFile;
|
|
7338
|
+
exports.getFirstAndLastNameOneCharacter = getFirstAndLastNameOneCharacter;
|
|
7339
|
+
exports.getResponsiveWidth = getResponsiveWidth;
|
|
7340
|
+
exports.getSocketConfig = getSocketConfig;
|
|
7341
|
+
exports.isEmpty = isEmpty;
|
|
7071
7342
|
exports.mergeChatLayoutConfig = mergeChatLayoutConfig;
|
|
7343
|
+
exports.showNotification = showNotification;
|
|
7344
|
+
exports.uniqueList = uniqueList;
|
|
7072
7345
|
exports.useChatContext = useChatContext;
|
|
7073
7346
|
exports.useChatController = useChatController;
|
|
7074
7347
|
//# sourceMappingURL=index.cjs.map
|