@realtimexsco/live-chat 1.5.3 → 1.5.4
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/dist/index.cjs +2077 -1693
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.mts +18 -4
- package/dist/index.d.ts +18 -4
- package/dist/index.mjs +1768 -1371
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -7,9 +7,9 @@ import { twMerge } from 'tailwind-merge';
|
|
|
7
7
|
import { Slot, Avatar as Avatar$1, Dialog as Dialog$1, Tooltip as Tooltip$1, Switch as Switch$1, ScrollArea as ScrollArea$1, Popover as Popover$1, DropdownMenu as DropdownMenu$1, AlertDialog as AlertDialog$1 } from 'radix-ui';
|
|
8
8
|
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
|
|
9
9
|
import { cva } from 'class-variance-authority';
|
|
10
|
-
import * as
|
|
11
|
-
import
|
|
12
|
-
import { Loader2, X, Video, Phone, PhoneOff, Sun, Moon, Settings, Search, UserPlus, ChevronDown, ArrowLeft,
|
|
10
|
+
import * as React11 from 'react';
|
|
11
|
+
import React11__default, { createContext, useState, useEffect, useMemo, useContext, useCallback, useRef, useLayoutEffect, Fragment as Fragment$1, useSyncExternalStore } from 'react';
|
|
12
|
+
import { Loader2, X, Users, Video, Phone, PhoneOff, Sun, Moon, Settings, Search, UserPlus, ChevronDown, ArrowLeft, WifiOff, Wifi, Sparkles, MessageSquareText, ChevronRight, Pin, Star, MoreVertical, Info, UserKey, ShieldUser, Paperclip, Clock, MessageSquareX, Trash2, LogOut, Ban, CheckCheck, Check, Camera, Save, Pencil, Mail, CornerUpLeft, CheckSquare, Smile, ChevronLeftIcon, ChevronRightIcon, ChevronDownIcon, MessageCircleMore, History, Shield, Send, AlertCircle, ShieldCheck, UserMinus, LayoutGrid, Rows3, MessageCircle, ExternalLink, Download, ChevronLeft, ZoomOut, ZoomIn, RotateCcw, Music, Hand, MicOff, MonitorUp, XIcon, ArrowRight, MoreHorizontal, PinOff, StarOff, ImageIcon, Film, Play, Pause, FileText, FileSpreadsheet, FileIcon, MessageSquare, Eye, Captions, Mic, VideoOff } from 'lucide-react';
|
|
13
13
|
import { createPortal, flushSync } from 'react-dom';
|
|
14
14
|
import EmojiPicker, { Theme } from 'emoji-picker-react';
|
|
15
15
|
import { getDefaultClassNames, DayPicker } from 'react-day-picker';
|
|
@@ -214,6 +214,8 @@ var init_socket_events_constants = __esm({
|
|
|
214
214
|
// any participant → server
|
|
215
215
|
CALL_ENDED: "call_ended",
|
|
216
216
|
// server → everyone on the call
|
|
217
|
+
CALL_PARTICIPANT_LEFT: "call_participant_left",
|
|
218
|
+
// server → everyone: a group member left, call continues
|
|
217
219
|
CALL_BUSY: "call_busy",
|
|
218
220
|
// server → caller: callee already in a call
|
|
219
221
|
CALL_MISSED: "call_missed"
|
|
@@ -373,302 +375,1072 @@ var init_listeners = __esm({
|
|
|
373
375
|
}
|
|
374
376
|
});
|
|
375
377
|
|
|
376
|
-
// src/
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
if (path.includes("/settings/effective")) return "effectiveSettings";
|
|
394
|
-
if (path.includes("/conversation/group/create")) return "createGroup";
|
|
395
|
-
if (path.includes("/conversation/group/permission/update")) return "updateGroupPermissions";
|
|
396
|
-
if (path.includes("/conversation/group/information/update")) return "updateGroupInfo";
|
|
397
|
-
if (path.includes("/conversation/group/participants/add-remove")) return "manageMembers";
|
|
398
|
-
if (path.includes("/conversation/group/admins/add-remove")) return "manageAdmins";
|
|
399
|
-
if (path.includes("/user/block-unblock")) return "blockUnblock";
|
|
400
|
-
if (path.includes("/aws/upload/get-presigned-urls")) return "presignedUrls";
|
|
401
|
-
return null;
|
|
402
|
-
}
|
|
403
|
-
function isChatGetApiKey(key) {
|
|
404
|
-
return !!key && CHAT_GET_API_KEYS.includes(key);
|
|
405
|
-
}
|
|
406
|
-
function normalizeQueryParams(params) {
|
|
407
|
-
if (!params || typeof params !== "object") return {};
|
|
408
|
-
const next = {};
|
|
409
|
-
for (const [key, value] of Object.entries(params)) {
|
|
410
|
-
if (value === void 0 || value === null) continue;
|
|
411
|
-
const trimmedKey = String(key).trim();
|
|
412
|
-
if (!trimmedKey) continue;
|
|
413
|
-
next[trimmedKey] = String(value);
|
|
414
|
-
}
|
|
415
|
-
return next;
|
|
416
|
-
}
|
|
417
|
-
function normalizeQueryParamsByApi(byApi) {
|
|
418
|
-
if (!byApi || typeof byApi !== "object") return {};
|
|
419
|
-
const next = {};
|
|
420
|
-
for (const [apiKey, params] of Object.entries(byApi)) {
|
|
421
|
-
if (apiKey === "all" || apiKey === "get") continue;
|
|
422
|
-
next[apiKey] = normalizeQueryParams(params);
|
|
423
|
-
}
|
|
424
|
-
return next;
|
|
425
|
-
}
|
|
426
|
-
var CHAT_GET_API_KEYS, CHAT_API_KEYS;
|
|
427
|
-
var init_query_params = __esm({
|
|
428
|
-
"src/services/api/query-params.ts"() {
|
|
429
|
-
CHAT_GET_API_KEYS = [
|
|
430
|
-
"users",
|
|
431
|
-
"conversations",
|
|
432
|
-
"conversationInfo",
|
|
433
|
-
"messages",
|
|
434
|
-
"pinnedMessages",
|
|
435
|
-
"starredMessages",
|
|
436
|
-
"searchMessages",
|
|
437
|
-
"searchMessagesContext",
|
|
438
|
-
"effectiveSettings"
|
|
439
|
-
];
|
|
440
|
-
CHAT_API_KEYS = [
|
|
441
|
-
"all",
|
|
442
|
-
/** All GET endpoints listed in CHAT_GET_API_KEYS */
|
|
443
|
-
"get",
|
|
444
|
-
"login",
|
|
445
|
-
...CHAT_GET_API_KEYS,
|
|
446
|
-
"createGroup",
|
|
447
|
-
"updateGroupPermissions",
|
|
448
|
-
"updateGroupInfo",
|
|
449
|
-
"manageMembers",
|
|
450
|
-
"manageAdmins",
|
|
451
|
-
"blockUnblock",
|
|
452
|
-
"presignedUrls"
|
|
453
|
-
];
|
|
378
|
+
// src/store/helpers/group-display.helpers.ts
|
|
379
|
+
var resolveGroupImage, getChannelAvatarInitial, isGroupCreationPreview;
|
|
380
|
+
var init_group_display_helpers = __esm({
|
|
381
|
+
"src/store/helpers/group-display.helpers.ts"() {
|
|
382
|
+
resolveGroupImage = (conversation) => {
|
|
383
|
+
if (!conversation) return void 0;
|
|
384
|
+
const raw = conversation.groupImage ?? conversation.image ?? conversation.avatar;
|
|
385
|
+
if (typeof raw !== "string") return void 0;
|
|
386
|
+
const trimmed = raw.trim();
|
|
387
|
+
return trimmed || void 0;
|
|
388
|
+
};
|
|
389
|
+
getChannelAvatarInitial = (name) => {
|
|
390
|
+
const trimmed = (name || "").trim();
|
|
391
|
+
if (!trimmed) return "?";
|
|
392
|
+
return trimmed.charAt(0).toUpperCase();
|
|
393
|
+
};
|
|
394
|
+
isGroupCreationPreview = (content) => !!content?.includes("New Group") && !!content?.includes("created and you are invited");
|
|
454
395
|
}
|
|
455
396
|
});
|
|
456
397
|
|
|
457
|
-
// src/
|
|
458
|
-
var
|
|
459
|
-
var
|
|
460
|
-
"src/
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
398
|
+
// src/store/helpers/message.helpers.ts
|
|
399
|
+
var getMessageSenderId, extractMessageData, statusRank, preferHigherStatus, mergeLastMessage, messageHasPreviewBody, getLatestMessageFromList, isOwnMessage, getMessageContent, getMessageTimestamp, resolveMessageStatus, mergeChatMessages;
|
|
400
|
+
var init_message_helpers = __esm({
|
|
401
|
+
"src/store/helpers/message.helpers.ts"() {
|
|
402
|
+
getMessageSenderId = (message) => String(message?.sender?._id || message?.sender?.id || message?.senderId || message?.fromUserId || message?.sender || "");
|
|
403
|
+
extractMessageData = (payload) => {
|
|
404
|
+
const messageId2 = payload.messageId || payload._id || payload.message?._id || payload.message?.id;
|
|
405
|
+
const content = payload.content || payload.message?.content;
|
|
406
|
+
const conversationId = payload.conversationId || payload.conversation_id || payload.message?.conversationId || payload.message?.conversation_id;
|
|
407
|
+
const status = payload.status || payload.message?.status;
|
|
408
|
+
const isPinned = payload.isPinned ?? payload.message?.isPinned;
|
|
409
|
+
const isStarred = payload.isStarred ?? payload.message?.isStarred;
|
|
410
|
+
const attachmentId = payload.attachmentId || payload.unpinAttachmentId || null;
|
|
411
|
+
const caption = payload.caption ?? payload.message?.caption ?? null;
|
|
412
|
+
return { messageId: messageId2, content, conversationId, status, isPinned, isStarred, attachmentId, caption };
|
|
465
413
|
};
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
const
|
|
469
|
-
|
|
470
|
-
|
|
414
|
+
statusRank = { sent: 0, delivered: 1, read: 2 };
|
|
415
|
+
preferHigherStatus = (base, incoming) => {
|
|
416
|
+
const baseRank = statusRank[base?.status] ?? 0;
|
|
417
|
+
const incomingRank = statusRank[incoming?.status] ?? 0;
|
|
418
|
+
if (incomingRank > baseRank) {
|
|
419
|
+
return { ...base, status: incoming.status };
|
|
420
|
+
}
|
|
421
|
+
return base;
|
|
471
422
|
};
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
const
|
|
478
|
-
|
|
423
|
+
mergeLastMessage = (existing, incoming) => {
|
|
424
|
+
if (!incoming) return existing ?? null;
|
|
425
|
+
if (!existing) return incoming;
|
|
426
|
+
const content = incoming.content?.trim() ? incoming.content : existing.content;
|
|
427
|
+
const createdAt = incoming.createdAt ?? incoming.timestamp ?? existing.createdAt ?? existing.timestamp;
|
|
428
|
+
const incomingSenderId = getMessageSenderId(incoming);
|
|
429
|
+
const resolvedSender = incomingSenderId ? incoming.sender ?? incoming.senderId ?? incoming.fromUserId : existing.sender ?? existing.senderId ?? existing.fromUserId ?? incoming.sender;
|
|
430
|
+
return {
|
|
431
|
+
...existing,
|
|
432
|
+
...incoming,
|
|
433
|
+
...content !== void 0 ? { content } : {},
|
|
434
|
+
...createdAt !== void 0 ? { createdAt } : {},
|
|
435
|
+
attachments: incoming.attachments?.length ? incoming.attachments : existing.attachments,
|
|
436
|
+
status: preferHigherStatus(existing, incoming).status ?? existing.status ?? incoming.status,
|
|
437
|
+
...resolvedSender !== void 0 ? { sender: resolvedSender } : {},
|
|
438
|
+
senderId: incoming.senderId ?? existing.senderId,
|
|
439
|
+
fromUserId: incoming.fromUserId ?? existing.fromUserId
|
|
440
|
+
};
|
|
479
441
|
};
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
}
|
|
489
|
-
return `${root}/${version}${normalizedPath}`;
|
|
442
|
+
messageHasPreviewBody = (message) => Boolean(String(message?.content || "").trim()) || Array.isArray(message?.attachments) && message.attachments.length > 0;
|
|
443
|
+
getLatestMessageFromList = (messages) => {
|
|
444
|
+
if (!messages?.length) return null;
|
|
445
|
+
return messages.reduce((latest, message) => {
|
|
446
|
+
const latestTime = new Date(latest.createdAt || latest.timestamp || 0).getTime();
|
|
447
|
+
const messageTime = new Date(message.createdAt || message.timestamp || 0).getTime();
|
|
448
|
+
return messageTime >= latestTime ? message : latest;
|
|
449
|
+
});
|
|
490
450
|
};
|
|
491
|
-
|
|
492
|
-
if (
|
|
493
|
-
if (
|
|
494
|
-
|
|
495
|
-
}
|
|
496
|
-
if (config.v2) {
|
|
497
|
-
apiVersions.v2 = normalizeApiVersion(config.v2);
|
|
498
|
-
}
|
|
451
|
+
isOwnMessage = (message, loggedUserId) => {
|
|
452
|
+
if (message?.isOwnMessage) return true;
|
|
453
|
+
if (!loggedUserId) return false;
|
|
454
|
+
return getMessageSenderId(message) === String(loggedUserId);
|
|
499
455
|
};
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
456
|
+
getMessageContent = (message) => String(message?.content || "").trim();
|
|
457
|
+
getMessageTimestamp = (message) => {
|
|
458
|
+
const value = message?.createdAt || message?.timestamp;
|
|
459
|
+
const time = value ? new Date(value).getTime() : NaN;
|
|
460
|
+
return Number.isFinite(time) ? time : 0;
|
|
505
461
|
};
|
|
506
|
-
|
|
507
|
-
const
|
|
508
|
-
if (
|
|
509
|
-
|
|
462
|
+
resolveMessageStatus = (message, conversation, loggedUserId) => {
|
|
463
|
+
const existing = message?.status || "sent";
|
|
464
|
+
if (!loggedUserId || !isOwnMessage(message, loggedUserId)) return existing;
|
|
465
|
+
const last = conversation?.lastMessage;
|
|
466
|
+
if (!last) return existing;
|
|
467
|
+
const msgId = String(message._id || message.id || "");
|
|
468
|
+
const lastId = String(last._id || last.id || "");
|
|
469
|
+
if (msgId && lastId && msgId === lastId && last.status) {
|
|
470
|
+
const lastStatus = last.status;
|
|
471
|
+
return statusRank[lastStatus] > statusRank[existing] ? lastStatus : existing;
|
|
510
472
|
}
|
|
473
|
+
return existing;
|
|
474
|
+
};
|
|
475
|
+
mergeChatMessages = (apiMessages, localMessages, loggedUserId) => {
|
|
476
|
+
const merged = /* @__PURE__ */ new Map();
|
|
477
|
+
apiMessages.forEach((message) => {
|
|
478
|
+
const id = String(message?._id || message?.id || "");
|
|
479
|
+
if (id) merged.set(id, message);
|
|
480
|
+
});
|
|
481
|
+
localMessages.forEach((localMessage) => {
|
|
482
|
+
if (!localMessage) return;
|
|
483
|
+
const localId = String(localMessage._id || localMessage.id || "");
|
|
484
|
+
if (localId && merged.has(localId)) {
|
|
485
|
+
const apiMessage = merged.get(localId);
|
|
486
|
+
const withStatus = preferHigherStatus(apiMessage, localMessage);
|
|
487
|
+
merged.set(localId, {
|
|
488
|
+
...withStatus,
|
|
489
|
+
isStarred: typeof localMessage.isStarred === "boolean" ? localMessage.isStarred : withStatus.isStarred,
|
|
490
|
+
isPinned: typeof localMessage.isPinned === "boolean" ? localMessage.isPinned : withStatus.isPinned
|
|
491
|
+
});
|
|
492
|
+
return;
|
|
493
|
+
}
|
|
494
|
+
const duplicateInApi = apiMessages.some((apiMessage) => {
|
|
495
|
+
const apiId = String(apiMessage?._id || apiMessage?.id || "");
|
|
496
|
+
if (localId && apiId && localId === apiId) return true;
|
|
497
|
+
if (!isOwnMessage(localMessage, loggedUserId)) return false;
|
|
498
|
+
if (!isOwnMessage(apiMessage, loggedUserId)) return false;
|
|
499
|
+
if (getMessageContent(localMessage) !== getMessageContent(apiMessage)) return false;
|
|
500
|
+
return Math.abs(getMessageTimestamp(localMessage) - getMessageTimestamp(apiMessage)) < 12e4;
|
|
501
|
+
});
|
|
502
|
+
if (duplicateInApi) return;
|
|
503
|
+
const key = localId || `pending-${getMessageTimestamp(localMessage)}-${getMessageContent(localMessage)}`;
|
|
504
|
+
if (!merged.has(key)) merged.set(key, localMessage);
|
|
505
|
+
});
|
|
506
|
+
return Array.from(merged.values());
|
|
511
507
|
};
|
|
512
508
|
}
|
|
513
509
|
});
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
getChatClientId = () => chatClientId;
|
|
544
|
-
setChatAccessToken = (token) => {
|
|
545
|
-
chatAccessToken = token.trim();
|
|
546
|
-
};
|
|
547
|
-
setChatBaseURL = (url) => {
|
|
548
|
-
chatBaseUrl = url.trim();
|
|
549
|
-
apiClient.defaults.baseURL = chatBaseUrl;
|
|
550
|
-
syncDefaultApiVersionFromBaseUrl(chatBaseUrl);
|
|
551
|
-
};
|
|
552
|
-
getChatBaseUrl = () => chatBaseUrl;
|
|
553
|
-
setChatSocketUrl = (url) => {
|
|
554
|
-
chatSocketUrl = url.trim();
|
|
510
|
+
|
|
511
|
+
// src/store/helpers/participant.helpers.ts
|
|
512
|
+
var mergeConversationParticipants, buildParticipantsFromMessagePayload, resolveOtherParticipant, getOtherParticipantId;
|
|
513
|
+
var init_participant_helpers = __esm({
|
|
514
|
+
"src/store/helpers/participant.helpers.ts"() {
|
|
515
|
+
mergeConversationParticipants = (existingParticipants, incomingParticipants) => {
|
|
516
|
+
const byId = /* @__PURE__ */ new Map();
|
|
517
|
+
const addParticipant = (participant) => {
|
|
518
|
+
const id = String(participant?._id || participant?.id || participant || "");
|
|
519
|
+
if (!id) return;
|
|
520
|
+
const incomingEntry = typeof participant === "object" && participant !== null ? participant : { _id: id };
|
|
521
|
+
const current = byId.get(id);
|
|
522
|
+
if (!current) {
|
|
523
|
+
byId.set(id, incomingEntry);
|
|
524
|
+
return;
|
|
525
|
+
}
|
|
526
|
+
const currentHasName = Boolean(current.name);
|
|
527
|
+
const incomingHasName = Boolean(incomingEntry.name);
|
|
528
|
+
if (incomingHasName && !currentHasName) {
|
|
529
|
+
byId.set(id, { ...current, ...incomingEntry });
|
|
530
|
+
} else if (!incomingHasName && currentHasName) {
|
|
531
|
+
byId.set(id, { ...incomingEntry, ...current });
|
|
532
|
+
} else {
|
|
533
|
+
byId.set(id, { ...current, ...incomingEntry });
|
|
534
|
+
}
|
|
535
|
+
};
|
|
536
|
+
(existingParticipants || []).forEach(addParticipant);
|
|
537
|
+
(incomingParticipants || []).forEach(addParticipant);
|
|
538
|
+
return Array.from(byId.values());
|
|
555
539
|
};
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
540
|
+
buildParticipantsFromMessagePayload = (payload, loggedUserId, existingParticipants) => {
|
|
541
|
+
const participants = [];
|
|
542
|
+
const seen = /* @__PURE__ */ new Set();
|
|
543
|
+
const addParticipant = (participant) => {
|
|
544
|
+
const id = String(participant?._id || participant?.id || participant || "");
|
|
545
|
+
if (!id || seen.has(id)) return;
|
|
546
|
+
seen.add(id);
|
|
547
|
+
participants.push(
|
|
548
|
+
typeof participant === "object" && participant !== null ? participant : { _id: id }
|
|
549
|
+
);
|
|
550
|
+
};
|
|
551
|
+
(existingParticipants || []).forEach(addParticipant);
|
|
552
|
+
addParticipant(payload?.sender);
|
|
553
|
+
addParticipant(payload?.receiver);
|
|
554
|
+
if (loggedUserId) addParticipant(loggedUserId);
|
|
555
|
+
const senderId = String(payload?.sender?._id || payload?.sender?.id || payload?.sender || "");
|
|
556
|
+
if (loggedUserId && senderId === String(loggedUserId) && participants.length < 2 && payload?.owner && String(payload.owner) !== String(loggedUserId)) {
|
|
557
|
+
addParticipant(payload.owner);
|
|
558
|
+
}
|
|
559
|
+
return participants;
|
|
559
560
|
};
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
chatQueryParamApis = ["get"];
|
|
564
|
-
return;
|
|
561
|
+
resolveOtherParticipant = (conversation, loggedUserId, allUsers) => {
|
|
562
|
+
if (!conversation || !loggedUserId) {
|
|
563
|
+
return { id: "", user: null };
|
|
565
564
|
}
|
|
566
|
-
|
|
565
|
+
const myId = String(loggedUserId);
|
|
566
|
+
const participantIds = (conversation.participants || []).map(
|
|
567
|
+
(participant) => String(participant?._id || participant?.id || participant || "")
|
|
568
|
+
);
|
|
569
|
+
const otherId = participantIds.find((id) => id && id !== myId) || (conversation.owner && String(conversation.owner) !== myId ? String(conversation.owner) : "");
|
|
570
|
+
if (!otherId) {
|
|
571
|
+
return { id: "", user: null };
|
|
572
|
+
}
|
|
573
|
+
const fromParticipants = (conversation.participants || []).find(
|
|
574
|
+
(participant) => String(participant?._id || participant?.id || participant) === otherId
|
|
575
|
+
);
|
|
576
|
+
const participantStub = typeof fromParticipants === "object" && fromParticipants !== null ? fromParticipants : { _id: otherId };
|
|
577
|
+
const fromAllUsers = (allUsers || []).find((user) => String(user._id) === otherId) || null;
|
|
578
|
+
return {
|
|
579
|
+
id: otherId,
|
|
580
|
+
user: fromAllUsers ? { ...participantStub, ...fromAllUsers } : participantStub
|
|
581
|
+
};
|
|
567
582
|
};
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
583
|
+
getOtherParticipantId = (conversation, loggedUserId) => {
|
|
584
|
+
if (!conversation?.participants?.length && !conversation?.owner) return "";
|
|
585
|
+
const otherId = resolveOtherParticipant(conversation, loggedUserId).id;
|
|
586
|
+
return otherId;
|
|
571
587
|
};
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
(
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
config.headers = config.headers ?? {};
|
|
585
|
-
config.headers.set("ngrok-skip-browser-warning", "true");
|
|
586
|
-
config.headers.set("platform", "web");
|
|
587
|
-
config.headers.set("is-tenant", "true");
|
|
588
|
-
config.headers.set("x-client-id", chatClientId);
|
|
589
|
-
const requestUrl = String(config.url || "");
|
|
590
|
-
const scopedParams = resolveParamsForRequest(requestUrl, config.method);
|
|
591
|
-
if (Object.keys(scopedParams).length > 0) {
|
|
592
|
-
config.params = { ...scopedParams, ...config.params };
|
|
593
|
-
}
|
|
594
|
-
try {
|
|
595
|
-
const { useStore: useStore2 } = (init_useStore(), __toCommonJS(useStore_exports));
|
|
596
|
-
const accessToken = useStore2.getState().accessToken || chatAccessToken;
|
|
597
|
-
if (accessToken) {
|
|
598
|
-
config.headers.set("Authorization", `Bearer ${accessToken}`);
|
|
599
|
-
}
|
|
600
|
-
} catch {
|
|
601
|
-
}
|
|
602
|
-
return config;
|
|
603
|
-
},
|
|
604
|
-
(error) => {
|
|
605
|
-
return Promise.reject(error);
|
|
588
|
+
}
|
|
589
|
+
});
|
|
590
|
+
|
|
591
|
+
// src/store/helpers/unread.helpers.ts
|
|
592
|
+
var normalizeUnreadCount, incrementUnreadCount, resolveNextUnreadCount;
|
|
593
|
+
var init_unread_helpers = __esm({
|
|
594
|
+
"src/store/helpers/unread.helpers.ts"() {
|
|
595
|
+
normalizeUnreadCount = (unreadCount, loggedUserId) => {
|
|
596
|
+
if (typeof unreadCount === "number" && Number.isFinite(unreadCount)) return unreadCount;
|
|
597
|
+
if (typeof unreadCount === "string" && unreadCount.trim() !== "") {
|
|
598
|
+
const parsed = Number.parseInt(unreadCount, 10);
|
|
599
|
+
if (Number.isFinite(parsed)) return parsed;
|
|
606
600
|
}
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
const resetSeconds = resetHeader ? parseInt(String(resetHeader), 10) : 120;
|
|
614
|
-
rateLimitResetAt = Date.now() + (Number.isFinite(resetSeconds) ? resetSeconds : 120) * 1e3;
|
|
615
|
-
try {
|
|
616
|
-
const { useSocketStore: useSocketStore2 } = (init_useSocketStore(), __toCommonJS(useSocketStore_exports));
|
|
617
|
-
const store = useSocketStore2.getState();
|
|
618
|
-
store.setRateLimitResetAt(rateLimitResetAt);
|
|
619
|
-
} catch {
|
|
601
|
+
if (typeof unreadCount === "object" && unreadCount !== null && !Array.isArray(unreadCount)) {
|
|
602
|
+
const map = unreadCount;
|
|
603
|
+
if (loggedUserId) {
|
|
604
|
+
const mine = map[String(loggedUserId)];
|
|
605
|
+
if (mine !== void 0 && mine !== null) {
|
|
606
|
+
return normalizeUnreadCount(mine, loggedUserId);
|
|
620
607
|
}
|
|
621
608
|
}
|
|
622
|
-
|
|
609
|
+
const values = Object.values(map).map((value) => normalizeUnreadCount(value, loggedUserId)).filter((value) => Number.isFinite(value));
|
|
610
|
+
return values.length ? Math.max(...values) : 0;
|
|
623
611
|
}
|
|
624
|
-
|
|
625
|
-
}
|
|
626
|
-
});
|
|
627
|
-
|
|
628
|
-
// src/call/call.api.ts
|
|
629
|
-
var callsApiUrl, apiCreateCallRoom, apiFetchJoinToken, apiFetchCallHistory, apiFetchCallDetail;
|
|
630
|
-
var init_call_api = __esm({
|
|
631
|
-
"src/call/call.api.ts"() {
|
|
632
|
-
init_client();
|
|
633
|
-
init_api_version();
|
|
634
|
-
callsApiUrl = (path) => buildVersionedApiUrl(path, { service: "v2" });
|
|
635
|
-
apiCreateCallRoom = async (conversationId, mode) => {
|
|
636
|
-
const { data } = await apiClient.post(callsApiUrl("/calls/rooms"), {
|
|
637
|
-
conversationId,
|
|
638
|
-
mode
|
|
639
|
-
});
|
|
640
|
-
return data?.data ?? data;
|
|
641
|
-
};
|
|
642
|
-
apiFetchJoinToken = async (meetingId) => {
|
|
643
|
-
const { data } = await apiClient.get(callsApiUrl("/calls/videosdk-token"), {
|
|
644
|
-
params: { meetingId }
|
|
645
|
-
});
|
|
646
|
-
return data?.data ?? data;
|
|
647
|
-
};
|
|
648
|
-
apiFetchCallHistory = async (conversationId, page = 1, limit = 20) => {
|
|
649
|
-
const { data } = await apiClient.get(callsApiUrl("/calls"), {
|
|
650
|
-
params: { conversationId, page, limit }
|
|
651
|
-
});
|
|
652
|
-
return data?.data ?? data;
|
|
612
|
+
return 0;
|
|
653
613
|
};
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
614
|
+
incrementUnreadCount = (unreadCount, loggedUserId) => normalizeUnreadCount(unreadCount, loggedUserId) + 1;
|
|
615
|
+
resolveNextUnreadCount = ({
|
|
616
|
+
currentUnread,
|
|
617
|
+
serverUnread,
|
|
618
|
+
isActiveConversation,
|
|
619
|
+
shouldIncrement,
|
|
620
|
+
loggedUserId
|
|
621
|
+
}) => {
|
|
622
|
+
if (isActiveConversation) return 0;
|
|
623
|
+
let next = currentUnread;
|
|
624
|
+
if (shouldIncrement) {
|
|
625
|
+
next = incrementUnreadCount(next, loggedUserId);
|
|
626
|
+
}
|
|
627
|
+
if (serverUnread !== void 0 && serverUnread !== null) {
|
|
628
|
+
next = Math.max(next, normalizeUnreadCount(serverUnread, loggedUserId));
|
|
629
|
+
}
|
|
630
|
+
return next;
|
|
657
631
|
};
|
|
658
632
|
}
|
|
659
633
|
});
|
|
660
634
|
|
|
661
|
-
// src/
|
|
662
|
-
var
|
|
663
|
-
var
|
|
664
|
-
"src/
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
635
|
+
// src/store/helpers/conversation.helpers.ts
|
|
636
|
+
var findConversationIdByMessageId, applyMessageStatusToStore, isGroupConversation, normalizeConversationRecord, resolveConversationLastMessage, syncConversationLastMessage, upsertConversationInList, findConversationById, findConversationIdByLastMessageId, findConversationForChannel, resolveConversationIdForChannel;
|
|
637
|
+
var init_conversation_helpers = __esm({
|
|
638
|
+
"src/store/helpers/conversation.helpers.ts"() {
|
|
639
|
+
init_group_display_helpers();
|
|
640
|
+
init_message_helpers();
|
|
641
|
+
init_participant_helpers();
|
|
642
|
+
init_unread_helpers();
|
|
643
|
+
findConversationIdByMessageId = (messagesByConversation, messageId2) => {
|
|
644
|
+
return Object.keys(messagesByConversation).find(
|
|
645
|
+
(cid) => messagesByConversation[cid].some(
|
|
646
|
+
(m) => String(m._id || m.id) === String(messageId2)
|
|
647
|
+
)
|
|
648
|
+
);
|
|
668
649
|
};
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
650
|
+
applyMessageStatusToStore = (state, messageId2, status, conversationId) => {
|
|
651
|
+
const normalizedMessageId = String(messageId2);
|
|
652
|
+
const resolvedConversationId = conversationId ? String(conversationId) : findConversationIdByMessageId(state.messagesByConversation, normalizedMessageId) || findConversationIdByLastMessageId(state.conversations, normalizedMessageId) || null;
|
|
653
|
+
const nextConversations = state.conversations.map((conversation) => {
|
|
654
|
+
const lastMessageId = String(
|
|
655
|
+
conversation?.lastMessage?._id || conversation?.lastMessage?.id || ""
|
|
656
|
+
);
|
|
657
|
+
if (lastMessageId !== normalizedMessageId) return conversation;
|
|
658
|
+
return {
|
|
659
|
+
...conversation,
|
|
660
|
+
lastMessage: mergeLastMessage(conversation.lastMessage, { status })
|
|
661
|
+
};
|
|
662
|
+
});
|
|
663
|
+
const nextMessagesByConversation = { ...state.messagesByConversation };
|
|
664
|
+
const patchMessages = (messages) => messages.map(
|
|
665
|
+
(message) => String(message._id || message.id) === normalizedMessageId ? preferHigherStatus(message, { status }) : message
|
|
666
|
+
);
|
|
667
|
+
if (resolvedConversationId && nextMessagesByConversation[resolvedConversationId]) {
|
|
668
|
+
nextMessagesByConversation[resolvedConversationId] = patchMessages(
|
|
669
|
+
nextMessagesByConversation[resolvedConversationId]
|
|
670
|
+
);
|
|
671
|
+
} else {
|
|
672
|
+
for (const cid of Object.keys(nextMessagesByConversation)) {
|
|
673
|
+
const patched = patchMessages(nextMessagesByConversation[cid]);
|
|
674
|
+
if (patched !== nextMessagesByConversation[cid]) {
|
|
675
|
+
nextMessagesByConversation[cid] = patched;
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
return {
|
|
680
|
+
conversations: nextConversations,
|
|
681
|
+
messagesByConversation: nextMessagesByConversation
|
|
682
|
+
};
|
|
683
|
+
};
|
|
684
|
+
isGroupConversation = (conversation) => {
|
|
685
|
+
if (!conversation) return false;
|
|
686
|
+
return conversation.conversationType === "group" || !!conversation.isGroup;
|
|
687
|
+
};
|
|
688
|
+
normalizeConversationRecord = (conversation, existing) => {
|
|
689
|
+
if (!conversation) return existing ?? null;
|
|
690
|
+
const resolvedBlocked = conversation.isBlocked !== void 0 ? Boolean(conversation.isBlocked) : conversation.blocked !== void 0 ? Boolean(conversation.blocked) : conversation.isUserBlocked !== void 0 ? Boolean(conversation.isUserBlocked) : existing?.isBlocked ?? false;
|
|
691
|
+
const mergedParticipants = conversation.participants !== void 0 ? mergeConversationParticipants(existing?.participants, conversation.participants) : existing?.participants ?? conversation.participants;
|
|
692
|
+
const isGroup = isGroupConversation(conversation) || isGroupConversation(existing);
|
|
693
|
+
const resolvedGroupImage = resolveGroupImage(conversation) || resolveGroupImage(existing);
|
|
694
|
+
const incomingGroupName = typeof conversation.groupName === "string" && conversation.groupName.trim() || typeof conversation.name === "string" && conversation.name.trim() || "";
|
|
695
|
+
const existingGroupName = typeof existing?.groupName === "string" && existing.groupName.trim() || typeof existing?.name === "string" && existing.name.trim() || "";
|
|
696
|
+
const resolvedGroupName = incomingGroupName || existingGroupName || void 0;
|
|
697
|
+
return {
|
|
698
|
+
...existing,
|
|
699
|
+
...conversation,
|
|
700
|
+
participants: mergedParticipants,
|
|
701
|
+
isGroup,
|
|
702
|
+
isBlocked: resolvedBlocked,
|
|
703
|
+
lastMessage: mergeLastMessage(existing?.lastMessage, conversation.lastMessage),
|
|
704
|
+
...isGroup ? {
|
|
705
|
+
...resolvedGroupName ? { groupName: resolvedGroupName, name: resolvedGroupName } : {},
|
|
706
|
+
...resolvedGroupImage ? { image: resolvedGroupImage, groupImage: resolvedGroupImage } : {}
|
|
707
|
+
} : {}
|
|
708
|
+
};
|
|
709
|
+
};
|
|
710
|
+
resolveConversationLastMessage = (conversation, messagesByConversation) => {
|
|
711
|
+
const conversationId = String(conversation._id || conversation.id || "");
|
|
712
|
+
const storedLast = conversation.lastMessage;
|
|
713
|
+
const latestFromMessages = getLatestMessageFromList(messagesByConversation[conversationId] || []);
|
|
714
|
+
if (!latestFromMessages && !storedLast) return null;
|
|
715
|
+
if (!latestFromMessages) return storedLast;
|
|
716
|
+
if (!messageHasPreviewBody(storedLast)) return latestFromMessages;
|
|
717
|
+
const storedTime = new Date(storedLast.createdAt || storedLast.timestamp || 0).getTime();
|
|
718
|
+
const latestTime = new Date(latestFromMessages.createdAt || latestFromMessages.timestamp || 0).getTime();
|
|
719
|
+
return latestTime >= storedTime ? mergeLastMessage(storedLast, latestFromMessages) : mergeLastMessage(latestFromMessages, storedLast);
|
|
720
|
+
};
|
|
721
|
+
syncConversationLastMessage = (conversations, conversationId, messages) => {
|
|
722
|
+
const latest = getLatestMessageFromList(messages);
|
|
723
|
+
if (!latest) return conversations;
|
|
724
|
+
return conversations.map((conversation) => {
|
|
725
|
+
if (String(conversation._id) !== String(conversationId)) return conversation;
|
|
726
|
+
return {
|
|
727
|
+
...conversation,
|
|
728
|
+
lastMessage: mergeLastMessage(conversation.lastMessage, latest)
|
|
729
|
+
};
|
|
730
|
+
});
|
|
731
|
+
};
|
|
732
|
+
upsertConversationInList = (conversations, incoming, options) => {
|
|
733
|
+
const conversationId = String(incoming?._id || incoming?.conversationId || "");
|
|
734
|
+
if (!conversationId) return conversations;
|
|
735
|
+
const index = conversations.findIndex((c) => String(c._id) === conversationId);
|
|
736
|
+
const existing = index >= 0 ? conversations[index] : null;
|
|
737
|
+
const mergedLastMessage = mergeLastMessage(
|
|
738
|
+
existing?.lastMessage,
|
|
739
|
+
options?.lastMessage ?? incoming.lastMessage
|
|
740
|
+
);
|
|
741
|
+
const isActiveConversation = !!options?.activeConversationId && String(options.activeConversationId) === conversationId;
|
|
742
|
+
const loggedUserId = options?.loggedUserId;
|
|
743
|
+
const incomingLastMessage = options?.lastMessage ?? incoming.lastMessage;
|
|
744
|
+
const lastMessageChanged = !!incomingLastMessage && String(mergedLastMessage?._id || mergedLastMessage?.id || "") !== String(existing?.lastMessage?._id || existing?.lastMessage?.id || "");
|
|
745
|
+
const senderId = String(
|
|
746
|
+
incomingLastMessage?.sender?._id || incomingLastMessage?.sender?.id || incomingLastMessage?.sender || ""
|
|
747
|
+
);
|
|
748
|
+
const isIncomingLastMessage = !!loggedUserId && !!senderId && senderId !== String(loggedUserId);
|
|
749
|
+
const normalized = normalizeConversationRecord(
|
|
750
|
+
{
|
|
751
|
+
...incoming,
|
|
752
|
+
_id: conversationId,
|
|
753
|
+
lastMessage: mergedLastMessage,
|
|
754
|
+
unreadCount: resolveNextUnreadCount({
|
|
755
|
+
currentUnread: normalizeUnreadCount(existing?.unreadCount, loggedUserId),
|
|
756
|
+
serverUnread: incoming.unreadCount,
|
|
757
|
+
isActiveConversation,
|
|
758
|
+
loggedUserId,
|
|
759
|
+
shouldIncrement: !isActiveConversation && lastMessageChanged && isIncomingLastMessage && !options?.isFromSelf
|
|
760
|
+
})
|
|
761
|
+
},
|
|
762
|
+
existing
|
|
763
|
+
);
|
|
764
|
+
if (index < 0) {
|
|
765
|
+
return [normalized, ...conversations];
|
|
766
|
+
}
|
|
767
|
+
if (lastMessageChanged) {
|
|
768
|
+
const withoutExisting = conversations.filter((_, itemIndex) => itemIndex !== index);
|
|
769
|
+
return [normalized, ...withoutExisting];
|
|
770
|
+
}
|
|
771
|
+
const next = [...conversations];
|
|
772
|
+
next[index] = normalized;
|
|
773
|
+
return next;
|
|
774
|
+
};
|
|
775
|
+
findConversationById = (conversations, conversationId) => {
|
|
776
|
+
if (!conversationId) return void 0;
|
|
777
|
+
return conversations.find((c) => String(c._id) === String(conversationId));
|
|
778
|
+
};
|
|
779
|
+
findConversationIdByLastMessageId = (conversations, messageId2) => {
|
|
780
|
+
if (!messageId2) return null;
|
|
781
|
+
const normalizedMessageId = String(messageId2);
|
|
782
|
+
const match = conversations.find(
|
|
783
|
+
(conversation) => String(conversation?.lastMessage?._id || conversation?.lastMessage?.id || "") === normalizedMessageId
|
|
784
|
+
);
|
|
785
|
+
return match?._id ? String(match._id) : null;
|
|
786
|
+
};
|
|
787
|
+
findConversationForChannel = (channel, loggedUserId, conversations) => {
|
|
788
|
+
if (!channel || !loggedUserId) return void 0;
|
|
789
|
+
if (channel.conversationId) {
|
|
790
|
+
const byId = findConversationById(conversations, channel.conversationId);
|
|
791
|
+
if (byId) return byId;
|
|
792
|
+
}
|
|
793
|
+
if (channel.isGroup) {
|
|
794
|
+
return conversations.find(
|
|
795
|
+
(c) => isGroupConversation(c) && String(c._id) === String(channel.id)
|
|
796
|
+
);
|
|
797
|
+
}
|
|
798
|
+
return conversations.find((c) => {
|
|
799
|
+
if (isGroupConversation(c)) return false;
|
|
800
|
+
const ids = (c.participants || []).map((p) => String(p._id || p));
|
|
801
|
+
return ids.includes(String(channel.id)) && ids.includes(String(loggedUserId));
|
|
802
|
+
});
|
|
803
|
+
};
|
|
804
|
+
resolveConversationIdForChannel = (channel, loggedUserId, conversations) => {
|
|
805
|
+
const found = findConversationForChannel(channel, loggedUserId, conversations);
|
|
806
|
+
if (found?._id) return String(found._id);
|
|
807
|
+
if (channel?.conversationId) return String(channel.conversationId);
|
|
808
|
+
return null;
|
|
809
|
+
};
|
|
810
|
+
}
|
|
811
|
+
});
|
|
812
|
+
|
|
813
|
+
// src/store/helpers/pagination.helpers.ts
|
|
814
|
+
var emptyMessagesMetadata, extractPagePagination;
|
|
815
|
+
var init_pagination_helpers = __esm({
|
|
816
|
+
"src/store/helpers/pagination.helpers.ts"() {
|
|
817
|
+
emptyMessagesMetadata = () => ({
|
|
818
|
+
nextCursor: null,
|
|
819
|
+
hasMore: false,
|
|
820
|
+
isFirstPage: true
|
|
821
|
+
});
|
|
822
|
+
extractPagePagination = (response) => response?.data?.pagination ?? response?.pagination ?? null;
|
|
823
|
+
}
|
|
824
|
+
});
|
|
825
|
+
|
|
826
|
+
// src/store/helpers/normalize.helpers.ts
|
|
827
|
+
var normalizeId, resolveConversationIdForIncomingMessage, resolveIsGroupFromPayload, resolveDmMarkReadReaderId;
|
|
828
|
+
var init_normalize_helpers = __esm({
|
|
829
|
+
"src/store/helpers/normalize.helpers.ts"() {
|
|
830
|
+
init_message_helpers();
|
|
831
|
+
init_conversation_helpers();
|
|
832
|
+
normalizeId = (id) => String(id?._id || id?.id || id || "");
|
|
833
|
+
resolveConversationIdForIncomingMessage = (payload, conversations, loggedUserId) => {
|
|
834
|
+
const fromPayload = extractMessageData(payload).conversationId;
|
|
835
|
+
if (fromPayload) return String(fromPayload);
|
|
836
|
+
if (!loggedUserId) return null;
|
|
837
|
+
const myId = String(loggedUserId);
|
|
838
|
+
const senderId = getMessageSenderId(payload);
|
|
839
|
+
const receiverId = String(payload?.receiver?._id || payload?.receiver?.id || payload?.receiver || "");
|
|
840
|
+
const otherUserId = senderId === myId ? receiverId : senderId;
|
|
841
|
+
if (!otherUserId) return null;
|
|
842
|
+
const match = conversations.find((conversation) => {
|
|
843
|
+
if (isGroupConversation(conversation)) return false;
|
|
844
|
+
const participants = (conversation.participants || []).map((p) => String(p._id || p));
|
|
845
|
+
return participants.includes(otherUserId);
|
|
846
|
+
});
|
|
847
|
+
return match?._id ? String(match._id) : null;
|
|
848
|
+
};
|
|
849
|
+
resolveIsGroupFromPayload = (payload, conversations) => {
|
|
850
|
+
const conversationId = payload?.conversationId || payload?.message?.conversationId || payload?.message?.conversation_id;
|
|
851
|
+
const fromStore = findConversationById(conversations, conversationId);
|
|
852
|
+
if (fromStore) return isGroupConversation(fromStore);
|
|
853
|
+
return !!payload?.isGroup || payload?.conversationType === "group" || payload?.message?.conversationType === "group";
|
|
854
|
+
};
|
|
855
|
+
resolveDmMarkReadReaderId = (payload, loggedUserId) => {
|
|
856
|
+
if (payload.readBy != null) return String(payload.readBy);
|
|
857
|
+
if (payload.userId != null) return String(payload.userId);
|
|
858
|
+
if (payload.readerId != null) return String(payload.readerId);
|
|
859
|
+
const sender = payload.sender?._id ?? payload.sender;
|
|
860
|
+
const receiver = payload.receiver?._id ?? payload.receiver;
|
|
861
|
+
const myId = loggedUserId != null ? String(loggedUserId) : null;
|
|
862
|
+
if (sender != null && receiver != null && myId) {
|
|
863
|
+
if (String(receiver) === myId) return String(sender);
|
|
864
|
+
if (String(sender) === myId) return String(sender);
|
|
865
|
+
}
|
|
866
|
+
if (sender != null) return String(sender);
|
|
867
|
+
if (payload.senderId != null) return String(payload.senderId);
|
|
868
|
+
return null;
|
|
869
|
+
};
|
|
870
|
+
}
|
|
871
|
+
});
|
|
872
|
+
|
|
873
|
+
// src/store/helpers/store.helpers.ts
|
|
874
|
+
var init_store_helpers = __esm({
|
|
875
|
+
"src/store/helpers/store.helpers.ts"() {
|
|
876
|
+
init_conversation_helpers();
|
|
877
|
+
init_message_helpers();
|
|
878
|
+
init_participant_helpers();
|
|
879
|
+
init_unread_helpers();
|
|
880
|
+
init_group_display_helpers();
|
|
881
|
+
init_pagination_helpers();
|
|
882
|
+
init_normalize_helpers();
|
|
883
|
+
}
|
|
884
|
+
});
|
|
885
|
+
|
|
886
|
+
// src/notifications/open-conversation.ts
|
|
887
|
+
var OPEN_CONVERSATION_EVENT, NOTIFICATION_CLICK_SW_TYPE, PUSH_BROADCAST_CHANNEL, PUSH_DATA_CACHE_NAME, PENDING_STORAGE_KEY, PENDING_OPEN_CACHE_PATH, resolveConversationIdFromPushData, normalizePushMeta, parsePendingRaw, peekPendingOpenConversation, peekPendingOpenRequest, consumePendingOpenConversation, consumePendingOpenRequest, requestOpenConversation, buildChannelFromPushRequest, mapConversationToChannel, stripConversationIdFromUrl, cacheRequestForPath, consumePendingOpenFromPushCache, readConversationIdFromLocation, consumedUrlConversationIds, lastHandledOpenId, lastHandledOpenAt, shouldHandleOpen, handleNotificationOpen, installNotificationClickBridge;
|
|
888
|
+
var init_open_conversation = __esm({
|
|
889
|
+
"src/notifications/open-conversation.ts"() {
|
|
890
|
+
init_store_helpers();
|
|
891
|
+
OPEN_CONVERSATION_EVENT = "realtimex:open-conversation";
|
|
892
|
+
NOTIFICATION_CLICK_SW_TYPE = "realtimex:notification-click";
|
|
893
|
+
PUSH_BROADCAST_CHANNEL = "realtimex-push";
|
|
894
|
+
PUSH_DATA_CACHE_NAME = "realtimex-push-data-v1";
|
|
895
|
+
PENDING_STORAGE_KEY = "realtimex:pending-open-conversation";
|
|
896
|
+
PENDING_OPEN_CACHE_PATH = "pending-open";
|
|
897
|
+
resolveConversationIdFromPushData = (data) => {
|
|
898
|
+
if (!data || typeof data !== "object") return "";
|
|
899
|
+
let fcmMsg = data.FCM_MSG;
|
|
900
|
+
if (typeof fcmMsg === "string") {
|
|
901
|
+
try {
|
|
902
|
+
fcmMsg = JSON.parse(fcmMsg);
|
|
903
|
+
} catch {
|
|
904
|
+
fcmMsg = void 0;
|
|
905
|
+
}
|
|
906
|
+
}
|
|
907
|
+
const nested = fcmMsg && typeof fcmMsg === "object" ? fcmMsg.data : void 0;
|
|
908
|
+
const source = { ...nested || {}, ...data };
|
|
909
|
+
return String(
|
|
910
|
+
source.conversationId || source.conversation_id || source.conversationID || ""
|
|
911
|
+
).trim();
|
|
912
|
+
};
|
|
913
|
+
normalizePushMeta = (meta) => {
|
|
914
|
+
if (!meta || typeof meta !== "object") return {};
|
|
915
|
+
return {
|
|
916
|
+
senderId: String(
|
|
917
|
+
meta.senderId || meta.sender_id || ""
|
|
918
|
+
).trim() || void 0,
|
|
919
|
+
kind: String(meta.kind || "").trim() || void 0,
|
|
920
|
+
messageId: String(
|
|
921
|
+
meta.messageId || meta.message_id || ""
|
|
922
|
+
).trim() || void 0,
|
|
923
|
+
title: String(meta.title || "").trim() || void 0
|
|
924
|
+
};
|
|
925
|
+
};
|
|
926
|
+
parsePendingRaw = (raw) => {
|
|
927
|
+
if (!raw) return null;
|
|
928
|
+
try {
|
|
929
|
+
const parsed = JSON.parse(raw);
|
|
930
|
+
if (parsed?.conversationId) {
|
|
931
|
+
return {
|
|
932
|
+
conversationId: String(parsed.conversationId).trim(),
|
|
933
|
+
...normalizePushMeta(parsed)
|
|
934
|
+
};
|
|
935
|
+
}
|
|
936
|
+
} catch {
|
|
937
|
+
}
|
|
938
|
+
const id = String(raw).trim();
|
|
939
|
+
return id ? { conversationId: id } : null;
|
|
940
|
+
};
|
|
941
|
+
peekPendingOpenConversation = () => peekPendingOpenRequest()?.conversationId ?? null;
|
|
942
|
+
peekPendingOpenRequest = () => {
|
|
943
|
+
try {
|
|
944
|
+
return parsePendingRaw(sessionStorage.getItem(PENDING_STORAGE_KEY));
|
|
945
|
+
} catch {
|
|
946
|
+
return null;
|
|
947
|
+
}
|
|
948
|
+
};
|
|
949
|
+
consumePendingOpenConversation = () => consumePendingOpenRequest()?.conversationId ?? null;
|
|
950
|
+
consumePendingOpenRequest = () => {
|
|
951
|
+
const request = peekPendingOpenRequest();
|
|
952
|
+
try {
|
|
953
|
+
sessionStorage.removeItem(PENDING_STORAGE_KEY);
|
|
954
|
+
} catch {
|
|
955
|
+
}
|
|
956
|
+
return request;
|
|
957
|
+
};
|
|
958
|
+
requestOpenConversation = (conversationId, meta) => {
|
|
959
|
+
const id = String(conversationId || "").trim();
|
|
960
|
+
if (!id) return;
|
|
961
|
+
const request = {
|
|
962
|
+
conversationId: id,
|
|
963
|
+
...normalizePushMeta(meta)
|
|
964
|
+
};
|
|
965
|
+
console.log("[realtimex-push] requestOpenConversation:", request);
|
|
966
|
+
try {
|
|
967
|
+
sessionStorage.setItem(PENDING_STORAGE_KEY, JSON.stringify(request));
|
|
968
|
+
} catch {
|
|
969
|
+
}
|
|
970
|
+
try {
|
|
971
|
+
window.dispatchEvent(
|
|
972
|
+
new CustomEvent(OPEN_CONVERSATION_EVENT, { detail: request })
|
|
973
|
+
);
|
|
974
|
+
} catch {
|
|
975
|
+
}
|
|
976
|
+
};
|
|
977
|
+
buildChannelFromPushRequest = (request, loggedUserId, allUsers) => {
|
|
978
|
+
const conversationId = request.conversationId;
|
|
979
|
+
const isGroup = request.kind === "group";
|
|
980
|
+
const senderId = String(request.senderId || "").trim();
|
|
981
|
+
if (isGroup) {
|
|
982
|
+
const rawName2 = request.title || "Group Chat";
|
|
983
|
+
return {
|
|
984
|
+
id: conversationId,
|
|
985
|
+
conversationId,
|
|
986
|
+
name: rawName2.charAt(0).toUpperCase() + rawName2.slice(1),
|
|
987
|
+
isGroup: true
|
|
988
|
+
};
|
|
989
|
+
}
|
|
990
|
+
const senderUser = senderId ? (allUsers || []).find((user) => String(user._id) === senderId) : null;
|
|
991
|
+
const rawName = senderUser?.name || request.title || "New message";
|
|
992
|
+
return {
|
|
993
|
+
id: senderId || conversationId,
|
|
994
|
+
conversationId,
|
|
995
|
+
name: rawName.charAt(0).toUpperCase() + rawName.slice(1),
|
|
996
|
+
image: senderUser?.image || void 0,
|
|
997
|
+
email: senderUser?.email,
|
|
998
|
+
isGroup: false
|
|
999
|
+
};
|
|
1000
|
+
};
|
|
1001
|
+
mapConversationToChannel = (conversation, loggedUserId, allUsers) => {
|
|
1002
|
+
const conversationId = String(conversation?._id || conversation?.id || "");
|
|
1003
|
+
const isGroup = isGroupConversation(conversation);
|
|
1004
|
+
if (isGroup) {
|
|
1005
|
+
const rawName2 = conversation.groupName || conversation.name || "Group Chat";
|
|
1006
|
+
return {
|
|
1007
|
+
id: conversationId,
|
|
1008
|
+
conversationId,
|
|
1009
|
+
name: rawName2.charAt(0).toUpperCase() + rawName2.slice(1),
|
|
1010
|
+
image: resolveGroupImage(conversation),
|
|
1011
|
+
isGroup: true,
|
|
1012
|
+
isLeft: !!conversation.isLeft,
|
|
1013
|
+
isBlocked: !!conversation.isBlocked,
|
|
1014
|
+
pinnedCount: conversation.pinnedCount,
|
|
1015
|
+
starredCount: conversation.starredCount
|
|
1016
|
+
};
|
|
1017
|
+
}
|
|
1018
|
+
const { id: otherId, user } = resolveOtherParticipant(
|
|
1019
|
+
conversation,
|
|
1020
|
+
loggedUserId,
|
|
1021
|
+
allUsers
|
|
1022
|
+
);
|
|
1023
|
+
const rawName = user?.name || "Unknown User";
|
|
1024
|
+
return {
|
|
1025
|
+
id: otherId || conversationId,
|
|
1026
|
+
conversationId,
|
|
1027
|
+
name: rawName.charAt(0).toUpperCase() + rawName.slice(1),
|
|
1028
|
+
image: user?.image || void 0,
|
|
1029
|
+
email: user?.email,
|
|
1030
|
+
isGroup: false,
|
|
1031
|
+
isLeft: !!conversation.isLeft,
|
|
1032
|
+
isBlocked: !!conversation.isBlocked,
|
|
1033
|
+
pinnedCount: conversation.pinnedCount,
|
|
1034
|
+
starredCount: conversation.starredCount
|
|
1035
|
+
};
|
|
1036
|
+
};
|
|
1037
|
+
stripConversationIdFromUrl = () => {
|
|
1038
|
+
if (typeof window === "undefined") return;
|
|
1039
|
+
try {
|
|
1040
|
+
const url = new URL(window.location.href);
|
|
1041
|
+
if (!url.searchParams.has("conversationId")) return;
|
|
1042
|
+
url.searchParams.delete("conversationId");
|
|
1043
|
+
const next = `${url.pathname}${url.search}${url.hash}`;
|
|
1044
|
+
window.history.replaceState(window.history.state, "", next);
|
|
1045
|
+
} catch {
|
|
1046
|
+
}
|
|
1047
|
+
};
|
|
1048
|
+
cacheRequestForPath = (path) => new Request(new URL(path, window.location.origin).toString());
|
|
1049
|
+
consumePendingOpenFromPushCache = async () => {
|
|
1050
|
+
if (typeof caches === "undefined") return null;
|
|
1051
|
+
try {
|
|
1052
|
+
const cache = await caches.open(PUSH_DATA_CACHE_NAME);
|
|
1053
|
+
const req = cacheRequestForPath(PENDING_OPEN_CACHE_PATH);
|
|
1054
|
+
const res = await cache.match(req);
|
|
1055
|
+
if (!res) return null;
|
|
1056
|
+
await cache.delete(req);
|
|
1057
|
+
const data = await res.json();
|
|
1058
|
+
const conversationId = resolveConversationIdFromPushData(data);
|
|
1059
|
+
if (!conversationId) return null;
|
|
1060
|
+
return {
|
|
1061
|
+
conversationId,
|
|
1062
|
+
...normalizePushMeta(data)
|
|
1063
|
+
};
|
|
1064
|
+
} catch {
|
|
1065
|
+
return null;
|
|
1066
|
+
}
|
|
1067
|
+
};
|
|
1068
|
+
readConversationIdFromLocation = () => {
|
|
1069
|
+
try {
|
|
1070
|
+
return new URLSearchParams(window.location.search).get("conversationId") || "";
|
|
1071
|
+
} catch {
|
|
1072
|
+
return "";
|
|
1073
|
+
}
|
|
1074
|
+
};
|
|
1075
|
+
consumedUrlConversationIds = /* @__PURE__ */ new Set();
|
|
1076
|
+
lastHandledOpenId = "";
|
|
1077
|
+
lastHandledOpenAt = 0;
|
|
1078
|
+
shouldHandleOpen = (conversationId) => {
|
|
1079
|
+
const id = String(conversationId || "").trim();
|
|
1080
|
+
if (!id) return false;
|
|
1081
|
+
const now = Date.now();
|
|
1082
|
+
if (lastHandledOpenId === id && now - lastHandledOpenAt < 2500) {
|
|
1083
|
+
return false;
|
|
1084
|
+
}
|
|
1085
|
+
lastHandledOpenId = id;
|
|
1086
|
+
lastHandledOpenAt = now;
|
|
1087
|
+
return true;
|
|
1088
|
+
};
|
|
1089
|
+
handleNotificationOpen = (conversationId, pushData, options) => {
|
|
1090
|
+
const id = String(conversationId || "").trim();
|
|
1091
|
+
if (!id) {
|
|
1092
|
+
console.warn("[realtimex-push] notification open ignored \u2014 no conversationId", pushData);
|
|
1093
|
+
return;
|
|
1094
|
+
}
|
|
1095
|
+
if (!shouldHandleOpen(id)) {
|
|
1096
|
+
stripConversationIdFromUrl();
|
|
1097
|
+
return;
|
|
1098
|
+
}
|
|
1099
|
+
requestOpenConversation(id, pushData);
|
|
1100
|
+
const chatPath = options.chatPath || "/chat";
|
|
1101
|
+
stripConversationIdFromUrl();
|
|
1102
|
+
options.onNavigate?.(id, chatPath);
|
|
1103
|
+
};
|
|
1104
|
+
installNotificationClickBridge = (options = {}) => {
|
|
1105
|
+
if (typeof window === "undefined") return () => void 0;
|
|
1106
|
+
const onSwMessage = (event) => {
|
|
1107
|
+
const data = event.data;
|
|
1108
|
+
console.log("[realtimex-push] SW \u2192 page message:", data);
|
|
1109
|
+
if (!data || data.type !== NOTIFICATION_CLICK_SW_TYPE) return;
|
|
1110
|
+
const pushData = data.data && typeof data.data === "object" ? data.data : void 0;
|
|
1111
|
+
const conversationId = String(data.conversationId || "") || resolveConversationIdFromPushData(pushData);
|
|
1112
|
+
console.log("[realtimex-push] notification click \u2192 open:", {
|
|
1113
|
+
conversationId,
|
|
1114
|
+
pushData
|
|
1115
|
+
});
|
|
1116
|
+
handleNotificationOpen(conversationId, pushData, options);
|
|
1117
|
+
};
|
|
1118
|
+
navigator.serviceWorker?.addEventListener("message", onSwMessage);
|
|
1119
|
+
let broadcast = null;
|
|
1120
|
+
try {
|
|
1121
|
+
broadcast = new BroadcastChannel(PUSH_BROADCAST_CHANNEL);
|
|
1122
|
+
broadcast.onmessage = onSwMessage;
|
|
1123
|
+
} catch {
|
|
1124
|
+
}
|
|
1125
|
+
const fromUrl = readConversationIdFromLocation();
|
|
1126
|
+
if (fromUrl && !consumedUrlConversationIds.has(fromUrl)) {
|
|
1127
|
+
consumedUrlConversationIds.add(fromUrl);
|
|
1128
|
+
handleNotificationOpen(fromUrl, void 0, options);
|
|
1129
|
+
} else {
|
|
1130
|
+
stripConversationIdFromUrl();
|
|
1131
|
+
}
|
|
1132
|
+
void consumePendingOpenFromPushCache().then((request) => {
|
|
1133
|
+
if (!request?.conversationId) return;
|
|
1134
|
+
handleNotificationOpen(
|
|
1135
|
+
request.conversationId,
|
|
1136
|
+
request,
|
|
1137
|
+
options
|
|
1138
|
+
);
|
|
1139
|
+
});
|
|
1140
|
+
return () => {
|
|
1141
|
+
navigator.serviceWorker?.removeEventListener("message", onSwMessage);
|
|
1142
|
+
broadcast?.close();
|
|
1143
|
+
};
|
|
1144
|
+
};
|
|
1145
|
+
}
|
|
1146
|
+
});
|
|
1147
|
+
|
|
1148
|
+
// src/services/api/query-params.ts
|
|
1149
|
+
function resolveChatApiKey(url) {
|
|
1150
|
+
if (!url) return null;
|
|
1151
|
+
const path = url.split("?")[0] || "";
|
|
1152
|
+
if (path.includes("/user/login/client-user")) return "login";
|
|
1153
|
+
if (path.includes("/user/list")) return "users";
|
|
1154
|
+
if (path.includes("/conversation/list")) return "conversations";
|
|
1155
|
+
if (path.includes("/conversation/get/information/")) return "conversationInfo";
|
|
1156
|
+
if (path.includes("/message/get-all-from-conversation")) return "messages";
|
|
1157
|
+
if (path.includes("message/pinned-messages") || path.includes("/message/pinned-messages")) {
|
|
1158
|
+
return "pinnedMessages";
|
|
1159
|
+
}
|
|
1160
|
+
if (path.includes("message/starred-messages") || path.includes("/message/starred-messages")) {
|
|
1161
|
+
return "starredMessages";
|
|
1162
|
+
}
|
|
1163
|
+
if (path.includes("/message/search-messages")) return "searchMessages";
|
|
1164
|
+
if (path.includes("/message/searched-message/context")) return "searchMessagesContext";
|
|
1165
|
+
if (path.includes("/settings/effective")) return "effectiveSettings";
|
|
1166
|
+
if (path.includes("/conversation/group/create")) return "createGroup";
|
|
1167
|
+
if (path.includes("/conversation/group/permission/update")) return "updateGroupPermissions";
|
|
1168
|
+
if (path.includes("/conversation/group/information/update")) return "updateGroupInfo";
|
|
1169
|
+
if (path.includes("/conversation/group/participants/add-remove")) return "manageMembers";
|
|
1170
|
+
if (path.includes("/conversation/group/admins/add-remove")) return "manageAdmins";
|
|
1171
|
+
if (path.includes("/user/block-unblock")) return "blockUnblock";
|
|
1172
|
+
if (path.includes("/aws/upload/get-presigned-urls")) return "presignedUrls";
|
|
1173
|
+
return null;
|
|
1174
|
+
}
|
|
1175
|
+
function isChatGetApiKey(key) {
|
|
1176
|
+
return !!key && CHAT_GET_API_KEYS.includes(key);
|
|
1177
|
+
}
|
|
1178
|
+
function normalizeQueryParams(params) {
|
|
1179
|
+
if (!params || typeof params !== "object") return {};
|
|
1180
|
+
const next = {};
|
|
1181
|
+
for (const [key, value] of Object.entries(params)) {
|
|
1182
|
+
if (value === void 0 || value === null) continue;
|
|
1183
|
+
const trimmedKey = String(key).trim();
|
|
1184
|
+
if (!trimmedKey) continue;
|
|
1185
|
+
next[trimmedKey] = String(value);
|
|
1186
|
+
}
|
|
1187
|
+
return next;
|
|
1188
|
+
}
|
|
1189
|
+
function normalizeQueryParamsByApi(byApi) {
|
|
1190
|
+
if (!byApi || typeof byApi !== "object") return {};
|
|
1191
|
+
const next = {};
|
|
1192
|
+
for (const [apiKey, params] of Object.entries(byApi)) {
|
|
1193
|
+
if (apiKey === "all" || apiKey === "get") continue;
|
|
1194
|
+
next[apiKey] = normalizeQueryParams(params);
|
|
1195
|
+
}
|
|
1196
|
+
return next;
|
|
1197
|
+
}
|
|
1198
|
+
var CHAT_GET_API_KEYS, CHAT_API_KEYS;
|
|
1199
|
+
var init_query_params = __esm({
|
|
1200
|
+
"src/services/api/query-params.ts"() {
|
|
1201
|
+
CHAT_GET_API_KEYS = [
|
|
1202
|
+
"users",
|
|
1203
|
+
"conversations",
|
|
1204
|
+
"conversationInfo",
|
|
1205
|
+
"messages",
|
|
1206
|
+
"pinnedMessages",
|
|
1207
|
+
"starredMessages",
|
|
1208
|
+
"searchMessages",
|
|
1209
|
+
"searchMessagesContext",
|
|
1210
|
+
"effectiveSettings"
|
|
1211
|
+
];
|
|
1212
|
+
CHAT_API_KEYS = [
|
|
1213
|
+
"all",
|
|
1214
|
+
/** All GET endpoints listed in CHAT_GET_API_KEYS */
|
|
1215
|
+
"get",
|
|
1216
|
+
"login",
|
|
1217
|
+
...CHAT_GET_API_KEYS,
|
|
1218
|
+
"createGroup",
|
|
1219
|
+
"updateGroupPermissions",
|
|
1220
|
+
"updateGroupInfo",
|
|
1221
|
+
"manageMembers",
|
|
1222
|
+
"manageAdmins",
|
|
1223
|
+
"blockUnblock",
|
|
1224
|
+
"presignedUrls"
|
|
1225
|
+
];
|
|
1226
|
+
}
|
|
1227
|
+
});
|
|
1228
|
+
|
|
1229
|
+
// src/services/api/api-version.ts
|
|
1230
|
+
var DEFAULT_VERSIONS, apiVersions, normalizeApiVersion, resolveApiRoot, buildVersionedApiUrl, setChatApiVersions, getChatApiVersions, setChatPushApiVersion, syncDefaultApiVersionFromBaseUrl;
|
|
1231
|
+
var init_api_version = __esm({
|
|
1232
|
+
"src/services/api/api-version.ts"() {
|
|
1233
|
+
init_client();
|
|
1234
|
+
DEFAULT_VERSIONS = {
|
|
1235
|
+
v1: "v1",
|
|
1236
|
+
v2: "v2"
|
|
1237
|
+
};
|
|
1238
|
+
apiVersions = { ...DEFAULT_VERSIONS };
|
|
1239
|
+
normalizeApiVersion = (version) => {
|
|
1240
|
+
const raw = String(version || "v1").trim().replace(/^\/+|\/+$/g, "");
|
|
1241
|
+
if (!raw) return "v1";
|
|
1242
|
+
return /^v\d+$/i.test(raw) ? raw.toLowerCase() : `v${raw}`;
|
|
1243
|
+
};
|
|
1244
|
+
resolveApiRoot = (baseUrl) => {
|
|
1245
|
+
const raw = String(
|
|
1246
|
+
baseUrl || getChatBaseUrl() || apiClient.defaults.baseURL || ""
|
|
1247
|
+
).trim().replace(/\/$/, "");
|
|
1248
|
+
if (!raw) return "";
|
|
1249
|
+
const withoutVersion = raw.replace(/\/v\d+$/i, "");
|
|
1250
|
+
return withoutVersion || raw;
|
|
1251
|
+
};
|
|
1252
|
+
buildVersionedApiUrl = (path, options) => {
|
|
1253
|
+
const root = resolveApiRoot(options?.baseUrl);
|
|
1254
|
+
const version = normalizeApiVersion(
|
|
1255
|
+
options?.version ?? (options?.service ? apiVersions[options.service] : apiVersions.v1)
|
|
1256
|
+
);
|
|
1257
|
+
const normalizedPath = path.startsWith("/") ? path : `/${path}`;
|
|
1258
|
+
if (!root) {
|
|
1259
|
+
return `/${version}${normalizedPath}`;
|
|
1260
|
+
}
|
|
1261
|
+
return `${root}/${version}${normalizedPath}`;
|
|
1262
|
+
};
|
|
1263
|
+
setChatApiVersions = (config) => {
|
|
1264
|
+
if (!config) return;
|
|
1265
|
+
if (config.v1) {
|
|
1266
|
+
apiVersions.v1 = normalizeApiVersion(config.v1);
|
|
1267
|
+
}
|
|
1268
|
+
if (config.v2) {
|
|
1269
|
+
apiVersions.v2 = normalizeApiVersion(config.v2);
|
|
1270
|
+
}
|
|
1271
|
+
};
|
|
1272
|
+
getChatApiVersions = () => ({
|
|
1273
|
+
...apiVersions
|
|
1274
|
+
});
|
|
1275
|
+
setChatPushApiVersion = (version) => {
|
|
1276
|
+
apiVersions.v2 = normalizeApiVersion(version);
|
|
1277
|
+
};
|
|
1278
|
+
syncDefaultApiVersionFromBaseUrl = (baseUrl) => {
|
|
1279
|
+
const match = String(baseUrl || "").trim().match(/\/(v\d+)\/?$/i);
|
|
1280
|
+
if (match?.[1]) {
|
|
1281
|
+
apiVersions.v1 = normalizeApiVersion(match[1]);
|
|
1282
|
+
}
|
|
1283
|
+
};
|
|
1284
|
+
}
|
|
1285
|
+
});
|
|
1286
|
+
function resolveParamsForRequest(url, method) {
|
|
1287
|
+
const apiKey = resolveChatApiKey(url);
|
|
1288
|
+
const httpMethod = (method || "get").toLowerCase();
|
|
1289
|
+
const isGet = httpMethod === "get";
|
|
1290
|
+
const perApi = apiKey ? chatQueryParamsByApi[apiKey] : void 0;
|
|
1291
|
+
const applyShared = Object.keys(chatQueryParams).length > 0 && (chatQueryParamApis.includes("all") || chatQueryParamApis.includes("get") && isGet && isChatGetApiKey(apiKey) || apiKey != null && chatQueryParamApis.includes(apiKey));
|
|
1292
|
+
if (!applyShared && !perApi) return {};
|
|
1293
|
+
return {
|
|
1294
|
+
...applyShared ? chatQueryParams : {},
|
|
1295
|
+
...perApi || {}
|
|
1296
|
+
};
|
|
1297
|
+
}
|
|
1298
|
+
var chatClientId, chatAccessToken, chatBaseUrl, chatSocketUrl, chatQueryParams, chatQueryParamApis, chatQueryParamsByApi, setChatClientId, getChatClientId, setChatAccessToken, setChatBaseURL, getChatBaseUrl, setChatSocketUrl, getChatSocketUrl, setChatQueryParams, getChatQueryParams, setChatQueryParamApis, getChatQueryParamApis, setChatQueryParamsByApi, getChatQueryParamsByApi, rateLimitResetAt, isRateLimited, apiClient;
|
|
1299
|
+
var init_client = __esm({
|
|
1300
|
+
"src/services/api/client.ts"() {
|
|
1301
|
+
init_query_params();
|
|
1302
|
+
init_api_version();
|
|
1303
|
+
init_query_params();
|
|
1304
|
+
init_api_version();
|
|
1305
|
+
chatClientId = "";
|
|
1306
|
+
chatAccessToken = "";
|
|
1307
|
+
chatBaseUrl = "";
|
|
1308
|
+
chatSocketUrl = "";
|
|
1309
|
+
chatQueryParams = {};
|
|
1310
|
+
chatQueryParamApis = ["get"];
|
|
1311
|
+
chatQueryParamsByApi = {};
|
|
1312
|
+
setChatClientId = (id) => {
|
|
1313
|
+
chatClientId = id.trim();
|
|
1314
|
+
};
|
|
1315
|
+
getChatClientId = () => chatClientId;
|
|
1316
|
+
setChatAccessToken = (token) => {
|
|
1317
|
+
chatAccessToken = token.trim();
|
|
1318
|
+
};
|
|
1319
|
+
setChatBaseURL = (url) => {
|
|
1320
|
+
chatBaseUrl = url.trim();
|
|
1321
|
+
apiClient.defaults.baseURL = chatBaseUrl;
|
|
1322
|
+
syncDefaultApiVersionFromBaseUrl(chatBaseUrl);
|
|
1323
|
+
};
|
|
1324
|
+
getChatBaseUrl = () => chatBaseUrl;
|
|
1325
|
+
setChatSocketUrl = (url) => {
|
|
1326
|
+
chatSocketUrl = url.trim();
|
|
1327
|
+
};
|
|
1328
|
+
getChatSocketUrl = () => chatSocketUrl;
|
|
1329
|
+
setChatQueryParams = (params) => {
|
|
1330
|
+
chatQueryParams = normalizeQueryParams(params);
|
|
1331
|
+
};
|
|
1332
|
+
getChatQueryParams = () => ({ ...chatQueryParams });
|
|
1333
|
+
setChatQueryParamApis = (apis) => {
|
|
1334
|
+
if (!apis?.length) {
|
|
1335
|
+
chatQueryParamApis = ["get"];
|
|
1336
|
+
return;
|
|
1337
|
+
}
|
|
1338
|
+
chatQueryParamApis = [...apis];
|
|
1339
|
+
};
|
|
1340
|
+
getChatQueryParamApis = () => [...chatQueryParamApis];
|
|
1341
|
+
setChatQueryParamsByApi = (byApi) => {
|
|
1342
|
+
chatQueryParamsByApi = normalizeQueryParamsByApi(byApi);
|
|
1343
|
+
};
|
|
1344
|
+
getChatQueryParamsByApi = () => ({ ...chatQueryParamsByApi });
|
|
1345
|
+
rateLimitResetAt = 0;
|
|
1346
|
+
isRateLimited = () => Date.now() < rateLimitResetAt;
|
|
1347
|
+
apiClient = axios.create({
|
|
1348
|
+
baseURL: chatBaseUrl || "http://localhost:5000/api/v1",
|
|
1349
|
+
withCredentials: true
|
|
1350
|
+
});
|
|
1351
|
+
apiClient.interceptors.request.use(
|
|
1352
|
+
(config) => {
|
|
1353
|
+
if (isRateLimited()) {
|
|
1354
|
+
return Promise.reject(new Error("Rate limit active \u2014 please wait before retrying."));
|
|
1355
|
+
}
|
|
1356
|
+
config.headers = config.headers ?? {};
|
|
1357
|
+
config.headers.set("ngrok-skip-browser-warning", "true");
|
|
1358
|
+
config.headers.set("platform", "web");
|
|
1359
|
+
config.headers.set("is-tenant", "true");
|
|
1360
|
+
config.headers.set("x-client-id", chatClientId);
|
|
1361
|
+
const requestUrl = String(config.url || "");
|
|
1362
|
+
const scopedParams = resolveParamsForRequest(requestUrl, config.method);
|
|
1363
|
+
if (Object.keys(scopedParams).length > 0) {
|
|
1364
|
+
config.params = { ...scopedParams, ...config.params };
|
|
1365
|
+
}
|
|
1366
|
+
try {
|
|
1367
|
+
const { useStore: useStore2 } = (init_useStore(), __toCommonJS(useStore_exports));
|
|
1368
|
+
const accessToken = useStore2.getState().accessToken || chatAccessToken;
|
|
1369
|
+
if (accessToken) {
|
|
1370
|
+
config.headers.set("Authorization", `Bearer ${accessToken}`);
|
|
1371
|
+
}
|
|
1372
|
+
} catch {
|
|
1373
|
+
}
|
|
1374
|
+
return config;
|
|
1375
|
+
},
|
|
1376
|
+
(error) => {
|
|
1377
|
+
return Promise.reject(error);
|
|
1378
|
+
}
|
|
1379
|
+
);
|
|
1380
|
+
apiClient.interceptors.response.use(
|
|
1381
|
+
(response) => response,
|
|
1382
|
+
(error) => {
|
|
1383
|
+
if (error.response?.status === 429) {
|
|
1384
|
+
const resetHeader = error.response.headers?.["ratelimit-reset"];
|
|
1385
|
+
const resetSeconds = resetHeader ? parseInt(String(resetHeader), 10) : 120;
|
|
1386
|
+
rateLimitResetAt = Date.now() + (Number.isFinite(resetSeconds) ? resetSeconds : 120) * 1e3;
|
|
1387
|
+
try {
|
|
1388
|
+
const { useSocketStore: useSocketStore2 } = (init_useSocketStore(), __toCommonJS(useSocketStore_exports));
|
|
1389
|
+
const store = useSocketStore2.getState();
|
|
1390
|
+
store.setRateLimitResetAt(rateLimitResetAt);
|
|
1391
|
+
} catch {
|
|
1392
|
+
}
|
|
1393
|
+
}
|
|
1394
|
+
return Promise.reject(error);
|
|
1395
|
+
}
|
|
1396
|
+
);
|
|
1397
|
+
}
|
|
1398
|
+
});
|
|
1399
|
+
|
|
1400
|
+
// src/call/call.api.ts
|
|
1401
|
+
var callsApiUrl, apiCreateCallRoom, apiFetchJoinToken, apiFetchCallHistory, apiFetchCallDetail;
|
|
1402
|
+
var init_call_api = __esm({
|
|
1403
|
+
"src/call/call.api.ts"() {
|
|
1404
|
+
init_client();
|
|
1405
|
+
init_api_version();
|
|
1406
|
+
callsApiUrl = (path) => buildVersionedApiUrl(path, { service: "v2" });
|
|
1407
|
+
apiCreateCallRoom = async (conversationId, mode) => {
|
|
1408
|
+
const { data } = await apiClient.post(callsApiUrl("/calls/rooms"), {
|
|
1409
|
+
conversationId,
|
|
1410
|
+
mode
|
|
1411
|
+
});
|
|
1412
|
+
return data?.data ?? data;
|
|
1413
|
+
};
|
|
1414
|
+
apiFetchJoinToken = async (meetingId) => {
|
|
1415
|
+
const { data } = await apiClient.get(callsApiUrl("/calls/videosdk-token"), {
|
|
1416
|
+
params: { meetingId }
|
|
1417
|
+
});
|
|
1418
|
+
return data?.data ?? data;
|
|
1419
|
+
};
|
|
1420
|
+
apiFetchCallHistory = async (conversationId, page = 1, limit = 20) => {
|
|
1421
|
+
const { data } = await apiClient.get(callsApiUrl("/calls"), {
|
|
1422
|
+
params: { conversationId, page, limit }
|
|
1423
|
+
});
|
|
1424
|
+
return data?.data ?? data;
|
|
1425
|
+
};
|
|
1426
|
+
apiFetchCallDetail = async (callId) => {
|
|
1427
|
+
const { data } = await apiClient.get(callsApiUrl(`/calls/${callId}`));
|
|
1428
|
+
return data?.data ?? data;
|
|
1429
|
+
};
|
|
1430
|
+
}
|
|
1431
|
+
});
|
|
1432
|
+
|
|
1433
|
+
// src/call/call.types.ts
|
|
1434
|
+
var CALL_MODES, CALL_STATUS, CALL_RING_TIMEOUT_MS, CLIENT_RING_FALLBACK_MS;
|
|
1435
|
+
var init_call_types = __esm({
|
|
1436
|
+
"src/call/call.types.ts"() {
|
|
1437
|
+
CALL_MODES = {
|
|
1438
|
+
AUDIO: "audio",
|
|
1439
|
+
VIDEO: "video"
|
|
1440
|
+
};
|
|
1441
|
+
CALL_STATUS = {
|
|
1442
|
+
RINGING: "ringing",
|
|
1443
|
+
ONGOING: "ongoing",
|
|
672
1444
|
ENDED: "ended",
|
|
673
1445
|
MISSED: "missed",
|
|
674
1446
|
REJECTED: "rejected",
|
|
@@ -678,11 +1450,13 @@ var init_call_types = __esm({
|
|
|
678
1450
|
CLIENT_RING_FALLBACK_MS = CALL_RING_TIMEOUT_MS + 5e3;
|
|
679
1451
|
}
|
|
680
1452
|
});
|
|
681
|
-
var ringTimer, clearRingTimer, scheduleRingTimer, idleState, useCallStore;
|
|
1453
|
+
var ringTimer, clearRingTimer, scheduleRingTimer, resolveGroupPeer, idleState, useCallStore;
|
|
682
1454
|
var init_call_store = __esm({
|
|
683
1455
|
"src/call/call.store.ts"() {
|
|
684
1456
|
init_useStore();
|
|
685
1457
|
init_socket_service2();
|
|
1458
|
+
init_open_conversation();
|
|
1459
|
+
init_store_helpers();
|
|
686
1460
|
init_call_api();
|
|
687
1461
|
init_call_types();
|
|
688
1462
|
ringTimer = null;
|
|
@@ -699,6 +1473,18 @@ var init_call_store = __esm({
|
|
|
699
1473
|
}
|
|
700
1474
|
}, CLIENT_RING_FALLBACK_MS);
|
|
701
1475
|
};
|
|
1476
|
+
resolveGroupPeer = (conversationId) => {
|
|
1477
|
+
const conversation = findConversationById(
|
|
1478
|
+
useStore.getState().conversations,
|
|
1479
|
+
conversationId
|
|
1480
|
+
);
|
|
1481
|
+
if (!conversation || !isGroupConversation(conversation)) return null;
|
|
1482
|
+
const rawName = conversation.groupName || conversation.name || "Group Chat";
|
|
1483
|
+
return {
|
|
1484
|
+
name: rawName.charAt(0).toUpperCase() + rawName.slice(1),
|
|
1485
|
+
avatar: resolveGroupImage(conversation) ?? null
|
|
1486
|
+
};
|
|
1487
|
+
};
|
|
702
1488
|
idleState = {
|
|
703
1489
|
uiStatus: "idle",
|
|
704
1490
|
callId: null,
|
|
@@ -709,14 +1495,22 @@ var init_call_store = __esm({
|
|
|
709
1495
|
caller: null,
|
|
710
1496
|
participants: [],
|
|
711
1497
|
error: null,
|
|
712
|
-
|
|
1498
|
+
peer: null,
|
|
1499
|
+
isGroupCall: false,
|
|
713
1500
|
callStartedAt: null
|
|
714
1501
|
};
|
|
715
1502
|
useCallStore = create((set, get) => ({
|
|
716
1503
|
...idleState,
|
|
717
|
-
startCall: async (conversationId, mode,
|
|
1504
|
+
startCall: async (conversationId, mode, peer, isGroupCall = false) => {
|
|
718
1505
|
if (get().uiStatus !== "idle") return;
|
|
719
|
-
set({
|
|
1506
|
+
set({
|
|
1507
|
+
...idleState,
|
|
1508
|
+
uiStatus: "creating",
|
|
1509
|
+
conversationId,
|
|
1510
|
+
mode,
|
|
1511
|
+
peer: peer ?? null,
|
|
1512
|
+
isGroupCall
|
|
1513
|
+
});
|
|
720
1514
|
try {
|
|
721
1515
|
const room = await apiCreateCallRoom(conversationId, mode);
|
|
722
1516
|
set({
|
|
@@ -730,6 +1524,7 @@ var init_call_store = __esm({
|
|
|
730
1524
|
});
|
|
731
1525
|
scheduleRingTimer(room.callId, get, set);
|
|
732
1526
|
socket_service_default.emitCallInvite({ callId: room.callId });
|
|
1527
|
+
requestOpenConversation(room.conversationId);
|
|
733
1528
|
} catch (err) {
|
|
734
1529
|
clearRingTimer();
|
|
735
1530
|
set({
|
|
@@ -747,7 +1542,7 @@ var init_call_store = __esm({
|
|
|
747
1542
|
set({ ...idleState });
|
|
748
1543
|
},
|
|
749
1544
|
acceptCall: async () => {
|
|
750
|
-
const { callId, meetingId } = get();
|
|
1545
|
+
const { callId, meetingId, conversationId } = get();
|
|
751
1546
|
if (!callId || !meetingId) return;
|
|
752
1547
|
clearRingTimer();
|
|
753
1548
|
set({ uiStatus: "joining" });
|
|
@@ -755,6 +1550,7 @@ var init_call_store = __esm({
|
|
|
755
1550
|
const join = await apiFetchJoinToken(meetingId);
|
|
756
1551
|
socket_service_default.emitCallAccept({ callId });
|
|
757
1552
|
set({ uiStatus: "in-call", token: join.token, callStartedAt: Date.now() });
|
|
1553
|
+
if (conversationId) requestOpenConversation(conversationId);
|
|
758
1554
|
} catch (err) {
|
|
759
1555
|
set({
|
|
760
1556
|
...idleState,
|
|
@@ -796,6 +1592,7 @@ var init_call_store = __esm({
|
|
|
796
1592
|
);
|
|
797
1593
|
return;
|
|
798
1594
|
}
|
|
1595
|
+
const groupPeer = resolveGroupPeer(payload.conversationId);
|
|
799
1596
|
set({
|
|
800
1597
|
uiStatus: "incoming",
|
|
801
1598
|
callId: payload.callId,
|
|
@@ -803,7 +1600,9 @@ var init_call_store = __esm({
|
|
|
803
1600
|
meetingId: payload.meetingId,
|
|
804
1601
|
mode: payload.mode,
|
|
805
1602
|
caller: payload.caller,
|
|
806
|
-
participants: payload.participants
|
|
1603
|
+
participants: payload.participants,
|
|
1604
|
+
peer: groupPeer,
|
|
1605
|
+
isGroupCall: !!groupPeer
|
|
807
1606
|
});
|
|
808
1607
|
scheduleRingTimer(payload.callId, get, set);
|
|
809
1608
|
},
|
|
@@ -857,6 +1656,22 @@ var init_call_store = __esm({
|
|
|
857
1656
|
clearRingTimer();
|
|
858
1657
|
set({ ...idleState });
|
|
859
1658
|
},
|
|
1659
|
+
// Group calls only (CALLS_API.md §3.1): call_end from a member is a
|
|
1660
|
+
// *leave*, not a hangup for everyone — the call keeps going for whoever
|
|
1661
|
+
// remains. Only reset local state if it's actually me who left (e.g.
|
|
1662
|
+
// confirming my own endCall()); otherwise just update who's still on
|
|
1663
|
+
// the call so the participant grid drops that tile.
|
|
1664
|
+
_receiveParticipantLeft: (payload) => {
|
|
1665
|
+
const state = get();
|
|
1666
|
+
if (state.callId !== payload.callId) return;
|
|
1667
|
+
const myUserId = useStore.getState().loggedUserDetails?._id;
|
|
1668
|
+
if (payload.userId === myUserId) {
|
|
1669
|
+
clearRingTimer();
|
|
1670
|
+
set({ ...idleState });
|
|
1671
|
+
return;
|
|
1672
|
+
}
|
|
1673
|
+
set({ participants: payload.activeParticipants });
|
|
1674
|
+
},
|
|
860
1675
|
_receiveBusy: (payload) => {
|
|
861
1676
|
const state = get();
|
|
862
1677
|
if (state.callId !== payload.callId) return;
|
|
@@ -877,776 +1692,272 @@ var init_call_store = __esm({
|
|
|
877
1692
|
// call_missed for a callId that was already call_accepted (timer not
|
|
878
1693
|
// cancelled on accept). Backend has since added an atomic
|
|
879
1694
|
// only-from-ringing guard (CALLS_API.md §3.3) so this should no longer
|
|
880
|
-
// happen — the uiStatus check below is kept as defense-in-depth rather
|
|
881
|
-
// than removed, since it costs nothing and a regression here would
|
|
882
|
-
// otherwise kill a live call outright.
|
|
883
|
-
_receiveMissed: (payload) => {
|
|
884
|
-
const state = get();
|
|
885
|
-
if (state.callId !== payload.callId) return;
|
|
886
|
-
if (state.uiStatus === "in-call") {
|
|
887
|
-
console.warn(
|
|
888
|
-
`[realtimex-call] call_missed for ${payload.callId} ignored \u2014 this call is already in-call locally; treating as a stale event.`
|
|
889
|
-
);
|
|
890
|
-
return;
|
|
891
|
-
}
|
|
892
|
-
clearRingTimer();
|
|
893
|
-
set({ ...idleState });
|
|
894
|
-
}
|
|
895
|
-
}));
|
|
896
|
-
}
|
|
897
|
-
});
|
|
898
|
-
|
|
899
|
-
// src/call/call.listeners.ts
|
|
900
|
-
var registerCallSocketListeners;
|
|
901
|
-
var init_call_listeners = __esm({
|
|
902
|
-
"src/call/call.listeners.ts"() {
|
|
903
|
-
init_socket_events_constants();
|
|
904
|
-
init_call_store();
|
|
905
|
-
registerCallSocketListeners = (socket) => {
|
|
906
|
-
console.debug("[realtimex-call] registerCallSocketListeners: attached to socket", socket.id);
|
|
907
|
-
socket.on(SOCKET_EVENTS.CALL_INCOMING, (payload) => {
|
|
908
|
-
console.log("[realtimex-call] call_incoming received:", payload);
|
|
909
|
-
useCallStore.getState()._receiveIncoming(payload);
|
|
910
|
-
});
|
|
911
|
-
socket.on(SOCKET_EVENTS.CALL_ACCEPTED, (payload) => {
|
|
912
|
-
console.log("[realtimex-call] call_accepted received:", payload);
|
|
913
|
-
useCallStore.getState()._receiveAccepted(payload);
|
|
914
|
-
});
|
|
915
|
-
socket.on(SOCKET_EVENTS.CALL_REJECTED, (payload) => {
|
|
916
|
-
console.log("[realtimex-call] call_rejected received:", payload);
|
|
917
|
-
useCallStore.getState()._receiveRejected(payload);
|
|
918
|
-
});
|
|
919
|
-
socket.on(SOCKET_EVENTS.CALL_CANCELLED, (payload) => {
|
|
920
|
-
console.log("[realtimex-call] call_cancelled received:", payload);
|
|
921
|
-
useCallStore.getState()._receiveCancelled(payload);
|
|
922
|
-
});
|
|
923
|
-
socket.on(SOCKET_EVENTS.CALL_ENDED, (payload) => {
|
|
924
|
-
console.log("[realtimex-call] call_ended received:", payload);
|
|
925
|
-
useCallStore.getState()._receiveEnded(payload);
|
|
926
|
-
});
|
|
927
|
-
socket.on(SOCKET_EVENTS.CALL_BUSY, (payload) => {
|
|
928
|
-
console.log("[realtimex-call] call_busy received:", payload);
|
|
929
|
-
useCallStore.getState()._receiveBusy(payload);
|
|
930
|
-
});
|
|
931
|
-
socket.on(SOCKET_EVENTS.CALL_MISSED, (payload) => {
|
|
932
|
-
console.log("[realtimex-call] call_missed received:", payload);
|
|
933
|
-
useCallStore.getState()._receiveMissed(payload);
|
|
934
|
-
});
|
|
935
|
-
};
|
|
936
|
-
}
|
|
937
|
-
});
|
|
938
|
-
var SocketService, socketService, socket_service_default;
|
|
939
|
-
var init_socket_service = __esm({
|
|
940
|
-
"src/services/socket/socket-service.ts"() {
|
|
941
|
-
init_socket_events_constants();
|
|
942
|
-
init_get_store();
|
|
943
|
-
init_listeners();
|
|
944
|
-
init_call_listeners();
|
|
945
|
-
SocketService = class {
|
|
946
|
-
constructor() {
|
|
947
|
-
this.socket = null;
|
|
948
|
-
this.currentConfig = null;
|
|
949
|
-
this.isConnecting = false;
|
|
950
|
-
}
|
|
951
|
-
connect(config) {
|
|
952
|
-
if (this.socket?.connected && this.currentConfig && this.currentConfig.url === config.url && JSON.stringify(this.currentConfig.options.auth) === JSON.stringify(config.options.auth) && JSON.stringify(this.currentConfig.options.query) === JSON.stringify(config.options.query)) {
|
|
953
|
-
return;
|
|
954
|
-
}
|
|
955
|
-
if (this.isConnecting && this.currentConfig?.url === config.url) {
|
|
956
|
-
return;
|
|
957
|
-
}
|
|
958
|
-
if (this.socket) {
|
|
959
|
-
console.log("Socket: Config changed or forced reconnect. Cleaning up old connection...", {
|
|
960
|
-
old: this.currentConfig?.url,
|
|
961
|
-
new: config.url
|
|
962
|
-
});
|
|
963
|
-
this.disconnect();
|
|
964
|
-
}
|
|
965
|
-
this.currentConfig = config;
|
|
966
|
-
this.isConnecting = true;
|
|
967
|
-
const store = getStore();
|
|
968
|
-
const { setConnecting, setConnected, setDisconnected, setError } = store.getState();
|
|
969
|
-
setConnecting();
|
|
970
|
-
this.socket = io(config.url, config.options);
|
|
971
|
-
this.socket.on("connect", () => {
|
|
972
|
-
this.isConnecting = false;
|
|
973
|
-
setConnected({ socketId: this.socket?.id || "" });
|
|
974
|
-
this.emitGetOnlineUsers();
|
|
975
|
-
});
|
|
976
|
-
this.socket.on("disconnect", (reason) => {
|
|
977
|
-
this.isConnecting = false;
|
|
978
|
-
console.log("Disconnected from socket. Reason:", reason);
|
|
979
|
-
setDisconnected();
|
|
980
|
-
});
|
|
981
|
-
this.socket.on("connect_error", (error) => {
|
|
982
|
-
this.isConnecting = false;
|
|
983
|
-
console.error("Socket connection error:", error);
|
|
984
|
-
setError(error.message);
|
|
985
|
-
});
|
|
986
|
-
registerSocketListeners(this.socket, () => this.emitGetOnlineUsers());
|
|
987
|
-
registerCallSocketListeners(this.socket);
|
|
988
|
-
}
|
|
989
|
-
disconnect() {
|
|
990
|
-
if (this.socket) {
|
|
991
|
-
this.socket.removeAllListeners();
|
|
992
|
-
this.socket.disconnect();
|
|
993
|
-
this.socket = null;
|
|
994
|
-
this.currentConfig = null;
|
|
995
|
-
this.isConnecting = false;
|
|
996
|
-
}
|
|
997
|
-
}
|
|
998
|
-
emit(event, payload) {
|
|
999
|
-
if (this.socket) {
|
|
1000
|
-
this.socket.emit(event, payload);
|
|
1001
|
-
} else {
|
|
1002
|
-
console.error(`SocketService: Socket is not initialized. Cannot emit ${event}`);
|
|
1003
|
-
}
|
|
1004
|
-
}
|
|
1005
|
-
getSocket() {
|
|
1006
|
-
return this.socket;
|
|
1007
|
-
}
|
|
1008
|
-
emitDmSend(payload) {
|
|
1009
|
-
this.emit(SOCKET_EVENTS.DM_SEND, payload);
|
|
1010
|
-
}
|
|
1011
|
-
emitDmEdit(payload) {
|
|
1012
|
-
this.emit(SOCKET_EVENTS.DM_EDIT, payload);
|
|
1013
|
-
}
|
|
1014
|
-
emitDmDelete(payload) {
|
|
1015
|
-
this.emit(SOCKET_EVENTS.DM_DELETE, payload);
|
|
1016
|
-
}
|
|
1017
|
-
emitDmClearChat(payload) {
|
|
1018
|
-
this.emit(SOCKET_EVENTS.DM_CLEAR_CHAT, payload);
|
|
1019
|
-
}
|
|
1020
|
-
emitGroupClearChat(payload) {
|
|
1021
|
-
this.emit(SOCKET_EVENTS.GROUP_CLEAR_CHAT, payload);
|
|
1022
|
-
}
|
|
1023
|
-
emitDmDeleteConversation(payload) {
|
|
1024
|
-
this.emit(SOCKET_EVENTS.DM_DELETE_CONVERSATION, payload);
|
|
1025
|
-
}
|
|
1026
|
-
emitGroupDeleteConversation(payload) {
|
|
1027
|
-
this.emit(SOCKET_EVENTS.GROUP_DELETE_CONVERSATION, payload);
|
|
1028
|
-
}
|
|
1029
|
-
emitDmReactionAdd(payload) {
|
|
1030
|
-
this.emit(SOCKET_EVENTS.DM_REACTION_ADD, payload);
|
|
1031
|
-
}
|
|
1032
|
-
emitDmReactionRemove(payload) {
|
|
1033
|
-
this.emit(SOCKET_EVENTS.DM_REACTION_REMOVE, payload);
|
|
1034
|
-
}
|
|
1035
|
-
emitTypingStart(payload) {
|
|
1036
|
-
this.emit(SOCKET_EVENTS.DM_TYPING_START, payload);
|
|
1037
|
-
}
|
|
1038
|
-
emitTypingStop(payload) {
|
|
1039
|
-
this.emit(SOCKET_EVENTS.DM_TYPING_STOP, payload);
|
|
1040
|
-
}
|
|
1041
|
-
emitMarkAsRead(payload) {
|
|
1042
|
-
this.emit(SOCKET_EVENTS.DM_MARK_READ, payload);
|
|
1043
|
-
}
|
|
1044
|
-
emitDmPin(payload) {
|
|
1045
|
-
this.emit(SOCKET_EVENTS.DM_PIN, payload);
|
|
1046
|
-
}
|
|
1047
|
-
emitDmUnpin(payload) {
|
|
1048
|
-
this.emit(SOCKET_EVENTS.DM_UNPIN, payload);
|
|
1049
|
-
}
|
|
1050
|
-
emitDmStar(payload) {
|
|
1051
|
-
this.emit(SOCKET_EVENTS.DM_STAR, payload);
|
|
1052
|
-
}
|
|
1053
|
-
emitDmUnstar(payload) {
|
|
1054
|
-
this.emit(SOCKET_EVENTS.DM_UNSTAR, payload);
|
|
1055
|
-
}
|
|
1056
|
-
emitDmForward(payload) {
|
|
1057
|
-
this.emit(SOCKET_EVENTS.DM_FORWARD, payload);
|
|
1058
|
-
}
|
|
1059
|
-
emitGroupJoin(payload) {
|
|
1060
|
-
this.emit(SOCKET_EVENTS.GROUP_JOIN, payload);
|
|
1061
|
-
}
|
|
1062
|
-
emitGroupMessageSend(payload) {
|
|
1063
|
-
this.emit(SOCKET_EVENTS.GROUP_MESSAGE_SEND, payload);
|
|
1064
|
-
}
|
|
1065
|
-
emitGroupTypingStart(payload) {
|
|
1066
|
-
this.emit(SOCKET_EVENTS.GROUP_TYPING_START, payload);
|
|
1067
|
-
}
|
|
1068
|
-
emitGroupTypingStop(payload) {
|
|
1069
|
-
this.emit(SOCKET_EVENTS.GROUP_TYPING_STOP, payload);
|
|
1070
|
-
}
|
|
1071
|
-
emitGroupEdit(payload) {
|
|
1072
|
-
this.emit(SOCKET_EVENTS.GROUP_EDIT, payload);
|
|
1073
|
-
}
|
|
1074
|
-
emitGroupReactionAdd(payload) {
|
|
1075
|
-
this.emit(SOCKET_EVENTS.GROUP_REACTION_ADD, payload);
|
|
1076
|
-
}
|
|
1077
|
-
emitGroupMarkRead(payload) {
|
|
1078
|
-
this.emit(SOCKET_EVENTS.GROUP_MESSAGE_READ, payload);
|
|
1079
|
-
}
|
|
1080
|
-
emitGroupReactionRemove(payload) {
|
|
1081
|
-
this.emit(SOCKET_EVENTS.GROUP_REACTION_REMOVE, payload);
|
|
1082
|
-
}
|
|
1083
|
-
emitGroupDelete(payload) {
|
|
1084
|
-
this.emit(SOCKET_EVENTS.GROUP_DELETE, payload);
|
|
1085
|
-
}
|
|
1086
|
-
emitGroupPin(payload) {
|
|
1087
|
-
this.emit(SOCKET_EVENTS.GROUP_PIN, payload);
|
|
1088
|
-
}
|
|
1089
|
-
emitGroupUnpin(payload) {
|
|
1090
|
-
this.emit(SOCKET_EVENTS.GROUP_UNPIN, payload);
|
|
1091
|
-
}
|
|
1092
|
-
emitGroupStar(payload) {
|
|
1093
|
-
this.emit(SOCKET_EVENTS.GROUP_STAR, payload);
|
|
1094
|
-
}
|
|
1095
|
-
emitGroupUnstar(payload) {
|
|
1096
|
-
this.emit(SOCKET_EVENTS.GROUP_UNSTAR, payload);
|
|
1097
|
-
}
|
|
1098
|
-
emitGroupForward(payload) {
|
|
1099
|
-
this.emit(SOCKET_EVENTS.GROUP_FORWARD, payload);
|
|
1100
|
-
}
|
|
1101
|
-
emitConversationUpdate(payload) {
|
|
1102
|
-
this.emit(SOCKET_EVENTS.CONVERSATION_UPDATE, payload);
|
|
1103
|
-
}
|
|
1104
|
-
emitGroupLeave(payload) {
|
|
1105
|
-
this.emit(SOCKET_EVENTS.GROUP_LEAVE, payload);
|
|
1106
|
-
}
|
|
1107
|
-
emitGetOnlineUsers() {
|
|
1108
|
-
this.emit(SOCKET_EVENTS.GET_ONLINE_USERS);
|
|
1109
|
-
}
|
|
1110
|
-
emitCallInvite(payload) {
|
|
1111
|
-
this.emit(SOCKET_EVENTS.CALL_INVITE, payload);
|
|
1112
|
-
}
|
|
1113
|
-
emitCallAccept(payload) {
|
|
1114
|
-
this.emit(SOCKET_EVENTS.CALL_ACCEPT, payload);
|
|
1115
|
-
}
|
|
1116
|
-
emitCallReject(payload) {
|
|
1117
|
-
this.emit(SOCKET_EVENTS.CALL_REJECT, payload);
|
|
1118
|
-
}
|
|
1119
|
-
emitCallCancel(payload) {
|
|
1120
|
-
this.emit(SOCKET_EVENTS.CALL_CANCEL, payload);
|
|
1121
|
-
}
|
|
1122
|
-
emitCallEnd(payload) {
|
|
1123
|
-
this.emit(SOCKET_EVENTS.CALL_END, payload);
|
|
1695
|
+
// happen — the uiStatus check below is kept as defense-in-depth rather
|
|
1696
|
+
// than removed, since it costs nothing and a regression here would
|
|
1697
|
+
// otherwise kill a live call outright.
|
|
1698
|
+
_receiveMissed: (payload) => {
|
|
1699
|
+
const state = get();
|
|
1700
|
+
if (state.callId !== payload.callId) return;
|
|
1701
|
+
if (state.uiStatus === "in-call") {
|
|
1702
|
+
console.warn(
|
|
1703
|
+
`[realtimex-call] call_missed for ${payload.callId} ignored \u2014 this call is already in-call locally; treating as a stale event.`
|
|
1704
|
+
);
|
|
1705
|
+
return;
|
|
1706
|
+
}
|
|
1707
|
+
clearRingTimer();
|
|
1708
|
+
set({ ...idleState });
|
|
1124
1709
|
}
|
|
1125
|
-
};
|
|
1126
|
-
socketService = new SocketService();
|
|
1127
|
-
socket_service_default = socketService;
|
|
1128
|
-
}
|
|
1129
|
-
});
|
|
1130
|
-
|
|
1131
|
-
// src/services/socket/index.ts
|
|
1132
|
-
var init_socket = __esm({
|
|
1133
|
-
"src/services/socket/index.ts"() {
|
|
1134
|
-
init_socket_service();
|
|
1135
|
-
}
|
|
1136
|
-
});
|
|
1137
|
-
|
|
1138
|
-
// src/services/socket.service.ts
|
|
1139
|
-
var init_socket_service2 = __esm({
|
|
1140
|
-
"src/services/socket.service.ts"() {
|
|
1141
|
-
init_socket();
|
|
1142
|
-
}
|
|
1143
|
-
});
|
|
1144
|
-
|
|
1145
|
-
// src/store/helpers/group-display.helpers.ts
|
|
1146
|
-
var resolveGroupImage, getChannelAvatarInitial, isGroupCreationPreview;
|
|
1147
|
-
var init_group_display_helpers = __esm({
|
|
1148
|
-
"src/store/helpers/group-display.helpers.ts"() {
|
|
1149
|
-
resolveGroupImage = (conversation) => {
|
|
1150
|
-
if (!conversation) return void 0;
|
|
1151
|
-
const raw = conversation.groupImage ?? conversation.image ?? conversation.avatar;
|
|
1152
|
-
if (typeof raw !== "string") return void 0;
|
|
1153
|
-
const trimmed = raw.trim();
|
|
1154
|
-
return trimmed || void 0;
|
|
1155
|
-
};
|
|
1156
|
-
getChannelAvatarInitial = (name) => {
|
|
1157
|
-
const trimmed = (name || "").trim();
|
|
1158
|
-
if (!trimmed) return "?";
|
|
1159
|
-
return trimmed.charAt(0).toUpperCase();
|
|
1160
|
-
};
|
|
1161
|
-
isGroupCreationPreview = (content) => !!content?.includes("New Group") && !!content?.includes("created and you are invited");
|
|
1710
|
+
}));
|
|
1162
1711
|
}
|
|
1163
1712
|
});
|
|
1164
1713
|
|
|
1165
|
-
// src/
|
|
1166
|
-
var
|
|
1167
|
-
var
|
|
1168
|
-
"src/
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
const isStarred = payload.isStarred ?? payload.message?.isStarred;
|
|
1177
|
-
const attachmentId = payload.attachmentId || payload.unpinAttachmentId || null;
|
|
1178
|
-
const caption = payload.caption ?? payload.message?.caption ?? null;
|
|
1179
|
-
return { messageId: messageId2, content, conversationId, status, isPinned, isStarred, attachmentId, caption };
|
|
1180
|
-
};
|
|
1181
|
-
statusRank = { sent: 0, delivered: 1, read: 2 };
|
|
1182
|
-
preferHigherStatus = (base, incoming) => {
|
|
1183
|
-
const baseRank = statusRank[base?.status] ?? 0;
|
|
1184
|
-
const incomingRank = statusRank[incoming?.status] ?? 0;
|
|
1185
|
-
if (incomingRank > baseRank) {
|
|
1186
|
-
return { ...base, status: incoming.status };
|
|
1187
|
-
}
|
|
1188
|
-
return base;
|
|
1189
|
-
};
|
|
1190
|
-
mergeLastMessage = (existing, incoming) => {
|
|
1191
|
-
if (!incoming) return existing ?? null;
|
|
1192
|
-
if (!existing) return incoming;
|
|
1193
|
-
const content = incoming.content?.trim() ? incoming.content : existing.content;
|
|
1194
|
-
const createdAt = incoming.createdAt ?? incoming.timestamp ?? existing.createdAt ?? existing.timestamp;
|
|
1195
|
-
const incomingSenderId = getMessageSenderId(incoming);
|
|
1196
|
-
const resolvedSender = incomingSenderId ? incoming.sender ?? incoming.senderId ?? incoming.fromUserId : existing.sender ?? existing.senderId ?? existing.fromUserId ?? incoming.sender;
|
|
1197
|
-
return {
|
|
1198
|
-
...existing,
|
|
1199
|
-
...incoming,
|
|
1200
|
-
...content !== void 0 ? { content } : {},
|
|
1201
|
-
...createdAt !== void 0 ? { createdAt } : {},
|
|
1202
|
-
attachments: incoming.attachments?.length ? incoming.attachments : existing.attachments,
|
|
1203
|
-
status: preferHigherStatus(existing, incoming).status ?? existing.status ?? incoming.status,
|
|
1204
|
-
...resolvedSender !== void 0 ? { sender: resolvedSender } : {},
|
|
1205
|
-
senderId: incoming.senderId ?? existing.senderId,
|
|
1206
|
-
fromUserId: incoming.fromUserId ?? existing.fromUserId
|
|
1207
|
-
};
|
|
1208
|
-
};
|
|
1209
|
-
messageHasPreviewBody = (message) => Boolean(String(message?.content || "").trim()) || Array.isArray(message?.attachments) && message.attachments.length > 0;
|
|
1210
|
-
getLatestMessageFromList = (messages) => {
|
|
1211
|
-
if (!messages?.length) return null;
|
|
1212
|
-
return messages.reduce((latest, message) => {
|
|
1213
|
-
const latestTime = new Date(latest.createdAt || latest.timestamp || 0).getTime();
|
|
1214
|
-
const messageTime = new Date(message.createdAt || message.timestamp || 0).getTime();
|
|
1215
|
-
return messageTime >= latestTime ? message : latest;
|
|
1714
|
+
// src/call/call.listeners.ts
|
|
1715
|
+
var registerCallSocketListeners;
|
|
1716
|
+
var init_call_listeners = __esm({
|
|
1717
|
+
"src/call/call.listeners.ts"() {
|
|
1718
|
+
init_socket_events_constants();
|
|
1719
|
+
init_call_store();
|
|
1720
|
+
registerCallSocketListeners = (socket) => {
|
|
1721
|
+
console.debug("[realtimex-call] registerCallSocketListeners: attached to socket", socket.id);
|
|
1722
|
+
socket.on(SOCKET_EVENTS.CALL_INCOMING, (payload) => {
|
|
1723
|
+
console.log("[realtimex-call] call_incoming received:", payload);
|
|
1724
|
+
useCallStore.getState()._receiveIncoming(payload);
|
|
1216
1725
|
});
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
if (!loggedUserId) return false;
|
|
1221
|
-
return getMessageSenderId(message) === String(loggedUserId);
|
|
1222
|
-
};
|
|
1223
|
-
getMessageContent = (message) => String(message?.content || "").trim();
|
|
1224
|
-
getMessageTimestamp = (message) => {
|
|
1225
|
-
const value = message?.createdAt || message?.timestamp;
|
|
1226
|
-
const time = value ? new Date(value).getTime() : NaN;
|
|
1227
|
-
return Number.isFinite(time) ? time : 0;
|
|
1228
|
-
};
|
|
1229
|
-
resolveMessageStatus = (message, conversation, loggedUserId) => {
|
|
1230
|
-
const existing = message?.status || "sent";
|
|
1231
|
-
if (!loggedUserId || !isOwnMessage(message, loggedUserId)) return existing;
|
|
1232
|
-
const last = conversation?.lastMessage;
|
|
1233
|
-
if (!last) return existing;
|
|
1234
|
-
const msgId = String(message._id || message.id || "");
|
|
1235
|
-
const lastId = String(last._id || last.id || "");
|
|
1236
|
-
if (msgId && lastId && msgId === lastId && last.status) {
|
|
1237
|
-
const lastStatus = last.status;
|
|
1238
|
-
return statusRank[lastStatus] > statusRank[existing] ? lastStatus : existing;
|
|
1239
|
-
}
|
|
1240
|
-
return existing;
|
|
1241
|
-
};
|
|
1242
|
-
mergeChatMessages = (apiMessages, localMessages, loggedUserId) => {
|
|
1243
|
-
const merged = /* @__PURE__ */ new Map();
|
|
1244
|
-
apiMessages.forEach((message) => {
|
|
1245
|
-
const id = String(message?._id || message?.id || "");
|
|
1246
|
-
if (id) merged.set(id, message);
|
|
1726
|
+
socket.on(SOCKET_EVENTS.CALL_ACCEPTED, (payload) => {
|
|
1727
|
+
console.log("[realtimex-call] call_accepted received:", payload);
|
|
1728
|
+
useCallStore.getState()._receiveAccepted(payload);
|
|
1247
1729
|
});
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
if (!merged.has(key)) merged.set(key, localMessage);
|
|
1730
|
+
socket.on(SOCKET_EVENTS.CALL_REJECTED, (payload) => {
|
|
1731
|
+
console.log("[realtimex-call] call_rejected received:", payload);
|
|
1732
|
+
useCallStore.getState()._receiveRejected(payload);
|
|
1733
|
+
});
|
|
1734
|
+
socket.on(SOCKET_EVENTS.CALL_CANCELLED, (payload) => {
|
|
1735
|
+
console.log("[realtimex-call] call_cancelled received:", payload);
|
|
1736
|
+
useCallStore.getState()._receiveCancelled(payload);
|
|
1737
|
+
});
|
|
1738
|
+
socket.on(SOCKET_EVENTS.CALL_ENDED, (payload) => {
|
|
1739
|
+
console.log("[realtimex-call] call_ended received:", payload);
|
|
1740
|
+
useCallStore.getState()._receiveEnded(payload);
|
|
1741
|
+
});
|
|
1742
|
+
socket.on(SOCKET_EVENTS.CALL_PARTICIPANT_LEFT, (payload) => {
|
|
1743
|
+
console.log("[realtimex-call] call_participant_left received:", payload);
|
|
1744
|
+
useCallStore.getState()._receiveParticipantLeft(payload);
|
|
1745
|
+
});
|
|
1746
|
+
socket.on(SOCKET_EVENTS.CALL_BUSY, (payload) => {
|
|
1747
|
+
console.log("[realtimex-call] call_busy received:", payload);
|
|
1748
|
+
useCallStore.getState()._receiveBusy(payload);
|
|
1749
|
+
});
|
|
1750
|
+
socket.on(SOCKET_EVENTS.CALL_MISSED, (payload) => {
|
|
1751
|
+
console.log("[realtimex-call] call_missed received:", payload);
|
|
1752
|
+
useCallStore.getState()._receiveMissed(payload);
|
|
1272
1753
|
});
|
|
1273
|
-
return Array.from(merged.values());
|
|
1274
1754
|
};
|
|
1275
1755
|
}
|
|
1276
1756
|
});
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1757
|
+
var SocketService, socketService, socket_service_default;
|
|
1758
|
+
var init_socket_service = __esm({
|
|
1759
|
+
"src/services/socket/socket-service.ts"() {
|
|
1760
|
+
init_socket_events_constants();
|
|
1761
|
+
init_get_store();
|
|
1762
|
+
init_listeners();
|
|
1763
|
+
init_call_listeners();
|
|
1764
|
+
SocketService = class {
|
|
1765
|
+
constructor() {
|
|
1766
|
+
this.socket = null;
|
|
1767
|
+
this.currentConfig = null;
|
|
1768
|
+
this.isConnecting = false;
|
|
1769
|
+
}
|
|
1770
|
+
connect(config) {
|
|
1771
|
+
if (this.socket?.connected && this.currentConfig && this.currentConfig.url === config.url && JSON.stringify(this.currentConfig.options.auth) === JSON.stringify(config.options.auth) && JSON.stringify(this.currentConfig.options.query) === JSON.stringify(config.options.query)) {
|
|
1772
|
+
return;
|
|
1773
|
+
}
|
|
1774
|
+
if (this.isConnecting && this.currentConfig?.url === config.url) {
|
|
1291
1775
|
return;
|
|
1292
1776
|
}
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1777
|
+
if (this.socket) {
|
|
1778
|
+
console.log("Socket: Config changed or forced reconnect. Cleaning up old connection...", {
|
|
1779
|
+
old: this.currentConfig?.url,
|
|
1780
|
+
new: config.url
|
|
1781
|
+
});
|
|
1782
|
+
this.disconnect();
|
|
1783
|
+
}
|
|
1784
|
+
this.currentConfig = config;
|
|
1785
|
+
this.isConnecting = true;
|
|
1786
|
+
const store = getStore();
|
|
1787
|
+
const { setConnecting, setConnected, setDisconnected, setError } = store.getState();
|
|
1788
|
+
setConnecting();
|
|
1789
|
+
this.socket = io(config.url, config.options);
|
|
1790
|
+
this.socket.on("connect", () => {
|
|
1791
|
+
this.isConnecting = false;
|
|
1792
|
+
setConnected({ socketId: this.socket?.id || "" });
|
|
1793
|
+
this.emitGetOnlineUsers();
|
|
1794
|
+
});
|
|
1795
|
+
this.socket.on("disconnect", (reason) => {
|
|
1796
|
+
this.isConnecting = false;
|
|
1797
|
+
console.log("Disconnected from socket. Reason:", reason);
|
|
1798
|
+
setDisconnected();
|
|
1799
|
+
});
|
|
1800
|
+
this.socket.on("connect_error", (error) => {
|
|
1801
|
+
this.isConnecting = false;
|
|
1802
|
+
console.error("Socket connection error:", error);
|
|
1803
|
+
setError(error.message);
|
|
1804
|
+
});
|
|
1805
|
+
registerSocketListeners(this.socket, () => this.emitGetOnlineUsers());
|
|
1806
|
+
registerCallSocketListeners(this.socket);
|
|
1807
|
+
}
|
|
1808
|
+
disconnect() {
|
|
1809
|
+
if (this.socket) {
|
|
1810
|
+
this.socket.removeAllListeners();
|
|
1811
|
+
this.socket.disconnect();
|
|
1812
|
+
this.socket = null;
|
|
1813
|
+
this.currentConfig = null;
|
|
1814
|
+
this.isConnecting = false;
|
|
1815
|
+
}
|
|
1816
|
+
}
|
|
1817
|
+
emit(event, payload) {
|
|
1818
|
+
if (this.socket) {
|
|
1819
|
+
this.socket.emit(event, payload);
|
|
1299
1820
|
} else {
|
|
1300
|
-
|
|
1821
|
+
console.error(`SocketService: Socket is not initialized. Cannot emit ${event}`);
|
|
1301
1822
|
}
|
|
1302
|
-
};
|
|
1303
|
-
(existingParticipants || []).forEach(addParticipant);
|
|
1304
|
-
(incomingParticipants || []).forEach(addParticipant);
|
|
1305
|
-
return Array.from(byId.values());
|
|
1306
|
-
};
|
|
1307
|
-
buildParticipantsFromMessagePayload = (payload, loggedUserId, existingParticipants) => {
|
|
1308
|
-
const participants = [];
|
|
1309
|
-
const seen = /* @__PURE__ */ new Set();
|
|
1310
|
-
const addParticipant = (participant) => {
|
|
1311
|
-
const id = String(participant?._id || participant?.id || participant || "");
|
|
1312
|
-
if (!id || seen.has(id)) return;
|
|
1313
|
-
seen.add(id);
|
|
1314
|
-
participants.push(
|
|
1315
|
-
typeof participant === "object" && participant !== null ? participant : { _id: id }
|
|
1316
|
-
);
|
|
1317
|
-
};
|
|
1318
|
-
(existingParticipants || []).forEach(addParticipant);
|
|
1319
|
-
addParticipant(payload?.sender);
|
|
1320
|
-
addParticipant(payload?.receiver);
|
|
1321
|
-
if (loggedUserId) addParticipant(loggedUserId);
|
|
1322
|
-
const senderId = String(payload?.sender?._id || payload?.sender?.id || payload?.sender || "");
|
|
1323
|
-
if (loggedUserId && senderId === String(loggedUserId) && participants.length < 2 && payload?.owner && String(payload.owner) !== String(loggedUserId)) {
|
|
1324
|
-
addParticipant(payload.owner);
|
|
1325
1823
|
}
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
resolveOtherParticipant = (conversation, loggedUserId, allUsers) => {
|
|
1329
|
-
if (!conversation || !loggedUserId) {
|
|
1330
|
-
return { id: "", user: null };
|
|
1824
|
+
getSocket() {
|
|
1825
|
+
return this.socket;
|
|
1331
1826
|
}
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
(participant) => String(participant?._id || participant?.id || participant || "")
|
|
1335
|
-
);
|
|
1336
|
-
const otherId = participantIds.find((id) => id && id !== myId) || (conversation.owner && String(conversation.owner) !== myId ? String(conversation.owner) : "");
|
|
1337
|
-
if (!otherId) {
|
|
1338
|
-
return { id: "", user: null };
|
|
1827
|
+
emitDmSend(payload) {
|
|
1828
|
+
this.emit(SOCKET_EVENTS.DM_SEND, payload);
|
|
1339
1829
|
}
|
|
1340
|
-
|
|
1341
|
-
(
|
|
1342
|
-
);
|
|
1343
|
-
const participantStub = typeof fromParticipants === "object" && fromParticipants !== null ? fromParticipants : { _id: otherId };
|
|
1344
|
-
const fromAllUsers = (allUsers || []).find((user) => String(user._id) === otherId) || null;
|
|
1345
|
-
return {
|
|
1346
|
-
id: otherId,
|
|
1347
|
-
user: fromAllUsers ? { ...participantStub, ...fromAllUsers } : participantStub
|
|
1348
|
-
};
|
|
1349
|
-
};
|
|
1350
|
-
getOtherParticipantId = (conversation, loggedUserId) => {
|
|
1351
|
-
if (!conversation?.participants?.length && !conversation?.owner) return "";
|
|
1352
|
-
const otherId = resolveOtherParticipant(conversation, loggedUserId).id;
|
|
1353
|
-
return otherId;
|
|
1354
|
-
};
|
|
1355
|
-
}
|
|
1356
|
-
});
|
|
1357
|
-
|
|
1358
|
-
// src/store/helpers/unread.helpers.ts
|
|
1359
|
-
var normalizeUnreadCount, incrementUnreadCount, resolveNextUnreadCount;
|
|
1360
|
-
var init_unread_helpers = __esm({
|
|
1361
|
-
"src/store/helpers/unread.helpers.ts"() {
|
|
1362
|
-
normalizeUnreadCount = (unreadCount, loggedUserId) => {
|
|
1363
|
-
if (typeof unreadCount === "number" && Number.isFinite(unreadCount)) return unreadCount;
|
|
1364
|
-
if (typeof unreadCount === "string" && unreadCount.trim() !== "") {
|
|
1365
|
-
const parsed = Number.parseInt(unreadCount, 10);
|
|
1366
|
-
if (Number.isFinite(parsed)) return parsed;
|
|
1830
|
+
emitDmEdit(payload) {
|
|
1831
|
+
this.emit(SOCKET_EVENTS.DM_EDIT, payload);
|
|
1367
1832
|
}
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
if (loggedUserId) {
|
|
1371
|
-
const mine = map[String(loggedUserId)];
|
|
1372
|
-
if (mine !== void 0 && mine !== null) {
|
|
1373
|
-
return normalizeUnreadCount(mine, loggedUserId);
|
|
1374
|
-
}
|
|
1375
|
-
}
|
|
1376
|
-
const values = Object.values(map).map((value) => normalizeUnreadCount(value, loggedUserId)).filter((value) => Number.isFinite(value));
|
|
1377
|
-
return values.length ? Math.max(...values) : 0;
|
|
1833
|
+
emitDmDelete(payload) {
|
|
1834
|
+
this.emit(SOCKET_EVENTS.DM_DELETE, payload);
|
|
1378
1835
|
}
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
incrementUnreadCount = (unreadCount, loggedUserId) => normalizeUnreadCount(unreadCount, loggedUserId) + 1;
|
|
1382
|
-
resolveNextUnreadCount = ({
|
|
1383
|
-
currentUnread,
|
|
1384
|
-
serverUnread,
|
|
1385
|
-
isActiveConversation,
|
|
1386
|
-
shouldIncrement,
|
|
1387
|
-
loggedUserId
|
|
1388
|
-
}) => {
|
|
1389
|
-
if (isActiveConversation) return 0;
|
|
1390
|
-
let next = currentUnread;
|
|
1391
|
-
if (shouldIncrement) {
|
|
1392
|
-
next = incrementUnreadCount(next, loggedUserId);
|
|
1836
|
+
emitDmClearChat(payload) {
|
|
1837
|
+
this.emit(SOCKET_EVENTS.DM_CLEAR_CHAT, payload);
|
|
1393
1838
|
}
|
|
1394
|
-
|
|
1395
|
-
|
|
1839
|
+
emitGroupClearChat(payload) {
|
|
1840
|
+
this.emit(SOCKET_EVENTS.GROUP_CLEAR_CHAT, payload);
|
|
1396
1841
|
}
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
)
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
}
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
)
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
);
|
|
1438
|
-
}
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1842
|
+
emitDmDeleteConversation(payload) {
|
|
1843
|
+
this.emit(SOCKET_EVENTS.DM_DELETE_CONVERSATION, payload);
|
|
1844
|
+
}
|
|
1845
|
+
emitGroupDeleteConversation(payload) {
|
|
1846
|
+
this.emit(SOCKET_EVENTS.GROUP_DELETE_CONVERSATION, payload);
|
|
1847
|
+
}
|
|
1848
|
+
emitDmReactionAdd(payload) {
|
|
1849
|
+
this.emit(SOCKET_EVENTS.DM_REACTION_ADD, payload);
|
|
1850
|
+
}
|
|
1851
|
+
emitDmReactionRemove(payload) {
|
|
1852
|
+
this.emit(SOCKET_EVENTS.DM_REACTION_REMOVE, payload);
|
|
1853
|
+
}
|
|
1854
|
+
emitTypingStart(payload) {
|
|
1855
|
+
this.emit(SOCKET_EVENTS.DM_TYPING_START, payload);
|
|
1856
|
+
}
|
|
1857
|
+
emitTypingStop(payload) {
|
|
1858
|
+
this.emit(SOCKET_EVENTS.DM_TYPING_STOP, payload);
|
|
1859
|
+
}
|
|
1860
|
+
emitMarkAsRead(payload) {
|
|
1861
|
+
this.emit(SOCKET_EVENTS.DM_MARK_READ, payload);
|
|
1862
|
+
}
|
|
1863
|
+
emitDmPin(payload) {
|
|
1864
|
+
this.emit(SOCKET_EVENTS.DM_PIN, payload);
|
|
1865
|
+
}
|
|
1866
|
+
emitDmUnpin(payload) {
|
|
1867
|
+
this.emit(SOCKET_EVENTS.DM_UNPIN, payload);
|
|
1868
|
+
}
|
|
1869
|
+
emitDmStar(payload) {
|
|
1870
|
+
this.emit(SOCKET_EVENTS.DM_STAR, payload);
|
|
1871
|
+
}
|
|
1872
|
+
emitDmUnstar(payload) {
|
|
1873
|
+
this.emit(SOCKET_EVENTS.DM_UNSTAR, payload);
|
|
1874
|
+
}
|
|
1875
|
+
emitDmForward(payload) {
|
|
1876
|
+
this.emit(SOCKET_EVENTS.DM_FORWARD, payload);
|
|
1877
|
+
}
|
|
1878
|
+
emitGroupJoin(payload) {
|
|
1879
|
+
this.emit(SOCKET_EVENTS.GROUP_JOIN, payload);
|
|
1880
|
+
}
|
|
1881
|
+
emitGroupMessageSend(payload) {
|
|
1882
|
+
this.emit(SOCKET_EVENTS.GROUP_MESSAGE_SEND, payload);
|
|
1883
|
+
}
|
|
1884
|
+
emitGroupTypingStart(payload) {
|
|
1885
|
+
this.emit(SOCKET_EVENTS.GROUP_TYPING_START, payload);
|
|
1886
|
+
}
|
|
1887
|
+
emitGroupTypingStop(payload) {
|
|
1888
|
+
this.emit(SOCKET_EVENTS.GROUP_TYPING_STOP, payload);
|
|
1889
|
+
}
|
|
1890
|
+
emitGroupEdit(payload) {
|
|
1891
|
+
this.emit(SOCKET_EVENTS.GROUP_EDIT, payload);
|
|
1892
|
+
}
|
|
1893
|
+
emitGroupReactionAdd(payload) {
|
|
1894
|
+
this.emit(SOCKET_EVENTS.GROUP_REACTION_ADD, payload);
|
|
1895
|
+
}
|
|
1896
|
+
emitGroupMarkRead(payload) {
|
|
1897
|
+
this.emit(SOCKET_EVENTS.GROUP_MESSAGE_READ, payload);
|
|
1898
|
+
}
|
|
1899
|
+
emitGroupReactionRemove(payload) {
|
|
1900
|
+
this.emit(SOCKET_EVENTS.GROUP_REACTION_REMOVE, payload);
|
|
1901
|
+
}
|
|
1902
|
+
emitGroupDelete(payload) {
|
|
1903
|
+
this.emit(SOCKET_EVENTS.GROUP_DELETE, payload);
|
|
1904
|
+
}
|
|
1905
|
+
emitGroupPin(payload) {
|
|
1906
|
+
this.emit(SOCKET_EVENTS.GROUP_PIN, payload);
|
|
1907
|
+
}
|
|
1908
|
+
emitGroupUnpin(payload) {
|
|
1909
|
+
this.emit(SOCKET_EVENTS.GROUP_UNPIN, payload);
|
|
1910
|
+
}
|
|
1911
|
+
emitGroupStar(payload) {
|
|
1912
|
+
this.emit(SOCKET_EVENTS.GROUP_STAR, payload);
|
|
1913
|
+
}
|
|
1914
|
+
emitGroupUnstar(payload) {
|
|
1915
|
+
this.emit(SOCKET_EVENTS.GROUP_UNSTAR, payload);
|
|
1916
|
+
}
|
|
1917
|
+
emitGroupForward(payload) {
|
|
1918
|
+
this.emit(SOCKET_EVENTS.GROUP_FORWARD, payload);
|
|
1919
|
+
}
|
|
1920
|
+
emitConversationUpdate(payload) {
|
|
1921
|
+
this.emit(SOCKET_EVENTS.CONVERSATION_UPDATE, payload);
|
|
1922
|
+
}
|
|
1923
|
+
emitGroupLeave(payload) {
|
|
1924
|
+
this.emit(SOCKET_EVENTS.GROUP_LEAVE, payload);
|
|
1925
|
+
}
|
|
1926
|
+
emitGetOnlineUsers() {
|
|
1927
|
+
this.emit(SOCKET_EVENTS.GET_ONLINE_USERS);
|
|
1928
|
+
}
|
|
1929
|
+
emitCallInvite(payload) {
|
|
1930
|
+
this.emit(SOCKET_EVENTS.CALL_INVITE, payload);
|
|
1445
1931
|
}
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
messagesByConversation: nextMessagesByConversation
|
|
1449
|
-
};
|
|
1450
|
-
};
|
|
1451
|
-
isGroupConversation = (conversation) => {
|
|
1452
|
-
if (!conversation) return false;
|
|
1453
|
-
return conversation.conversationType === "group" || !!conversation.isGroup;
|
|
1454
|
-
};
|
|
1455
|
-
normalizeConversationRecord = (conversation, existing) => {
|
|
1456
|
-
if (!conversation) return existing ?? null;
|
|
1457
|
-
const resolvedBlocked = conversation.isBlocked !== void 0 ? Boolean(conversation.isBlocked) : conversation.blocked !== void 0 ? Boolean(conversation.blocked) : conversation.isUserBlocked !== void 0 ? Boolean(conversation.isUserBlocked) : existing?.isBlocked ?? false;
|
|
1458
|
-
const mergedParticipants = conversation.participants !== void 0 ? mergeConversationParticipants(existing?.participants, conversation.participants) : existing?.participants ?? conversation.participants;
|
|
1459
|
-
const isGroup = isGroupConversation(conversation) || isGroupConversation(existing);
|
|
1460
|
-
const resolvedGroupImage = resolveGroupImage(conversation) || resolveGroupImage(existing);
|
|
1461
|
-
const incomingGroupName = typeof conversation.groupName === "string" && conversation.groupName.trim() || typeof conversation.name === "string" && conversation.name.trim() || "";
|
|
1462
|
-
const existingGroupName = typeof existing?.groupName === "string" && existing.groupName.trim() || typeof existing?.name === "string" && existing.name.trim() || "";
|
|
1463
|
-
const resolvedGroupName = incomingGroupName || existingGroupName || void 0;
|
|
1464
|
-
return {
|
|
1465
|
-
...existing,
|
|
1466
|
-
...conversation,
|
|
1467
|
-
participants: mergedParticipants,
|
|
1468
|
-
isGroup,
|
|
1469
|
-
isBlocked: resolvedBlocked,
|
|
1470
|
-
lastMessage: mergeLastMessage(existing?.lastMessage, conversation.lastMessage),
|
|
1471
|
-
...isGroup ? {
|
|
1472
|
-
...resolvedGroupName ? { groupName: resolvedGroupName, name: resolvedGroupName } : {},
|
|
1473
|
-
...resolvedGroupImage ? { image: resolvedGroupImage, groupImage: resolvedGroupImage } : {}
|
|
1474
|
-
} : {}
|
|
1475
|
-
};
|
|
1476
|
-
};
|
|
1477
|
-
resolveConversationLastMessage = (conversation, messagesByConversation) => {
|
|
1478
|
-
const conversationId = String(conversation._id || conversation.id || "");
|
|
1479
|
-
const storedLast = conversation.lastMessage;
|
|
1480
|
-
const latestFromMessages = getLatestMessageFromList(messagesByConversation[conversationId] || []);
|
|
1481
|
-
if (!latestFromMessages && !storedLast) return null;
|
|
1482
|
-
if (!latestFromMessages) return storedLast;
|
|
1483
|
-
if (!messageHasPreviewBody(storedLast)) return latestFromMessages;
|
|
1484
|
-
const storedTime = new Date(storedLast.createdAt || storedLast.timestamp || 0).getTime();
|
|
1485
|
-
const latestTime = new Date(latestFromMessages.createdAt || latestFromMessages.timestamp || 0).getTime();
|
|
1486
|
-
return latestTime >= storedTime ? mergeLastMessage(storedLast, latestFromMessages) : mergeLastMessage(latestFromMessages, storedLast);
|
|
1487
|
-
};
|
|
1488
|
-
syncConversationLastMessage = (conversations, conversationId, messages) => {
|
|
1489
|
-
const latest = getLatestMessageFromList(messages);
|
|
1490
|
-
if (!latest) return conversations;
|
|
1491
|
-
return conversations.map((conversation) => {
|
|
1492
|
-
if (String(conversation._id) !== String(conversationId)) return conversation;
|
|
1493
|
-
return {
|
|
1494
|
-
...conversation,
|
|
1495
|
-
lastMessage: mergeLastMessage(conversation.lastMessage, latest)
|
|
1496
|
-
};
|
|
1497
|
-
});
|
|
1498
|
-
};
|
|
1499
|
-
upsertConversationInList = (conversations, incoming, options) => {
|
|
1500
|
-
const conversationId = String(incoming?._id || incoming?.conversationId || "");
|
|
1501
|
-
if (!conversationId) return conversations;
|
|
1502
|
-
const index = conversations.findIndex((c) => String(c._id) === conversationId);
|
|
1503
|
-
const existing = index >= 0 ? conversations[index] : null;
|
|
1504
|
-
const mergedLastMessage = mergeLastMessage(
|
|
1505
|
-
existing?.lastMessage,
|
|
1506
|
-
options?.lastMessage ?? incoming.lastMessage
|
|
1507
|
-
);
|
|
1508
|
-
const isActiveConversation = !!options?.activeConversationId && String(options.activeConversationId) === conversationId;
|
|
1509
|
-
const loggedUserId = options?.loggedUserId;
|
|
1510
|
-
const incomingLastMessage = options?.lastMessage ?? incoming.lastMessage;
|
|
1511
|
-
const lastMessageChanged = !!incomingLastMessage && String(mergedLastMessage?._id || mergedLastMessage?.id || "") !== String(existing?.lastMessage?._id || existing?.lastMessage?.id || "");
|
|
1512
|
-
const senderId = String(
|
|
1513
|
-
incomingLastMessage?.sender?._id || incomingLastMessage?.sender?.id || incomingLastMessage?.sender || ""
|
|
1514
|
-
);
|
|
1515
|
-
const isIncomingLastMessage = !!loggedUserId && !!senderId && senderId !== String(loggedUserId);
|
|
1516
|
-
const normalized = normalizeConversationRecord(
|
|
1517
|
-
{
|
|
1518
|
-
...incoming,
|
|
1519
|
-
_id: conversationId,
|
|
1520
|
-
lastMessage: mergedLastMessage,
|
|
1521
|
-
unreadCount: resolveNextUnreadCount({
|
|
1522
|
-
currentUnread: normalizeUnreadCount(existing?.unreadCount, loggedUserId),
|
|
1523
|
-
serverUnread: incoming.unreadCount,
|
|
1524
|
-
isActiveConversation,
|
|
1525
|
-
loggedUserId,
|
|
1526
|
-
shouldIncrement: !isActiveConversation && lastMessageChanged && isIncomingLastMessage && !options?.isFromSelf
|
|
1527
|
-
})
|
|
1528
|
-
},
|
|
1529
|
-
existing
|
|
1530
|
-
);
|
|
1531
|
-
if (index < 0) {
|
|
1532
|
-
return [normalized, ...conversations];
|
|
1932
|
+
emitCallAccept(payload) {
|
|
1933
|
+
this.emit(SOCKET_EVENTS.CALL_ACCEPT, payload);
|
|
1533
1934
|
}
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
return [normalized, ...withoutExisting];
|
|
1935
|
+
emitCallReject(payload) {
|
|
1936
|
+
this.emit(SOCKET_EVENTS.CALL_REJECT, payload);
|
|
1537
1937
|
}
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
return next;
|
|
1541
|
-
};
|
|
1542
|
-
findConversationById = (conversations, conversationId) => {
|
|
1543
|
-
if (!conversationId) return void 0;
|
|
1544
|
-
return conversations.find((c) => String(c._id) === String(conversationId));
|
|
1545
|
-
};
|
|
1546
|
-
findConversationIdByLastMessageId = (conversations, messageId2) => {
|
|
1547
|
-
if (!messageId2) return null;
|
|
1548
|
-
const normalizedMessageId = String(messageId2);
|
|
1549
|
-
const match = conversations.find(
|
|
1550
|
-
(conversation) => String(conversation?.lastMessage?._id || conversation?.lastMessage?.id || "") === normalizedMessageId
|
|
1551
|
-
);
|
|
1552
|
-
return match?._id ? String(match._id) : null;
|
|
1553
|
-
};
|
|
1554
|
-
findConversationForChannel = (channel, loggedUserId, conversations) => {
|
|
1555
|
-
if (!channel || !loggedUserId) return void 0;
|
|
1556
|
-
if (channel.conversationId) {
|
|
1557
|
-
const byId = findConversationById(conversations, channel.conversationId);
|
|
1558
|
-
if (byId) return byId;
|
|
1938
|
+
emitCallCancel(payload) {
|
|
1939
|
+
this.emit(SOCKET_EVENTS.CALL_CANCEL, payload);
|
|
1559
1940
|
}
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
(c) => isGroupConversation(c) && String(c._id) === String(channel.id)
|
|
1563
|
-
);
|
|
1941
|
+
emitCallEnd(payload) {
|
|
1942
|
+
this.emit(SOCKET_EVENTS.CALL_END, payload);
|
|
1564
1943
|
}
|
|
1565
|
-
return conversations.find((c) => {
|
|
1566
|
-
if (isGroupConversation(c)) return false;
|
|
1567
|
-
const ids = (c.participants || []).map((p) => String(p._id || p));
|
|
1568
|
-
return ids.includes(String(channel.id)) && ids.includes(String(loggedUserId));
|
|
1569
|
-
});
|
|
1570
1944
|
};
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
if (found?._id) return String(found._id);
|
|
1574
|
-
if (channel?.conversationId) return String(channel.conversationId);
|
|
1575
|
-
return null;
|
|
1576
|
-
};
|
|
1577
|
-
}
|
|
1578
|
-
});
|
|
1579
|
-
|
|
1580
|
-
// src/store/helpers/pagination.helpers.ts
|
|
1581
|
-
var emptyMessagesMetadata, extractPagePagination;
|
|
1582
|
-
var init_pagination_helpers = __esm({
|
|
1583
|
-
"src/store/helpers/pagination.helpers.ts"() {
|
|
1584
|
-
emptyMessagesMetadata = () => ({
|
|
1585
|
-
nextCursor: null,
|
|
1586
|
-
hasMore: false,
|
|
1587
|
-
isFirstPage: true
|
|
1588
|
-
});
|
|
1589
|
-
extractPagePagination = (response) => response?.data?.pagination ?? response?.pagination ?? null;
|
|
1945
|
+
socketService = new SocketService();
|
|
1946
|
+
socket_service_default = socketService;
|
|
1590
1947
|
}
|
|
1591
1948
|
});
|
|
1592
1949
|
|
|
1593
|
-
// src/
|
|
1594
|
-
var
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
init_message_helpers();
|
|
1598
|
-
init_conversation_helpers();
|
|
1599
|
-
normalizeId = (id) => String(id?._id || id?.id || id || "");
|
|
1600
|
-
resolveConversationIdForIncomingMessage = (payload, conversations, loggedUserId) => {
|
|
1601
|
-
const fromPayload = extractMessageData(payload).conversationId;
|
|
1602
|
-
if (fromPayload) return String(fromPayload);
|
|
1603
|
-
if (!loggedUserId) return null;
|
|
1604
|
-
const myId = String(loggedUserId);
|
|
1605
|
-
const senderId = getMessageSenderId(payload);
|
|
1606
|
-
const receiverId = String(payload?.receiver?._id || payload?.receiver?.id || payload?.receiver || "");
|
|
1607
|
-
const otherUserId = senderId === myId ? receiverId : senderId;
|
|
1608
|
-
if (!otherUserId) return null;
|
|
1609
|
-
const match = conversations.find((conversation) => {
|
|
1610
|
-
if (isGroupConversation(conversation)) return false;
|
|
1611
|
-
const participants = (conversation.participants || []).map((p) => String(p._id || p));
|
|
1612
|
-
return participants.includes(otherUserId);
|
|
1613
|
-
});
|
|
1614
|
-
return match?._id ? String(match._id) : null;
|
|
1615
|
-
};
|
|
1616
|
-
resolveIsGroupFromPayload = (payload, conversations) => {
|
|
1617
|
-
const conversationId = payload?.conversationId || payload?.message?.conversationId || payload?.message?.conversation_id;
|
|
1618
|
-
const fromStore = findConversationById(conversations, conversationId);
|
|
1619
|
-
if (fromStore) return isGroupConversation(fromStore);
|
|
1620
|
-
return !!payload?.isGroup || payload?.conversationType === "group" || payload?.message?.conversationType === "group";
|
|
1621
|
-
};
|
|
1622
|
-
resolveDmMarkReadReaderId = (payload, loggedUserId) => {
|
|
1623
|
-
if (payload.readBy != null) return String(payload.readBy);
|
|
1624
|
-
if (payload.userId != null) return String(payload.userId);
|
|
1625
|
-
if (payload.readerId != null) return String(payload.readerId);
|
|
1626
|
-
const sender = payload.sender?._id ?? payload.sender;
|
|
1627
|
-
const receiver = payload.receiver?._id ?? payload.receiver;
|
|
1628
|
-
const myId = loggedUserId != null ? String(loggedUserId) : null;
|
|
1629
|
-
if (sender != null && receiver != null && myId) {
|
|
1630
|
-
if (String(receiver) === myId) return String(sender);
|
|
1631
|
-
if (String(sender) === myId) return String(sender);
|
|
1632
|
-
}
|
|
1633
|
-
if (sender != null) return String(sender);
|
|
1634
|
-
if (payload.senderId != null) return String(payload.senderId);
|
|
1635
|
-
return null;
|
|
1636
|
-
};
|
|
1950
|
+
// src/services/socket/index.ts
|
|
1951
|
+
var init_socket = __esm({
|
|
1952
|
+
"src/services/socket/index.ts"() {
|
|
1953
|
+
init_socket_service();
|
|
1637
1954
|
}
|
|
1638
1955
|
});
|
|
1639
1956
|
|
|
1640
|
-
// src/
|
|
1641
|
-
var
|
|
1642
|
-
"src/
|
|
1643
|
-
|
|
1644
|
-
init_message_helpers();
|
|
1645
|
-
init_participant_helpers();
|
|
1646
|
-
init_unread_helpers();
|
|
1647
|
-
init_group_display_helpers();
|
|
1648
|
-
init_pagination_helpers();
|
|
1649
|
-
init_normalize_helpers();
|
|
1957
|
+
// src/services/socket.service.ts
|
|
1958
|
+
var init_socket_service2 = __esm({
|
|
1959
|
+
"src/services/socket.service.ts"() {
|
|
1960
|
+
init_socket();
|
|
1650
1961
|
}
|
|
1651
1962
|
});
|
|
1652
1963
|
|
|
@@ -4436,6 +4747,7 @@ var init_ParticipantView = __esm({
|
|
|
4436
4747
|
isLocal = false,
|
|
4437
4748
|
fallbackName,
|
|
4438
4749
|
fallbackImage,
|
|
4750
|
+
isHandRaised = false,
|
|
4439
4751
|
className
|
|
4440
4752
|
}) => {
|
|
4441
4753
|
const { webcamStream, micStream, webcamOn, micOn, displayName, isActiveSpeaker } = sdk.useParticipant(participantId);
|
|
@@ -4484,11 +4796,20 @@ var init_ParticipantView = __esm({
|
|
|
4484
4796
|
onPlaying: () => setHasFrame(true),
|
|
4485
4797
|
className: cn(
|
|
4486
4798
|
"size-full object-cover",
|
|
4487
|
-
showVideo ? "block" : "hidden"
|
|
4799
|
+
showVideo ? "block" : "hidden",
|
|
4800
|
+
// Mirror only the local self-view (how every call app
|
|
4801
|
+
// shows you — moving your right hand appears to move
|
|
4802
|
+
// right in your own preview, like a mirror). The video
|
|
4803
|
+
// actually sent to other participants is untouched by
|
|
4804
|
+
// this CSS, so remote viewers always see it the right
|
|
4805
|
+
// way round; only flip our own local tile, never a
|
|
4806
|
+
// remote one.
|
|
4807
|
+
isLocal && "-scale-x-100"
|
|
4488
4808
|
)
|
|
4489
4809
|
}
|
|
4490
4810
|
),
|
|
4491
4811
|
!isLocal && /* @__PURE__ */ jsx("audio", { ref: audioRef, autoPlay: true, playsInline: true }),
|
|
4812
|
+
isHandRaised && /* @__PURE__ */ jsx("span", { className: "absolute top-2 left-2 flex size-7 items-center justify-center rounded-full bg-amber-400 text-neutral-900 shadow", children: /* @__PURE__ */ jsx(Hand, { size: 14 }) }),
|
|
4492
4813
|
!showVideo && /* @__PURE__ */ jsxs(Avatar, { size: "lg", className: "size-20", children: [
|
|
4493
4814
|
/* @__PURE__ */ jsx(AvatarImage, { src: fallbackImage, alt: name }),
|
|
4494
4815
|
/* @__PURE__ */ jsx(AvatarFallback, { className: "bg-neutral-700 text-xl font-medium text-white", children: name.charAt(0).toUpperCase() })
|
|
@@ -4513,7 +4834,8 @@ var init_AudioCallView = __esm({
|
|
|
4513
4834
|
sdk,
|
|
4514
4835
|
remoteParticipantId,
|
|
4515
4836
|
peerName,
|
|
4516
|
-
peerAvatar
|
|
4837
|
+
peerAvatar,
|
|
4838
|
+
isRemoteHandRaised = false
|
|
4517
4839
|
}) => {
|
|
4518
4840
|
const remote = remoteParticipantId ? sdk.useParticipant(remoteParticipantId) : null;
|
|
4519
4841
|
const name = remote?.displayName || peerName || "In call";
|
|
@@ -4543,7 +4865,8 @@ var init_AudioCallView = __esm({
|
|
|
4543
4865
|
]
|
|
4544
4866
|
}
|
|
4545
4867
|
),
|
|
4546
|
-
isMuted && /* @__PURE__ */ jsx("span", { className: "absolute -right-1 -bottom-1 flex size-9 items-center justify-center rounded-full bg-neutral-700 ring-4 ring-neutral-900", children: /* @__PURE__ */ jsx(MicOff, { size: 16, className: "text-white/80" }) })
|
|
4868
|
+
isMuted && /* @__PURE__ */ jsx("span", { className: "absolute -right-1 -bottom-1 flex size-9 items-center justify-center rounded-full bg-neutral-700 ring-4 ring-neutral-900", children: /* @__PURE__ */ jsx(MicOff, { size: 16, className: "text-white/80" }) }),
|
|
4869
|
+
isRemoteHandRaised && /* @__PURE__ */ jsx("span", { className: "absolute -left-1 -top-1 flex size-9 items-center justify-center rounded-full bg-amber-400 text-neutral-900 ring-4 ring-neutral-900", children: /* @__PURE__ */ jsx(Hand, { size: 16 }) })
|
|
4547
4870
|
] }),
|
|
4548
4871
|
/* @__PURE__ */ jsx("p", { className: "text-xl font-semibold tracking-tight", children: name })
|
|
4549
4872
|
] });
|
|
@@ -4551,50 +4874,176 @@ var init_AudioCallView = __esm({
|
|
|
4551
4874
|
AudioCallView_default = AudioCallView;
|
|
4552
4875
|
}
|
|
4553
4876
|
});
|
|
4554
|
-
var
|
|
4877
|
+
var ScreenShareView, ScreenShareView_default;
|
|
4878
|
+
var init_ScreenShareView = __esm({
|
|
4879
|
+
"src/call/ScreenShareView.tsx"() {
|
|
4880
|
+
ScreenShareView = ({ sdk, presenterId, isLocal, className }) => {
|
|
4881
|
+
const { screenShareStream, screenShareOn, displayName } = sdk.useParticipant(presenterId);
|
|
4882
|
+
const videoRef = useRef(null);
|
|
4883
|
+
useEffect(() => {
|
|
4884
|
+
const el = videoRef.current;
|
|
4885
|
+
if (!el) return;
|
|
4886
|
+
if (screenShareOn && screenShareStream?.track) {
|
|
4887
|
+
el.srcObject = new MediaStream([screenShareStream.track]);
|
|
4888
|
+
el.play().catch(() => void 0);
|
|
4889
|
+
} else {
|
|
4890
|
+
el.srcObject = null;
|
|
4891
|
+
}
|
|
4892
|
+
}, [screenShareOn, screenShareStream]);
|
|
4893
|
+
return /* @__PURE__ */ jsxs("div", { className, children: [
|
|
4894
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5 px-1 pb-2 text-xs font-medium text-white/70", children: [
|
|
4895
|
+
/* @__PURE__ */ jsx(MonitorUp, { size: 14 }),
|
|
4896
|
+
isLocal ? "You are presenting" : `${displayName || "Someone"} is presenting`
|
|
4897
|
+
] }),
|
|
4898
|
+
/* @__PURE__ */ jsx("div", { className: "relative flex flex-1 items-center justify-center overflow-hidden rounded-xl bg-black", children: /* @__PURE__ */ jsx("video", { ref: videoRef, playsInline: true, muted: isLocal, className: "max-h-full max-w-full object-contain" }) })
|
|
4899
|
+
] });
|
|
4900
|
+
};
|
|
4901
|
+
ScreenShareView_default = ScreenShareView;
|
|
4902
|
+
}
|
|
4903
|
+
});
|
|
4904
|
+
function Popover({
|
|
4905
|
+
...props
|
|
4906
|
+
}) {
|
|
4907
|
+
return /* @__PURE__ */ jsx(Popover$1.Root, { "data-slot": "popover", ...props });
|
|
4908
|
+
}
|
|
4909
|
+
function PopoverTrigger({
|
|
4910
|
+
...props
|
|
4911
|
+
}) {
|
|
4912
|
+
return /* @__PURE__ */ jsx(Popover$1.Trigger, { "data-slot": "popover-trigger", ...props });
|
|
4913
|
+
}
|
|
4914
|
+
function PopoverContent({
|
|
4915
|
+
className,
|
|
4916
|
+
align = "center",
|
|
4917
|
+
sideOffset = 4,
|
|
4918
|
+
...props
|
|
4919
|
+
}) {
|
|
4920
|
+
return /* @__PURE__ */ jsx(Popover$1.Portal, { children: /* @__PURE__ */ jsx(
|
|
4921
|
+
Popover$1.Content,
|
|
4922
|
+
{
|
|
4923
|
+
"data-slot": "popover-content",
|
|
4924
|
+
align,
|
|
4925
|
+
sideOffset,
|
|
4926
|
+
className: cn(
|
|
4927
|
+
"bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 w-72 origin-(--radix-popover-content-transform-origin) rounded-md border p-4 shadow-md outline-hidden",
|
|
4928
|
+
className
|
|
4929
|
+
),
|
|
4930
|
+
...props
|
|
4931
|
+
}
|
|
4932
|
+
) });
|
|
4933
|
+
}
|
|
4934
|
+
var init_popover = __esm({
|
|
4935
|
+
"src/ui/popover.tsx"() {
|
|
4936
|
+
init_utils2();
|
|
4937
|
+
}
|
|
4938
|
+
});
|
|
4939
|
+
var circleBtn, idleBtnClass, activeBtnClass, QUICK_REACTIONS, CallControls, CallControls_default;
|
|
4555
4940
|
var init_CallControls = __esm({
|
|
4556
4941
|
"src/call/CallControls.tsx"() {
|
|
4557
4942
|
init_button();
|
|
4943
|
+
init_popover();
|
|
4558
4944
|
init_utils2();
|
|
4559
4945
|
init_call_store();
|
|
4560
4946
|
circleBtn = "size-13 rounded-full text-white transition-all duration-150 disabled:opacity-40 hover:scale-105 active:scale-95";
|
|
4561
|
-
|
|
4562
|
-
|
|
4947
|
+
idleBtnClass = "bg-white/10 hover:bg-white/20";
|
|
4948
|
+
activeBtnClass = "bg-white text-neutral-900 hover:bg-white/90";
|
|
4949
|
+
QUICK_REACTIONS = ["\u{1F44D}", "\u2764\uFE0F", "\u{1F602}", "\u{1F62E}", "\u{1F44F}", "\u{1F389}"];
|
|
4950
|
+
CallControls = ({
|
|
4951
|
+
sdk,
|
|
4952
|
+
onReaction,
|
|
4953
|
+
isHandRaised,
|
|
4954
|
+
onToggleRaiseHand
|
|
4955
|
+
}) => {
|
|
4956
|
+
const {
|
|
4957
|
+
toggleMic,
|
|
4958
|
+
toggleWebcam,
|
|
4959
|
+
localMicOn,
|
|
4960
|
+
localWebcamOn,
|
|
4961
|
+
localScreenShareOn,
|
|
4962
|
+
toggleScreenShare,
|
|
4963
|
+
leave
|
|
4964
|
+
} = sdk.useMeeting();
|
|
4563
4965
|
const endCall = useCallStore((s) => s.endCall);
|
|
4564
4966
|
const handleLeave = () => {
|
|
4565
4967
|
leave();
|
|
4566
4968
|
endCall();
|
|
4567
4969
|
};
|
|
4568
4970
|
return /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-center gap-5 py-6", children: [
|
|
4569
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-
|
|
4971
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 rounded-full bg-white/10 p-2 backdrop-blur-sm", children: [
|
|
4570
4972
|
/* @__PURE__ */ jsx(
|
|
4571
4973
|
Button,
|
|
4572
4974
|
{
|
|
4573
4975
|
variant: "ghost",
|
|
4574
4976
|
size: "icon",
|
|
4575
|
-
className: cn(
|
|
4576
|
-
circleBtn,
|
|
4577
|
-
localMicOn ? "bg-white/10 hover:bg-white/20" : "bg-white text-neutral-900 hover:bg-white/90"
|
|
4578
|
-
),
|
|
4977
|
+
className: cn(circleBtn, localMicOn ? idleBtnClass : activeBtnClass),
|
|
4579
4978
|
"aria-label": localMicOn ? "Mute microphone" : "Unmute microphone",
|
|
4580
4979
|
onClick: () => toggleMic(),
|
|
4581
4980
|
children: localMicOn ? /* @__PURE__ */ jsx(Mic, { size: 20 }) : /* @__PURE__ */ jsx(MicOff, { size: 20 })
|
|
4582
4981
|
}
|
|
4583
4982
|
),
|
|
4584
|
-
|
|
4983
|
+
/* @__PURE__ */ jsx(
|
|
4585
4984
|
Button,
|
|
4586
4985
|
{
|
|
4587
4986
|
variant: "ghost",
|
|
4588
4987
|
size: "icon",
|
|
4589
|
-
className: cn(
|
|
4590
|
-
circleBtn,
|
|
4591
|
-
localWebcamOn ? "bg-white/10 hover:bg-white/20" : "bg-white text-neutral-900 hover:bg-white/90"
|
|
4592
|
-
),
|
|
4988
|
+
className: cn(circleBtn, localWebcamOn ? idleBtnClass : activeBtnClass),
|
|
4593
4989
|
"aria-label": localWebcamOn ? "Turn camera off" : "Turn camera on",
|
|
4594
4990
|
onClick: () => toggleWebcam(),
|
|
4595
4991
|
children: localWebcamOn ? /* @__PURE__ */ jsx(Video, { size: 20 }) : /* @__PURE__ */ jsx(VideoOff, { size: 20 })
|
|
4596
4992
|
}
|
|
4597
|
-
)
|
|
4993
|
+
),
|
|
4994
|
+
/* @__PURE__ */ jsx(
|
|
4995
|
+
Button,
|
|
4996
|
+
{
|
|
4997
|
+
variant: "ghost",
|
|
4998
|
+
size: "icon",
|
|
4999
|
+
className: cn(circleBtn, localScreenShareOn ? "bg-indigo-500 hover:bg-indigo-400" : idleBtnClass),
|
|
5000
|
+
"aria-label": localScreenShareOn ? "Stop sharing your screen" : "Share your screen",
|
|
5001
|
+
onClick: () => toggleScreenShare(),
|
|
5002
|
+
children: /* @__PURE__ */ jsx(MonitorUp, { size: 20 })
|
|
5003
|
+
}
|
|
5004
|
+
),
|
|
5005
|
+
/* @__PURE__ */ jsx(
|
|
5006
|
+
Button,
|
|
5007
|
+
{
|
|
5008
|
+
variant: "ghost",
|
|
5009
|
+
size: "icon",
|
|
5010
|
+
className: cn(circleBtn, isHandRaised ? "bg-amber-400 text-neutral-900 hover:bg-amber-300" : idleBtnClass),
|
|
5011
|
+
"aria-label": isHandRaised ? "Lower hand" : "Raise hand",
|
|
5012
|
+
onClick: onToggleRaiseHand,
|
|
5013
|
+
children: /* @__PURE__ */ jsx(Hand, { size: 20 })
|
|
5014
|
+
}
|
|
5015
|
+
),
|
|
5016
|
+
/* @__PURE__ */ jsxs(Popover, { children: [
|
|
5017
|
+
/* @__PURE__ */ jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsx(
|
|
5018
|
+
Button,
|
|
5019
|
+
{
|
|
5020
|
+
variant: "ghost",
|
|
5021
|
+
size: "icon",
|
|
5022
|
+
className: cn(circleBtn, idleBtnClass),
|
|
5023
|
+
"aria-label": "Send a reaction",
|
|
5024
|
+
children: /* @__PURE__ */ jsx(Smile, { size: 20 })
|
|
5025
|
+
}
|
|
5026
|
+
) }),
|
|
5027
|
+
/* @__PURE__ */ jsx(
|
|
5028
|
+
PopoverContent,
|
|
5029
|
+
{
|
|
5030
|
+
side: "top",
|
|
5031
|
+
align: "center",
|
|
5032
|
+
className: "w-auto rounded-full border-white/10 bg-neutral-900 p-1.5 shadow-xl",
|
|
5033
|
+
children: /* @__PURE__ */ jsx("div", { className: "flex items-center gap-0.5", children: QUICK_REACTIONS.map((emoji) => /* @__PURE__ */ jsx(
|
|
5034
|
+
"button",
|
|
5035
|
+
{
|
|
5036
|
+
type: "button",
|
|
5037
|
+
className: "flex size-10 items-center justify-center rounded-full text-xl transition-transform hover:scale-125 hover:bg-white/10",
|
|
5038
|
+
"aria-label": `React with ${emoji}`,
|
|
5039
|
+
onClick: () => onReaction(emoji),
|
|
5040
|
+
children: emoji
|
|
5041
|
+
},
|
|
5042
|
+
emoji
|
|
5043
|
+
)) })
|
|
5044
|
+
}
|
|
5045
|
+
)
|
|
5046
|
+
] })
|
|
4598
5047
|
] }),
|
|
4599
5048
|
/* @__PURE__ */ jsx(
|
|
4600
5049
|
Button,
|
|
@@ -4612,18 +5061,123 @@ var init_CallControls = __esm({
|
|
|
4612
5061
|
CallControls_default = CallControls;
|
|
4613
5062
|
}
|
|
4614
5063
|
});
|
|
5064
|
+
var FloatingReactions, ReactionBubble, FloatingReactions_default;
|
|
5065
|
+
var init_FloatingReactions = __esm({
|
|
5066
|
+
"src/call/FloatingReactions.tsx"() {
|
|
5067
|
+
FloatingReactions = ({ reactions }) => {
|
|
5068
|
+
if (!reactions.length) return null;
|
|
5069
|
+
return /* @__PURE__ */ jsxs("div", { className: "pointer-events-none absolute inset-0 z-10 overflow-hidden", children: [
|
|
5070
|
+
/* @__PURE__ */ jsx("style", { children: `
|
|
5071
|
+
@keyframes realtimex-call-reaction-float {
|
|
5072
|
+
0% { transform: translateY(0) scale(0.5); opacity: 0; }
|
|
5073
|
+
15% { transform: translateY(-24px) scale(1.15); opacity: 1; }
|
|
5074
|
+
100% { transform: translateY(-220px) scale(1); opacity: 0; }
|
|
5075
|
+
}
|
|
5076
|
+
` }),
|
|
5077
|
+
reactions.map((reaction) => /* @__PURE__ */ jsx(ReactionBubble, { emoji: reaction.emoji }, reaction.id))
|
|
5078
|
+
] });
|
|
5079
|
+
};
|
|
5080
|
+
ReactionBubble = ({ emoji }) => {
|
|
5081
|
+
const left = useMemo(() => 20 + Math.random() * 60, []);
|
|
5082
|
+
return /* @__PURE__ */ jsx(
|
|
5083
|
+
"span",
|
|
5084
|
+
{
|
|
5085
|
+
className: "absolute bottom-16 text-4xl drop-shadow",
|
|
5086
|
+
style: {
|
|
5087
|
+
left: `${left}%`,
|
|
5088
|
+
animation: "realtimex-call-reaction-float 3s ease-out forwards"
|
|
5089
|
+
},
|
|
5090
|
+
children: emoji
|
|
5091
|
+
}
|
|
5092
|
+
);
|
|
5093
|
+
};
|
|
5094
|
+
FloatingReactions_default = FloatingReactions;
|
|
5095
|
+
}
|
|
5096
|
+
});
|
|
5097
|
+
var FLOATING_REACTION_LIFETIME_MS, useCallReactions;
|
|
5098
|
+
var init_useCallReactions = __esm({
|
|
5099
|
+
"src/call/useCallReactions.ts"() {
|
|
5100
|
+
FLOATING_REACTION_LIFETIME_MS = 3e3;
|
|
5101
|
+
useCallReactions = (sdk) => {
|
|
5102
|
+
const [floatingReactions, setFloatingReactions] = useState([]);
|
|
5103
|
+
const [raisedHands, setRaisedHands] = useState({});
|
|
5104
|
+
const [isHandRaised, setIsHandRaised] = useState(false);
|
|
5105
|
+
const nextId = useRef(0);
|
|
5106
|
+
const addFloatingReaction = useCallback((emoji, fromId) => {
|
|
5107
|
+
const id = `${Date.now()}-${nextId.current++}`;
|
|
5108
|
+
setFloatingReactions((prev) => [...prev, { id, emoji, fromId }]);
|
|
5109
|
+
setTimeout(() => {
|
|
5110
|
+
setFloatingReactions((prev) => prev.filter((r) => r.id !== id));
|
|
5111
|
+
}, FLOATING_REACTION_LIFETIME_MS);
|
|
5112
|
+
}, []);
|
|
5113
|
+
const { send, localParticipant } = sdk.useMeeting({
|
|
5114
|
+
onData: (data) => {
|
|
5115
|
+
try {
|
|
5116
|
+
const raw = typeof data.payload === "string" ? data.payload : new TextDecoder().decode(data.payload);
|
|
5117
|
+
const message = JSON.parse(raw);
|
|
5118
|
+
if (message.type === "reaction") {
|
|
5119
|
+
addFloatingReaction(message.emoji, data.from);
|
|
5120
|
+
} else if (message.type === "raise-hand") {
|
|
5121
|
+
setRaisedHands((prev) => ({ ...prev, [data.from]: message.raised }));
|
|
5122
|
+
}
|
|
5123
|
+
} catch {
|
|
5124
|
+
}
|
|
5125
|
+
}
|
|
5126
|
+
});
|
|
5127
|
+
useEffect(() => {
|
|
5128
|
+
setRaisedHands((prev) => {
|
|
5129
|
+
const next = {};
|
|
5130
|
+
for (const id of Object.keys(prev)) {
|
|
5131
|
+
if (prev[id]) next[id] = true;
|
|
5132
|
+
}
|
|
5133
|
+
return next;
|
|
5134
|
+
});
|
|
5135
|
+
}, [localParticipant?.id]);
|
|
5136
|
+
const sendReaction = useCallback(
|
|
5137
|
+
(emoji) => {
|
|
5138
|
+
if (localParticipant?.id) addFloatingReaction(emoji, localParticipant.id);
|
|
5139
|
+
void send(JSON.stringify({ type: "reaction", emoji }));
|
|
5140
|
+
},
|
|
5141
|
+
[send, localParticipant?.id, addFloatingReaction]
|
|
5142
|
+
);
|
|
5143
|
+
const toggleRaiseHand = useCallback(() => {
|
|
5144
|
+
setIsHandRaised((prev) => {
|
|
5145
|
+
const next = !prev;
|
|
5146
|
+
if (localParticipant?.id) {
|
|
5147
|
+
setRaisedHands((hands) => ({ ...hands, [localParticipant.id]: next }));
|
|
5148
|
+
}
|
|
5149
|
+
void send(JSON.stringify({ type: "raise-hand", raised: next }));
|
|
5150
|
+
return next;
|
|
5151
|
+
});
|
|
5152
|
+
}, [send, localParticipant?.id]);
|
|
5153
|
+
return { floatingReactions, raisedHands, isHandRaised, sendReaction, toggleRaiseHand };
|
|
5154
|
+
};
|
|
5155
|
+
}
|
|
5156
|
+
});
|
|
4615
5157
|
var MeetingView, MeetingView_default;
|
|
4616
5158
|
var init_MeetingView = __esm({
|
|
4617
5159
|
"src/call/MeetingView.tsx"() {
|
|
4618
5160
|
init_ParticipantView();
|
|
4619
5161
|
init_AudioCallView();
|
|
5162
|
+
init_ScreenShareView();
|
|
4620
5163
|
init_CallControls();
|
|
5164
|
+
init_FloatingReactions();
|
|
5165
|
+
init_useCallReactions();
|
|
4621
5166
|
init_call_store();
|
|
5167
|
+
init_utils2();
|
|
4622
5168
|
MeetingView = ({ sdk, mode, peerName, peerAvatar }) => {
|
|
4623
5169
|
const endCall = useCallStore((s) => s.endCall);
|
|
4624
5170
|
const [hasJoined, setHasJoined] = useState(false);
|
|
4625
5171
|
const hasJoinedRef = useRef(false);
|
|
4626
|
-
const
|
|
5172
|
+
const [groupView, setGroupView] = useState("grid");
|
|
5173
|
+
const {
|
|
5174
|
+
join,
|
|
5175
|
+
participants,
|
|
5176
|
+
localParticipant,
|
|
5177
|
+
localWebcamOn,
|
|
5178
|
+
presenterId,
|
|
5179
|
+
activeSpeakerId
|
|
5180
|
+
} = sdk.useMeeting({
|
|
4627
5181
|
onMeetingJoined: () => {
|
|
4628
5182
|
hasJoinedRef.current = true;
|
|
4629
5183
|
setHasJoined(true);
|
|
@@ -4633,6 +5187,7 @@ var init_MeetingView = __esm({
|
|
|
4633
5187
|
endCall();
|
|
4634
5188
|
}
|
|
4635
5189
|
});
|
|
5190
|
+
const { floatingReactions, raisedHands, isHandRaised, sendReaction, toggleRaiseHand } = useCallReactions(sdk);
|
|
4636
5191
|
useEffect(() => {
|
|
4637
5192
|
join();
|
|
4638
5193
|
}, []);
|
|
@@ -4645,22 +5200,133 @@ var init_MeetingView = __esm({
|
|
|
4645
5200
|
const remoteIds = Array.from(participants.keys()).filter(
|
|
4646
5201
|
(id) => id !== localParticipant?.id
|
|
4647
5202
|
);
|
|
4648
|
-
|
|
4649
|
-
|
|
5203
|
+
const isGroup = remoteIds.length > 1;
|
|
5204
|
+
const controls = /* @__PURE__ */ jsx(
|
|
5205
|
+
CallControls_default,
|
|
5206
|
+
{
|
|
5207
|
+
sdk,
|
|
5208
|
+
onReaction: sendReaction,
|
|
5209
|
+
isHandRaised,
|
|
5210
|
+
onToggleRaiseHand: toggleRaiseHand
|
|
5211
|
+
}
|
|
5212
|
+
);
|
|
5213
|
+
if (presenterId) {
|
|
5214
|
+
return /* @__PURE__ */ jsxs("div", { className: "relative flex flex-1 flex-col", children: [
|
|
5215
|
+
/* @__PURE__ */ jsx(FloatingReactions_default, { reactions: floatingReactions }),
|
|
5216
|
+
/* @__PURE__ */ jsx(
|
|
5217
|
+
ScreenShareView_default,
|
|
5218
|
+
{
|
|
5219
|
+
sdk,
|
|
5220
|
+
presenterId,
|
|
5221
|
+
isLocal: presenterId === localParticipant?.id,
|
|
5222
|
+
className: "flex flex-1 flex-col overflow-hidden p-4 pb-2"
|
|
5223
|
+
}
|
|
5224
|
+
),
|
|
5225
|
+
/* @__PURE__ */ jsxs("div", { className: "flex gap-2 overflow-x-auto px-4 pb-2", children: [
|
|
5226
|
+
localParticipant?.id && /* @__PURE__ */ jsx(
|
|
5227
|
+
ParticipantView_default,
|
|
5228
|
+
{
|
|
5229
|
+
sdk,
|
|
5230
|
+
participantId: localParticipant.id,
|
|
5231
|
+
isLocal: true,
|
|
5232
|
+
isHandRaised: !!raisedHands[localParticipant.id],
|
|
5233
|
+
className: "aspect-video h-20 min-h-0 w-32 shrink-0"
|
|
5234
|
+
}
|
|
5235
|
+
),
|
|
5236
|
+
remoteIds.map((id) => /* @__PURE__ */ jsx(
|
|
5237
|
+
ParticipantView_default,
|
|
5238
|
+
{
|
|
5239
|
+
sdk,
|
|
5240
|
+
participantId: id,
|
|
5241
|
+
fallbackName: peerName,
|
|
5242
|
+
fallbackImage: peerAvatar,
|
|
5243
|
+
isHandRaised: !!raisedHands[id],
|
|
5244
|
+
className: "aspect-video h-20 min-h-0 w-32 shrink-0"
|
|
5245
|
+
},
|
|
5246
|
+
id
|
|
5247
|
+
))
|
|
5248
|
+
] }),
|
|
5249
|
+
controls
|
|
5250
|
+
] });
|
|
5251
|
+
}
|
|
5252
|
+
const showVideoGrid = mode === "video" || localWebcamOn;
|
|
5253
|
+
if (!showVideoGrid) {
|
|
5254
|
+
return /* @__PURE__ */ jsxs("div", { className: "relative flex flex-1 flex-col", children: [
|
|
5255
|
+
/* @__PURE__ */ jsx(FloatingReactions_default, { reactions: floatingReactions }),
|
|
4650
5256
|
/* @__PURE__ */ jsx(
|
|
4651
5257
|
AudioCallView_default,
|
|
4652
5258
|
{
|
|
4653
5259
|
sdk,
|
|
4654
5260
|
remoteParticipantId: remoteIds[0],
|
|
4655
5261
|
peerName,
|
|
4656
|
-
peerAvatar
|
|
5262
|
+
peerAvatar,
|
|
5263
|
+
isRemoteHandRaised: !!(remoteIds[0] && raisedHands[remoteIds[0]])
|
|
4657
5264
|
}
|
|
4658
5265
|
),
|
|
4659
|
-
|
|
5266
|
+
controls
|
|
4660
5267
|
] });
|
|
4661
5268
|
}
|
|
4662
|
-
|
|
4663
|
-
|
|
5269
|
+
const focusedId = groupView === "speaker" ? activeSpeakerId && activeSpeakerId !== localParticipant?.id ? activeSpeakerId : remoteIds[0] || localParticipant?.id : null;
|
|
5270
|
+
const stripIds = focusedId ? [localParticipant?.id, ...remoteIds].filter(
|
|
5271
|
+
(id) => !!id && id !== focusedId
|
|
5272
|
+
) : [];
|
|
5273
|
+
return /* @__PURE__ */ jsxs("div", { className: "relative flex flex-1 flex-col", children: [
|
|
5274
|
+
/* @__PURE__ */ jsx(FloatingReactions_default, { reactions: floatingReactions }),
|
|
5275
|
+
isGroup && /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-end gap-1 px-4 pt-2", children: [
|
|
5276
|
+
/* @__PURE__ */ jsx(
|
|
5277
|
+
"button",
|
|
5278
|
+
{
|
|
5279
|
+
type: "button",
|
|
5280
|
+
"aria-label": "Grid view",
|
|
5281
|
+
onClick: () => setGroupView("grid"),
|
|
5282
|
+
className: cn(
|
|
5283
|
+
"flex size-8 items-center justify-center rounded-lg transition-colors",
|
|
5284
|
+
groupView === "grid" ? "bg-white/15 text-white" : "text-white/50 hover:text-white/80"
|
|
5285
|
+
),
|
|
5286
|
+
children: /* @__PURE__ */ jsx(LayoutGrid, { size: 16 })
|
|
5287
|
+
}
|
|
5288
|
+
),
|
|
5289
|
+
/* @__PURE__ */ jsx(
|
|
5290
|
+
"button",
|
|
5291
|
+
{
|
|
5292
|
+
type: "button",
|
|
5293
|
+
"aria-label": "Speaker view",
|
|
5294
|
+
onClick: () => setGroupView("speaker"),
|
|
5295
|
+
className: cn(
|
|
5296
|
+
"flex size-8 items-center justify-center rounded-lg transition-colors",
|
|
5297
|
+
groupView === "speaker" ? "bg-white/15 text-white" : "text-white/50 hover:text-white/80"
|
|
5298
|
+
),
|
|
5299
|
+
children: /* @__PURE__ */ jsx(Rows3, { size: 16 })
|
|
5300
|
+
}
|
|
5301
|
+
)
|
|
5302
|
+
] }),
|
|
5303
|
+
focusedId ? /* @__PURE__ */ jsxs("div", { className: "flex flex-1 flex-col gap-3 overflow-y-auto p-4", children: [
|
|
5304
|
+
/* @__PURE__ */ jsx(
|
|
5305
|
+
ParticipantView_default,
|
|
5306
|
+
{
|
|
5307
|
+
sdk,
|
|
5308
|
+
participantId: focusedId,
|
|
5309
|
+
isLocal: focusedId === localParticipant?.id,
|
|
5310
|
+
fallbackName: peerName,
|
|
5311
|
+
fallbackImage: peerAvatar,
|
|
5312
|
+
isHandRaised: !!raisedHands[focusedId],
|
|
5313
|
+
className: "min-h-[240px] flex-1"
|
|
5314
|
+
}
|
|
5315
|
+
),
|
|
5316
|
+
stripIds.length > 0 && /* @__PURE__ */ jsx("div", { className: "flex gap-2 overflow-x-auto", children: stripIds.map((id) => /* @__PURE__ */ jsx(
|
|
5317
|
+
ParticipantView_default,
|
|
5318
|
+
{
|
|
5319
|
+
sdk,
|
|
5320
|
+
participantId: id,
|
|
5321
|
+
isLocal: id === localParticipant?.id,
|
|
5322
|
+
fallbackName: peerName,
|
|
5323
|
+
fallbackImage: peerAvatar,
|
|
5324
|
+
isHandRaised: !!raisedHands[id],
|
|
5325
|
+
className: "aspect-video h-20 min-h-0 w-32 shrink-0"
|
|
5326
|
+
},
|
|
5327
|
+
id
|
|
5328
|
+
)) })
|
|
5329
|
+
] }) : /* @__PURE__ */ jsxs(
|
|
4664
5330
|
"div",
|
|
4665
5331
|
{
|
|
4666
5332
|
className: "grid flex-1 auto-rows-fr gap-3 overflow-y-auto p-4",
|
|
@@ -4671,21 +5337,30 @@ var init_MeetingView = __esm({
|
|
|
4671
5337
|
)}, minmax(0, 1fr))`
|
|
4672
5338
|
},
|
|
4673
5339
|
children: [
|
|
4674
|
-
localParticipant?.id && /* @__PURE__ */ jsx(
|
|
5340
|
+
localParticipant?.id && /* @__PURE__ */ jsx(
|
|
5341
|
+
ParticipantView_default,
|
|
5342
|
+
{
|
|
5343
|
+
sdk,
|
|
5344
|
+
participantId: localParticipant.id,
|
|
5345
|
+
isLocal: true,
|
|
5346
|
+
isHandRaised: !!raisedHands[localParticipant.id]
|
|
5347
|
+
}
|
|
5348
|
+
),
|
|
4675
5349
|
remoteIds.map((id) => /* @__PURE__ */ jsx(
|
|
4676
5350
|
ParticipantView_default,
|
|
4677
5351
|
{
|
|
4678
5352
|
sdk,
|
|
4679
5353
|
participantId: id,
|
|
4680
5354
|
fallbackName: peerName,
|
|
4681
|
-
fallbackImage: peerAvatar
|
|
5355
|
+
fallbackImage: peerAvatar,
|
|
5356
|
+
isHandRaised: !!raisedHands[id]
|
|
4682
5357
|
},
|
|
4683
5358
|
id
|
|
4684
5359
|
))
|
|
4685
5360
|
]
|
|
4686
5361
|
}
|
|
4687
5362
|
),
|
|
4688
|
-
|
|
5363
|
+
controls
|
|
4689
5364
|
] });
|
|
4690
5365
|
};
|
|
4691
5366
|
MeetingView_default = MeetingView;
|
|
@@ -5744,7 +6419,7 @@ var useCallDuration = (startedAt) => {
|
|
|
5744
6419
|
const pad = (n) => String(n).padStart(2, "0");
|
|
5745
6420
|
return hours > 0 ? `${hours}:${pad(minutes)}:${pad(seconds)}` : `${pad(minutes)}:${pad(seconds)}`;
|
|
5746
6421
|
};
|
|
5747
|
-
var CallSession2 =
|
|
6422
|
+
var CallSession2 = React11__default.lazy(() => Promise.resolve().then(() => (init_CallSession(), CallSession_exports)));
|
|
5748
6423
|
var CallOverlay = () => {
|
|
5749
6424
|
const uiStatus = useCallStore((s) => s.uiStatus);
|
|
5750
6425
|
const mode = useCallStore((s) => s.mode);
|
|
@@ -5752,8 +6427,9 @@ var CallOverlay = () => {
|
|
|
5752
6427
|
const meetingId = useCallStore((s) => s.meetingId);
|
|
5753
6428
|
const token = useCallStore((s) => s.token);
|
|
5754
6429
|
const error = useCallStore((s) => s.error);
|
|
5755
|
-
const
|
|
6430
|
+
const peer = useCallStore((s) => s.peer);
|
|
5756
6431
|
const caller = useCallStore((s) => s.caller);
|
|
6432
|
+
const isGroupCall = useCallStore((s) => s.isGroupCall);
|
|
5757
6433
|
const callStartedAt = useCallStore((s) => s.callStartedAt);
|
|
5758
6434
|
const cancelCall = useCallStore((s) => s.cancelCall);
|
|
5759
6435
|
const reset = useCallStore((s) => s.reset);
|
|
@@ -5770,8 +6446,8 @@ var CallOverlay = () => {
|
|
|
5770
6446
|
const isOpen = uiStatus !== "idle" && uiStatus !== "incoming";
|
|
5771
6447
|
if (!isOpen || typeof document === "undefined") return null;
|
|
5772
6448
|
const displayError = error || sessionError;
|
|
5773
|
-
const peerName =
|
|
5774
|
-
const peerAvatar =
|
|
6449
|
+
const peerName = peer?.name || caller?.name || "";
|
|
6450
|
+
const peerAvatar = peer?.avatar || caller?.profileImage || void 0;
|
|
5775
6451
|
const isVideo = mode === "video";
|
|
5776
6452
|
return createPortal(
|
|
5777
6453
|
/* @__PURE__ */ jsx(
|
|
@@ -5793,12 +6469,13 @@ var CallOverlay = () => {
|
|
|
5793
6469
|
] })
|
|
5794
6470
|
] }) : uiStatus === "in-call" && meetingId && token && mode ? /* @__PURE__ */ jsxs("div", { className: "flex flex-1 flex-col", children: [
|
|
5795
6471
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-center gap-2 px-4 py-3 text-sm text-white/70", children: [
|
|
6472
|
+
isGroupCall && /* @__PURE__ */ jsx(Users, { size: 14, className: "text-white/50" }),
|
|
5796
6473
|
peerName && /* @__PURE__ */ jsx("span", { className: "font-medium text-white/90", children: peerName }),
|
|
5797
6474
|
/* @__PURE__ */ jsx("span", { "aria-hidden": true, className: "text-white/30", children: "\xB7" }),
|
|
5798
6475
|
/* @__PURE__ */ jsx("span", { className: "tabular-nums", children: duration })
|
|
5799
6476
|
] }),
|
|
5800
6477
|
/* @__PURE__ */ jsx(
|
|
5801
|
-
|
|
6478
|
+
React11__default.Suspense,
|
|
5802
6479
|
{
|
|
5803
6480
|
fallback: /* @__PURE__ */ jsxs("div", { className: "flex flex-1 flex-col items-center justify-center gap-3 text-white/80", children: [
|
|
5804
6481
|
/* @__PURE__ */ jsx(Loader2, { className: "size-8 animate-spin" }),
|
|
@@ -5835,7 +6512,10 @@ var CallOverlay = () => {
|
|
|
5835
6512
|
] }) : /* @__PURE__ */ jsx("div", { className: "relative flex size-28 items-center justify-center rounded-full bg-white/10", children: isVideo ? /* @__PURE__ */ jsx(Video, { size: 36 }) : /* @__PURE__ */ jsx(Phone, { size: 36 }) })
|
|
5836
6513
|
] }),
|
|
5837
6514
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
5838
|
-
peerName && /* @__PURE__ */
|
|
6515
|
+
peerName && /* @__PURE__ */ jsxs("p", { className: "flex items-center justify-center gap-1.5 text-xl font-semibold tracking-tight", children: [
|
|
6516
|
+
isGroupCall && /* @__PURE__ */ jsx(Users, { size: 16, className: "text-white/50" }),
|
|
6517
|
+
peerName
|
|
6518
|
+
] }),
|
|
5839
6519
|
/* @__PURE__ */ jsxs("p", { className: "mt-1 text-sm text-white/60", children: [
|
|
5840
6520
|
uiStatus === "creating" && "Starting call\u2026",
|
|
5841
6521
|
uiStatus === "ringing-out" && `${isVideo ? "Video calling" : "Calling"}\u2026 \xB7 ${secondsLeft}s`,
|
|
@@ -6411,6 +7091,8 @@ var IncomingCallDialog = ({ ringtoneUrl }) => {
|
|
|
6411
7091
|
const uiStatus = useCallStore((s) => s.uiStatus);
|
|
6412
7092
|
const callId = useCallStore((s) => s.callId);
|
|
6413
7093
|
const caller = useCallStore((s) => s.caller);
|
|
7094
|
+
const peer = useCallStore((s) => s.peer);
|
|
7095
|
+
const isGroupCall = useCallStore((s) => s.isGroupCall);
|
|
6414
7096
|
const mode = useCallStore((s) => s.mode);
|
|
6415
7097
|
const acceptCall = useCallStore((s) => s.acceptCall);
|
|
6416
7098
|
const rejectCall = useCallStore((s) => s.rejectCall);
|
|
@@ -6428,7 +7110,9 @@ var IncomingCallDialog = ({ ringtoneUrl }) => {
|
|
|
6428
7110
|
}
|
|
6429
7111
|
}, [isOpen, ringtoneUrl]);
|
|
6430
7112
|
if (!isOpen) return null;
|
|
6431
|
-
const
|
|
7113
|
+
const callerName = caller?.name || "Unknown caller";
|
|
7114
|
+
const name = isGroupCall ? peer?.name || callerName : callerName;
|
|
7115
|
+
const avatar = isGroupCall ? peer?.avatar || void 0 : caller?.profileImage;
|
|
6432
7116
|
const isVideo = mode === "video";
|
|
6433
7117
|
return /* @__PURE__ */ jsxs(Dialog, { open: isOpen, onOpenChange: () => void 0, children: [
|
|
6434
7118
|
ringtoneUrl && /* @__PURE__ */ jsx("audio", { ref: audioRef, src: ringtoneUrl, loop: true }),
|
|
@@ -6450,7 +7134,7 @@ var IncomingCallDialog = ({ ringtoneUrl }) => {
|
|
|
6450
7134
|
}
|
|
6451
7135
|
),
|
|
6452
7136
|
/* @__PURE__ */ jsxs(Avatar, { className: "relative size-24 ring-4 ring-white/10", children: [
|
|
6453
|
-
/* @__PURE__ */ jsx(AvatarImage, { src:
|
|
7137
|
+
/* @__PURE__ */ jsx(AvatarImage, { src: avatar, alt: name }),
|
|
6454
7138
|
/* @__PURE__ */ jsx(AvatarFallback, { className: "bg-neutral-700 text-2xl font-medium text-white", children: name.charAt(0).toUpperCase() })
|
|
6455
7139
|
] }),
|
|
6456
7140
|
/* @__PURE__ */ jsx(
|
|
@@ -6466,13 +7150,7 @@ var IncomingCallDialog = ({ ringtoneUrl }) => {
|
|
|
6466
7150
|
] }),
|
|
6467
7151
|
/* @__PURE__ */ jsxs("div", { className: "text-center", children: [
|
|
6468
7152
|
/* @__PURE__ */ jsx("p", { className: "text-xl font-semibold tracking-tight", children: name }),
|
|
6469
|
-
/* @__PURE__ */
|
|
6470
|
-
"Incoming ",
|
|
6471
|
-
isVideo ? "video" : "voice",
|
|
6472
|
-
" call \xB7 ",
|
|
6473
|
-
secondsLeft,
|
|
6474
|
-
"s"
|
|
6475
|
-
] })
|
|
7153
|
+
/* @__PURE__ */ jsx("p", { className: "mt-1 text-sm text-white/50", children: isGroupCall ? `${callerName} is calling \xB7 ${isVideo ? "video" : "voice"} call \xB7 ${secondsLeft}s` : `Incoming ${isVideo ? "video" : "voice"} call \xB7 ${secondsLeft}s` })
|
|
6476
7154
|
] }),
|
|
6477
7155
|
/* @__PURE__ */ jsxs("div", { className: "mt-1 flex items-center justify-center gap-10", children: [
|
|
6478
7156
|
/* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center gap-2", children: [
|
|
@@ -6559,7 +7237,7 @@ var Chat = ({
|
|
|
6559
7237
|
const status = useStore((s) => s.status);
|
|
6560
7238
|
const error = useStore((s) => s.error);
|
|
6561
7239
|
useChatSync();
|
|
6562
|
-
|
|
7240
|
+
React11__default.useEffect(() => {
|
|
6563
7241
|
const clientId = client?.id;
|
|
6564
7242
|
const initializeChat = async () => {
|
|
6565
7243
|
if (!clientId || !loggedUserDetails) return;
|
|
@@ -6649,28 +7327,28 @@ var Chat = ({
|
|
|
6649
7327
|
queryParamApis: queryParamApis ?? ["get"],
|
|
6650
7328
|
queryParamsByApi: queryParamsByApi ?? {}
|
|
6651
7329
|
});
|
|
6652
|
-
|
|
7330
|
+
React11__default.useEffect(() => {
|
|
6653
7331
|
setChatQueryParams(queryParams);
|
|
6654
7332
|
setChatQueryParamApis(queryParamApis);
|
|
6655
7333
|
setChatQueryParamsByApi(queryParamsByApi);
|
|
6656
7334
|
}, [queryConfigKey]);
|
|
6657
|
-
|
|
7335
|
+
React11__default.useEffect(() => {
|
|
6658
7336
|
if (lastDM && onMessageReceived) {
|
|
6659
7337
|
onMessageReceived(lastDM);
|
|
6660
7338
|
}
|
|
6661
7339
|
}, [lastDM, onMessageReceived]);
|
|
6662
|
-
|
|
7340
|
+
React11__default.useEffect(() => {
|
|
6663
7341
|
if (status === "connected" && onSocketConnected) {
|
|
6664
7342
|
const socketId = useStore.getState().socketId;
|
|
6665
7343
|
if (socketId) onSocketConnected(socketId);
|
|
6666
7344
|
}
|
|
6667
7345
|
}, [status, onSocketConnected]);
|
|
6668
|
-
|
|
7346
|
+
React11__default.useEffect(() => {
|
|
6669
7347
|
if (status === "error" && error && onSocketError) {
|
|
6670
7348
|
onSocketError(error);
|
|
6671
7349
|
}
|
|
6672
7350
|
}, [status, error, onSocketError]);
|
|
6673
|
-
const contextValue =
|
|
7351
|
+
const contextValue = React11__default.useMemo(
|
|
6674
7352
|
() => ({
|
|
6675
7353
|
onSendMessage,
|
|
6676
7354
|
client,
|
|
@@ -6690,7 +7368,7 @@ var Chat = ({
|
|
|
6690
7368
|
messagesData
|
|
6691
7369
|
]
|
|
6692
7370
|
);
|
|
6693
|
-
const customization =
|
|
7371
|
+
const customization = React11__default.useMemo(
|
|
6694
7372
|
() => ({
|
|
6695
7373
|
components: uiConfig.components,
|
|
6696
7374
|
classNames: uiConfig.classNames,
|
|
@@ -6710,265 +7388,7 @@ init_utils2();
|
|
|
6710
7388
|
// src/hooks/useChatController.ts
|
|
6711
7389
|
init_useStore();
|
|
6712
7390
|
init_store_helpers();
|
|
6713
|
-
|
|
6714
|
-
// src/notifications/open-conversation.ts
|
|
6715
|
-
init_store_helpers();
|
|
6716
|
-
var OPEN_CONVERSATION_EVENT = "realtimex:open-conversation";
|
|
6717
|
-
var NOTIFICATION_CLICK_SW_TYPE = "realtimex:notification-click";
|
|
6718
|
-
var PUSH_BROADCAST_CHANNEL = "realtimex-push";
|
|
6719
|
-
var PUSH_DATA_CACHE_NAME = "realtimex-push-data-v1";
|
|
6720
|
-
var PENDING_STORAGE_KEY = "realtimex:pending-open-conversation";
|
|
6721
|
-
var PENDING_OPEN_CACHE_PATH = "pending-open";
|
|
6722
|
-
var resolveConversationIdFromPushData = (data) => {
|
|
6723
|
-
if (!data || typeof data !== "object") return "";
|
|
6724
|
-
let fcmMsg = data.FCM_MSG;
|
|
6725
|
-
if (typeof fcmMsg === "string") {
|
|
6726
|
-
try {
|
|
6727
|
-
fcmMsg = JSON.parse(fcmMsg);
|
|
6728
|
-
} catch {
|
|
6729
|
-
fcmMsg = void 0;
|
|
6730
|
-
}
|
|
6731
|
-
}
|
|
6732
|
-
const nested = fcmMsg && typeof fcmMsg === "object" ? fcmMsg.data : void 0;
|
|
6733
|
-
const source = { ...nested || {}, ...data };
|
|
6734
|
-
return String(
|
|
6735
|
-
source.conversationId || source.conversation_id || source.conversationID || ""
|
|
6736
|
-
).trim();
|
|
6737
|
-
};
|
|
6738
|
-
var normalizePushMeta = (meta) => {
|
|
6739
|
-
if (!meta || typeof meta !== "object") return {};
|
|
6740
|
-
return {
|
|
6741
|
-
senderId: String(
|
|
6742
|
-
meta.senderId || meta.sender_id || ""
|
|
6743
|
-
).trim() || void 0,
|
|
6744
|
-
kind: String(meta.kind || "").trim() || void 0,
|
|
6745
|
-
messageId: String(
|
|
6746
|
-
meta.messageId || meta.message_id || ""
|
|
6747
|
-
).trim() || void 0,
|
|
6748
|
-
title: String(meta.title || "").trim() || void 0
|
|
6749
|
-
};
|
|
6750
|
-
};
|
|
6751
|
-
var parsePendingRaw = (raw) => {
|
|
6752
|
-
if (!raw) return null;
|
|
6753
|
-
try {
|
|
6754
|
-
const parsed = JSON.parse(raw);
|
|
6755
|
-
if (parsed?.conversationId) {
|
|
6756
|
-
return {
|
|
6757
|
-
conversationId: String(parsed.conversationId).trim(),
|
|
6758
|
-
...normalizePushMeta(parsed)
|
|
6759
|
-
};
|
|
6760
|
-
}
|
|
6761
|
-
} catch {
|
|
6762
|
-
}
|
|
6763
|
-
const id = String(raw).trim();
|
|
6764
|
-
return id ? { conversationId: id } : null;
|
|
6765
|
-
};
|
|
6766
|
-
var peekPendingOpenConversation = () => peekPendingOpenRequest()?.conversationId ?? null;
|
|
6767
|
-
var peekPendingOpenRequest = () => {
|
|
6768
|
-
try {
|
|
6769
|
-
return parsePendingRaw(sessionStorage.getItem(PENDING_STORAGE_KEY));
|
|
6770
|
-
} catch {
|
|
6771
|
-
return null;
|
|
6772
|
-
}
|
|
6773
|
-
};
|
|
6774
|
-
var consumePendingOpenConversation = () => consumePendingOpenRequest()?.conversationId ?? null;
|
|
6775
|
-
var consumePendingOpenRequest = () => {
|
|
6776
|
-
const request = peekPendingOpenRequest();
|
|
6777
|
-
try {
|
|
6778
|
-
sessionStorage.removeItem(PENDING_STORAGE_KEY);
|
|
6779
|
-
} catch {
|
|
6780
|
-
}
|
|
6781
|
-
return request;
|
|
6782
|
-
};
|
|
6783
|
-
var requestOpenConversation = (conversationId, meta) => {
|
|
6784
|
-
const id = String(conversationId || "").trim();
|
|
6785
|
-
if (!id) return;
|
|
6786
|
-
const request = {
|
|
6787
|
-
conversationId: id,
|
|
6788
|
-
...normalizePushMeta(meta)
|
|
6789
|
-
};
|
|
6790
|
-
console.log("[realtimex-push] requestOpenConversation:", request);
|
|
6791
|
-
try {
|
|
6792
|
-
sessionStorage.setItem(PENDING_STORAGE_KEY, JSON.stringify(request));
|
|
6793
|
-
} catch {
|
|
6794
|
-
}
|
|
6795
|
-
try {
|
|
6796
|
-
window.dispatchEvent(
|
|
6797
|
-
new CustomEvent(OPEN_CONVERSATION_EVENT, { detail: request })
|
|
6798
|
-
);
|
|
6799
|
-
} catch {
|
|
6800
|
-
}
|
|
6801
|
-
};
|
|
6802
|
-
var buildChannelFromPushRequest = (request, loggedUserId, allUsers) => {
|
|
6803
|
-
const conversationId = request.conversationId;
|
|
6804
|
-
const isGroup = request.kind === "group";
|
|
6805
|
-
const senderId = String(request.senderId || "").trim();
|
|
6806
|
-
if (isGroup) {
|
|
6807
|
-
const rawName2 = request.title || "Group Chat";
|
|
6808
|
-
return {
|
|
6809
|
-
id: conversationId,
|
|
6810
|
-
conversationId,
|
|
6811
|
-
name: rawName2.charAt(0).toUpperCase() + rawName2.slice(1),
|
|
6812
|
-
isGroup: true
|
|
6813
|
-
};
|
|
6814
|
-
}
|
|
6815
|
-
const senderUser = senderId ? (allUsers || []).find((user) => String(user._id) === senderId) : null;
|
|
6816
|
-
const rawName = senderUser?.name || request.title || "New message";
|
|
6817
|
-
return {
|
|
6818
|
-
id: senderId || conversationId,
|
|
6819
|
-
conversationId,
|
|
6820
|
-
name: rawName.charAt(0).toUpperCase() + rawName.slice(1),
|
|
6821
|
-
image: senderUser?.image || void 0,
|
|
6822
|
-
email: senderUser?.email,
|
|
6823
|
-
isGroup: false
|
|
6824
|
-
};
|
|
6825
|
-
};
|
|
6826
|
-
var mapConversationToChannel = (conversation, loggedUserId, allUsers) => {
|
|
6827
|
-
const conversationId = String(conversation?._id || conversation?.id || "");
|
|
6828
|
-
const isGroup = isGroupConversation(conversation);
|
|
6829
|
-
if (isGroup) {
|
|
6830
|
-
const rawName2 = conversation.groupName || conversation.name || "Group Chat";
|
|
6831
|
-
return {
|
|
6832
|
-
id: conversationId,
|
|
6833
|
-
conversationId,
|
|
6834
|
-
name: rawName2.charAt(0).toUpperCase() + rawName2.slice(1),
|
|
6835
|
-
image: resolveGroupImage(conversation),
|
|
6836
|
-
isGroup: true,
|
|
6837
|
-
isLeft: !!conversation.isLeft,
|
|
6838
|
-
isBlocked: !!conversation.isBlocked,
|
|
6839
|
-
pinnedCount: conversation.pinnedCount,
|
|
6840
|
-
starredCount: conversation.starredCount
|
|
6841
|
-
};
|
|
6842
|
-
}
|
|
6843
|
-
const { id: otherId, user } = resolveOtherParticipant(
|
|
6844
|
-
conversation,
|
|
6845
|
-
loggedUserId,
|
|
6846
|
-
allUsers
|
|
6847
|
-
);
|
|
6848
|
-
const rawName = user?.name || "Unknown User";
|
|
6849
|
-
return {
|
|
6850
|
-
id: otherId || conversationId,
|
|
6851
|
-
conversationId,
|
|
6852
|
-
name: rawName.charAt(0).toUpperCase() + rawName.slice(1),
|
|
6853
|
-
image: user?.image || void 0,
|
|
6854
|
-
email: user?.email,
|
|
6855
|
-
isGroup: false,
|
|
6856
|
-
isLeft: !!conversation.isLeft,
|
|
6857
|
-
isBlocked: !!conversation.isBlocked,
|
|
6858
|
-
pinnedCount: conversation.pinnedCount,
|
|
6859
|
-
starredCount: conversation.starredCount
|
|
6860
|
-
};
|
|
6861
|
-
};
|
|
6862
|
-
var stripConversationIdFromUrl = () => {
|
|
6863
|
-
if (typeof window === "undefined") return;
|
|
6864
|
-
try {
|
|
6865
|
-
const url = new URL(window.location.href);
|
|
6866
|
-
if (!url.searchParams.has("conversationId")) return;
|
|
6867
|
-
url.searchParams.delete("conversationId");
|
|
6868
|
-
const next = `${url.pathname}${url.search}${url.hash}`;
|
|
6869
|
-
window.history.replaceState(window.history.state, "", next);
|
|
6870
|
-
} catch {
|
|
6871
|
-
}
|
|
6872
|
-
};
|
|
6873
|
-
var cacheRequestForPath = (path) => new Request(new URL(path, window.location.origin).toString());
|
|
6874
|
-
var consumePendingOpenFromPushCache = async () => {
|
|
6875
|
-
if (typeof caches === "undefined") return null;
|
|
6876
|
-
try {
|
|
6877
|
-
const cache = await caches.open(PUSH_DATA_CACHE_NAME);
|
|
6878
|
-
const req = cacheRequestForPath(PENDING_OPEN_CACHE_PATH);
|
|
6879
|
-
const res = await cache.match(req);
|
|
6880
|
-
if (!res) return null;
|
|
6881
|
-
await cache.delete(req);
|
|
6882
|
-
const data = await res.json();
|
|
6883
|
-
const conversationId = resolveConversationIdFromPushData(data);
|
|
6884
|
-
if (!conversationId) return null;
|
|
6885
|
-
return {
|
|
6886
|
-
conversationId,
|
|
6887
|
-
...normalizePushMeta(data)
|
|
6888
|
-
};
|
|
6889
|
-
} catch {
|
|
6890
|
-
return null;
|
|
6891
|
-
}
|
|
6892
|
-
};
|
|
6893
|
-
var readConversationIdFromLocation = () => {
|
|
6894
|
-
try {
|
|
6895
|
-
return new URLSearchParams(window.location.search).get("conversationId") || "";
|
|
6896
|
-
} catch {
|
|
6897
|
-
return "";
|
|
6898
|
-
}
|
|
6899
|
-
};
|
|
6900
|
-
var consumedUrlConversationIds = /* @__PURE__ */ new Set();
|
|
6901
|
-
var lastHandledOpenId = "";
|
|
6902
|
-
var lastHandledOpenAt = 0;
|
|
6903
|
-
var shouldHandleOpen = (conversationId) => {
|
|
6904
|
-
const id = String(conversationId || "").trim();
|
|
6905
|
-
if (!id) return false;
|
|
6906
|
-
const now = Date.now();
|
|
6907
|
-
if (lastHandledOpenId === id && now - lastHandledOpenAt < 2500) {
|
|
6908
|
-
return false;
|
|
6909
|
-
}
|
|
6910
|
-
lastHandledOpenId = id;
|
|
6911
|
-
lastHandledOpenAt = now;
|
|
6912
|
-
return true;
|
|
6913
|
-
};
|
|
6914
|
-
var handleNotificationOpen = (conversationId, pushData, options) => {
|
|
6915
|
-
const id = String(conversationId || "").trim();
|
|
6916
|
-
if (!id) {
|
|
6917
|
-
console.warn("[realtimex-push] notification open ignored \u2014 no conversationId", pushData);
|
|
6918
|
-
return;
|
|
6919
|
-
}
|
|
6920
|
-
if (!shouldHandleOpen(id)) {
|
|
6921
|
-
stripConversationIdFromUrl();
|
|
6922
|
-
return;
|
|
6923
|
-
}
|
|
6924
|
-
requestOpenConversation(id, pushData);
|
|
6925
|
-
const chatPath = options.chatPath || "/chat";
|
|
6926
|
-
stripConversationIdFromUrl();
|
|
6927
|
-
options.onNavigate?.(id, chatPath);
|
|
6928
|
-
};
|
|
6929
|
-
var installNotificationClickBridge = (options = {}) => {
|
|
6930
|
-
if (typeof window === "undefined") return () => void 0;
|
|
6931
|
-
const onSwMessage = (event) => {
|
|
6932
|
-
const data = event.data;
|
|
6933
|
-
console.log("[realtimex-push] SW \u2192 page message:", data);
|
|
6934
|
-
if (!data || data.type !== NOTIFICATION_CLICK_SW_TYPE) return;
|
|
6935
|
-
const pushData = data.data && typeof data.data === "object" ? data.data : void 0;
|
|
6936
|
-
const conversationId = String(data.conversationId || "") || resolveConversationIdFromPushData(pushData);
|
|
6937
|
-
console.log("[realtimex-push] notification click \u2192 open:", {
|
|
6938
|
-
conversationId,
|
|
6939
|
-
pushData
|
|
6940
|
-
});
|
|
6941
|
-
handleNotificationOpen(conversationId, pushData, options);
|
|
6942
|
-
};
|
|
6943
|
-
navigator.serviceWorker?.addEventListener("message", onSwMessage);
|
|
6944
|
-
let broadcast = null;
|
|
6945
|
-
try {
|
|
6946
|
-
broadcast = new BroadcastChannel(PUSH_BROADCAST_CHANNEL);
|
|
6947
|
-
broadcast.onmessage = onSwMessage;
|
|
6948
|
-
} catch {
|
|
6949
|
-
}
|
|
6950
|
-
const fromUrl = readConversationIdFromLocation();
|
|
6951
|
-
if (fromUrl && !consumedUrlConversationIds.has(fromUrl)) {
|
|
6952
|
-
consumedUrlConversationIds.add(fromUrl);
|
|
6953
|
-
handleNotificationOpen(fromUrl, void 0, options);
|
|
6954
|
-
} else {
|
|
6955
|
-
stripConversationIdFromUrl();
|
|
6956
|
-
}
|
|
6957
|
-
void consumePendingOpenFromPushCache().then((request) => {
|
|
6958
|
-
if (!request?.conversationId) return;
|
|
6959
|
-
handleNotificationOpen(
|
|
6960
|
-
request.conversationId,
|
|
6961
|
-
request,
|
|
6962
|
-
options
|
|
6963
|
-
);
|
|
6964
|
-
});
|
|
6965
|
-
return () => {
|
|
6966
|
-
navigator.serviceWorker?.removeEventListener("message", onSwMessage);
|
|
6967
|
-
broadcast?.close();
|
|
6968
|
-
};
|
|
6969
|
-
};
|
|
6970
|
-
|
|
6971
|
-
// src/hooks/useChatController.ts
|
|
7391
|
+
init_open_conversation();
|
|
6972
7392
|
function resolveMessageType(content, attachments) {
|
|
6973
7393
|
if (attachments.length === 0) return "text";
|
|
6974
7394
|
if (!content.trim() && attachments.length === 1) {
|
|
@@ -7931,9 +8351,9 @@ function AdminPermissionTooltip({
|
|
|
7931
8351
|
if (!disabled || !message) {
|
|
7932
8352
|
return /* @__PURE__ */ jsx(Fragment, { children });
|
|
7933
8353
|
}
|
|
7934
|
-
const gatedChildren =
|
|
7935
|
-
if (!
|
|
7936
|
-
return
|
|
8354
|
+
const gatedChildren = React11__default.Children.map(children, (child) => {
|
|
8355
|
+
if (!React11__default.isValidElement(child)) return child;
|
|
8356
|
+
return React11__default.cloneElement(child, {
|
|
7937
8357
|
className: cn(child.props.className, "pointer-events-none")
|
|
7938
8358
|
});
|
|
7939
8359
|
});
|
|
@@ -9873,41 +10293,7 @@ var ChatSocketStatus_default = ChatSocketStatus;
|
|
|
9873
10293
|
|
|
9874
10294
|
// src/chat/header/HeaderSearchPopover.tsx
|
|
9875
10295
|
init_button();
|
|
9876
|
-
|
|
9877
|
-
// src/ui/popover.tsx
|
|
9878
|
-
init_utils2();
|
|
9879
|
-
function Popover({
|
|
9880
|
-
...props
|
|
9881
|
-
}) {
|
|
9882
|
-
return /* @__PURE__ */ jsx(Popover$1.Root, { "data-slot": "popover", ...props });
|
|
9883
|
-
}
|
|
9884
|
-
function PopoverTrigger({
|
|
9885
|
-
...props
|
|
9886
|
-
}) {
|
|
9887
|
-
return /* @__PURE__ */ jsx(Popover$1.Trigger, { "data-slot": "popover-trigger", ...props });
|
|
9888
|
-
}
|
|
9889
|
-
function PopoverContent({
|
|
9890
|
-
className,
|
|
9891
|
-
align = "center",
|
|
9892
|
-
sideOffset = 4,
|
|
9893
|
-
...props
|
|
9894
|
-
}) {
|
|
9895
|
-
return /* @__PURE__ */ jsx(Popover$1.Portal, { children: /* @__PURE__ */ jsx(
|
|
9896
|
-
Popover$1.Content,
|
|
9897
|
-
{
|
|
9898
|
-
"data-slot": "popover-content",
|
|
9899
|
-
align,
|
|
9900
|
-
sideOffset,
|
|
9901
|
-
className: cn(
|
|
9902
|
-
"bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 w-72 origin-(--radix-popover-content-transform-origin) rounded-md border p-4 shadow-md outline-hidden",
|
|
9903
|
-
className
|
|
9904
|
-
),
|
|
9905
|
-
...props
|
|
9906
|
-
}
|
|
9907
|
-
) });
|
|
9908
|
-
}
|
|
9909
|
-
|
|
9910
|
-
// src/chat/header/HeaderSearchPopover.tsx
|
|
10296
|
+
init_popover();
|
|
9911
10297
|
init_utils2();
|
|
9912
10298
|
function escapeRegExp(value) {
|
|
9913
10299
|
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
@@ -9924,7 +10310,7 @@ function highlightSearchMatch(text, query) {
|
|
|
9924
10310
|
children: part
|
|
9925
10311
|
},
|
|
9926
10312
|
`${part}-${index}`
|
|
9927
|
-
) : /* @__PURE__ */ jsx(
|
|
10313
|
+
) : /* @__PURE__ */ jsx(React11__default.Fragment, { children: part }, `${part}-${index}`)
|
|
9928
10314
|
);
|
|
9929
10315
|
}
|
|
9930
10316
|
function getMessagePreview(message) {
|
|
@@ -10322,20 +10708,20 @@ var HeaderRightSide = ({
|
|
|
10322
10708
|
const startCall = useCallStore((s) => s.startCall);
|
|
10323
10709
|
const showPhoneCall = features.showPhoneCall ?? defaultChatFeatures.showPhoneCall;
|
|
10324
10710
|
const showVideoCall = features.showVideoCall ?? defaultChatFeatures.showVideoCall;
|
|
10325
|
-
const
|
|
10711
|
+
const peer = { name: activeChannel.name, avatar: activeChannel.avatar || activeChannel.image };
|
|
10326
10712
|
const handlePhoneCall = () => {
|
|
10327
10713
|
if (features.onPhoneCall) {
|
|
10328
10714
|
features.onPhoneCall({ activeChannel, conversationId });
|
|
10329
10715
|
return;
|
|
10330
10716
|
}
|
|
10331
|
-
if (conversationId) startCall(conversationId, "audio",
|
|
10717
|
+
if (conversationId) startCall(conversationId, "audio", peer, activeChannel.isGroup);
|
|
10332
10718
|
};
|
|
10333
10719
|
const handleVideoCall = () => {
|
|
10334
10720
|
if (features.onVideoCall) {
|
|
10335
10721
|
features.onVideoCall({ activeChannel, conversationId });
|
|
10336
10722
|
return;
|
|
10337
10723
|
}
|
|
10338
|
-
if (conversationId) startCall(conversationId, "video",
|
|
10724
|
+
if (conversationId) startCall(conversationId, "video", peer, activeChannel.isGroup);
|
|
10339
10725
|
};
|
|
10340
10726
|
const headerItemGapClass = "gap-0.5";
|
|
10341
10727
|
const headerIconBtnClass = "inline-flex size-6 shrink-0 items-center justify-center rounded-lg p-0 text-(--chat-muted) transition-colors hover:bg-(--chat-hover) hover:text-(--chat-text) has-[>svg]:p-0";
|
|
@@ -15980,6 +16366,7 @@ var MessageReactions = ({
|
|
|
15980
16366
|
var MessageReactions_default = MessageReactions;
|
|
15981
16367
|
init_utils2();
|
|
15982
16368
|
init_button();
|
|
16369
|
+
init_popover();
|
|
15983
16370
|
var MessageToolbar = ({
|
|
15984
16371
|
isSender,
|
|
15985
16372
|
isRecent,
|
|
@@ -18130,6 +18517,9 @@ function useActiveChannelMessages({
|
|
|
18130
18517
|
};
|
|
18131
18518
|
}
|
|
18132
18519
|
|
|
18520
|
+
// src/chat/active-channel-messages/ChatDateJumpMenu.tsx
|
|
18521
|
+
init_popover();
|
|
18522
|
+
|
|
18133
18523
|
// src/chat/ui/calendar.tsx
|
|
18134
18524
|
init_utils2();
|
|
18135
18525
|
init_button();
|
|
@@ -18286,8 +18676,8 @@ function CalendarDayButton({
|
|
|
18286
18676
|
...props
|
|
18287
18677
|
}) {
|
|
18288
18678
|
const defaultClassNames = getDefaultClassNames();
|
|
18289
|
-
const ref =
|
|
18290
|
-
|
|
18679
|
+
const ref = React11.useRef(null);
|
|
18680
|
+
React11.useEffect(() => {
|
|
18291
18681
|
if (modifiers.focused) ref.current?.focus();
|
|
18292
18682
|
}, [modifiers.focused]);
|
|
18293
18683
|
return /* @__PURE__ */ jsx(
|
|
@@ -18663,6 +19053,9 @@ function ComposerStatusBanner({
|
|
|
18663
19053
|
] }) });
|
|
18664
19054
|
}
|
|
18665
19055
|
|
|
19056
|
+
// src/chat/channel-message-box/ChannelMessageBoxView.tsx
|
|
19057
|
+
init_popover();
|
|
19058
|
+
|
|
18666
19059
|
// src/chat/composer-attachment/ComposerAttachmentPreview.tsx
|
|
18667
19060
|
init_utils2();
|
|
18668
19061
|
init_button();
|
|
@@ -19259,10 +19652,10 @@ function useChannelMessageBox({
|
|
|
19259
19652
|
const composerDisabledReason = conversationId ? composerDisabledByConversation[conversationId] : void 0;
|
|
19260
19653
|
const fileInputRef = useRef(null);
|
|
19261
19654
|
const attachmentsCountRef = useRef(0);
|
|
19262
|
-
|
|
19655
|
+
React11__default.useEffect(() => {
|
|
19263
19656
|
attachmentsCountRef.current = state.attachments.length;
|
|
19264
19657
|
}, [state.attachments.length]);
|
|
19265
|
-
|
|
19658
|
+
React11__default.useEffect(() => {
|
|
19266
19659
|
const draft = {
|
|
19267
19660
|
messageInput: state.messageInput,
|
|
19268
19661
|
attachments: state.attachments
|
|
@@ -19270,7 +19663,7 @@ function useChannelMessageBox({
|
|
|
19270
19663
|
syncComposerRef(draft);
|
|
19271
19664
|
updateConversationDraft(conversationIdRef.current, draft);
|
|
19272
19665
|
}, [state.messageInput, state.attachments, syncComposerRef]);
|
|
19273
|
-
|
|
19666
|
+
React11__default.useEffect(() => {
|
|
19274
19667
|
if (typingTimeoutRef.current) {
|
|
19275
19668
|
clearTimeout(typingTimeoutRef.current);
|
|
19276
19669
|
typingTimeoutRef.current = null;
|
|
@@ -20331,6 +20724,7 @@ var ChatViewport_default = ChatViewport;
|
|
|
20331
20724
|
init_utils2();
|
|
20332
20725
|
init_normalize_helpers();
|
|
20333
20726
|
init_useStore();
|
|
20727
|
+
init_open_conversation();
|
|
20334
20728
|
var RETRY_DELAYS_MS = [1e3, 3e3, 8e3, 2e4];
|
|
20335
20729
|
var recentToastIds = /* @__PURE__ */ new Set();
|
|
20336
20730
|
var rememberToastId = (id) => {
|
|
@@ -20631,8 +21025,8 @@ var ChatMain = ({
|
|
|
20631
21025
|
viewportHeight = "full",
|
|
20632
21026
|
viewportClassName
|
|
20633
21027
|
}) => {
|
|
20634
|
-
const clientObj =
|
|
20635
|
-
const uiConfigObj =
|
|
21028
|
+
const clientObj = React11__default.useMemo(() => ({ id: clientId }), [clientId]);
|
|
21029
|
+
const uiConfigObj = React11__default.useMemo(
|
|
20636
21030
|
() => ({
|
|
20637
21031
|
...uiConfig || {},
|
|
20638
21032
|
components: { ...uiConfig?.components, ...components },
|
|
@@ -21009,6 +21403,9 @@ var packageDefaultComponents = {
|
|
|
21009
21403
|
ChatSocketStatus: ChatSocketStatus_default
|
|
21010
21404
|
};
|
|
21011
21405
|
|
|
21406
|
+
// src/index.ts
|
|
21407
|
+
init_open_conversation();
|
|
21408
|
+
|
|
21012
21409
|
// src/hooks/useAdminFeatureGate.ts
|
|
21013
21410
|
function useAdminFeatureGate(feature) {
|
|
21014
21411
|
const ctx = useEffectiveSettingsOptional();
|