@realtimexsco/live-chat 1.5.3 → 1.5.5
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 +2418 -1777
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.mts +19 -5
- package/dist/index.d.ts +19 -5
- package/dist/index.mjs +2105 -1451
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -7,10 +7,10 @@ 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,
|
|
13
|
-
import {
|
|
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
|
+
import { flushSync } from 'react-dom';
|
|
14
14
|
import EmojiPicker, { Theme } from 'emoji-picker-react';
|
|
15
15
|
import { getDefaultClassNames, DayPicker } from 'react-day-picker';
|
|
16
16
|
import toast$1, { Toaster, toast } from 'react-hot-toast';
|
|
@@ -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;
|
|
@@ -924,6 +1739,10 @@ var init_call_listeners = __esm({
|
|
|
924
1739
|
console.log("[realtimex-call] call_ended received:", payload);
|
|
925
1740
|
useCallStore.getState()._receiveEnded(payload);
|
|
926
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
|
+
});
|
|
927
1746
|
socket.on(SOCKET_EVENTS.CALL_BUSY, (payload) => {
|
|
928
1747
|
console.log("[realtimex-call] call_busy received:", payload);
|
|
929
1748
|
useCallStore.getState()._receiveBusy(payload);
|
|
@@ -999,654 +1818,146 @@ var init_socket_service = __esm({
|
|
|
999
1818
|
if (this.socket) {
|
|
1000
1819
|
this.socket.emit(event, payload);
|
|
1001
1820
|
} 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);
|
|
1821
|
+
console.error(`SocketService: Socket is not initialized. Cannot emit ${event}`);
|
|
1822
|
+
}
|
|
1085
1823
|
}
|
|
1086
|
-
|
|
1087
|
-
this.
|
|
1824
|
+
getSocket() {
|
|
1825
|
+
return this.socket;
|
|
1088
1826
|
}
|
|
1089
|
-
|
|
1090
|
-
this.emit(SOCKET_EVENTS.
|
|
1827
|
+
emitDmSend(payload) {
|
|
1828
|
+
this.emit(SOCKET_EVENTS.DM_SEND, payload);
|
|
1091
1829
|
}
|
|
1092
|
-
|
|
1093
|
-
this.emit(SOCKET_EVENTS.
|
|
1830
|
+
emitDmEdit(payload) {
|
|
1831
|
+
this.emit(SOCKET_EVENTS.DM_EDIT, payload);
|
|
1094
1832
|
}
|
|
1095
|
-
|
|
1096
|
-
this.emit(SOCKET_EVENTS.
|
|
1833
|
+
emitDmDelete(payload) {
|
|
1834
|
+
this.emit(SOCKET_EVENTS.DM_DELETE, payload);
|
|
1097
1835
|
}
|
|
1098
|
-
|
|
1099
|
-
this.emit(SOCKET_EVENTS.
|
|
1836
|
+
emitDmClearChat(payload) {
|
|
1837
|
+
this.emit(SOCKET_EVENTS.DM_CLEAR_CHAT, payload);
|
|
1100
1838
|
}
|
|
1101
|
-
|
|
1102
|
-
this.emit(SOCKET_EVENTS.
|
|
1839
|
+
emitGroupClearChat(payload) {
|
|
1840
|
+
this.emit(SOCKET_EVENTS.GROUP_CLEAR_CHAT, payload);
|
|
1103
1841
|
}
|
|
1104
|
-
|
|
1105
|
-
this.emit(SOCKET_EVENTS.
|
|
1842
|
+
emitDmDeleteConversation(payload) {
|
|
1843
|
+
this.emit(SOCKET_EVENTS.DM_DELETE_CONVERSATION, payload);
|
|
1106
1844
|
}
|
|
1107
|
-
|
|
1108
|
-
this.emit(SOCKET_EVENTS.
|
|
1845
|
+
emitGroupDeleteConversation(payload) {
|
|
1846
|
+
this.emit(SOCKET_EVENTS.GROUP_DELETE_CONVERSATION, payload);
|
|
1109
1847
|
}
|
|
1110
|
-
|
|
1111
|
-
this.emit(SOCKET_EVENTS.
|
|
1848
|
+
emitDmReactionAdd(payload) {
|
|
1849
|
+
this.emit(SOCKET_EVENTS.DM_REACTION_ADD, payload);
|
|
1112
1850
|
}
|
|
1113
|
-
|
|
1114
|
-
this.emit(SOCKET_EVENTS.
|
|
1851
|
+
emitDmReactionRemove(payload) {
|
|
1852
|
+
this.emit(SOCKET_EVENTS.DM_REACTION_REMOVE, payload);
|
|
1115
1853
|
}
|
|
1116
|
-
|
|
1117
|
-
this.emit(SOCKET_EVENTS.
|
|
1854
|
+
emitTypingStart(payload) {
|
|
1855
|
+
this.emit(SOCKET_EVENTS.DM_TYPING_START, payload);
|
|
1118
1856
|
}
|
|
1119
|
-
|
|
1120
|
-
this.emit(SOCKET_EVENTS.
|
|
1857
|
+
emitTypingStop(payload) {
|
|
1858
|
+
this.emit(SOCKET_EVENTS.DM_TYPING_STOP, payload);
|
|
1121
1859
|
}
|
|
1122
|
-
|
|
1123
|
-
this.emit(SOCKET_EVENTS.
|
|
1860
|
+
emitMarkAsRead(payload) {
|
|
1861
|
+
this.emit(SOCKET_EVENTS.DM_MARK_READ, payload);
|
|
1124
1862
|
}
|
|
1125
|
-
|
|
1126
|
-
|
|
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");
|
|
1162
|
-
}
|
|
1163
|
-
});
|
|
1164
|
-
|
|
1165
|
-
// src/store/helpers/message.helpers.ts
|
|
1166
|
-
var getMessageSenderId, extractMessageData, statusRank, preferHigherStatus, mergeLastMessage, messageHasPreviewBody, getLatestMessageFromList, isOwnMessage, getMessageContent, getMessageTimestamp, resolveMessageStatus, mergeChatMessages;
|
|
1167
|
-
var init_message_helpers = __esm({
|
|
1168
|
-
"src/store/helpers/message.helpers.ts"() {
|
|
1169
|
-
getMessageSenderId = (message) => String(message?.sender?._id || message?.sender?.id || message?.senderId || message?.fromUserId || message?.sender || "");
|
|
1170
|
-
extractMessageData = (payload) => {
|
|
1171
|
-
const messageId2 = payload.messageId || payload._id || payload.message?._id || payload.message?.id;
|
|
1172
|
-
const content = payload.content || payload.message?.content;
|
|
1173
|
-
const conversationId = payload.conversationId || payload.conversation_id || payload.message?.conversationId || payload.message?.conversation_id;
|
|
1174
|
-
const status = payload.status || payload.message?.status;
|
|
1175
|
-
const isPinned = payload.isPinned ?? payload.message?.isPinned;
|
|
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 };
|
|
1863
|
+
emitDmPin(payload) {
|
|
1864
|
+
this.emit(SOCKET_EVENTS.DM_PIN, payload);
|
|
1187
1865
|
}
|
|
1188
|
-
|
|
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;
|
|
1216
|
-
});
|
|
1217
|
-
};
|
|
1218
|
-
isOwnMessage = (message, loggedUserId) => {
|
|
1219
|
-
if (message?.isOwnMessage) return true;
|
|
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;
|
|
1866
|
+
emitDmUnpin(payload) {
|
|
1867
|
+
this.emit(SOCKET_EVENTS.DM_UNPIN, payload);
|
|
1239
1868
|
}
|
|
1240
|
-
|
|
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);
|
|
1247
|
-
});
|
|
1248
|
-
localMessages.forEach((localMessage) => {
|
|
1249
|
-
if (!localMessage) return;
|
|
1250
|
-
const localId = String(localMessage._id || localMessage.id || "");
|
|
1251
|
-
if (localId && merged.has(localId)) {
|
|
1252
|
-
const apiMessage = merged.get(localId);
|
|
1253
|
-
const withStatus = preferHigherStatus(apiMessage, localMessage);
|
|
1254
|
-
merged.set(localId, {
|
|
1255
|
-
...withStatus,
|
|
1256
|
-
isStarred: typeof localMessage.isStarred === "boolean" ? localMessage.isStarred : withStatus.isStarred,
|
|
1257
|
-
isPinned: typeof localMessage.isPinned === "boolean" ? localMessage.isPinned : withStatus.isPinned
|
|
1258
|
-
});
|
|
1259
|
-
return;
|
|
1260
|
-
}
|
|
1261
|
-
const duplicateInApi = apiMessages.some((apiMessage) => {
|
|
1262
|
-
const apiId = String(apiMessage?._id || apiMessage?.id || "");
|
|
1263
|
-
if (localId && apiId && localId === apiId) return true;
|
|
1264
|
-
if (!isOwnMessage(localMessage, loggedUserId)) return false;
|
|
1265
|
-
if (!isOwnMessage(apiMessage, loggedUserId)) return false;
|
|
1266
|
-
if (getMessageContent(localMessage) !== getMessageContent(apiMessage)) return false;
|
|
1267
|
-
return Math.abs(getMessageTimestamp(localMessage) - getMessageTimestamp(apiMessage)) < 12e4;
|
|
1268
|
-
});
|
|
1269
|
-
if (duplicateInApi) return;
|
|
1270
|
-
const key = localId || `pending-${getMessageTimestamp(localMessage)}-${getMessageContent(localMessage)}`;
|
|
1271
|
-
if (!merged.has(key)) merged.set(key, localMessage);
|
|
1272
|
-
});
|
|
1273
|
-
return Array.from(merged.values());
|
|
1274
|
-
};
|
|
1275
|
-
}
|
|
1276
|
-
});
|
|
1277
|
-
|
|
1278
|
-
// src/store/helpers/participant.helpers.ts
|
|
1279
|
-
var mergeConversationParticipants, buildParticipantsFromMessagePayload, resolveOtherParticipant, getOtherParticipantId;
|
|
1280
|
-
var init_participant_helpers = __esm({
|
|
1281
|
-
"src/store/helpers/participant.helpers.ts"() {
|
|
1282
|
-
mergeConversationParticipants = (existingParticipants, incomingParticipants) => {
|
|
1283
|
-
const byId = /* @__PURE__ */ new Map();
|
|
1284
|
-
const addParticipant = (participant) => {
|
|
1285
|
-
const id = String(participant?._id || participant?.id || participant || "");
|
|
1286
|
-
if (!id) return;
|
|
1287
|
-
const incomingEntry = typeof participant === "object" && participant !== null ? participant : { _id: id };
|
|
1288
|
-
const current = byId.get(id);
|
|
1289
|
-
if (!current) {
|
|
1290
|
-
byId.set(id, incomingEntry);
|
|
1291
|
-
return;
|
|
1292
|
-
}
|
|
1293
|
-
const currentHasName = Boolean(current.name);
|
|
1294
|
-
const incomingHasName = Boolean(incomingEntry.name);
|
|
1295
|
-
if (incomingHasName && !currentHasName) {
|
|
1296
|
-
byId.set(id, { ...current, ...incomingEntry });
|
|
1297
|
-
} else if (!incomingHasName && currentHasName) {
|
|
1298
|
-
byId.set(id, { ...incomingEntry, ...current });
|
|
1299
|
-
} else {
|
|
1300
|
-
byId.set(id, { ...current, ...incomingEntry });
|
|
1301
|
-
}
|
|
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);
|
|
1869
|
+
emitDmStar(payload) {
|
|
1870
|
+
this.emit(SOCKET_EVENTS.DM_STAR, payload);
|
|
1325
1871
|
}
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
resolveOtherParticipant = (conversation, loggedUserId, allUsers) => {
|
|
1329
|
-
if (!conversation || !loggedUserId) {
|
|
1330
|
-
return { id: "", user: null };
|
|
1872
|
+
emitDmUnstar(payload) {
|
|
1873
|
+
this.emit(SOCKET_EVENTS.DM_UNSTAR, payload);
|
|
1331
1874
|
}
|
|
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 };
|
|
1875
|
+
emitDmForward(payload) {
|
|
1876
|
+
this.emit(SOCKET_EVENTS.DM_FORWARD, payload);
|
|
1339
1877
|
}
|
|
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;
|
|
1878
|
+
emitGroupJoin(payload) {
|
|
1879
|
+
this.emit(SOCKET_EVENTS.GROUP_JOIN, payload);
|
|
1367
1880
|
}
|
|
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;
|
|
1881
|
+
emitGroupMessageSend(payload) {
|
|
1882
|
+
this.emit(SOCKET_EVENTS.GROUP_MESSAGE_SEND, payload);
|
|
1378
1883
|
}
|
|
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);
|
|
1884
|
+
emitGroupTypingStart(payload) {
|
|
1885
|
+
this.emit(SOCKET_EVENTS.GROUP_TYPING_START, payload);
|
|
1393
1886
|
}
|
|
1394
|
-
|
|
1395
|
-
|
|
1887
|
+
emitGroupTypingStop(payload) {
|
|
1888
|
+
this.emit(SOCKET_EVENTS.GROUP_TYPING_STOP, payload);
|
|
1396
1889
|
}
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
}
|
|
1400
|
-
});
|
|
1401
|
-
|
|
1402
|
-
// src/store/helpers/conversation.helpers.ts
|
|
1403
|
-
var findConversationIdByMessageId, applyMessageStatusToStore, isGroupConversation, normalizeConversationRecord, resolveConversationLastMessage, syncConversationLastMessage, upsertConversationInList, findConversationById, findConversationIdByLastMessageId, findConversationForChannel, resolveConversationIdForChannel;
|
|
1404
|
-
var init_conversation_helpers = __esm({
|
|
1405
|
-
"src/store/helpers/conversation.helpers.ts"() {
|
|
1406
|
-
init_group_display_helpers();
|
|
1407
|
-
init_message_helpers();
|
|
1408
|
-
init_participant_helpers();
|
|
1409
|
-
init_unread_helpers();
|
|
1410
|
-
findConversationIdByMessageId = (messagesByConversation, messageId2) => {
|
|
1411
|
-
return Object.keys(messagesByConversation).find(
|
|
1412
|
-
(cid) => messagesByConversation[cid].some(
|
|
1413
|
-
(m) => String(m._id || m.id) === String(messageId2)
|
|
1414
|
-
)
|
|
1415
|
-
);
|
|
1416
|
-
};
|
|
1417
|
-
applyMessageStatusToStore = (state, messageId2, status, conversationId) => {
|
|
1418
|
-
const normalizedMessageId = String(messageId2);
|
|
1419
|
-
const resolvedConversationId = conversationId ? String(conversationId) : findConversationIdByMessageId(state.messagesByConversation, normalizedMessageId) || findConversationIdByLastMessageId(state.conversations, normalizedMessageId) || null;
|
|
1420
|
-
const nextConversations = state.conversations.map((conversation) => {
|
|
1421
|
-
const lastMessageId = String(
|
|
1422
|
-
conversation?.lastMessage?._id || conversation?.lastMessage?.id || ""
|
|
1423
|
-
);
|
|
1424
|
-
if (lastMessageId !== normalizedMessageId) return conversation;
|
|
1425
|
-
return {
|
|
1426
|
-
...conversation,
|
|
1427
|
-
lastMessage: mergeLastMessage(conversation.lastMessage, { status })
|
|
1428
|
-
};
|
|
1429
|
-
});
|
|
1430
|
-
const nextMessagesByConversation = { ...state.messagesByConversation };
|
|
1431
|
-
const patchMessages = (messages) => messages.map(
|
|
1432
|
-
(message) => String(message._id || message.id) === normalizedMessageId ? preferHigherStatus(message, { status }) : message
|
|
1433
|
-
);
|
|
1434
|
-
if (resolvedConversationId && nextMessagesByConversation[resolvedConversationId]) {
|
|
1435
|
-
nextMessagesByConversation[resolvedConversationId] = patchMessages(
|
|
1436
|
-
nextMessagesByConversation[resolvedConversationId]
|
|
1437
|
-
);
|
|
1438
|
-
} else {
|
|
1439
|
-
for (const cid of Object.keys(nextMessagesByConversation)) {
|
|
1440
|
-
const patched = patchMessages(nextMessagesByConversation[cid]);
|
|
1441
|
-
if (patched !== nextMessagesByConversation[cid]) {
|
|
1442
|
-
nextMessagesByConversation[cid] = patched;
|
|
1443
|
-
}
|
|
1444
|
-
}
|
|
1890
|
+
emitGroupEdit(payload) {
|
|
1891
|
+
this.emit(SOCKET_EVENTS.GROUP_EDIT, payload);
|
|
1445
1892
|
}
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
}
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
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];
|
|
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);
|
|
1931
|
+
}
|
|
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
|
|
|
@@ -4300,6 +4611,67 @@ var init_utils2 = __esm({
|
|
|
4300
4611
|
"src/lib/utils.ts"() {
|
|
4301
4612
|
}
|
|
4302
4613
|
});
|
|
4614
|
+
function TooltipProvider({
|
|
4615
|
+
delayDuration = 0,
|
|
4616
|
+
...props
|
|
4617
|
+
}) {
|
|
4618
|
+
return /* @__PURE__ */ jsx(
|
|
4619
|
+
Tooltip$1.Provider,
|
|
4620
|
+
{
|
|
4621
|
+
"data-slot": "tooltip-provider",
|
|
4622
|
+
delayDuration,
|
|
4623
|
+
...props
|
|
4624
|
+
}
|
|
4625
|
+
);
|
|
4626
|
+
}
|
|
4627
|
+
function Tooltip({
|
|
4628
|
+
...props
|
|
4629
|
+
}) {
|
|
4630
|
+
return /* @__PURE__ */ jsx(Tooltip$1.Root, { "data-slot": "tooltip", ...props });
|
|
4631
|
+
}
|
|
4632
|
+
function TooltipTrigger({
|
|
4633
|
+
...props
|
|
4634
|
+
}) {
|
|
4635
|
+
return /* @__PURE__ */ jsx(Tooltip$1.Trigger, { "data-slot": "tooltip-trigger", ...props });
|
|
4636
|
+
}
|
|
4637
|
+
function TooltipContent({
|
|
4638
|
+
className,
|
|
4639
|
+
sideOffset = 0,
|
|
4640
|
+
children,
|
|
4641
|
+
showArrow = true,
|
|
4642
|
+
arrowClassName,
|
|
4643
|
+
...props
|
|
4644
|
+
}) {
|
|
4645
|
+
return /* @__PURE__ */ jsx(Tooltip$1.Portal, { children: /* @__PURE__ */ jsxs(
|
|
4646
|
+
Tooltip$1.Content,
|
|
4647
|
+
{
|
|
4648
|
+
"data-slot": "tooltip-content",
|
|
4649
|
+
sideOffset,
|
|
4650
|
+
className: cn(
|
|
4651
|
+
"bg-foreground text-background animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-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-fit origin-(--radix-tooltip-content-transform-origin) rounded-md px-3 py-1.5 text-xs text-balance",
|
|
4652
|
+
className
|
|
4653
|
+
),
|
|
4654
|
+
...props,
|
|
4655
|
+
children: [
|
|
4656
|
+
children,
|
|
4657
|
+
showArrow ? /* @__PURE__ */ jsx(
|
|
4658
|
+
Tooltip$1.Arrow,
|
|
4659
|
+
{
|
|
4660
|
+
className: cn(
|
|
4661
|
+
"bg-foreground fill-foreground z-50 size-2.5 translate-y-[calc(-50%_-_2px)] rotate-45 rounded-[2px]",
|
|
4662
|
+
arrowClassName
|
|
4663
|
+
)
|
|
4664
|
+
}
|
|
4665
|
+
) : null
|
|
4666
|
+
]
|
|
4667
|
+
}
|
|
4668
|
+
) });
|
|
4669
|
+
}
|
|
4670
|
+
var init_tooltip = __esm({
|
|
4671
|
+
"src/ui/tooltip.tsx"() {
|
|
4672
|
+
init_utils2();
|
|
4673
|
+
}
|
|
4674
|
+
});
|
|
4303
4675
|
function Avatar({
|
|
4304
4676
|
className,
|
|
4305
4677
|
size = "default",
|
|
@@ -4436,6 +4808,7 @@ var init_ParticipantView = __esm({
|
|
|
4436
4808
|
isLocal = false,
|
|
4437
4809
|
fallbackName,
|
|
4438
4810
|
fallbackImage,
|
|
4811
|
+
isHandRaised = false,
|
|
4439
4812
|
className
|
|
4440
4813
|
}) => {
|
|
4441
4814
|
const { webcamStream, micStream, webcamOn, micOn, displayName, isActiveSpeaker } = sdk.useParticipant(participantId);
|
|
@@ -4484,11 +4857,20 @@ var init_ParticipantView = __esm({
|
|
|
4484
4857
|
onPlaying: () => setHasFrame(true),
|
|
4485
4858
|
className: cn(
|
|
4486
4859
|
"size-full object-cover",
|
|
4487
|
-
showVideo ? "block" : "hidden"
|
|
4860
|
+
showVideo ? "block" : "hidden",
|
|
4861
|
+
// Mirror only the local self-view (how every call app
|
|
4862
|
+
// shows you — moving your right hand appears to move
|
|
4863
|
+
// right in your own preview, like a mirror). The video
|
|
4864
|
+
// actually sent to other participants is untouched by
|
|
4865
|
+
// this CSS, so remote viewers always see it the right
|
|
4866
|
+
// way round; only flip our own local tile, never a
|
|
4867
|
+
// remote one.
|
|
4868
|
+
isLocal && "-scale-x-100"
|
|
4488
4869
|
)
|
|
4489
4870
|
}
|
|
4490
4871
|
),
|
|
4491
4872
|
!isLocal && /* @__PURE__ */ jsx("audio", { ref: audioRef, autoPlay: true, playsInline: true }),
|
|
4873
|
+
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-lg shadow-amber-400/30 animate-[bounce_1s_ease-in-out_2]", children: /* @__PURE__ */ jsx(Hand, { size: 14 }) }),
|
|
4492
4874
|
!showVideo && /* @__PURE__ */ jsxs(Avatar, { size: "lg", className: "size-20", children: [
|
|
4493
4875
|
/* @__PURE__ */ jsx(AvatarImage, { src: fallbackImage, alt: name }),
|
|
4494
4876
|
/* @__PURE__ */ jsx(AvatarFallback, { className: "bg-neutral-700 text-xl font-medium text-white", children: name.charAt(0).toUpperCase() })
|
|
@@ -4513,7 +4895,8 @@ var init_AudioCallView = __esm({
|
|
|
4513
4895
|
sdk,
|
|
4514
4896
|
remoteParticipantId,
|
|
4515
4897
|
peerName,
|
|
4516
|
-
peerAvatar
|
|
4898
|
+
peerAvatar,
|
|
4899
|
+
isRemoteHandRaised = false
|
|
4517
4900
|
}) => {
|
|
4518
4901
|
const remote = remoteParticipantId ? sdk.useParticipant(remoteParticipantId) : null;
|
|
4519
4902
|
const name = remote?.displayName || peerName || "In call";
|
|
@@ -4543,7 +4926,8 @@ var init_AudioCallView = __esm({
|
|
|
4543
4926
|
]
|
|
4544
4927
|
}
|
|
4545
4928
|
),
|
|
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" }) })
|
|
4929
|
+
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" }) }),
|
|
4930
|
+
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 shadow-lg shadow-amber-400/30 ring-4 ring-neutral-900 animate-[bounce_1s_ease-in-out_2]", children: /* @__PURE__ */ jsx(Hand, { size: 16 }) })
|
|
4547
4931
|
] }),
|
|
4548
4932
|
/* @__PURE__ */ jsx("p", { className: "text-xl font-semibold tracking-tight", children: name })
|
|
4549
4933
|
] });
|
|
@@ -4551,79 +4935,383 @@ var init_AudioCallView = __esm({
|
|
|
4551
4935
|
AudioCallView_default = AudioCallView;
|
|
4552
4936
|
}
|
|
4553
4937
|
});
|
|
4554
|
-
var
|
|
4938
|
+
var ScreenShareView, ScreenShareView_default;
|
|
4939
|
+
var init_ScreenShareView = __esm({
|
|
4940
|
+
"src/call/ScreenShareView.tsx"() {
|
|
4941
|
+
ScreenShareView = ({ sdk, presenterId, isLocal, className }) => {
|
|
4942
|
+
const { screenShareStream, screenShareOn, displayName } = sdk.useParticipant(presenterId);
|
|
4943
|
+
const videoRef = useRef(null);
|
|
4944
|
+
useEffect(() => {
|
|
4945
|
+
const el = videoRef.current;
|
|
4946
|
+
if (!el) return;
|
|
4947
|
+
if (screenShareOn && screenShareStream?.track) {
|
|
4948
|
+
el.srcObject = new MediaStream([screenShareStream.track]);
|
|
4949
|
+
el.play().catch(() => void 0);
|
|
4950
|
+
} else {
|
|
4951
|
+
el.srcObject = null;
|
|
4952
|
+
}
|
|
4953
|
+
}, [screenShareOn, screenShareStream]);
|
|
4954
|
+
return /* @__PURE__ */ jsx("div", { className, children: /* @__PURE__ */ jsxs("div", { className: "relative flex flex-1 items-center justify-center overflow-hidden rounded-2xl bg-black ring-1 ring-white/10 shadow-2xl", children: [
|
|
4955
|
+
/* @__PURE__ */ jsx("video", { ref: videoRef, playsInline: true, muted: isLocal, className: "max-h-full max-w-full object-contain" }),
|
|
4956
|
+
/* @__PURE__ */ jsxs("div", { className: "absolute top-3 left-3 flex items-center gap-1.5 rounded-full bg-black/70 px-3 py-1.5 text-xs font-medium text-white shadow backdrop-blur-sm", children: [
|
|
4957
|
+
/* @__PURE__ */ jsx(MonitorUp, { size: 13, className: "text-indigo-300" }),
|
|
4958
|
+
isLocal ? "You are presenting" : `${displayName || "Someone"} is presenting`
|
|
4959
|
+
] })
|
|
4960
|
+
] }) });
|
|
4961
|
+
};
|
|
4962
|
+
ScreenShareView_default = ScreenShareView;
|
|
4963
|
+
}
|
|
4964
|
+
});
|
|
4965
|
+
function Popover({
|
|
4966
|
+
...props
|
|
4967
|
+
}) {
|
|
4968
|
+
return /* @__PURE__ */ jsx(Popover$1.Root, { "data-slot": "popover", ...props });
|
|
4969
|
+
}
|
|
4970
|
+
function PopoverTrigger({
|
|
4971
|
+
...props
|
|
4972
|
+
}) {
|
|
4973
|
+
return /* @__PURE__ */ jsx(Popover$1.Trigger, { "data-slot": "popover-trigger", ...props });
|
|
4974
|
+
}
|
|
4975
|
+
function PopoverContent({
|
|
4976
|
+
className,
|
|
4977
|
+
align = "center",
|
|
4978
|
+
sideOffset = 4,
|
|
4979
|
+
...props
|
|
4980
|
+
}) {
|
|
4981
|
+
return /* @__PURE__ */ jsx(Popover$1.Portal, { children: /* @__PURE__ */ jsx(
|
|
4982
|
+
Popover$1.Content,
|
|
4983
|
+
{
|
|
4984
|
+
"data-slot": "popover-content",
|
|
4985
|
+
align,
|
|
4986
|
+
sideOffset,
|
|
4987
|
+
className: cn(
|
|
4988
|
+
"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",
|
|
4989
|
+
className
|
|
4990
|
+
),
|
|
4991
|
+
...props
|
|
4992
|
+
}
|
|
4993
|
+
) });
|
|
4994
|
+
}
|
|
4995
|
+
var init_popover = __esm({
|
|
4996
|
+
"src/ui/popover.tsx"() {
|
|
4997
|
+
init_utils2();
|
|
4998
|
+
}
|
|
4999
|
+
});
|
|
5000
|
+
var circleBtn, idleBtnClass, activeBtnClass, ControlButton, QUICK_REACTIONS, CallControls, CallControls_default;
|
|
4555
5001
|
var init_CallControls = __esm({
|
|
4556
5002
|
"src/call/CallControls.tsx"() {
|
|
4557
5003
|
init_button();
|
|
5004
|
+
init_popover();
|
|
5005
|
+
init_tooltip();
|
|
4558
5006
|
init_utils2();
|
|
4559
5007
|
init_call_store();
|
|
4560
5008
|
circleBtn = "size-13 rounded-full text-white transition-all duration-150 disabled:opacity-40 hover:scale-105 active:scale-95";
|
|
4561
|
-
|
|
4562
|
-
|
|
5009
|
+
idleBtnClass = "bg-white/10 hover:bg-white/20";
|
|
5010
|
+
activeBtnClass = "bg-white text-neutral-900 hover:bg-white/90";
|
|
5011
|
+
ControlButton = ({
|
|
5012
|
+
icon,
|
|
5013
|
+
label,
|
|
5014
|
+
active,
|
|
5015
|
+
activeClassName = activeBtnClass,
|
|
5016
|
+
className,
|
|
5017
|
+
onClick
|
|
5018
|
+
}) => /* @__PURE__ */ jsxs(Tooltip, { children: [
|
|
5019
|
+
/* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx(
|
|
5020
|
+
Button,
|
|
5021
|
+
{
|
|
5022
|
+
variant: "ghost",
|
|
5023
|
+
size: "icon",
|
|
5024
|
+
"aria-label": label,
|
|
5025
|
+
onClick,
|
|
5026
|
+
className: cn(circleBtn, active ? activeClassName : idleBtnClass, className),
|
|
5027
|
+
children: icon
|
|
5028
|
+
}
|
|
5029
|
+
) }),
|
|
5030
|
+
/* @__PURE__ */ jsx(TooltipContent, { side: "top", className: "z-[1000000]", children: label })
|
|
5031
|
+
] });
|
|
5032
|
+
QUICK_REACTIONS = ["\u{1F44D}", "\u2764\uFE0F", "\u{1F602}", "\u{1F62E}", "\u{1F44F}", "\u{1F389}"];
|
|
5033
|
+
CallControls = ({
|
|
5034
|
+
sdk,
|
|
5035
|
+
onReaction,
|
|
5036
|
+
isHandRaised,
|
|
5037
|
+
onToggleRaiseHand,
|
|
5038
|
+
isChatOpen,
|
|
5039
|
+
onToggleChat
|
|
5040
|
+
}) => {
|
|
5041
|
+
const {
|
|
5042
|
+
toggleMic,
|
|
5043
|
+
toggleWebcam,
|
|
5044
|
+
localMicOn,
|
|
5045
|
+
localWebcamOn,
|
|
5046
|
+
localScreenShareOn,
|
|
5047
|
+
toggleScreenShare,
|
|
5048
|
+
leave
|
|
5049
|
+
} = sdk.useMeeting();
|
|
4563
5050
|
const endCall = useCallStore((s) => s.endCall);
|
|
4564
5051
|
const handleLeave = () => {
|
|
4565
5052
|
leave();
|
|
4566
5053
|
endCall();
|
|
4567
5054
|
};
|
|
4568
5055
|
return /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-center gap-5 py-6", children: [
|
|
4569
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-
|
|
5056
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5 rounded-full bg-white/10 p-2 shadow-lg shadow-black/20 backdrop-blur-sm", children: [
|
|
4570
5057
|
/* @__PURE__ */ jsx(
|
|
4571
|
-
|
|
5058
|
+
ControlButton,
|
|
4572
5059
|
{
|
|
4573
|
-
|
|
4574
|
-
|
|
4575
|
-
|
|
4576
|
-
|
|
4577
|
-
|
|
4578
|
-
|
|
4579
|
-
|
|
4580
|
-
|
|
4581
|
-
|
|
5060
|
+
icon: localMicOn ? /* @__PURE__ */ jsx(Mic, { size: 20 }) : /* @__PURE__ */ jsx(MicOff, { size: 20 }),
|
|
5061
|
+
label: localMicOn ? "Mute microphone" : "Unmute microphone",
|
|
5062
|
+
active: !localMicOn,
|
|
5063
|
+
onClick: () => toggleMic()
|
|
5064
|
+
}
|
|
5065
|
+
),
|
|
5066
|
+
/* @__PURE__ */ jsx(
|
|
5067
|
+
ControlButton,
|
|
5068
|
+
{
|
|
5069
|
+
icon: localWebcamOn ? /* @__PURE__ */ jsx(Video, { size: 20 }) : /* @__PURE__ */ jsx(VideoOff, { size: 20 }),
|
|
5070
|
+
label: localWebcamOn ? "Turn off camera" : "Turn on camera",
|
|
5071
|
+
active: !localWebcamOn,
|
|
5072
|
+
onClick: () => toggleWebcam()
|
|
5073
|
+
}
|
|
5074
|
+
),
|
|
5075
|
+
/* @__PURE__ */ jsx("span", { "aria-hidden": true, className: "mx-0.5 h-7 w-px shrink-0 bg-white/15" }),
|
|
5076
|
+
/* @__PURE__ */ jsx(
|
|
5077
|
+
ControlButton,
|
|
5078
|
+
{
|
|
5079
|
+
icon: /* @__PURE__ */ jsx(MonitorUp, { size: 20 }),
|
|
5080
|
+
label: localScreenShareOn ? "Stop sharing your screen" : "Share your screen",
|
|
5081
|
+
active: localScreenShareOn,
|
|
5082
|
+
activeClassName: "bg-indigo-500 hover:bg-indigo-400",
|
|
5083
|
+
onClick: () => toggleScreenShare()
|
|
5084
|
+
}
|
|
5085
|
+
),
|
|
5086
|
+
/* @__PURE__ */ jsx(
|
|
5087
|
+
ControlButton,
|
|
5088
|
+
{
|
|
5089
|
+
icon: /* @__PURE__ */ jsx(Hand, { size: 20, className: isHandRaised ? "animate-[bounce_1s_ease-in-out_2]" : void 0 }),
|
|
5090
|
+
label: isHandRaised ? "Lower hand" : "Raise hand",
|
|
5091
|
+
active: isHandRaised,
|
|
5092
|
+
activeClassName: "bg-amber-400 text-neutral-900 hover:bg-amber-300",
|
|
5093
|
+
onClick: onToggleRaiseHand
|
|
5094
|
+
}
|
|
5095
|
+
),
|
|
5096
|
+
/* @__PURE__ */ jsx(
|
|
5097
|
+
ControlButton,
|
|
5098
|
+
{
|
|
5099
|
+
icon: /* @__PURE__ */ jsx(MessageSquare, { size: 20 }),
|
|
5100
|
+
label: isChatOpen ? "Close in-call chat" : "Open in-call chat",
|
|
5101
|
+
active: isChatOpen,
|
|
5102
|
+
onClick: onToggleChat
|
|
4582
5103
|
}
|
|
4583
5104
|
),
|
|
4584
|
-
|
|
5105
|
+
/* @__PURE__ */ jsxs(Popover, { children: [
|
|
5106
|
+
/* @__PURE__ */ jsxs(Tooltip, { children: [
|
|
5107
|
+
/* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsx(
|
|
5108
|
+
Button,
|
|
5109
|
+
{
|
|
5110
|
+
variant: "ghost",
|
|
5111
|
+
size: "icon",
|
|
5112
|
+
className: cn(circleBtn, idleBtnClass),
|
|
5113
|
+
"aria-label": "Send a reaction",
|
|
5114
|
+
children: /* @__PURE__ */ jsx(Smile, { size: 20 })
|
|
5115
|
+
}
|
|
5116
|
+
) }) }),
|
|
5117
|
+
/* @__PURE__ */ jsx(TooltipContent, { side: "top", className: "z-[1000000]", children: "React" })
|
|
5118
|
+
] }),
|
|
5119
|
+
/* @__PURE__ */ jsx(
|
|
5120
|
+
PopoverContent,
|
|
5121
|
+
{
|
|
5122
|
+
side: "top",
|
|
5123
|
+
align: "center",
|
|
5124
|
+
className: "z-[1000000] w-auto rounded-full border-white/10 bg-neutral-900 p-1.5 shadow-xl",
|
|
5125
|
+
children: /* @__PURE__ */ jsx("div", { className: "flex items-center gap-0.5", children: QUICK_REACTIONS.map((emoji) => /* @__PURE__ */ jsx(
|
|
5126
|
+
"button",
|
|
5127
|
+
{
|
|
5128
|
+
type: "button",
|
|
5129
|
+
className: "flex size-10 items-center justify-center rounded-full text-xl transition-transform hover:scale-125 hover:bg-white/10 active:scale-95",
|
|
5130
|
+
"aria-label": `React with ${emoji}`,
|
|
5131
|
+
onClick: () => onReaction(emoji),
|
|
5132
|
+
children: emoji
|
|
5133
|
+
},
|
|
5134
|
+
emoji
|
|
5135
|
+
)) })
|
|
5136
|
+
}
|
|
5137
|
+
)
|
|
5138
|
+
] })
|
|
5139
|
+
] }),
|
|
5140
|
+
/* @__PURE__ */ jsxs(Tooltip, { children: [
|
|
5141
|
+
/* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx(
|
|
4585
5142
|
Button,
|
|
4586
5143
|
{
|
|
4587
5144
|
variant: "ghost",
|
|
4588
5145
|
size: "icon",
|
|
4589
|
-
className: cn(
|
|
4590
|
-
|
|
4591
|
-
|
|
4592
|
-
)
|
|
4593
|
-
"aria-label": localWebcamOn ? "Turn camera off" : "Turn camera on",
|
|
4594
|
-
onClick: () => toggleWebcam(),
|
|
4595
|
-
children: localWebcamOn ? /* @__PURE__ */ jsx(Video, { size: 20 }) : /* @__PURE__ */ jsx(VideoOff, { size: 20 })
|
|
5146
|
+
className: cn(circleBtn, "bg-red-600 shadow-lg shadow-red-600/30 hover:bg-red-500"),
|
|
5147
|
+
"aria-label": "End call",
|
|
5148
|
+
onClick: handleLeave,
|
|
5149
|
+
children: /* @__PURE__ */ jsx(PhoneOff, { size: 22 })
|
|
4596
5150
|
}
|
|
4597
|
-
)
|
|
4598
|
-
|
|
4599
|
-
|
|
4600
|
-
Button,
|
|
4601
|
-
{
|
|
4602
|
-
variant: "ghost",
|
|
4603
|
-
size: "icon",
|
|
4604
|
-
className: cn(circleBtn, "bg-red-600 shadow-lg shadow-red-600/30 hover:bg-red-500"),
|
|
4605
|
-
"aria-label": "End call",
|
|
4606
|
-
onClick: handleLeave,
|
|
4607
|
-
children: /* @__PURE__ */ jsx(PhoneOff, { size: 22 })
|
|
4608
|
-
}
|
|
4609
|
-
)
|
|
5151
|
+
) }),
|
|
5152
|
+
/* @__PURE__ */ jsx(TooltipContent, { side: "top", className: "z-[1000000]", children: "Leave call" })
|
|
5153
|
+
] })
|
|
4610
5154
|
] });
|
|
4611
5155
|
};
|
|
4612
5156
|
CallControls_default = CallControls;
|
|
4613
5157
|
}
|
|
4614
5158
|
});
|
|
4615
|
-
var
|
|
5159
|
+
var FloatingReactions, ReactionBubble, FloatingReactions_default;
|
|
5160
|
+
var init_FloatingReactions = __esm({
|
|
5161
|
+
"src/call/FloatingReactions.tsx"() {
|
|
5162
|
+
FloatingReactions = ({ reactions }) => {
|
|
5163
|
+
if (!reactions.length) return null;
|
|
5164
|
+
return /* @__PURE__ */ jsxs("div", { className: "pointer-events-none absolute inset-0 z-10 overflow-hidden", children: [
|
|
5165
|
+
/* @__PURE__ */ jsx("style", { children: `
|
|
5166
|
+
@keyframes realtimex-call-reaction-float {
|
|
5167
|
+
0% { transform: translateY(0) scale(0.5); opacity: 0; }
|
|
5168
|
+
15% { transform: translateY(-24px) scale(1.15); opacity: 1; }
|
|
5169
|
+
100% { transform: translateY(-220px) scale(1); opacity: 0; }
|
|
5170
|
+
}
|
|
5171
|
+
` }),
|
|
5172
|
+
reactions.map((reaction) => /* @__PURE__ */ jsx(ReactionBubble, { emoji: reaction.emoji }, reaction.id))
|
|
5173
|
+
] });
|
|
5174
|
+
};
|
|
5175
|
+
ReactionBubble = ({ emoji }) => {
|
|
5176
|
+
const left = useMemo(() => 20 + Math.random() * 60, []);
|
|
5177
|
+
return /* @__PURE__ */ jsx(
|
|
5178
|
+
"span",
|
|
5179
|
+
{
|
|
5180
|
+
className: "absolute bottom-16 text-4xl drop-shadow",
|
|
5181
|
+
style: {
|
|
5182
|
+
left: `${left}%`,
|
|
5183
|
+
animation: "realtimex-call-reaction-float 3s ease-out forwards"
|
|
5184
|
+
},
|
|
5185
|
+
children: emoji
|
|
5186
|
+
}
|
|
5187
|
+
);
|
|
5188
|
+
};
|
|
5189
|
+
FloatingReactions_default = FloatingReactions;
|
|
5190
|
+
}
|
|
5191
|
+
});
|
|
5192
|
+
var FLOATING_REACTION_LIFETIME_MS, useCallReactions;
|
|
5193
|
+
var init_useCallReactions = __esm({
|
|
5194
|
+
"src/call/useCallReactions.ts"() {
|
|
5195
|
+
FLOATING_REACTION_LIFETIME_MS = 3e3;
|
|
5196
|
+
useCallReactions = (sdk) => {
|
|
5197
|
+
const [floatingReactions, setFloatingReactions] = useState([]);
|
|
5198
|
+
const [raisedHands, setRaisedHands] = useState({});
|
|
5199
|
+
const [isHandRaised, setIsHandRaised] = useState(false);
|
|
5200
|
+
const nextId = useRef(0);
|
|
5201
|
+
const addFloatingReaction = useCallback((emoji, fromId) => {
|
|
5202
|
+
const id = `${Date.now()}-${nextId.current++}`;
|
|
5203
|
+
setFloatingReactions((prev) => [...prev, { id, emoji, fromId }]);
|
|
5204
|
+
setTimeout(() => {
|
|
5205
|
+
setFloatingReactions((prev) => prev.filter((r) => r.id !== id));
|
|
5206
|
+
}, FLOATING_REACTION_LIFETIME_MS);
|
|
5207
|
+
}, []);
|
|
5208
|
+
const { send, localParticipant } = sdk.useMeeting({
|
|
5209
|
+
onData: (data) => {
|
|
5210
|
+
console.log("[realtimex-call] data channel received:", data);
|
|
5211
|
+
try {
|
|
5212
|
+
const raw = typeof data.payload === "string" ? data.payload : new TextDecoder().decode(data.payload);
|
|
5213
|
+
const message = JSON.parse(raw);
|
|
5214
|
+
console.log("[realtimex-call] data channel parsed:", message, "from", data.from);
|
|
5215
|
+
if (message.type === "reaction") {
|
|
5216
|
+
addFloatingReaction(message.emoji, data.from);
|
|
5217
|
+
} else if (message.type === "raise-hand") {
|
|
5218
|
+
setRaisedHands((prev) => ({ ...prev, [data.from]: message.raised }));
|
|
5219
|
+
}
|
|
5220
|
+
} catch (err) {
|
|
5221
|
+
console.warn("[realtimex-call] data channel payload not decodable:", err, data);
|
|
5222
|
+
}
|
|
5223
|
+
}
|
|
5224
|
+
});
|
|
5225
|
+
useEffect(() => {
|
|
5226
|
+
setRaisedHands((prev) => {
|
|
5227
|
+
const next = {};
|
|
5228
|
+
for (const id of Object.keys(prev)) {
|
|
5229
|
+
if (prev[id]) next[id] = true;
|
|
5230
|
+
}
|
|
5231
|
+
return next;
|
|
5232
|
+
});
|
|
5233
|
+
}, [localParticipant?.id]);
|
|
5234
|
+
const sendDataMessage = useCallback(
|
|
5235
|
+
(message) => {
|
|
5236
|
+
send(JSON.stringify(message)).then((ok) => {
|
|
5237
|
+
console.log("[realtimex-call] data channel send result:", ok, message);
|
|
5238
|
+
}).catch((err) => {
|
|
5239
|
+
console.warn("[realtimex-call] data channel send failed:", err, message);
|
|
5240
|
+
});
|
|
5241
|
+
},
|
|
5242
|
+
[send]
|
|
5243
|
+
);
|
|
5244
|
+
const sendReaction = useCallback(
|
|
5245
|
+
(emoji) => {
|
|
5246
|
+
if (localParticipant?.id) addFloatingReaction(emoji, localParticipant.id);
|
|
5247
|
+
sendDataMessage({ type: "reaction", emoji });
|
|
5248
|
+
},
|
|
5249
|
+
[localParticipant?.id, addFloatingReaction, sendDataMessage]
|
|
5250
|
+
);
|
|
5251
|
+
const toggleRaiseHand = useCallback(() => {
|
|
5252
|
+
setIsHandRaised((prev) => {
|
|
5253
|
+
const next = !prev;
|
|
5254
|
+
if (localParticipant?.id) {
|
|
5255
|
+
setRaisedHands((hands) => ({ ...hands, [localParticipant.id]: next }));
|
|
5256
|
+
}
|
|
5257
|
+
sendDataMessage({ type: "raise-hand", raised: next });
|
|
5258
|
+
return next;
|
|
5259
|
+
});
|
|
5260
|
+
}, [localParticipant?.id, sendDataMessage]);
|
|
5261
|
+
return { floatingReactions, raisedHands, isHandRaised, sendReaction, toggleRaiseHand };
|
|
5262
|
+
};
|
|
5263
|
+
}
|
|
5264
|
+
});
|
|
5265
|
+
var RemoteWebcamWatcher, MeetingView, MeetingView_default;
|
|
4616
5266
|
var init_MeetingView = __esm({
|
|
4617
5267
|
"src/call/MeetingView.tsx"() {
|
|
4618
5268
|
init_ParticipantView();
|
|
4619
5269
|
init_AudioCallView();
|
|
5270
|
+
init_ScreenShareView();
|
|
4620
5271
|
init_CallControls();
|
|
5272
|
+
init_FloatingReactions();
|
|
5273
|
+
init_useCallReactions();
|
|
4621
5274
|
init_call_store();
|
|
4622
|
-
|
|
5275
|
+
init_utils2();
|
|
5276
|
+
RemoteWebcamWatcher = ({
|
|
5277
|
+
sdk,
|
|
5278
|
+
participantId,
|
|
5279
|
+
onChange
|
|
5280
|
+
}) => {
|
|
5281
|
+
const { webcamOn } = sdk.useParticipant(participantId);
|
|
5282
|
+
useEffect(() => {
|
|
5283
|
+
onChange(participantId, webcamOn);
|
|
5284
|
+
return () => onChange(participantId, false);
|
|
5285
|
+
}, [participantId, webcamOn]);
|
|
5286
|
+
return null;
|
|
5287
|
+
};
|
|
5288
|
+
MeetingView = ({
|
|
5289
|
+
sdk,
|
|
5290
|
+
mode,
|
|
5291
|
+
peerName,
|
|
5292
|
+
peerAvatar,
|
|
5293
|
+
isChatOpen,
|
|
5294
|
+
onToggleChat
|
|
5295
|
+
}) => {
|
|
4623
5296
|
const endCall = useCallStore((s) => s.endCall);
|
|
4624
5297
|
const [hasJoined, setHasJoined] = useState(false);
|
|
4625
5298
|
const hasJoinedRef = useRef(false);
|
|
4626
|
-
const
|
|
5299
|
+
const [groupView, setGroupView] = useState("grid");
|
|
5300
|
+
const [meetingState, setMeetingState] = useState("CONNECTING");
|
|
5301
|
+
const [remoteWebcamOn, setRemoteWebcamOn] = useState({});
|
|
5302
|
+
const handleRemoteWebcamChange = useCallback((participantId, on) => {
|
|
5303
|
+
setRemoteWebcamOn(
|
|
5304
|
+
(prev) => prev[participantId] === on ? prev : { ...prev, [participantId]: on }
|
|
5305
|
+
);
|
|
5306
|
+
}, []);
|
|
5307
|
+
const {
|
|
5308
|
+
join,
|
|
5309
|
+
participants,
|
|
5310
|
+
localParticipant,
|
|
5311
|
+
localWebcamOn,
|
|
5312
|
+
presenterId,
|
|
5313
|
+
activeSpeakerId
|
|
5314
|
+
} = sdk.useMeeting({
|
|
4627
5315
|
onMeetingJoined: () => {
|
|
4628
5316
|
hasJoinedRef.current = true;
|
|
4629
5317
|
setHasJoined(true);
|
|
@@ -4631,8 +5319,10 @@ var init_MeetingView = __esm({
|
|
|
4631
5319
|
onMeetingLeft: () => {
|
|
4632
5320
|
if (!hasJoinedRef.current) return;
|
|
4633
5321
|
endCall();
|
|
4634
|
-
}
|
|
5322
|
+
},
|
|
5323
|
+
onMeetingStateChanged: ({ state }) => setMeetingState(state)
|
|
4635
5324
|
});
|
|
5325
|
+
const { floatingReactions, raisedHands, isHandRaised, sendReaction, toggleRaiseHand } = useCallReactions(sdk);
|
|
4636
5326
|
useEffect(() => {
|
|
4637
5327
|
join();
|
|
4638
5328
|
}, []);
|
|
@@ -4645,47 +5335,188 @@ var init_MeetingView = __esm({
|
|
|
4645
5335
|
const remoteIds = Array.from(participants.keys()).filter(
|
|
4646
5336
|
(id) => id !== localParticipant?.id
|
|
4647
5337
|
);
|
|
4648
|
-
|
|
4649
|
-
|
|
4650
|
-
|
|
4651
|
-
|
|
4652
|
-
|
|
4653
|
-
|
|
4654
|
-
|
|
4655
|
-
|
|
4656
|
-
|
|
4657
|
-
|
|
4658
|
-
|
|
4659
|
-
|
|
4660
|
-
|
|
4661
|
-
|
|
4662
|
-
|
|
4663
|
-
/* @__PURE__ */
|
|
4664
|
-
|
|
4665
|
-
|
|
4666
|
-
|
|
4667
|
-
|
|
4668
|
-
|
|
4669
|
-
|
|
4670
|
-
|
|
4671
|
-
|
|
4672
|
-
|
|
4673
|
-
|
|
4674
|
-
|
|
5338
|
+
const isGroup = remoteIds.length > 1;
|
|
5339
|
+
const webcamWatchers = remoteIds.map((id) => /* @__PURE__ */ jsx(RemoteWebcamWatcher, { sdk, participantId: id, onChange: handleRemoteWebcamChange }, id));
|
|
5340
|
+
const controls = /* @__PURE__ */ jsx(
|
|
5341
|
+
CallControls_default,
|
|
5342
|
+
{
|
|
5343
|
+
sdk,
|
|
5344
|
+
onReaction: sendReaction,
|
|
5345
|
+
isHandRaised,
|
|
5346
|
+
onToggleRaiseHand: toggleRaiseHand,
|
|
5347
|
+
isChatOpen,
|
|
5348
|
+
onToggleChat
|
|
5349
|
+
}
|
|
5350
|
+
);
|
|
5351
|
+
const connectionStatusText = meetingState === "CONNECTING" ? "Connecting\u2026" : meetingState === "DISCONNECTED" ? "Reconnecting\u2026" : meetingState === "FAILED" ? "Connection lost" : null;
|
|
5352
|
+
const statusBanner = connectionStatusText && /* @__PURE__ */ jsxs("div", { className: "flex shrink-0 items-center justify-center gap-1.5 bg-amber-500/15 px-4 py-1.5 text-xs font-medium text-amber-300", children: [
|
|
5353
|
+
/* @__PURE__ */ jsx("span", { className: "size-1.5 animate-pulse rounded-full bg-amber-400" }),
|
|
5354
|
+
connectionStatusText
|
|
5355
|
+
] });
|
|
5356
|
+
if (presenterId) {
|
|
5357
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
5358
|
+
webcamWatchers,
|
|
5359
|
+
/* @__PURE__ */ jsxs("div", { className: "relative flex flex-1 flex-col", children: [
|
|
5360
|
+
statusBanner,
|
|
5361
|
+
/* @__PURE__ */ jsx(FloatingReactions_default, { reactions: floatingReactions }),
|
|
5362
|
+
/* @__PURE__ */ jsx(
|
|
5363
|
+
ScreenShareView_default,
|
|
5364
|
+
{
|
|
5365
|
+
sdk,
|
|
5366
|
+
presenterId,
|
|
5367
|
+
isLocal: presenterId === localParticipant?.id,
|
|
5368
|
+
className: "flex flex-1 flex-col overflow-hidden p-4 pb-2"
|
|
5369
|
+
}
|
|
5370
|
+
),
|
|
5371
|
+
/* @__PURE__ */ jsxs("div", { className: "flex gap-2 overflow-x-auto px-4 pb-2", children: [
|
|
5372
|
+
localParticipant?.id && /* @__PURE__ */ jsx(
|
|
5373
|
+
ParticipantView_default,
|
|
5374
|
+
{
|
|
5375
|
+
sdk,
|
|
5376
|
+
participantId: localParticipant.id,
|
|
5377
|
+
isLocal: true,
|
|
5378
|
+
isHandRaised: !!raisedHands[localParticipant.id],
|
|
5379
|
+
className: "aspect-video h-20 min-h-0 w-32 shrink-0"
|
|
5380
|
+
}
|
|
5381
|
+
),
|
|
4675
5382
|
remoteIds.map((id) => /* @__PURE__ */ jsx(
|
|
4676
5383
|
ParticipantView_default,
|
|
4677
5384
|
{
|
|
4678
5385
|
sdk,
|
|
4679
5386
|
participantId: id,
|
|
4680
5387
|
fallbackName: peerName,
|
|
4681
|
-
fallbackImage: peerAvatar
|
|
5388
|
+
fallbackImage: peerAvatar,
|
|
5389
|
+
isHandRaised: !!raisedHands[id],
|
|
5390
|
+
className: "aspect-video h-20 min-h-0 w-32 shrink-0"
|
|
4682
5391
|
},
|
|
4683
5392
|
id
|
|
4684
5393
|
))
|
|
4685
|
-
]
|
|
4686
|
-
|
|
4687
|
-
|
|
4688
|
-
|
|
5394
|
+
] }),
|
|
5395
|
+
controls
|
|
5396
|
+
] })
|
|
5397
|
+
] });
|
|
5398
|
+
}
|
|
5399
|
+
const anyRemoteWebcamOn = Object.values(remoteWebcamOn).some(Boolean);
|
|
5400
|
+
const showVideoGrid = mode === "video" || localWebcamOn || anyRemoteWebcamOn;
|
|
5401
|
+
if (!showVideoGrid) {
|
|
5402
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
5403
|
+
webcamWatchers,
|
|
5404
|
+
/* @__PURE__ */ jsxs("div", { className: "relative flex flex-1 flex-col", children: [
|
|
5405
|
+
statusBanner,
|
|
5406
|
+
/* @__PURE__ */ jsx(FloatingReactions_default, { reactions: floatingReactions }),
|
|
5407
|
+
/* @__PURE__ */ jsx(
|
|
5408
|
+
AudioCallView_default,
|
|
5409
|
+
{
|
|
5410
|
+
sdk,
|
|
5411
|
+
remoteParticipantId: remoteIds[0],
|
|
5412
|
+
peerName,
|
|
5413
|
+
peerAvatar,
|
|
5414
|
+
isRemoteHandRaised: !!(remoteIds[0] && raisedHands[remoteIds[0]])
|
|
5415
|
+
}
|
|
5416
|
+
),
|
|
5417
|
+
controls
|
|
5418
|
+
] })
|
|
5419
|
+
] });
|
|
5420
|
+
}
|
|
5421
|
+
const focusedId = groupView === "speaker" ? activeSpeakerId && activeSpeakerId !== localParticipant?.id ? activeSpeakerId : remoteIds[0] || localParticipant?.id : null;
|
|
5422
|
+
const stripIds = focusedId ? [localParticipant?.id, ...remoteIds].filter(
|
|
5423
|
+
(id) => !!id && id !== focusedId
|
|
5424
|
+
) : [];
|
|
5425
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
5426
|
+
webcamWatchers,
|
|
5427
|
+
/* @__PURE__ */ jsxs("div", { className: "relative flex flex-1 flex-col", children: [
|
|
5428
|
+
statusBanner,
|
|
5429
|
+
/* @__PURE__ */ jsx(FloatingReactions_default, { reactions: floatingReactions }),
|
|
5430
|
+
isGroup && /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-end gap-1 px-4 pt-2", children: [
|
|
5431
|
+
/* @__PURE__ */ jsx(
|
|
5432
|
+
"button",
|
|
5433
|
+
{
|
|
5434
|
+
type: "button",
|
|
5435
|
+
"aria-label": "Grid view",
|
|
5436
|
+
onClick: () => setGroupView("grid"),
|
|
5437
|
+
className: cn(
|
|
5438
|
+
"flex size-8 items-center justify-center rounded-lg transition-colors",
|
|
5439
|
+
groupView === "grid" ? "bg-white/15 text-white" : "text-white/50 hover:text-white/80"
|
|
5440
|
+
),
|
|
5441
|
+
children: /* @__PURE__ */ jsx(LayoutGrid, { size: 16 })
|
|
5442
|
+
}
|
|
5443
|
+
),
|
|
5444
|
+
/* @__PURE__ */ jsx(
|
|
5445
|
+
"button",
|
|
5446
|
+
{
|
|
5447
|
+
type: "button",
|
|
5448
|
+
"aria-label": "Speaker view",
|
|
5449
|
+
onClick: () => setGroupView("speaker"),
|
|
5450
|
+
className: cn(
|
|
5451
|
+
"flex size-8 items-center justify-center rounded-lg transition-colors",
|
|
5452
|
+
groupView === "speaker" ? "bg-white/15 text-white" : "text-white/50 hover:text-white/80"
|
|
5453
|
+
),
|
|
5454
|
+
children: /* @__PURE__ */ jsx(Rows3, { size: 16 })
|
|
5455
|
+
}
|
|
5456
|
+
)
|
|
5457
|
+
] }),
|
|
5458
|
+
focusedId ? /* @__PURE__ */ jsxs("div", { className: "flex flex-1 flex-col gap-3 overflow-y-auto p-4", children: [
|
|
5459
|
+
/* @__PURE__ */ jsx(
|
|
5460
|
+
ParticipantView_default,
|
|
5461
|
+
{
|
|
5462
|
+
sdk,
|
|
5463
|
+
participantId: focusedId,
|
|
5464
|
+
isLocal: focusedId === localParticipant?.id,
|
|
5465
|
+
fallbackName: peerName,
|
|
5466
|
+
fallbackImage: peerAvatar,
|
|
5467
|
+
isHandRaised: !!raisedHands[focusedId],
|
|
5468
|
+
className: "min-h-[240px] flex-1"
|
|
5469
|
+
}
|
|
5470
|
+
),
|
|
5471
|
+
stripIds.length > 0 && /* @__PURE__ */ jsx("div", { className: "flex gap-2 overflow-x-auto", children: stripIds.map((id) => /* @__PURE__ */ jsx(
|
|
5472
|
+
ParticipantView_default,
|
|
5473
|
+
{
|
|
5474
|
+
sdk,
|
|
5475
|
+
participantId: id,
|
|
5476
|
+
isLocal: id === localParticipant?.id,
|
|
5477
|
+
fallbackName: peerName,
|
|
5478
|
+
fallbackImage: peerAvatar,
|
|
5479
|
+
isHandRaised: !!raisedHands[id],
|
|
5480
|
+
className: "aspect-video h-20 min-h-0 w-32 shrink-0"
|
|
5481
|
+
},
|
|
5482
|
+
id
|
|
5483
|
+
)) })
|
|
5484
|
+
] }) : /* @__PURE__ */ jsxs(
|
|
5485
|
+
"div",
|
|
5486
|
+
{
|
|
5487
|
+
className: "grid flex-1 auto-rows-fr gap-3 overflow-y-auto p-4",
|
|
5488
|
+
style: {
|
|
5489
|
+
gridTemplateColumns: `repeat(${Math.min(
|
|
5490
|
+
Math.max(remoteIds.length + 1, 1),
|
|
5491
|
+
3
|
|
5492
|
+
)}, minmax(0, 1fr))`
|
|
5493
|
+
},
|
|
5494
|
+
children: [
|
|
5495
|
+
localParticipant?.id && /* @__PURE__ */ jsx(
|
|
5496
|
+
ParticipantView_default,
|
|
5497
|
+
{
|
|
5498
|
+
sdk,
|
|
5499
|
+
participantId: localParticipant.id,
|
|
5500
|
+
isLocal: true,
|
|
5501
|
+
isHandRaised: !!raisedHands[localParticipant.id]
|
|
5502
|
+
}
|
|
5503
|
+
),
|
|
5504
|
+
remoteIds.map((id) => /* @__PURE__ */ jsx(
|
|
5505
|
+
ParticipantView_default,
|
|
5506
|
+
{
|
|
5507
|
+
sdk,
|
|
5508
|
+
participantId: id,
|
|
5509
|
+
fallbackName: peerName,
|
|
5510
|
+
fallbackImage: peerAvatar,
|
|
5511
|
+
isHandRaised: !!raisedHands[id]
|
|
5512
|
+
},
|
|
5513
|
+
id
|
|
5514
|
+
))
|
|
5515
|
+
]
|
|
5516
|
+
}
|
|
5517
|
+
),
|
|
5518
|
+
controls
|
|
5519
|
+
] })
|
|
4689
5520
|
] });
|
|
4690
5521
|
};
|
|
4691
5522
|
MeetingView_default = MeetingView;
|
|
@@ -4709,6 +5540,8 @@ var init_CallSession = __esm({
|
|
|
4709
5540
|
mode,
|
|
4710
5541
|
peerName,
|
|
4711
5542
|
peerAvatar,
|
|
5543
|
+
isChatOpen,
|
|
5544
|
+
onToggleChat,
|
|
4712
5545
|
onError
|
|
4713
5546
|
}) => {
|
|
4714
5547
|
const [sdk, setSdk] = useState(null);
|
|
@@ -4751,7 +5584,17 @@ var init_CallSession = __esm({
|
|
|
4751
5584
|
token,
|
|
4752
5585
|
reinitialiseMeetingOnConfigChange: false,
|
|
4753
5586
|
joinWithoutUserInteraction: true,
|
|
4754
|
-
children: /* @__PURE__ */ jsx(
|
|
5587
|
+
children: /* @__PURE__ */ jsx(
|
|
5588
|
+
MeetingView_default,
|
|
5589
|
+
{
|
|
5590
|
+
sdk,
|
|
5591
|
+
mode,
|
|
5592
|
+
peerName,
|
|
5593
|
+
peerAvatar,
|
|
5594
|
+
isChatOpen,
|
|
5595
|
+
onToggleChat
|
|
5596
|
+
}
|
|
5597
|
+
)
|
|
4755
5598
|
}
|
|
4756
5599
|
);
|
|
4757
5600
|
};
|
|
@@ -4763,67 +5606,7 @@ var init_CallSession = __esm({
|
|
|
4763
5606
|
init_useStore();
|
|
4764
5607
|
init_api_service();
|
|
4765
5608
|
init_socket_service2();
|
|
4766
|
-
|
|
4767
|
-
// src/ui/tooltip.tsx
|
|
4768
|
-
init_utils2();
|
|
4769
|
-
function TooltipProvider({
|
|
4770
|
-
delayDuration = 0,
|
|
4771
|
-
...props
|
|
4772
|
-
}) {
|
|
4773
|
-
return /* @__PURE__ */ jsx(
|
|
4774
|
-
Tooltip$1.Provider,
|
|
4775
|
-
{
|
|
4776
|
-
"data-slot": "tooltip-provider",
|
|
4777
|
-
delayDuration,
|
|
4778
|
-
...props
|
|
4779
|
-
}
|
|
4780
|
-
);
|
|
4781
|
-
}
|
|
4782
|
-
function Tooltip({
|
|
4783
|
-
...props
|
|
4784
|
-
}) {
|
|
4785
|
-
return /* @__PURE__ */ jsx(Tooltip$1.Root, { "data-slot": "tooltip", ...props });
|
|
4786
|
-
}
|
|
4787
|
-
function TooltipTrigger({
|
|
4788
|
-
...props
|
|
4789
|
-
}) {
|
|
4790
|
-
return /* @__PURE__ */ jsx(Tooltip$1.Trigger, { "data-slot": "tooltip-trigger", ...props });
|
|
4791
|
-
}
|
|
4792
|
-
function TooltipContent({
|
|
4793
|
-
className,
|
|
4794
|
-
sideOffset = 0,
|
|
4795
|
-
children,
|
|
4796
|
-
showArrow = true,
|
|
4797
|
-
arrowClassName,
|
|
4798
|
-
...props
|
|
4799
|
-
}) {
|
|
4800
|
-
return /* @__PURE__ */ jsx(Tooltip$1.Portal, { children: /* @__PURE__ */ jsxs(
|
|
4801
|
-
Tooltip$1.Content,
|
|
4802
|
-
{
|
|
4803
|
-
"data-slot": "tooltip-content",
|
|
4804
|
-
sideOffset,
|
|
4805
|
-
className: cn(
|
|
4806
|
-
"bg-foreground text-background animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-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-fit origin-(--radix-tooltip-content-transform-origin) rounded-md px-3 py-1.5 text-xs text-balance",
|
|
4807
|
-
className
|
|
4808
|
-
),
|
|
4809
|
-
...props,
|
|
4810
|
-
children: [
|
|
4811
|
-
children,
|
|
4812
|
-
showArrow ? /* @__PURE__ */ jsx(
|
|
4813
|
-
Tooltip$1.Arrow,
|
|
4814
|
-
{
|
|
4815
|
-
className: cn(
|
|
4816
|
-
"bg-foreground fill-foreground z-50 size-2.5 translate-y-[calc(-50%_-_2px)] rotate-45 rounded-[2px]",
|
|
4817
|
-
arrowClassName
|
|
4818
|
-
)
|
|
4819
|
-
}
|
|
4820
|
-
) : null
|
|
4821
|
-
]
|
|
4822
|
-
}
|
|
4823
|
-
) });
|
|
4824
|
-
}
|
|
4825
|
-
|
|
4826
|
-
// src/chat/ChatContext.tsx
|
|
5609
|
+
init_tooltip();
|
|
4827
5610
|
init_utils2();
|
|
4828
5611
|
|
|
4829
5612
|
// src/config/chat-endpoints.ts
|
|
@@ -5733,134 +6516,280 @@ var useCallDuration = (startedAt) => {
|
|
|
5733
6516
|
setElapsedMs(0);
|
|
5734
6517
|
return;
|
|
5735
6518
|
}
|
|
5736
|
-
setElapsedMs(Date.now() - startedAt);
|
|
5737
|
-
const id = setInterval(() => setElapsedMs(Date.now() - startedAt), 1e3);
|
|
5738
|
-
return () => clearInterval(id);
|
|
5739
|
-
}, [startedAt]);
|
|
5740
|
-
const totalSeconds = Math.max(0, Math.floor(elapsedMs / 1e3));
|
|
5741
|
-
const hours = Math.floor(totalSeconds / 3600);
|
|
5742
|
-
const minutes = Math.floor(totalSeconds % 3600 / 60);
|
|
5743
|
-
const seconds = totalSeconds % 60;
|
|
5744
|
-
const pad = (n) => String(n).padStart(2, "0");
|
|
5745
|
-
return hours > 0 ? `${hours}:${pad(minutes)}:${pad(seconds)}` : `${pad(minutes)}:${pad(seconds)}`;
|
|
6519
|
+
setElapsedMs(Date.now() - startedAt);
|
|
6520
|
+
const id = setInterval(() => setElapsedMs(Date.now() - startedAt), 1e3);
|
|
6521
|
+
return () => clearInterval(id);
|
|
6522
|
+
}, [startedAt]);
|
|
6523
|
+
const totalSeconds = Math.max(0, Math.floor(elapsedMs / 1e3));
|
|
6524
|
+
const hours = Math.floor(totalSeconds / 3600);
|
|
6525
|
+
const minutes = Math.floor(totalSeconds % 3600 / 60);
|
|
6526
|
+
const seconds = totalSeconds % 60;
|
|
6527
|
+
const pad = (n) => String(n).padStart(2, "0");
|
|
6528
|
+
return hours > 0 ? `${hours}:${pad(minutes)}:${pad(seconds)}` : `${pad(minutes)}:${pad(seconds)}`;
|
|
6529
|
+
};
|
|
6530
|
+
|
|
6531
|
+
// src/call/CallChatPanel.tsx
|
|
6532
|
+
init_useStore();
|
|
6533
|
+
init_store_helpers();
|
|
6534
|
+
init_utils2();
|
|
6535
|
+
init_button();
|
|
6536
|
+
var CallChatPanel = ({ conversationId, isGroupCall, onClose, className }) => {
|
|
6537
|
+
const rawMessages = useStore((s) => s.messagesByConversation[conversationId]);
|
|
6538
|
+
const loggedUserDetails = useStore((s) => s.loggedUserDetails);
|
|
6539
|
+
const conversations = useStore((s) => s.conversations);
|
|
6540
|
+
const emitDmSend = useStore((s) => s.emitDmSend);
|
|
6541
|
+
const emitGroupMessageSend2 = useStore((s) => s.emitGroupMessageSend);
|
|
6542
|
+
const [text, setText] = useState("");
|
|
6543
|
+
const [optimistic, setOptimistic] = useState([]);
|
|
6544
|
+
const listEndRef = useRef(null);
|
|
6545
|
+
const loggedUserId = String(loggedUserDetails?._id || "");
|
|
6546
|
+
const serverMessages = useMemo(() => {
|
|
6547
|
+
return (rawMessages || []).filter((m) => !m?.isSystemMessage && !m?.isDeletedForEveryone).map((m) => {
|
|
6548
|
+
const senderId = String(m.sender?._id || m.sender || "");
|
|
6549
|
+
return {
|
|
6550
|
+
id: String(m._id || m.id || ""),
|
|
6551
|
+
content: String(m.content || ""),
|
|
6552
|
+
senderName: senderId === loggedUserId ? "You" : m.sender?.name || "Unknown",
|
|
6553
|
+
isOwn: senderId === loggedUserId,
|
|
6554
|
+
timestamp: m.createdAt || m.timestamp || (/* @__PURE__ */ new Date()).toISOString()
|
|
6555
|
+
};
|
|
6556
|
+
});
|
|
6557
|
+
}, [rawMessages, loggedUserId]);
|
|
6558
|
+
useEffect(() => {
|
|
6559
|
+
if (!optimistic.length) return;
|
|
6560
|
+
setOptimistic(
|
|
6561
|
+
(prev) => prev.filter(
|
|
6562
|
+
(pending) => !serverMessages.some(
|
|
6563
|
+
(real) => real.isOwn && real.content === pending.content
|
|
6564
|
+
)
|
|
6565
|
+
)
|
|
6566
|
+
);
|
|
6567
|
+
}, [serverMessages]);
|
|
6568
|
+
const messages = useMemo(
|
|
6569
|
+
() => [...serverMessages, ...optimistic].sort(
|
|
6570
|
+
(a, b) => new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime()
|
|
6571
|
+
),
|
|
6572
|
+
[serverMessages, optimistic]
|
|
6573
|
+
);
|
|
6574
|
+
useEffect(() => {
|
|
6575
|
+
listEndRef.current?.scrollIntoView({ block: "end" });
|
|
6576
|
+
}, [messages.length]);
|
|
6577
|
+
const handleSend = () => {
|
|
6578
|
+
const content = text.trim();
|
|
6579
|
+
if (!content) return;
|
|
6580
|
+
setText("");
|
|
6581
|
+
setOptimistic((prev) => [
|
|
6582
|
+
...prev,
|
|
6583
|
+
{
|
|
6584
|
+
id: `pending-${Date.now()}`,
|
|
6585
|
+
content,
|
|
6586
|
+
senderName: "You",
|
|
6587
|
+
isOwn: true,
|
|
6588
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
6589
|
+
pending: true
|
|
6590
|
+
}
|
|
6591
|
+
]);
|
|
6592
|
+
const payload = {
|
|
6593
|
+
tempId: `temp-${Date.now()}`,
|
|
6594
|
+
sender: loggedUserId,
|
|
6595
|
+
content,
|
|
6596
|
+
type: "text",
|
|
6597
|
+
conversationId
|
|
6598
|
+
};
|
|
6599
|
+
if (isGroupCall) {
|
|
6600
|
+
emitGroupMessageSend2(payload);
|
|
6601
|
+
} else {
|
|
6602
|
+
const conversation = findConversationById(conversations, conversationId);
|
|
6603
|
+
payload.receiver = getOtherParticipantId(conversation, loggedUserId);
|
|
6604
|
+
emitDmSend(payload);
|
|
6605
|
+
}
|
|
6606
|
+
};
|
|
6607
|
+
return /* @__PURE__ */ jsxs("div", { className: cn("flex flex-col border-l border-white/10 bg-neutral-950/95", className), children: [
|
|
6608
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between border-b border-white/10 px-4 py-3", children: [
|
|
6609
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm font-semibold text-white/90", children: "In-call chat" }),
|
|
6610
|
+
/* @__PURE__ */ jsx(
|
|
6611
|
+
"button",
|
|
6612
|
+
{
|
|
6613
|
+
type: "button",
|
|
6614
|
+
"aria-label": "Close chat panel",
|
|
6615
|
+
onClick: onClose,
|
|
6616
|
+
className: "flex size-7 items-center justify-center rounded-lg text-white/60 transition-colors hover:bg-white/10 hover:text-white",
|
|
6617
|
+
children: /* @__PURE__ */ jsx(X, { size: 16 })
|
|
6618
|
+
}
|
|
6619
|
+
)
|
|
6620
|
+
] }),
|
|
6621
|
+
/* @__PURE__ */ jsxs("div", { className: "flex-1 space-y-3 overflow-y-auto px-4 py-3", children: [
|
|
6622
|
+
messages.length === 0 && /* @__PURE__ */ jsx("p", { className: "pt-6 text-center text-xs text-white/40", children: "No messages yet \u2014 say hi without leaving the call." }),
|
|
6623
|
+
messages.map((message) => /* @__PURE__ */ jsxs("div", { className: cn("flex flex-col", message.isOwn && "items-end"), children: [
|
|
6624
|
+
/* @__PURE__ */ jsx("span", { className: "mb-0.5 text-[11px] font-medium text-white/40", children: message.isOwn ? "You" : message.senderName }),
|
|
6625
|
+
/* @__PURE__ */ jsx(
|
|
6626
|
+
"div",
|
|
6627
|
+
{
|
|
6628
|
+
className: cn(
|
|
6629
|
+
"max-w-[85%] rounded-2xl px-3 py-1.5 text-sm break-words",
|
|
6630
|
+
message.isOwn ? "rounded-br-sm bg-(--chat-theme,#4f46e5) text-white" : "rounded-bl-sm bg-white/10 text-white/90",
|
|
6631
|
+
message.pending && "opacity-60"
|
|
6632
|
+
),
|
|
6633
|
+
children: message.content
|
|
6634
|
+
}
|
|
6635
|
+
)
|
|
6636
|
+
] }, message.id)),
|
|
6637
|
+
/* @__PURE__ */ jsx("div", { ref: listEndRef })
|
|
6638
|
+
] }),
|
|
6639
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 border-t border-white/10 p-3", children: [
|
|
6640
|
+
/* @__PURE__ */ jsx(
|
|
6641
|
+
"input",
|
|
6642
|
+
{
|
|
6643
|
+
value: text,
|
|
6644
|
+
onChange: (e) => setText(e.target.value),
|
|
6645
|
+
onKeyDown: (e) => {
|
|
6646
|
+
if (e.key === "Enter" && !e.shiftKey) {
|
|
6647
|
+
e.preventDefault();
|
|
6648
|
+
handleSend();
|
|
6649
|
+
}
|
|
6650
|
+
},
|
|
6651
|
+
placeholder: "Message\u2026",
|
|
6652
|
+
className: "min-w-0 flex-1 rounded-full border border-white/10 bg-white/5 px-3.5 py-2 text-sm text-white placeholder:text-white/40 focus:outline-none focus:ring-1 focus:ring-white/30"
|
|
6653
|
+
}
|
|
6654
|
+
),
|
|
6655
|
+
/* @__PURE__ */ jsx(
|
|
6656
|
+
Button,
|
|
6657
|
+
{
|
|
6658
|
+
variant: "ghost",
|
|
6659
|
+
size: "icon",
|
|
6660
|
+
"aria-label": "Send message",
|
|
6661
|
+
disabled: !text.trim(),
|
|
6662
|
+
onClick: handleSend,
|
|
6663
|
+
className: "size-9 shrink-0 rounded-full bg-white/10 text-white hover:bg-white/20 disabled:opacity-30",
|
|
6664
|
+
children: /* @__PURE__ */ jsx(Send, { size: 16 })
|
|
6665
|
+
}
|
|
6666
|
+
)
|
|
6667
|
+
] })
|
|
6668
|
+
] });
|
|
5746
6669
|
};
|
|
5747
|
-
var
|
|
6670
|
+
var CallChatPanel_default = CallChatPanel;
|
|
6671
|
+
var CallSession2 = React11__default.lazy(() => Promise.resolve().then(() => (init_CallSession(), CallSession_exports)));
|
|
5748
6672
|
var CallOverlay = () => {
|
|
5749
6673
|
const uiStatus = useCallStore((s) => s.uiStatus);
|
|
5750
6674
|
const mode = useCallStore((s) => s.mode);
|
|
5751
6675
|
const callId = useCallStore((s) => s.callId);
|
|
6676
|
+
const conversationId = useCallStore((s) => s.conversationId);
|
|
5752
6677
|
const meetingId = useCallStore((s) => s.meetingId);
|
|
5753
6678
|
const token = useCallStore((s) => s.token);
|
|
5754
6679
|
const error = useCallStore((s) => s.error);
|
|
5755
|
-
const
|
|
6680
|
+
const peer = useCallStore((s) => s.peer);
|
|
5756
6681
|
const caller = useCallStore((s) => s.caller);
|
|
6682
|
+
const isGroupCall = useCallStore((s) => s.isGroupCall);
|
|
5757
6683
|
const callStartedAt = useCallStore((s) => s.callStartedAt);
|
|
5758
6684
|
const cancelCall = useCallStore((s) => s.cancelCall);
|
|
5759
6685
|
const reset = useCallStore((s) => s.reset);
|
|
5760
6686
|
const notifyServerCallFailed = useCallStore((s) => s.notifyServerCallFailed);
|
|
5761
6687
|
const [sessionError, setSessionError] = useState(null);
|
|
6688
|
+
const [isChatOpen, setIsChatOpen] = useState(false);
|
|
5762
6689
|
const secondsLeft = useRingCountdown(uiStatus === "ringing-out", callId);
|
|
5763
6690
|
const duration = useCallDuration(callStartedAt);
|
|
5764
6691
|
useEffect(() => {
|
|
5765
6692
|
setSessionError(null);
|
|
6693
|
+
setIsChatOpen(false);
|
|
5766
6694
|
}, [callId]);
|
|
5767
6695
|
useEffect(() => {
|
|
5768
6696
|
if (sessionError) notifyServerCallFailed();
|
|
5769
6697
|
}, [sessionError, notifyServerCallFailed]);
|
|
5770
6698
|
const isOpen = uiStatus !== "idle" && uiStatus !== "incoming";
|
|
5771
|
-
if (!isOpen
|
|
6699
|
+
if (!isOpen) return null;
|
|
5772
6700
|
const displayError = error || sessionError;
|
|
5773
|
-
const peerName =
|
|
5774
|
-
const peerAvatar =
|
|
6701
|
+
const peerName = peer?.name || caller?.name || "";
|
|
6702
|
+
const peerAvatar = peer?.avatar || caller?.profileImage || void 0;
|
|
5775
6703
|
const isVideo = mode === "video";
|
|
5776
|
-
return
|
|
5777
|
-
/* @__PURE__ */ jsx(
|
|
5778
|
-
|
|
5779
|
-
{
|
|
5780
|
-
|
|
5781
|
-
|
|
5782
|
-
|
|
5783
|
-
|
|
5784
|
-
|
|
5785
|
-
|
|
5786
|
-
|
|
5787
|
-
|
|
5788
|
-
|
|
5789
|
-
|
|
5790
|
-
|
|
5791
|
-
|
|
5792
|
-
|
|
5793
|
-
|
|
5794
|
-
|
|
5795
|
-
|
|
5796
|
-
peerName && /* @__PURE__ */ jsx("span", { className: "font-medium text-white/90", children: peerName }),
|
|
5797
|
-
/* @__PURE__ */ jsx("span", { "aria-hidden": true, className: "text-white/30", children: "\xB7" }),
|
|
5798
|
-
/* @__PURE__ */ jsx("span", { className: "tabular-nums", children: duration })
|
|
6704
|
+
return /* @__PURE__ */ jsx("div", { className: "absolute inset-0 z-40 flex flex-col overflow-hidden bg-gradient-to-b from-neutral-900 via-neutral-950 to-black text-white", children: displayError ? /* @__PURE__ */ jsxs("div", { className: "flex flex-1 flex-col items-center justify-center gap-4 p-6 text-center", children: [
|
|
6705
|
+
/* @__PURE__ */ jsx("p", { className: "max-w-sm text-sm text-white/80", children: displayError }),
|
|
6706
|
+
/* @__PURE__ */ jsxs(Button, { variant: "secondary", onClick: reset, children: [
|
|
6707
|
+
/* @__PURE__ */ jsx(X, { size: 16, className: "mr-1" }),
|
|
6708
|
+
" Close"
|
|
6709
|
+
] })
|
|
6710
|
+
] }) : uiStatus === "in-call" && meetingId && token && mode && conversationId ? /* @__PURE__ */ jsxs("div", { className: "flex flex-1 flex-col min-h-0", children: [
|
|
6711
|
+
/* @__PURE__ */ jsxs("div", { className: "flex shrink-0 items-center justify-center gap-2 px-4 py-3 text-sm text-white/70", children: [
|
|
6712
|
+
isGroupCall && /* @__PURE__ */ jsx(Users, { size: 14, className: "text-white/50" }),
|
|
6713
|
+
peerName && /* @__PURE__ */ jsx("span", { className: "font-medium text-white/90", children: peerName }),
|
|
6714
|
+
/* @__PURE__ */ jsx("span", { "aria-hidden": true, className: "text-white/30", children: "\xB7" }),
|
|
6715
|
+
/* @__PURE__ */ jsx("span", { className: "tabular-nums", children: duration })
|
|
6716
|
+
] }),
|
|
6717
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-1 min-h-0", children: [
|
|
6718
|
+
/* @__PURE__ */ jsx(
|
|
6719
|
+
React11__default.Suspense,
|
|
6720
|
+
{
|
|
6721
|
+
fallback: /* @__PURE__ */ jsxs("div", { className: "flex flex-1 flex-col items-center justify-center gap-3 text-white/80", children: [
|
|
6722
|
+
/* @__PURE__ */ jsx(Loader2, { className: "size-8 animate-spin" }),
|
|
6723
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm", children: "Loading call\u2026" })
|
|
5799
6724
|
] }),
|
|
5800
|
-
/* @__PURE__ */ jsx(
|
|
5801
|
-
|
|
6725
|
+
children: /* @__PURE__ */ jsx(
|
|
6726
|
+
CallSession2,
|
|
5802
6727
|
{
|
|
5803
|
-
|
|
5804
|
-
|
|
5805
|
-
|
|
5806
|
-
|
|
5807
|
-
|
|
5808
|
-
|
|
5809
|
-
|
|
5810
|
-
|
|
5811
|
-
token,
|
|
5812
|
-
mode,
|
|
5813
|
-
peerName,
|
|
5814
|
-
peerAvatar,
|
|
5815
|
-
onError: setSessionError
|
|
5816
|
-
}
|
|
5817
|
-
)
|
|
6728
|
+
meetingId,
|
|
6729
|
+
token,
|
|
6730
|
+
mode,
|
|
6731
|
+
peerName,
|
|
6732
|
+
peerAvatar,
|
|
6733
|
+
isChatOpen,
|
|
6734
|
+
onToggleChat: () => setIsChatOpen((v) => !v),
|
|
6735
|
+
onError: setSessionError
|
|
5818
6736
|
}
|
|
5819
6737
|
)
|
|
5820
|
-
|
|
5821
|
-
|
|
5822
|
-
|
|
5823
|
-
|
|
5824
|
-
|
|
5825
|
-
|
|
5826
|
-
|
|
5827
|
-
|
|
5828
|
-
|
|
5829
|
-
|
|
5830
|
-
|
|
5831
|
-
|
|
5832
|
-
|
|
5833
|
-
|
|
5834
|
-
|
|
5835
|
-
|
|
5836
|
-
|
|
5837
|
-
|
|
5838
|
-
|
|
5839
|
-
|
|
5840
|
-
|
|
5841
|
-
|
|
5842
|
-
|
|
5843
|
-
|
|
5844
|
-
|
|
5845
|
-
|
|
5846
|
-
|
|
5847
|
-
|
|
5848
|
-
|
|
5849
|
-
|
|
5850
|
-
|
|
5851
|
-
|
|
5852
|
-
|
|
5853
|
-
|
|
5854
|
-
|
|
5855
|
-
|
|
5856
|
-
|
|
5857
|
-
|
|
5858
|
-
|
|
5859
|
-
|
|
5860
|
-
|
|
5861
|
-
|
|
5862
|
-
|
|
5863
|
-
|
|
6738
|
+
}
|
|
6739
|
+
),
|
|
6740
|
+
isChatOpen && /* @__PURE__ */ jsx(
|
|
6741
|
+
CallChatPanel_default,
|
|
6742
|
+
{
|
|
6743
|
+
conversationId,
|
|
6744
|
+
isGroupCall,
|
|
6745
|
+
onClose: () => setIsChatOpen(false),
|
|
6746
|
+
className: "hidden w-80 shrink-0 sm:flex"
|
|
6747
|
+
}
|
|
6748
|
+
)
|
|
6749
|
+
] })
|
|
6750
|
+
] }) : /* @__PURE__ */ jsxs("div", { className: "flex flex-1 flex-col items-center justify-center gap-6 p-6 text-center", children: [
|
|
6751
|
+
/* @__PURE__ */ jsxs("div", { className: "relative flex items-center justify-center", children: [
|
|
6752
|
+
uiStatus === "ringing-out" && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
6753
|
+
/* @__PURE__ */ jsx("span", { className: "absolute inline-flex size-32 animate-ping rounded-full bg-white/10" }),
|
|
6754
|
+
/* @__PURE__ */ jsx(
|
|
6755
|
+
"span",
|
|
6756
|
+
{
|
|
6757
|
+
className: "absolute inline-flex size-32 animate-ping rounded-full bg-white/5",
|
|
6758
|
+
style: { animationDelay: "0.6s" }
|
|
6759
|
+
}
|
|
6760
|
+
)
|
|
6761
|
+
] }),
|
|
6762
|
+
peerName ? /* @__PURE__ */ jsxs(Avatar, { className: "relative size-28 ring-4 ring-white/10", children: [
|
|
6763
|
+
/* @__PURE__ */ jsx(AvatarImage, { src: peerAvatar, alt: peerName }),
|
|
6764
|
+
/* @__PURE__ */ jsx(AvatarFallback, { className: "bg-neutral-700 text-3xl font-medium text-white", children: peerName.charAt(0).toUpperCase() })
|
|
6765
|
+
] }) : /* @__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 }) })
|
|
6766
|
+
] }),
|
|
6767
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
6768
|
+
peerName && /* @__PURE__ */ jsxs("p", { className: "flex items-center justify-center gap-1.5 text-xl font-semibold tracking-tight", children: [
|
|
6769
|
+
isGroupCall && /* @__PURE__ */ jsx(Users, { size: 16, className: "text-white/50" }),
|
|
6770
|
+
peerName
|
|
6771
|
+
] }),
|
|
6772
|
+
/* @__PURE__ */ jsxs("p", { className: "mt-1 text-sm text-white/60", children: [
|
|
6773
|
+
uiStatus === "creating" && "Starting call\u2026",
|
|
6774
|
+
uiStatus === "ringing-out" && `${isVideo ? "Video calling" : "Calling"}\u2026 \xB7 ${secondsLeft}s`,
|
|
6775
|
+
uiStatus === "joining" && "Connecting\u2026"
|
|
6776
|
+
] })
|
|
6777
|
+
] }),
|
|
6778
|
+
uiStatus === "ringing-out" && /* @__PURE__ */ jsxs("div", { className: "mt-1 flex flex-col items-center gap-2", children: [
|
|
6779
|
+
/* @__PURE__ */ jsx(
|
|
6780
|
+
Button,
|
|
6781
|
+
{
|
|
6782
|
+
variant: "ghost",
|
|
6783
|
+
size: "icon",
|
|
6784
|
+
className: "size-16 rounded-full bg-red-600 text-white shadow-lg shadow-red-600/30 transition-transform hover:scale-105 hover:bg-red-500 active:scale-95",
|
|
6785
|
+
"aria-label": "Cancel call",
|
|
6786
|
+
onClick: cancelCall,
|
|
6787
|
+
children: /* @__PURE__ */ jsx(PhoneOff, { size: 24 })
|
|
6788
|
+
}
|
|
6789
|
+
),
|
|
6790
|
+
/* @__PURE__ */ jsx("span", { className: "text-xs font-medium text-white/60", children: "Cancel" })
|
|
6791
|
+
] })
|
|
6792
|
+
] }) });
|
|
5864
6793
|
};
|
|
5865
6794
|
var CallOverlay_default = CallOverlay;
|
|
5866
6795
|
|
|
@@ -6411,6 +7340,8 @@ var IncomingCallDialog = ({ ringtoneUrl }) => {
|
|
|
6411
7340
|
const uiStatus = useCallStore((s) => s.uiStatus);
|
|
6412
7341
|
const callId = useCallStore((s) => s.callId);
|
|
6413
7342
|
const caller = useCallStore((s) => s.caller);
|
|
7343
|
+
const peer = useCallStore((s) => s.peer);
|
|
7344
|
+
const isGroupCall = useCallStore((s) => s.isGroupCall);
|
|
6414
7345
|
const mode = useCallStore((s) => s.mode);
|
|
6415
7346
|
const acceptCall = useCallStore((s) => s.acceptCall);
|
|
6416
7347
|
const rejectCall = useCallStore((s) => s.rejectCall);
|
|
@@ -6428,7 +7359,9 @@ var IncomingCallDialog = ({ ringtoneUrl }) => {
|
|
|
6428
7359
|
}
|
|
6429
7360
|
}, [isOpen, ringtoneUrl]);
|
|
6430
7361
|
if (!isOpen) return null;
|
|
6431
|
-
const
|
|
7362
|
+
const callerName = caller?.name || "Unknown caller";
|
|
7363
|
+
const name = isGroupCall ? peer?.name || callerName : callerName;
|
|
7364
|
+
const avatar = isGroupCall ? peer?.avatar || void 0 : caller?.profileImage;
|
|
6432
7365
|
const isVideo = mode === "video";
|
|
6433
7366
|
return /* @__PURE__ */ jsxs(Dialog, { open: isOpen, onOpenChange: () => void 0, children: [
|
|
6434
7367
|
ringtoneUrl && /* @__PURE__ */ jsx("audio", { ref: audioRef, src: ringtoneUrl, loop: true }),
|
|
@@ -6450,7 +7383,7 @@ var IncomingCallDialog = ({ ringtoneUrl }) => {
|
|
|
6450
7383
|
}
|
|
6451
7384
|
),
|
|
6452
7385
|
/* @__PURE__ */ jsxs(Avatar, { className: "relative size-24 ring-4 ring-white/10", children: [
|
|
6453
|
-
/* @__PURE__ */ jsx(AvatarImage, { src:
|
|
7386
|
+
/* @__PURE__ */ jsx(AvatarImage, { src: avatar, alt: name }),
|
|
6454
7387
|
/* @__PURE__ */ jsx(AvatarFallback, { className: "bg-neutral-700 text-2xl font-medium text-white", children: name.charAt(0).toUpperCase() })
|
|
6455
7388
|
] }),
|
|
6456
7389
|
/* @__PURE__ */ jsx(
|
|
@@ -6466,13 +7399,7 @@ var IncomingCallDialog = ({ ringtoneUrl }) => {
|
|
|
6466
7399
|
] }),
|
|
6467
7400
|
/* @__PURE__ */ jsxs("div", { className: "text-center", children: [
|
|
6468
7401
|
/* @__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
|
-
] })
|
|
7402
|
+
/* @__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
7403
|
] }),
|
|
6477
7404
|
/* @__PURE__ */ jsxs("div", { className: "mt-1 flex items-center justify-center gap-10", children: [
|
|
6478
7405
|
/* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center gap-2", children: [
|
|
@@ -6559,7 +7486,7 @@ var Chat = ({
|
|
|
6559
7486
|
const status = useStore((s) => s.status);
|
|
6560
7487
|
const error = useStore((s) => s.error);
|
|
6561
7488
|
useChatSync();
|
|
6562
|
-
|
|
7489
|
+
React11__default.useEffect(() => {
|
|
6563
7490
|
const clientId = client?.id;
|
|
6564
7491
|
const initializeChat = async () => {
|
|
6565
7492
|
if (!clientId || !loggedUserDetails) return;
|
|
@@ -6649,28 +7576,28 @@ var Chat = ({
|
|
|
6649
7576
|
queryParamApis: queryParamApis ?? ["get"],
|
|
6650
7577
|
queryParamsByApi: queryParamsByApi ?? {}
|
|
6651
7578
|
});
|
|
6652
|
-
|
|
7579
|
+
React11__default.useEffect(() => {
|
|
6653
7580
|
setChatQueryParams(queryParams);
|
|
6654
7581
|
setChatQueryParamApis(queryParamApis);
|
|
6655
7582
|
setChatQueryParamsByApi(queryParamsByApi);
|
|
6656
7583
|
}, [queryConfigKey]);
|
|
6657
|
-
|
|
7584
|
+
React11__default.useEffect(() => {
|
|
6658
7585
|
if (lastDM && onMessageReceived) {
|
|
6659
7586
|
onMessageReceived(lastDM);
|
|
6660
7587
|
}
|
|
6661
7588
|
}, [lastDM, onMessageReceived]);
|
|
6662
|
-
|
|
7589
|
+
React11__default.useEffect(() => {
|
|
6663
7590
|
if (status === "connected" && onSocketConnected) {
|
|
6664
7591
|
const socketId = useStore.getState().socketId;
|
|
6665
7592
|
if (socketId) onSocketConnected(socketId);
|
|
6666
7593
|
}
|
|
6667
7594
|
}, [status, onSocketConnected]);
|
|
6668
|
-
|
|
7595
|
+
React11__default.useEffect(() => {
|
|
6669
7596
|
if (status === "error" && error && onSocketError) {
|
|
6670
7597
|
onSocketError(error);
|
|
6671
7598
|
}
|
|
6672
7599
|
}, [status, error, onSocketError]);
|
|
6673
|
-
const contextValue =
|
|
7600
|
+
const contextValue = React11__default.useMemo(
|
|
6674
7601
|
() => ({
|
|
6675
7602
|
onSendMessage,
|
|
6676
7603
|
client,
|
|
@@ -6690,7 +7617,7 @@ var Chat = ({
|
|
|
6690
7617
|
messagesData
|
|
6691
7618
|
]
|
|
6692
7619
|
);
|
|
6693
|
-
const customization =
|
|
7620
|
+
const customization = React11__default.useMemo(
|
|
6694
7621
|
() => ({
|
|
6695
7622
|
components: uiConfig.components,
|
|
6696
7623
|
classNames: uiConfig.classNames,
|
|
@@ -6698,7 +7625,7 @@ var Chat = ({
|
|
|
6698
7625
|
}),
|
|
6699
7626
|
[uiConfig.components, uiConfig.classNames, uiConfig.features]
|
|
6700
7627
|
);
|
|
6701
|
-
return /* @__PURE__ */ jsx(ChatContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsx(ChatCustomizationProvider, { customization, children: /* @__PURE__ */ jsx(TooltipProvider, { children: /* @__PURE__ */ jsx("div", { className: "chat-container-root flex h-full min-h-0 w-full flex-1 flex-col overflow-hidden", children: isSessionReady ? /* @__PURE__ */ jsxs(ChatEffectiveSettingsProvider, { enabled: true, children: [
|
|
7628
|
+
return /* @__PURE__ */ jsx(ChatContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsx(ChatCustomizationProvider, { customization, children: /* @__PURE__ */ jsx(TooltipProvider, { children: /* @__PURE__ */ jsx("div", { className: "chat-container-root relative flex h-full min-h-0 w-full flex-1 flex-col overflow-hidden", children: isSessionReady ? /* @__PURE__ */ jsxs(ChatEffectiveSettingsProvider, { enabled: true, children: [
|
|
6702
7629
|
children,
|
|
6703
7630
|
/* @__PURE__ */ jsx(CallLayer_default, {})
|
|
6704
7631
|
] }) : /* @__PURE__ */ jsx("div", { className: "flex h-full items-center justify-center text-gray-400 font-bold", children: "Initializing session..." }) }) }) }) });
|
|
@@ -6710,265 +7637,7 @@ init_utils2();
|
|
|
6710
7637
|
// src/hooks/useChatController.ts
|
|
6711
7638
|
init_useStore();
|
|
6712
7639
|
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
|
|
7640
|
+
init_open_conversation();
|
|
6972
7641
|
function resolveMessageType(content, attachments) {
|
|
6973
7642
|
if (attachments.length === 0) return "text";
|
|
6974
7643
|
if (!content.trim() && attachments.length === 1) {
|
|
@@ -7413,6 +8082,7 @@ init_avatar();
|
|
|
7413
8082
|
|
|
7414
8083
|
// src/chat/ChatThemeToggle.tsx
|
|
7415
8084
|
init_button();
|
|
8085
|
+
init_tooltip();
|
|
7416
8086
|
init_utils2();
|
|
7417
8087
|
var ChatThemeToggle = ({ className }) => {
|
|
7418
8088
|
const { isDark, toggleColorMode } = useChatTheme();
|
|
@@ -7470,6 +8140,7 @@ var buildLoggedUserProfile = (loggedUserDetails) => ({
|
|
|
7470
8140
|
|
|
7471
8141
|
// src/chat/sidebar/create-chat-dialog/CreateChatDialog.tsx
|
|
7472
8142
|
init_button();
|
|
8143
|
+
init_tooltip();
|
|
7473
8144
|
init_utils2();
|
|
7474
8145
|
|
|
7475
8146
|
// src/chat/sidebar/create-chat-dialog/useCreateChatDialog.ts
|
|
@@ -7920,6 +8591,9 @@ function GroupCreateTab({
|
|
|
7920
8591
|
) })
|
|
7921
8592
|
] });
|
|
7922
8593
|
}
|
|
8594
|
+
|
|
8595
|
+
// src/chat/components/AdminPermissionTooltip.tsx
|
|
8596
|
+
init_tooltip();
|
|
7923
8597
|
init_utils2();
|
|
7924
8598
|
function AdminPermissionTooltip({
|
|
7925
8599
|
disabled,
|
|
@@ -7931,9 +8605,9 @@ function AdminPermissionTooltip({
|
|
|
7931
8605
|
if (!disabled || !message) {
|
|
7932
8606
|
return /* @__PURE__ */ jsx(Fragment, { children });
|
|
7933
8607
|
}
|
|
7934
|
-
const gatedChildren =
|
|
7935
|
-
if (!
|
|
7936
|
-
return
|
|
8608
|
+
const gatedChildren = React11__default.Children.map(children, (child) => {
|
|
8609
|
+
if (!React11__default.isValidElement(child)) return child;
|
|
8610
|
+
return React11__default.cloneElement(child, {
|
|
7937
8611
|
className: cn(child.props.className, "pointer-events-none")
|
|
7938
8612
|
});
|
|
7939
8613
|
});
|
|
@@ -8546,6 +9220,7 @@ var LoggedUserSettingsContent_default = LoggedUserSettingsContent;
|
|
|
8546
9220
|
|
|
8547
9221
|
// src/chat/sidebar/LoggedUserSettingsTrigger.tsx
|
|
8548
9222
|
init_button();
|
|
9223
|
+
init_tooltip();
|
|
8549
9224
|
init_utils2();
|
|
8550
9225
|
var LoggedUserSettingsTrigger = ({
|
|
8551
9226
|
onClick,
|
|
@@ -9873,41 +10548,7 @@ var ChatSocketStatus_default = ChatSocketStatus;
|
|
|
9873
10548
|
|
|
9874
10549
|
// src/chat/header/HeaderSearchPopover.tsx
|
|
9875
10550
|
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
|
|
10551
|
+
init_popover();
|
|
9911
10552
|
init_utils2();
|
|
9912
10553
|
function escapeRegExp(value) {
|
|
9913
10554
|
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
@@ -9924,7 +10565,7 @@ function highlightSearchMatch(text, query) {
|
|
|
9924
10565
|
children: part
|
|
9925
10566
|
},
|
|
9926
10567
|
`${part}-${index}`
|
|
9927
|
-
) : /* @__PURE__ */ jsx(
|
|
10568
|
+
) : /* @__PURE__ */ jsx(React11__default.Fragment, { children: part }, `${part}-${index}`)
|
|
9928
10569
|
);
|
|
9929
10570
|
}
|
|
9930
10571
|
function getMessagePreview(message) {
|
|
@@ -10246,6 +10887,7 @@ init_call_store();
|
|
|
10246
10887
|
init_utils2();
|
|
10247
10888
|
|
|
10248
10889
|
// src/chat/header/header-right/OptionsMenuItem.tsx
|
|
10890
|
+
init_tooltip();
|
|
10249
10891
|
init_utils2();
|
|
10250
10892
|
function OptionsMenuItem({
|
|
10251
10893
|
icon: Icon,
|
|
@@ -10322,20 +10964,20 @@ var HeaderRightSide = ({
|
|
|
10322
10964
|
const startCall = useCallStore((s) => s.startCall);
|
|
10323
10965
|
const showPhoneCall = features.showPhoneCall ?? defaultChatFeatures.showPhoneCall;
|
|
10324
10966
|
const showVideoCall = features.showVideoCall ?? defaultChatFeatures.showVideoCall;
|
|
10325
|
-
const
|
|
10967
|
+
const peer = { name: activeChannel.name, avatar: activeChannel.avatar || activeChannel.image };
|
|
10326
10968
|
const handlePhoneCall = () => {
|
|
10327
10969
|
if (features.onPhoneCall) {
|
|
10328
10970
|
features.onPhoneCall({ activeChannel, conversationId });
|
|
10329
10971
|
return;
|
|
10330
10972
|
}
|
|
10331
|
-
if (conversationId) startCall(conversationId, "audio",
|
|
10973
|
+
if (conversationId) startCall(conversationId, "audio", peer, activeChannel.isGroup);
|
|
10332
10974
|
};
|
|
10333
10975
|
const handleVideoCall = () => {
|
|
10334
10976
|
if (features.onVideoCall) {
|
|
10335
10977
|
features.onVideoCall({ activeChannel, conversationId });
|
|
10336
10978
|
return;
|
|
10337
10979
|
}
|
|
10338
|
-
if (conversationId) startCall(conversationId, "video",
|
|
10980
|
+
if (conversationId) startCall(conversationId, "video", peer, activeChannel.isGroup);
|
|
10339
10981
|
};
|
|
10340
10982
|
const headerItemGapClass = "gap-0.5";
|
|
10341
10983
|
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";
|
|
@@ -15928,6 +16570,7 @@ init_avatar();
|
|
|
15928
16570
|
|
|
15929
16571
|
// src/chat/message/MessageReactions.tsx
|
|
15930
16572
|
init_utils2();
|
|
16573
|
+
init_tooltip();
|
|
15931
16574
|
var MessageReactions = ({
|
|
15932
16575
|
groupedReactions,
|
|
15933
16576
|
reactions,
|
|
@@ -15980,6 +16623,7 @@ var MessageReactions = ({
|
|
|
15980
16623
|
var MessageReactions_default = MessageReactions;
|
|
15981
16624
|
init_utils2();
|
|
15982
16625
|
init_button();
|
|
16626
|
+
init_popover();
|
|
15983
16627
|
var MessageToolbar = ({
|
|
15984
16628
|
isSender,
|
|
15985
16629
|
isRecent,
|
|
@@ -18130,6 +18774,9 @@ function useActiveChannelMessages({
|
|
|
18130
18774
|
};
|
|
18131
18775
|
}
|
|
18132
18776
|
|
|
18777
|
+
// src/chat/active-channel-messages/ChatDateJumpMenu.tsx
|
|
18778
|
+
init_popover();
|
|
18779
|
+
|
|
18133
18780
|
// src/chat/ui/calendar.tsx
|
|
18134
18781
|
init_utils2();
|
|
18135
18782
|
init_button();
|
|
@@ -18286,8 +18933,8 @@ function CalendarDayButton({
|
|
|
18286
18933
|
...props
|
|
18287
18934
|
}) {
|
|
18288
18935
|
const defaultClassNames = getDefaultClassNames();
|
|
18289
|
-
const ref =
|
|
18290
|
-
|
|
18936
|
+
const ref = React11.useRef(null);
|
|
18937
|
+
React11.useEffect(() => {
|
|
18291
18938
|
if (modifiers.focused) ref.current?.focus();
|
|
18292
18939
|
}, [modifiers.focused]);
|
|
18293
18940
|
return /* @__PURE__ */ jsx(
|
|
@@ -18663,6 +19310,9 @@ function ComposerStatusBanner({
|
|
|
18663
19310
|
] }) });
|
|
18664
19311
|
}
|
|
18665
19312
|
|
|
19313
|
+
// src/chat/channel-message-box/ChannelMessageBoxView.tsx
|
|
19314
|
+
init_popover();
|
|
19315
|
+
|
|
18666
19316
|
// src/chat/composer-attachment/ComposerAttachmentPreview.tsx
|
|
18667
19317
|
init_utils2();
|
|
18668
19318
|
init_button();
|
|
@@ -19259,10 +19909,10 @@ function useChannelMessageBox({
|
|
|
19259
19909
|
const composerDisabledReason = conversationId ? composerDisabledByConversation[conversationId] : void 0;
|
|
19260
19910
|
const fileInputRef = useRef(null);
|
|
19261
19911
|
const attachmentsCountRef = useRef(0);
|
|
19262
|
-
|
|
19912
|
+
React11__default.useEffect(() => {
|
|
19263
19913
|
attachmentsCountRef.current = state.attachments.length;
|
|
19264
19914
|
}, [state.attachments.length]);
|
|
19265
|
-
|
|
19915
|
+
React11__default.useEffect(() => {
|
|
19266
19916
|
const draft = {
|
|
19267
19917
|
messageInput: state.messageInput,
|
|
19268
19918
|
attachments: state.attachments
|
|
@@ -19270,7 +19920,7 @@ function useChannelMessageBox({
|
|
|
19270
19920
|
syncComposerRef(draft);
|
|
19271
19921
|
updateConversationDraft(conversationIdRef.current, draft);
|
|
19272
19922
|
}, [state.messageInput, state.attachments, syncComposerRef]);
|
|
19273
|
-
|
|
19923
|
+
React11__default.useEffect(() => {
|
|
19274
19924
|
if (typingTimeoutRef.current) {
|
|
19275
19925
|
clearTimeout(typingTimeoutRef.current);
|
|
19276
19926
|
typingTimeoutRef.current = null;
|
|
@@ -20331,6 +20981,7 @@ var ChatViewport_default = ChatViewport;
|
|
|
20331
20981
|
init_utils2();
|
|
20332
20982
|
init_normalize_helpers();
|
|
20333
20983
|
init_useStore();
|
|
20984
|
+
init_open_conversation();
|
|
20334
20985
|
var RETRY_DELAYS_MS = [1e3, 3e3, 8e3, 2e4];
|
|
20335
20986
|
var recentToastIds = /* @__PURE__ */ new Set();
|
|
20336
20987
|
var rememberToastId = (id) => {
|
|
@@ -20631,8 +21282,8 @@ var ChatMain = ({
|
|
|
20631
21282
|
viewportHeight = "full",
|
|
20632
21283
|
viewportClassName
|
|
20633
21284
|
}) => {
|
|
20634
|
-
const clientObj =
|
|
20635
|
-
const uiConfigObj =
|
|
21285
|
+
const clientObj = React11__default.useMemo(() => ({ id: clientId }), [clientId]);
|
|
21286
|
+
const uiConfigObj = React11__default.useMemo(
|
|
20636
21287
|
() => ({
|
|
20637
21288
|
...uiConfig || {},
|
|
20638
21289
|
components: { ...uiConfig?.components, ...components },
|
|
@@ -21009,6 +21660,9 @@ var packageDefaultComponents = {
|
|
|
21009
21660
|
ChatSocketStatus: ChatSocketStatus_default
|
|
21010
21661
|
};
|
|
21011
21662
|
|
|
21663
|
+
// src/index.ts
|
|
21664
|
+
init_open_conversation();
|
|
21665
|
+
|
|
21012
21666
|
// src/hooks/useAdminFeatureGate.ts
|
|
21013
21667
|
function useAdminFeatureGate(feature) {
|
|
21014
21668
|
const ctx = useEffectiveSettingsOptional();
|