@realtimexsco/live-chat 1.5.2 → 1.5.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +2278 -1837
- 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 +1964 -1510
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -4,13 +4,13 @@ import { io } from 'socket.io-client';
|
|
|
4
4
|
import { persist } from 'zustand/middleware';
|
|
5
5
|
import { clsx } from 'clsx';
|
|
6
6
|
import { twMerge } from 'tailwind-merge';
|
|
7
|
-
import {
|
|
8
|
-
import { Dialog as Dialog$1, Slot, Avatar as Avatar$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';
|
|
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';
|
|
9
8
|
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
|
|
10
|
-
import
|
|
11
|
-
import
|
|
12
|
-
import
|
|
13
|
-
import {
|
|
9
|
+
import { cva } from 'class-variance-authority';
|
|
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 { createPortal, 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,301 +375,1071 @@ 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
|
-
|
|
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",
|
|
671
1443
|
ONGOING: "ongoing",
|
|
672
1444
|
ENDED: "ended",
|
|
673
1445
|
MISSED: "missed",
|
|
@@ -676,22 +1448,17 @@ var init_call_types = __esm({
|
|
|
676
1448
|
};
|
|
677
1449
|
CALL_RING_TIMEOUT_MS = 4e4;
|
|
678
1450
|
CLIENT_RING_FALLBACK_MS = CALL_RING_TIMEOUT_MS + 5e3;
|
|
679
|
-
CALL_LOG_EVENT = "realtimex:call-log";
|
|
680
1451
|
}
|
|
681
1452
|
});
|
|
682
|
-
var
|
|
1453
|
+
var ringTimer, clearRingTimer, scheduleRingTimer, resolveGroupPeer, idleState, useCallStore;
|
|
683
1454
|
var init_call_store = __esm({
|
|
684
1455
|
"src/call/call.store.ts"() {
|
|
685
1456
|
init_useStore();
|
|
686
1457
|
init_socket_service2();
|
|
1458
|
+
init_open_conversation();
|
|
1459
|
+
init_store_helpers();
|
|
687
1460
|
init_call_api();
|
|
688
1461
|
init_call_types();
|
|
689
|
-
emitCallLog = (entry) => {
|
|
690
|
-
try {
|
|
691
|
-
window.dispatchEvent(new CustomEvent(CALL_LOG_EVENT, { detail: entry }));
|
|
692
|
-
} catch {
|
|
693
|
-
}
|
|
694
|
-
};
|
|
695
1462
|
ringTimer = null;
|
|
696
1463
|
clearRingTimer = () => {
|
|
697
1464
|
if (ringTimer) clearTimeout(ringTimer);
|
|
@@ -706,6 +1473,18 @@ var init_call_store = __esm({
|
|
|
706
1473
|
}
|
|
707
1474
|
}, CLIENT_RING_FALLBACK_MS);
|
|
708
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
|
+
};
|
|
709
1488
|
idleState = {
|
|
710
1489
|
uiStatus: "idle",
|
|
711
1490
|
callId: null,
|
|
@@ -716,14 +1495,22 @@ var init_call_store = __esm({
|
|
|
716
1495
|
caller: null,
|
|
717
1496
|
participants: [],
|
|
718
1497
|
error: null,
|
|
719
|
-
|
|
1498
|
+
peer: null,
|
|
1499
|
+
isGroupCall: false,
|
|
720
1500
|
callStartedAt: null
|
|
721
1501
|
};
|
|
722
1502
|
useCallStore = create((set, get) => ({
|
|
723
1503
|
...idleState,
|
|
724
|
-
startCall: async (conversationId, mode,
|
|
1504
|
+
startCall: async (conversationId, mode, peer, isGroupCall = false) => {
|
|
725
1505
|
if (get().uiStatus !== "idle") return;
|
|
726
|
-
set({
|
|
1506
|
+
set({
|
|
1507
|
+
...idleState,
|
|
1508
|
+
uiStatus: "creating",
|
|
1509
|
+
conversationId,
|
|
1510
|
+
mode,
|
|
1511
|
+
peer: peer ?? null,
|
|
1512
|
+
isGroupCall
|
|
1513
|
+
});
|
|
727
1514
|
try {
|
|
728
1515
|
const room = await apiCreateCallRoom(conversationId, mode);
|
|
729
1516
|
set({
|
|
@@ -737,6 +1524,7 @@ var init_call_store = __esm({
|
|
|
737
1524
|
});
|
|
738
1525
|
scheduleRingTimer(room.callId, get, set);
|
|
739
1526
|
socket_service_default.emitCallInvite({ callId: room.callId });
|
|
1527
|
+
requestOpenConversation(room.conversationId);
|
|
740
1528
|
} catch (err) {
|
|
741
1529
|
clearRingTimer();
|
|
742
1530
|
set({
|
|
@@ -754,7 +1542,7 @@ var init_call_store = __esm({
|
|
|
754
1542
|
set({ ...idleState });
|
|
755
1543
|
},
|
|
756
1544
|
acceptCall: async () => {
|
|
757
|
-
const { callId, meetingId } = get();
|
|
1545
|
+
const { callId, meetingId, conversationId } = get();
|
|
758
1546
|
if (!callId || !meetingId) return;
|
|
759
1547
|
clearRingTimer();
|
|
760
1548
|
set({ uiStatus: "joining" });
|
|
@@ -762,6 +1550,7 @@ var init_call_store = __esm({
|
|
|
762
1550
|
const join = await apiFetchJoinToken(meetingId);
|
|
763
1551
|
socket_service_default.emitCallAccept({ callId });
|
|
764
1552
|
set({ uiStatus: "in-call", token: join.token, callStartedAt: Date.now() });
|
|
1553
|
+
if (conversationId) requestOpenConversation(conversationId);
|
|
765
1554
|
} catch (err) {
|
|
766
1555
|
set({
|
|
767
1556
|
...idleState,
|
|
@@ -803,6 +1592,7 @@ var init_call_store = __esm({
|
|
|
803
1592
|
);
|
|
804
1593
|
return;
|
|
805
1594
|
}
|
|
1595
|
+
const groupPeer = resolveGroupPeer(payload.conversationId);
|
|
806
1596
|
set({
|
|
807
1597
|
uiStatus: "incoming",
|
|
808
1598
|
callId: payload.callId,
|
|
@@ -810,7 +1600,9 @@ var init_call_store = __esm({
|
|
|
810
1600
|
meetingId: payload.meetingId,
|
|
811
1601
|
mode: payload.mode,
|
|
812
1602
|
caller: payload.caller,
|
|
813
|
-
participants: payload.participants
|
|
1603
|
+
participants: payload.participants,
|
|
1604
|
+
peer: groupPeer,
|
|
1605
|
+
isGroupCall: !!groupPeer
|
|
814
1606
|
});
|
|
815
1607
|
scheduleRingTimer(payload.callId, get, set);
|
|
816
1608
|
},
|
|
@@ -843,53 +1635,76 @@ var init_call_store = __esm({
|
|
|
843
1635
|
}));
|
|
844
1636
|
},
|
|
845
1637
|
_receiveCancelled: (payload) => {
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
outcome: "missed",
|
|
854
|
-
durationSeconds: 0
|
|
855
|
-
});
|
|
1638
|
+
const state = get();
|
|
1639
|
+
if (state.callId !== payload.callId) return;
|
|
1640
|
+
if (state.uiStatus === "in-call") {
|
|
1641
|
+
console.warn(
|
|
1642
|
+
`[realtimex-call] call_cancelled for ${payload.callId} ignored \u2014 this call is already in-call locally; treating as a stale event.`
|
|
1643
|
+
);
|
|
1644
|
+
return;
|
|
856
1645
|
}
|
|
1646
|
+
clearRingTimer();
|
|
857
1647
|
set({ ...idleState });
|
|
858
1648
|
},
|
|
1649
|
+
// Missed/ended/declined/etc. call log messages are now persisted by the
|
|
1650
|
+
// backend as real chat messages (CALLS_API.md §3.5) — they arrive
|
|
1651
|
+
// through the normal dm_receive/group_message_receive pipeline with
|
|
1652
|
+
// isSystemMessage: true, so they render automatically with zero extra
|
|
1653
|
+
// code here. Nothing to do on this event beyond resetting local state.
|
|
859
1654
|
_receiveEnded: (payload) => {
|
|
860
1655
|
if (get().callId !== payload.callId) return;
|
|
861
1656
|
clearRingTimer();
|
|
862
|
-
emitCallLog({
|
|
863
|
-
conversationId: payload.conversationId,
|
|
864
|
-
mode: payload.mode,
|
|
865
|
-
outcome: payload.durationSeconds > 0 ? "completed" : "declined",
|
|
866
|
-
durationSeconds: payload.durationSeconds
|
|
867
|
-
});
|
|
868
1657
|
set({ ...idleState });
|
|
869
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
|
+
},
|
|
870
1675
|
_receiveBusy: (payload) => {
|
|
871
|
-
|
|
1676
|
+
const state = get();
|
|
1677
|
+
if (state.callId !== payload.callId) return;
|
|
1678
|
+
if (state.uiStatus === "in-call") {
|
|
1679
|
+
console.warn(
|
|
1680
|
+
`[realtimex-call] call_busy for ${payload.callId} ignored \u2014 this call is already in-call locally; treating as a stale event.`
|
|
1681
|
+
);
|
|
1682
|
+
return;
|
|
1683
|
+
}
|
|
872
1684
|
clearRingTimer();
|
|
873
|
-
emitCallLog({
|
|
874
|
-
conversationId: payload.conversationId,
|
|
875
|
-
mode: payload.mode,
|
|
876
|
-
outcome: "busy",
|
|
877
|
-
durationSeconds: 0
|
|
878
|
-
});
|
|
879
1685
|
set({ ...idleState, error: "That user is already on another call." });
|
|
880
1686
|
},
|
|
881
1687
|
// Guarded by callId like every other _receive* handler — a late/stale
|
|
882
1688
|
// call_missed for a call that has already ended (accepted, rejected,
|
|
883
1689
|
// or replaced by a new one) must not clobber whatever is happening now.
|
|
1690
|
+
//
|
|
1691
|
+
// Live testing once caught the backend's 40s ring-timeout firing
|
|
1692
|
+
// call_missed for a callId that was already call_accepted (timer not
|
|
1693
|
+
// cancelled on accept). Backend has since added an atomic
|
|
1694
|
+
// only-from-ringing guard (CALLS_API.md §3.3) so this should no longer
|
|
1695
|
+
// happen — the uiStatus check below is kept as defense-in-depth rather
|
|
1696
|
+
// than removed, since it costs nothing and a regression here would
|
|
1697
|
+
// otherwise kill a live call outright.
|
|
884
1698
|
_receiveMissed: (payload) => {
|
|
885
|
-
|
|
1699
|
+
const state = get();
|
|
1700
|
+
if (state.callId !== payload.callId) return;
|
|
1701
|
+
if (state.uiStatus === "in-call") {
|
|
1702
|
+
console.warn(
|
|
1703
|
+
`[realtimex-call] call_missed for ${payload.callId} ignored \u2014 this call is already in-call locally; treating as a stale event.`
|
|
1704
|
+
);
|
|
1705
|
+
return;
|
|
1706
|
+
}
|
|
886
1707
|
clearRingTimer();
|
|
887
|
-
emitCallLog({
|
|
888
|
-
conversationId: payload.conversationId,
|
|
889
|
-
mode: payload.mode,
|
|
890
|
-
outcome: "missed",
|
|
891
|
-
durationSeconds: 0
|
|
892
|
-
});
|
|
893
1708
|
set({ ...idleState });
|
|
894
1709
|
}
|
|
895
1710
|
}));
|
|
@@ -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);
|
|
@@ -1009,644 +1828,136 @@ var init_socket_service = __esm({
|
|
|
1009
1828
|
this.emit(SOCKET_EVENTS.DM_SEND, payload);
|
|
1010
1829
|
}
|
|
1011
1830
|
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);
|
|
1831
|
+
this.emit(SOCKET_EVENTS.DM_EDIT, payload);
|
|
1085
1832
|
}
|
|
1086
|
-
|
|
1087
|
-
this.emit(SOCKET_EVENTS.
|
|
1833
|
+
emitDmDelete(payload) {
|
|
1834
|
+
this.emit(SOCKET_EVENTS.DM_DELETE, payload);
|
|
1088
1835
|
}
|
|
1089
|
-
|
|
1090
|
-
this.emit(SOCKET_EVENTS.
|
|
1836
|
+
emitDmClearChat(payload) {
|
|
1837
|
+
this.emit(SOCKET_EVENTS.DM_CLEAR_CHAT, payload);
|
|
1091
1838
|
}
|
|
1092
|
-
|
|
1093
|
-
this.emit(SOCKET_EVENTS.
|
|
1839
|
+
emitGroupClearChat(payload) {
|
|
1840
|
+
this.emit(SOCKET_EVENTS.GROUP_CLEAR_CHAT, payload);
|
|
1094
1841
|
}
|
|
1095
|
-
|
|
1096
|
-
this.emit(SOCKET_EVENTS.
|
|
1842
|
+
emitDmDeleteConversation(payload) {
|
|
1843
|
+
this.emit(SOCKET_EVENTS.DM_DELETE_CONVERSATION, payload);
|
|
1097
1844
|
}
|
|
1098
|
-
|
|
1099
|
-
this.emit(SOCKET_EVENTS.
|
|
1845
|
+
emitGroupDeleteConversation(payload) {
|
|
1846
|
+
this.emit(SOCKET_EVENTS.GROUP_DELETE_CONVERSATION, payload);
|
|
1100
1847
|
}
|
|
1101
|
-
|
|
1102
|
-
this.emit(SOCKET_EVENTS.
|
|
1848
|
+
emitDmReactionAdd(payload) {
|
|
1849
|
+
this.emit(SOCKET_EVENTS.DM_REACTION_ADD, payload);
|
|
1103
1850
|
}
|
|
1104
|
-
|
|
1105
|
-
this.emit(SOCKET_EVENTS.
|
|
1851
|
+
emitDmReactionRemove(payload) {
|
|
1852
|
+
this.emit(SOCKET_EVENTS.DM_REACTION_REMOVE, payload);
|
|
1106
1853
|
}
|
|
1107
|
-
|
|
1108
|
-
this.emit(SOCKET_EVENTS.
|
|
1854
|
+
emitTypingStart(payload) {
|
|
1855
|
+
this.emit(SOCKET_EVENTS.DM_TYPING_START, payload);
|
|
1109
1856
|
}
|
|
1110
|
-
|
|
1111
|
-
this.emit(SOCKET_EVENTS.
|
|
1857
|
+
emitTypingStop(payload) {
|
|
1858
|
+
this.emit(SOCKET_EVENTS.DM_TYPING_STOP, payload);
|
|
1112
1859
|
}
|
|
1113
|
-
|
|
1114
|
-
this.emit(SOCKET_EVENTS.
|
|
1860
|
+
emitMarkAsRead(payload) {
|
|
1861
|
+
this.emit(SOCKET_EVENTS.DM_MARK_READ, payload);
|
|
1115
1862
|
}
|
|
1116
|
-
|
|
1117
|
-
this.emit(SOCKET_EVENTS.
|
|
1863
|
+
emitDmPin(payload) {
|
|
1864
|
+
this.emit(SOCKET_EVENTS.DM_PIN, payload);
|
|
1118
1865
|
}
|
|
1119
|
-
|
|
1120
|
-
this.emit(SOCKET_EVENTS.
|
|
1866
|
+
emitDmUnpin(payload) {
|
|
1867
|
+
this.emit(SOCKET_EVENTS.DM_UNPIN, payload);
|
|
1121
1868
|
}
|
|
1122
|
-
|
|
1123
|
-
this.emit(SOCKET_EVENTS.
|
|
1869
|
+
emitDmStar(payload) {
|
|
1870
|
+
this.emit(SOCKET_EVENTS.DM_STAR, payload);
|
|
1124
1871
|
}
|
|
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 };
|
|
1872
|
+
emitDmUnstar(payload) {
|
|
1873
|
+
this.emit(SOCKET_EVENTS.DM_UNSTAR, payload);
|
|
1187
1874
|
}
|
|
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;
|
|
1875
|
+
emitDmForward(payload) {
|
|
1876
|
+
this.emit(SOCKET_EVENTS.DM_FORWARD, payload);
|
|
1239
1877
|
}
|
|
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);
|
|
1878
|
+
emitGroupJoin(payload) {
|
|
1879
|
+
this.emit(SOCKET_EVENTS.GROUP_JOIN, payload);
|
|
1325
1880
|
}
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
resolveOtherParticipant = (conversation, loggedUserId, allUsers) => {
|
|
1329
|
-
if (!conversation || !loggedUserId) {
|
|
1330
|
-
return { id: "", user: null };
|
|
1881
|
+
emitGroupMessageSend(payload) {
|
|
1882
|
+
this.emit(SOCKET_EVENTS.GROUP_MESSAGE_SEND, payload);
|
|
1331
1883
|
}
|
|
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 };
|
|
1884
|
+
emitGroupTypingStart(payload) {
|
|
1885
|
+
this.emit(SOCKET_EVENTS.GROUP_TYPING_START, payload);
|
|
1339
1886
|
}
|
|
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;
|
|
1887
|
+
emitGroupTypingStop(payload) {
|
|
1888
|
+
this.emit(SOCKET_EVENTS.GROUP_TYPING_STOP, payload);
|
|
1367
1889
|
}
|
|
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;
|
|
1890
|
+
emitGroupEdit(payload) {
|
|
1891
|
+
this.emit(SOCKET_EVENTS.GROUP_EDIT, payload);
|
|
1378
1892
|
}
|
|
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);
|
|
1893
|
+
emitGroupReactionAdd(payload) {
|
|
1894
|
+
this.emit(SOCKET_EVENTS.GROUP_REACTION_ADD, payload);
|
|
1393
1895
|
}
|
|
1394
|
-
|
|
1395
|
-
|
|
1896
|
+
emitGroupMarkRead(payload) {
|
|
1897
|
+
this.emit(SOCKET_EVENTS.GROUP_MESSAGE_READ, payload);
|
|
1396
1898
|
}
|
|
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
|
-
}
|
|
1899
|
+
emitGroupReactionRemove(payload) {
|
|
1900
|
+
this.emit(SOCKET_EVENTS.GROUP_REACTION_REMOVE, payload);
|
|
1445
1901
|
}
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
messagesByConversation: nextMessagesByConversation
|
|
1449
|
-
};
|
|
1450
|
-
};
|
|
1451
|
-
isGroupConversation = (conversation) => {
|
|
1452
|
-
if (!conversation) return false;
|
|
1453
|
-
return conversation.conversationType === "group" || !!conversation.isGroup;
|
|
1454
|
-
};
|
|
1455
|
-
normalizeConversationRecord = (conversation, existing) => {
|
|
1456
|
-
if (!conversation) return existing ?? null;
|
|
1457
|
-
const resolvedBlocked = conversation.isBlocked !== void 0 ? Boolean(conversation.isBlocked) : conversation.blocked !== void 0 ? Boolean(conversation.blocked) : conversation.isUserBlocked !== void 0 ? Boolean(conversation.isUserBlocked) : existing?.isBlocked ?? false;
|
|
1458
|
-
const mergedParticipants = conversation.participants !== void 0 ? mergeConversationParticipants(existing?.participants, conversation.participants) : existing?.participants ?? conversation.participants;
|
|
1459
|
-
const isGroup = isGroupConversation(conversation) || isGroupConversation(existing);
|
|
1460
|
-
const resolvedGroupImage = resolveGroupImage(conversation) || resolveGroupImage(existing);
|
|
1461
|
-
const incomingGroupName = typeof conversation.groupName === "string" && conversation.groupName.trim() || typeof conversation.name === "string" && conversation.name.trim() || "";
|
|
1462
|
-
const existingGroupName = typeof existing?.groupName === "string" && existing.groupName.trim() || typeof existing?.name === "string" && existing.name.trim() || "";
|
|
1463
|
-
const resolvedGroupName = incomingGroupName || existingGroupName || void 0;
|
|
1464
|
-
return {
|
|
1465
|
-
...existing,
|
|
1466
|
-
...conversation,
|
|
1467
|
-
participants: mergedParticipants,
|
|
1468
|
-
isGroup,
|
|
1469
|
-
isBlocked: resolvedBlocked,
|
|
1470
|
-
lastMessage: mergeLastMessage(existing?.lastMessage, conversation.lastMessage),
|
|
1471
|
-
...isGroup ? {
|
|
1472
|
-
...resolvedGroupName ? { groupName: resolvedGroupName, name: resolvedGroupName } : {},
|
|
1473
|
-
...resolvedGroupImage ? { image: resolvedGroupImage, groupImage: resolvedGroupImage } : {}
|
|
1474
|
-
} : {}
|
|
1475
|
-
};
|
|
1476
|
-
};
|
|
1477
|
-
resolveConversationLastMessage = (conversation, messagesByConversation) => {
|
|
1478
|
-
const conversationId = String(conversation._id || conversation.id || "");
|
|
1479
|
-
const storedLast = conversation.lastMessage;
|
|
1480
|
-
const latestFromMessages = getLatestMessageFromList(messagesByConversation[conversationId] || []);
|
|
1481
|
-
if (!latestFromMessages && !storedLast) return null;
|
|
1482
|
-
if (!latestFromMessages) return storedLast;
|
|
1483
|
-
if (!messageHasPreviewBody(storedLast)) return latestFromMessages;
|
|
1484
|
-
const storedTime = new Date(storedLast.createdAt || storedLast.timestamp || 0).getTime();
|
|
1485
|
-
const latestTime = new Date(latestFromMessages.createdAt || latestFromMessages.timestamp || 0).getTime();
|
|
1486
|
-
return latestTime >= storedTime ? mergeLastMessage(storedLast, latestFromMessages) : mergeLastMessage(latestFromMessages, storedLast);
|
|
1487
|
-
};
|
|
1488
|
-
syncConversationLastMessage = (conversations, conversationId, messages) => {
|
|
1489
|
-
const latest = getLatestMessageFromList(messages);
|
|
1490
|
-
if (!latest) return conversations;
|
|
1491
|
-
return conversations.map((conversation) => {
|
|
1492
|
-
if (String(conversation._id) !== String(conversationId)) return conversation;
|
|
1493
|
-
return {
|
|
1494
|
-
...conversation,
|
|
1495
|
-
lastMessage: mergeLastMessage(conversation.lastMessage, latest)
|
|
1496
|
-
};
|
|
1497
|
-
});
|
|
1498
|
-
};
|
|
1499
|
-
upsertConversationInList = (conversations, incoming, options) => {
|
|
1500
|
-
const conversationId = String(incoming?._id || incoming?.conversationId || "");
|
|
1501
|
-
if (!conversationId) return conversations;
|
|
1502
|
-
const index = conversations.findIndex((c) => String(c._id) === conversationId);
|
|
1503
|
-
const existing = index >= 0 ? conversations[index] : null;
|
|
1504
|
-
const mergedLastMessage = mergeLastMessage(
|
|
1505
|
-
existing?.lastMessage,
|
|
1506
|
-
options?.lastMessage ?? incoming.lastMessage
|
|
1507
|
-
);
|
|
1508
|
-
const isActiveConversation = !!options?.activeConversationId && String(options.activeConversationId) === conversationId;
|
|
1509
|
-
const loggedUserId = options?.loggedUserId;
|
|
1510
|
-
const incomingLastMessage = options?.lastMessage ?? incoming.lastMessage;
|
|
1511
|
-
const lastMessageChanged = !!incomingLastMessage && String(mergedLastMessage?._id || mergedLastMessage?.id || "") !== String(existing?.lastMessage?._id || existing?.lastMessage?.id || "");
|
|
1512
|
-
const senderId = String(
|
|
1513
|
-
incomingLastMessage?.sender?._id || incomingLastMessage?.sender?.id || incomingLastMessage?.sender || ""
|
|
1514
|
-
);
|
|
1515
|
-
const isIncomingLastMessage = !!loggedUserId && !!senderId && senderId !== String(loggedUserId);
|
|
1516
|
-
const normalized = normalizeConversationRecord(
|
|
1517
|
-
{
|
|
1518
|
-
...incoming,
|
|
1519
|
-
_id: conversationId,
|
|
1520
|
-
lastMessage: mergedLastMessage,
|
|
1521
|
-
unreadCount: resolveNextUnreadCount({
|
|
1522
|
-
currentUnread: normalizeUnreadCount(existing?.unreadCount, loggedUserId),
|
|
1523
|
-
serverUnread: incoming.unreadCount,
|
|
1524
|
-
isActiveConversation,
|
|
1525
|
-
loggedUserId,
|
|
1526
|
-
shouldIncrement: !isActiveConversation && lastMessageChanged && isIncomingLastMessage && !options?.isFromSelf
|
|
1527
|
-
})
|
|
1528
|
-
},
|
|
1529
|
-
existing
|
|
1530
|
-
);
|
|
1531
|
-
if (index < 0) {
|
|
1532
|
-
return [normalized, ...conversations];
|
|
1902
|
+
emitGroupDelete(payload) {
|
|
1903
|
+
this.emit(SOCKET_EVENTS.GROUP_DELETE, payload);
|
|
1533
1904
|
}
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
return [normalized, ...withoutExisting];
|
|
1905
|
+
emitGroupPin(payload) {
|
|
1906
|
+
this.emit(SOCKET_EVENTS.GROUP_PIN, payload);
|
|
1537
1907
|
}
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
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;
|
|
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);
|
|
1559
1922
|
}
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
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);
|
|
1934
|
+
}
|
|
1935
|
+
emitCallReject(payload) {
|
|
1936
|
+
this.emit(SOCKET_EVENTS.CALL_REJECT, payload);
|
|
1937
|
+
}
|
|
1938
|
+
emitCallCancel(payload) {
|
|
1939
|
+
this.emit(SOCKET_EVENTS.CALL_CANCEL, payload);
|
|
1940
|
+
}
|
|
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
|
-
};
|
|
1571
|
-
resolveConversationIdForChannel = (channel, loggedUserId, conversations) => {
|
|
1572
|
-
const found = findConversationForChannel(channel, loggedUserId, conversations);
|
|
1573
|
-
if (found?._id) return String(found._id);
|
|
1574
|
-
if (channel?.conversationId) return String(channel.conversationId);
|
|
1575
|
-
return null;
|
|
1576
1944
|
};
|
|
1945
|
+
socketService = new SocketService();
|
|
1946
|
+
socket_service_default = socketService;
|
|
1577
1947
|
}
|
|
1578
1948
|
});
|
|
1579
1949
|
|
|
1580
|
-
// src/
|
|
1581
|
-
var
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
emptyMessagesMetadata = () => ({
|
|
1585
|
-
nextCursor: null,
|
|
1586
|
-
hasMore: false,
|
|
1587
|
-
isFirstPage: true
|
|
1588
|
-
});
|
|
1589
|
-
extractPagePagination = (response) => response?.data?.pagination ?? response?.pagination ?? null;
|
|
1590
|
-
}
|
|
1591
|
-
});
|
|
1592
|
-
|
|
1593
|
-
// src/store/helpers/normalize.helpers.ts
|
|
1594
|
-
var normalizeId, resolveConversationIdForIncomingMessage, resolveIsGroupFromPayload, resolveDmMarkReadReaderId;
|
|
1595
|
-
var init_normalize_helpers = __esm({
|
|
1596
|
-
"src/store/helpers/normalize.helpers.ts"() {
|
|
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,58 @@ var init_utils2 = __esm({
|
|
|
4300
4611
|
"src/lib/utils.ts"() {
|
|
4301
4612
|
}
|
|
4302
4613
|
});
|
|
4614
|
+
function Avatar({
|
|
4615
|
+
className,
|
|
4616
|
+
size = "default",
|
|
4617
|
+
...props
|
|
4618
|
+
}) {
|
|
4619
|
+
return /* @__PURE__ */ jsx(
|
|
4620
|
+
Avatar$1.Root,
|
|
4621
|
+
{
|
|
4622
|
+
"data-slot": "avatar",
|
|
4623
|
+
"data-size": size,
|
|
4624
|
+
className: cn(
|
|
4625
|
+
"group/avatar relative flex size-8 shrink-0 overflow-hidden rounded-full select-none data-[size=lg]:size-10 data-[size=sm]:size-6",
|
|
4626
|
+
className
|
|
4627
|
+
),
|
|
4628
|
+
...props
|
|
4629
|
+
}
|
|
4630
|
+
);
|
|
4631
|
+
}
|
|
4632
|
+
function AvatarImage({
|
|
4633
|
+
className,
|
|
4634
|
+
...props
|
|
4635
|
+
}) {
|
|
4636
|
+
return /* @__PURE__ */ jsx(
|
|
4637
|
+
Avatar$1.Image,
|
|
4638
|
+
{
|
|
4639
|
+
"data-slot": "avatar-image",
|
|
4640
|
+
className: cn("aspect-square size-full", className),
|
|
4641
|
+
...props
|
|
4642
|
+
}
|
|
4643
|
+
);
|
|
4644
|
+
}
|
|
4645
|
+
function AvatarFallback({
|
|
4646
|
+
className,
|
|
4647
|
+
...props
|
|
4648
|
+
}) {
|
|
4649
|
+
return /* @__PURE__ */ jsx(
|
|
4650
|
+
Avatar$1.Fallback,
|
|
4651
|
+
{
|
|
4652
|
+
"data-slot": "avatar-fallback",
|
|
4653
|
+
className: cn(
|
|
4654
|
+
"bg-muted text-muted-foreground flex size-full items-center justify-center rounded-full text-sm group-data-[size=sm]/avatar:text-xs",
|
|
4655
|
+
className
|
|
4656
|
+
),
|
|
4657
|
+
...props
|
|
4658
|
+
}
|
|
4659
|
+
);
|
|
4660
|
+
}
|
|
4661
|
+
var init_avatar = __esm({
|
|
4662
|
+
"src/ui/avatar.tsx"() {
|
|
4663
|
+
init_utils2();
|
|
4664
|
+
}
|
|
4665
|
+
});
|
|
4303
4666
|
function Button({
|
|
4304
4667
|
className,
|
|
4305
4668
|
variant = "default",
|
|
@@ -4354,58 +4717,6 @@ var init_button = __esm({
|
|
|
4354
4717
|
);
|
|
4355
4718
|
}
|
|
4356
4719
|
});
|
|
4357
|
-
function Avatar({
|
|
4358
|
-
className,
|
|
4359
|
-
size = "default",
|
|
4360
|
-
...props
|
|
4361
|
-
}) {
|
|
4362
|
-
return /* @__PURE__ */ jsx(
|
|
4363
|
-
Avatar$1.Root,
|
|
4364
|
-
{
|
|
4365
|
-
"data-slot": "avatar",
|
|
4366
|
-
"data-size": size,
|
|
4367
|
-
className: cn(
|
|
4368
|
-
"group/avatar relative flex size-8 shrink-0 overflow-hidden rounded-full select-none data-[size=lg]:size-10 data-[size=sm]:size-6",
|
|
4369
|
-
className
|
|
4370
|
-
),
|
|
4371
|
-
...props
|
|
4372
|
-
}
|
|
4373
|
-
);
|
|
4374
|
-
}
|
|
4375
|
-
function AvatarImage({
|
|
4376
|
-
className,
|
|
4377
|
-
...props
|
|
4378
|
-
}) {
|
|
4379
|
-
return /* @__PURE__ */ jsx(
|
|
4380
|
-
Avatar$1.Image,
|
|
4381
|
-
{
|
|
4382
|
-
"data-slot": "avatar-image",
|
|
4383
|
-
className: cn("aspect-square size-full", className),
|
|
4384
|
-
...props
|
|
4385
|
-
}
|
|
4386
|
-
);
|
|
4387
|
-
}
|
|
4388
|
-
function AvatarFallback({
|
|
4389
|
-
className,
|
|
4390
|
-
...props
|
|
4391
|
-
}) {
|
|
4392
|
-
return /* @__PURE__ */ jsx(
|
|
4393
|
-
Avatar$1.Fallback,
|
|
4394
|
-
{
|
|
4395
|
-
"data-slot": "avatar-fallback",
|
|
4396
|
-
className: cn(
|
|
4397
|
-
"bg-muted text-muted-foreground flex size-full items-center justify-center rounded-full text-sm group-data-[size=sm]/avatar:text-xs",
|
|
4398
|
-
className
|
|
4399
|
-
),
|
|
4400
|
-
...props
|
|
4401
|
-
}
|
|
4402
|
-
);
|
|
4403
|
-
}
|
|
4404
|
-
var init_avatar = __esm({
|
|
4405
|
-
"src/ui/avatar.tsx"() {
|
|
4406
|
-
init_utils2();
|
|
4407
|
-
}
|
|
4408
|
-
});
|
|
4409
4720
|
|
|
4410
4721
|
// src/call/videosdk.loader.ts
|
|
4411
4722
|
var loadVideoSDK;
|
|
@@ -4436,11 +4747,13 @@ var init_ParticipantView = __esm({
|
|
|
4436
4747
|
isLocal = false,
|
|
4437
4748
|
fallbackName,
|
|
4438
4749
|
fallbackImage,
|
|
4750
|
+
isHandRaised = false,
|
|
4439
4751
|
className
|
|
4440
4752
|
}) => {
|
|
4441
4753
|
const { webcamStream, micStream, webcamOn, micOn, displayName, isActiveSpeaker } = sdk.useParticipant(participantId);
|
|
4442
4754
|
const videoRef = useRef(null);
|
|
4443
4755
|
const audioRef = useRef(null);
|
|
4756
|
+
const [hasFrame, setHasFrame] = useState(false);
|
|
4444
4757
|
useEffect(() => {
|
|
4445
4758
|
const el = videoRef.current;
|
|
4446
4759
|
if (!el) return;
|
|
@@ -4449,6 +4762,7 @@ var init_ParticipantView = __esm({
|
|
|
4449
4762
|
el.play().catch(() => void 0);
|
|
4450
4763
|
} else {
|
|
4451
4764
|
el.srcObject = null;
|
|
4765
|
+
setHasFrame(false);
|
|
4452
4766
|
}
|
|
4453
4767
|
}, [webcamOn, webcamStream]);
|
|
4454
4768
|
useEffect(() => {
|
|
@@ -4463,6 +4777,7 @@ var init_ParticipantView = __esm({
|
|
|
4463
4777
|
}
|
|
4464
4778
|
}, [micOn, micStream, isLocal]);
|
|
4465
4779
|
const name = displayName || fallbackName || "Participant";
|
|
4780
|
+
const showVideo = webcamOn && hasFrame;
|
|
4466
4781
|
return /* @__PURE__ */ jsxs(
|
|
4467
4782
|
"div",
|
|
4468
4783
|
{
|
|
@@ -4478,14 +4793,24 @@ var init_ParticipantView = __esm({
|
|
|
4478
4793
|
ref: videoRef,
|
|
4479
4794
|
muted: isLocal,
|
|
4480
4795
|
playsInline: true,
|
|
4796
|
+
onPlaying: () => setHasFrame(true),
|
|
4481
4797
|
className: cn(
|
|
4482
4798
|
"size-full object-cover",
|
|
4483
|
-
|
|
4799
|
+
showVideo ? "block" : "hidden",
|
|
4800
|
+
// Mirror only the local self-view (how every call app
|
|
4801
|
+
// shows you — moving your right hand appears to move
|
|
4802
|
+
// right in your own preview, like a mirror). The video
|
|
4803
|
+
// actually sent to other participants is untouched by
|
|
4804
|
+
// this CSS, so remote viewers always see it the right
|
|
4805
|
+
// way round; only flip our own local tile, never a
|
|
4806
|
+
// remote one.
|
|
4807
|
+
isLocal && "-scale-x-100"
|
|
4484
4808
|
)
|
|
4485
4809
|
}
|
|
4486
4810
|
),
|
|
4487
4811
|
!isLocal && /* @__PURE__ */ jsx("audio", { ref: audioRef, autoPlay: true, playsInline: true }),
|
|
4488
|
-
|
|
4812
|
+
isHandRaised && /* @__PURE__ */ jsx("span", { className: "absolute top-2 left-2 flex size-7 items-center justify-center rounded-full bg-amber-400 text-neutral-900 shadow", children: /* @__PURE__ */ jsx(Hand, { size: 14 }) }),
|
|
4813
|
+
!showVideo && /* @__PURE__ */ jsxs(Avatar, { size: "lg", className: "size-20", children: [
|
|
4489
4814
|
/* @__PURE__ */ jsx(AvatarImage, { src: fallbackImage, alt: name }),
|
|
4490
4815
|
/* @__PURE__ */ jsx(AvatarFallback, { className: "bg-neutral-700 text-xl font-medium text-white", children: name.charAt(0).toUpperCase() })
|
|
4491
4816
|
] }),
|
|
@@ -4500,50 +4825,225 @@ var init_ParticipantView = __esm({
|
|
|
4500
4825
|
ParticipantView_default = ParticipantView;
|
|
4501
4826
|
}
|
|
4502
4827
|
});
|
|
4503
|
-
var
|
|
4828
|
+
var AudioCallView, AudioCallView_default;
|
|
4829
|
+
var init_AudioCallView = __esm({
|
|
4830
|
+
"src/call/AudioCallView.tsx"() {
|
|
4831
|
+
init_avatar();
|
|
4832
|
+
init_utils2();
|
|
4833
|
+
AudioCallView = ({
|
|
4834
|
+
sdk,
|
|
4835
|
+
remoteParticipantId,
|
|
4836
|
+
peerName,
|
|
4837
|
+
peerAvatar,
|
|
4838
|
+
isRemoteHandRaised = false
|
|
4839
|
+
}) => {
|
|
4840
|
+
const remote = remoteParticipantId ? sdk.useParticipant(remoteParticipantId) : null;
|
|
4841
|
+
const name = remote?.displayName || peerName || "In call";
|
|
4842
|
+
const isSpeaking = !!remote?.isActiveSpeaker && remote?.micOn;
|
|
4843
|
+
const isMuted = remote != null && !remote.micOn;
|
|
4844
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex flex-1 flex-col items-center justify-center gap-6 p-6 text-center", children: [
|
|
4845
|
+
/* @__PURE__ */ jsxs("div", { className: "relative flex items-center justify-center", children: [
|
|
4846
|
+
/* @__PURE__ */ jsx(
|
|
4847
|
+
"span",
|
|
4848
|
+
{
|
|
4849
|
+
className: cn(
|
|
4850
|
+
"absolute inline-flex size-36 rounded-full bg-emerald-400/15 transition-opacity duration-300",
|
|
4851
|
+
isSpeaking ? "animate-ping opacity-100" : "opacity-0"
|
|
4852
|
+
)
|
|
4853
|
+
}
|
|
4854
|
+
),
|
|
4855
|
+
/* @__PURE__ */ jsxs(
|
|
4856
|
+
Avatar,
|
|
4857
|
+
{
|
|
4858
|
+
className: cn(
|
|
4859
|
+
"relative size-28 ring-4 transition-colors duration-300",
|
|
4860
|
+
isSpeaking ? "ring-emerald-400/80" : "ring-white/10"
|
|
4861
|
+
),
|
|
4862
|
+
children: [
|
|
4863
|
+
/* @__PURE__ */ jsx(AvatarImage, { src: peerAvatar, alt: name }),
|
|
4864
|
+
/* @__PURE__ */ jsx(AvatarFallback, { className: "bg-neutral-700 text-3xl font-medium text-white", children: name.charAt(0).toUpperCase() })
|
|
4865
|
+
]
|
|
4866
|
+
}
|
|
4867
|
+
),
|
|
4868
|
+
isMuted && /* @__PURE__ */ jsx("span", { className: "absolute -right-1 -bottom-1 flex size-9 items-center justify-center rounded-full bg-neutral-700 ring-4 ring-neutral-900", children: /* @__PURE__ */ jsx(MicOff, { size: 16, className: "text-white/80" }) }),
|
|
4869
|
+
isRemoteHandRaised && /* @__PURE__ */ jsx("span", { className: "absolute -left-1 -top-1 flex size-9 items-center justify-center rounded-full bg-amber-400 text-neutral-900 ring-4 ring-neutral-900", children: /* @__PURE__ */ jsx(Hand, { size: 16 }) })
|
|
4870
|
+
] }),
|
|
4871
|
+
/* @__PURE__ */ jsx("p", { className: "text-xl font-semibold tracking-tight", children: name })
|
|
4872
|
+
] });
|
|
4873
|
+
};
|
|
4874
|
+
AudioCallView_default = AudioCallView;
|
|
4875
|
+
}
|
|
4876
|
+
});
|
|
4877
|
+
var ScreenShareView, ScreenShareView_default;
|
|
4878
|
+
var init_ScreenShareView = __esm({
|
|
4879
|
+
"src/call/ScreenShareView.tsx"() {
|
|
4880
|
+
ScreenShareView = ({ sdk, presenterId, isLocal, className }) => {
|
|
4881
|
+
const { screenShareStream, screenShareOn, displayName } = sdk.useParticipant(presenterId);
|
|
4882
|
+
const videoRef = useRef(null);
|
|
4883
|
+
useEffect(() => {
|
|
4884
|
+
const el = videoRef.current;
|
|
4885
|
+
if (!el) return;
|
|
4886
|
+
if (screenShareOn && screenShareStream?.track) {
|
|
4887
|
+
el.srcObject = new MediaStream([screenShareStream.track]);
|
|
4888
|
+
el.play().catch(() => void 0);
|
|
4889
|
+
} else {
|
|
4890
|
+
el.srcObject = null;
|
|
4891
|
+
}
|
|
4892
|
+
}, [screenShareOn, screenShareStream]);
|
|
4893
|
+
return /* @__PURE__ */ jsxs("div", { className, children: [
|
|
4894
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5 px-1 pb-2 text-xs font-medium text-white/70", children: [
|
|
4895
|
+
/* @__PURE__ */ jsx(MonitorUp, { size: 14 }),
|
|
4896
|
+
isLocal ? "You are presenting" : `${displayName || "Someone"} is presenting`
|
|
4897
|
+
] }),
|
|
4898
|
+
/* @__PURE__ */ jsx("div", { className: "relative flex flex-1 items-center justify-center overflow-hidden rounded-xl bg-black", children: /* @__PURE__ */ jsx("video", { ref: videoRef, playsInline: true, muted: isLocal, className: "max-h-full max-w-full object-contain" }) })
|
|
4899
|
+
] });
|
|
4900
|
+
};
|
|
4901
|
+
ScreenShareView_default = ScreenShareView;
|
|
4902
|
+
}
|
|
4903
|
+
});
|
|
4904
|
+
function Popover({
|
|
4905
|
+
...props
|
|
4906
|
+
}) {
|
|
4907
|
+
return /* @__PURE__ */ jsx(Popover$1.Root, { "data-slot": "popover", ...props });
|
|
4908
|
+
}
|
|
4909
|
+
function PopoverTrigger({
|
|
4910
|
+
...props
|
|
4911
|
+
}) {
|
|
4912
|
+
return /* @__PURE__ */ jsx(Popover$1.Trigger, { "data-slot": "popover-trigger", ...props });
|
|
4913
|
+
}
|
|
4914
|
+
function PopoverContent({
|
|
4915
|
+
className,
|
|
4916
|
+
align = "center",
|
|
4917
|
+
sideOffset = 4,
|
|
4918
|
+
...props
|
|
4919
|
+
}) {
|
|
4920
|
+
return /* @__PURE__ */ jsx(Popover$1.Portal, { children: /* @__PURE__ */ jsx(
|
|
4921
|
+
Popover$1.Content,
|
|
4922
|
+
{
|
|
4923
|
+
"data-slot": "popover-content",
|
|
4924
|
+
align,
|
|
4925
|
+
sideOffset,
|
|
4926
|
+
className: cn(
|
|
4927
|
+
"bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 w-72 origin-(--radix-popover-content-transform-origin) rounded-md border p-4 shadow-md outline-hidden",
|
|
4928
|
+
className
|
|
4929
|
+
),
|
|
4930
|
+
...props
|
|
4931
|
+
}
|
|
4932
|
+
) });
|
|
4933
|
+
}
|
|
4934
|
+
var init_popover = __esm({
|
|
4935
|
+
"src/ui/popover.tsx"() {
|
|
4936
|
+
init_utils2();
|
|
4937
|
+
}
|
|
4938
|
+
});
|
|
4939
|
+
var circleBtn, idleBtnClass, activeBtnClass, QUICK_REACTIONS, CallControls, CallControls_default;
|
|
4504
4940
|
var init_CallControls = __esm({
|
|
4505
4941
|
"src/call/CallControls.tsx"() {
|
|
4506
4942
|
init_button();
|
|
4943
|
+
init_popover();
|
|
4507
4944
|
init_utils2();
|
|
4508
4945
|
init_call_store();
|
|
4509
4946
|
circleBtn = "size-13 rounded-full text-white transition-all duration-150 disabled:opacity-40 hover:scale-105 active:scale-95";
|
|
4510
|
-
|
|
4511
|
-
|
|
4947
|
+
idleBtnClass = "bg-white/10 hover:bg-white/20";
|
|
4948
|
+
activeBtnClass = "bg-white text-neutral-900 hover:bg-white/90";
|
|
4949
|
+
QUICK_REACTIONS = ["\u{1F44D}", "\u2764\uFE0F", "\u{1F602}", "\u{1F62E}", "\u{1F44F}", "\u{1F389}"];
|
|
4950
|
+
CallControls = ({
|
|
4951
|
+
sdk,
|
|
4952
|
+
onReaction,
|
|
4953
|
+
isHandRaised,
|
|
4954
|
+
onToggleRaiseHand
|
|
4955
|
+
}) => {
|
|
4956
|
+
const {
|
|
4957
|
+
toggleMic,
|
|
4958
|
+
toggleWebcam,
|
|
4959
|
+
localMicOn,
|
|
4960
|
+
localWebcamOn,
|
|
4961
|
+
localScreenShareOn,
|
|
4962
|
+
toggleScreenShare,
|
|
4963
|
+
leave
|
|
4964
|
+
} = sdk.useMeeting();
|
|
4512
4965
|
const endCall = useCallStore((s) => s.endCall);
|
|
4513
4966
|
const handleLeave = () => {
|
|
4514
4967
|
leave();
|
|
4515
4968
|
endCall();
|
|
4516
4969
|
};
|
|
4517
4970
|
return /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-center gap-5 py-6", children: [
|
|
4518
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-
|
|
4971
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 rounded-full bg-white/10 p-2 backdrop-blur-sm", children: [
|
|
4519
4972
|
/* @__PURE__ */ jsx(
|
|
4520
4973
|
Button,
|
|
4521
4974
|
{
|
|
4522
4975
|
variant: "ghost",
|
|
4523
4976
|
size: "icon",
|
|
4524
|
-
className: cn(
|
|
4525
|
-
circleBtn,
|
|
4526
|
-
localMicOn ? "bg-white/10 hover:bg-white/20" : "bg-white text-neutral-900 hover:bg-white/90"
|
|
4527
|
-
),
|
|
4977
|
+
className: cn(circleBtn, localMicOn ? idleBtnClass : activeBtnClass),
|
|
4528
4978
|
"aria-label": localMicOn ? "Mute microphone" : "Unmute microphone",
|
|
4529
4979
|
onClick: () => toggleMic(),
|
|
4530
4980
|
children: localMicOn ? /* @__PURE__ */ jsx(Mic, { size: 20 }) : /* @__PURE__ */ jsx(MicOff, { size: 20 })
|
|
4531
4981
|
}
|
|
4532
4982
|
),
|
|
4533
|
-
|
|
4983
|
+
/* @__PURE__ */ jsx(
|
|
4534
4984
|
Button,
|
|
4535
4985
|
{
|
|
4536
4986
|
variant: "ghost",
|
|
4537
4987
|
size: "icon",
|
|
4538
|
-
className: cn(
|
|
4539
|
-
circleBtn,
|
|
4540
|
-
localWebcamOn ? "bg-white/10 hover:bg-white/20" : "bg-white text-neutral-900 hover:bg-white/90"
|
|
4541
|
-
),
|
|
4988
|
+
className: cn(circleBtn, localWebcamOn ? idleBtnClass : activeBtnClass),
|
|
4542
4989
|
"aria-label": localWebcamOn ? "Turn camera off" : "Turn camera on",
|
|
4543
4990
|
onClick: () => toggleWebcam(),
|
|
4544
4991
|
children: localWebcamOn ? /* @__PURE__ */ jsx(Video, { size: 20 }) : /* @__PURE__ */ jsx(VideoOff, { size: 20 })
|
|
4545
4992
|
}
|
|
4546
|
-
)
|
|
4993
|
+
),
|
|
4994
|
+
/* @__PURE__ */ jsx(
|
|
4995
|
+
Button,
|
|
4996
|
+
{
|
|
4997
|
+
variant: "ghost",
|
|
4998
|
+
size: "icon",
|
|
4999
|
+
className: cn(circleBtn, localScreenShareOn ? "bg-indigo-500 hover:bg-indigo-400" : idleBtnClass),
|
|
5000
|
+
"aria-label": localScreenShareOn ? "Stop sharing your screen" : "Share your screen",
|
|
5001
|
+
onClick: () => toggleScreenShare(),
|
|
5002
|
+
children: /* @__PURE__ */ jsx(MonitorUp, { size: 20 })
|
|
5003
|
+
}
|
|
5004
|
+
),
|
|
5005
|
+
/* @__PURE__ */ jsx(
|
|
5006
|
+
Button,
|
|
5007
|
+
{
|
|
5008
|
+
variant: "ghost",
|
|
5009
|
+
size: "icon",
|
|
5010
|
+
className: cn(circleBtn, isHandRaised ? "bg-amber-400 text-neutral-900 hover:bg-amber-300" : idleBtnClass),
|
|
5011
|
+
"aria-label": isHandRaised ? "Lower hand" : "Raise hand",
|
|
5012
|
+
onClick: onToggleRaiseHand,
|
|
5013
|
+
children: /* @__PURE__ */ jsx(Hand, { size: 20 })
|
|
5014
|
+
}
|
|
5015
|
+
),
|
|
5016
|
+
/* @__PURE__ */ jsxs(Popover, { children: [
|
|
5017
|
+
/* @__PURE__ */ jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsx(
|
|
5018
|
+
Button,
|
|
5019
|
+
{
|
|
5020
|
+
variant: "ghost",
|
|
5021
|
+
size: "icon",
|
|
5022
|
+
className: cn(circleBtn, idleBtnClass),
|
|
5023
|
+
"aria-label": "Send a reaction",
|
|
5024
|
+
children: /* @__PURE__ */ jsx(Smile, { size: 20 })
|
|
5025
|
+
}
|
|
5026
|
+
) }),
|
|
5027
|
+
/* @__PURE__ */ jsx(
|
|
5028
|
+
PopoverContent,
|
|
5029
|
+
{
|
|
5030
|
+
side: "top",
|
|
5031
|
+
align: "center",
|
|
5032
|
+
className: "w-auto rounded-full border-white/10 bg-neutral-900 p-1.5 shadow-xl",
|
|
5033
|
+
children: /* @__PURE__ */ jsx("div", { className: "flex items-center gap-0.5", children: QUICK_REACTIONS.map((emoji) => /* @__PURE__ */ jsx(
|
|
5034
|
+
"button",
|
|
5035
|
+
{
|
|
5036
|
+
type: "button",
|
|
5037
|
+
className: "flex size-10 items-center justify-center rounded-full text-xl transition-transform hover:scale-125 hover:bg-white/10",
|
|
5038
|
+
"aria-label": `React with ${emoji}`,
|
|
5039
|
+
onClick: () => onReaction(emoji),
|
|
5040
|
+
children: emoji
|
|
5041
|
+
},
|
|
5042
|
+
emoji
|
|
5043
|
+
)) })
|
|
5044
|
+
}
|
|
5045
|
+
)
|
|
5046
|
+
] })
|
|
4547
5047
|
] }),
|
|
4548
5048
|
/* @__PURE__ */ jsx(
|
|
4549
5049
|
Button,
|
|
@@ -4561,17 +5061,123 @@ var init_CallControls = __esm({
|
|
|
4561
5061
|
CallControls_default = CallControls;
|
|
4562
5062
|
}
|
|
4563
5063
|
});
|
|
5064
|
+
var FloatingReactions, ReactionBubble, FloatingReactions_default;
|
|
5065
|
+
var init_FloatingReactions = __esm({
|
|
5066
|
+
"src/call/FloatingReactions.tsx"() {
|
|
5067
|
+
FloatingReactions = ({ reactions }) => {
|
|
5068
|
+
if (!reactions.length) return null;
|
|
5069
|
+
return /* @__PURE__ */ jsxs("div", { className: "pointer-events-none absolute inset-0 z-10 overflow-hidden", children: [
|
|
5070
|
+
/* @__PURE__ */ jsx("style", { children: `
|
|
5071
|
+
@keyframes realtimex-call-reaction-float {
|
|
5072
|
+
0% { transform: translateY(0) scale(0.5); opacity: 0; }
|
|
5073
|
+
15% { transform: translateY(-24px) scale(1.15); opacity: 1; }
|
|
5074
|
+
100% { transform: translateY(-220px) scale(1); opacity: 0; }
|
|
5075
|
+
}
|
|
5076
|
+
` }),
|
|
5077
|
+
reactions.map((reaction) => /* @__PURE__ */ jsx(ReactionBubble, { emoji: reaction.emoji }, reaction.id))
|
|
5078
|
+
] });
|
|
5079
|
+
};
|
|
5080
|
+
ReactionBubble = ({ emoji }) => {
|
|
5081
|
+
const left = useMemo(() => 20 + Math.random() * 60, []);
|
|
5082
|
+
return /* @__PURE__ */ jsx(
|
|
5083
|
+
"span",
|
|
5084
|
+
{
|
|
5085
|
+
className: "absolute bottom-16 text-4xl drop-shadow",
|
|
5086
|
+
style: {
|
|
5087
|
+
left: `${left}%`,
|
|
5088
|
+
animation: "realtimex-call-reaction-float 3s ease-out forwards"
|
|
5089
|
+
},
|
|
5090
|
+
children: emoji
|
|
5091
|
+
}
|
|
5092
|
+
);
|
|
5093
|
+
};
|
|
5094
|
+
FloatingReactions_default = FloatingReactions;
|
|
5095
|
+
}
|
|
5096
|
+
});
|
|
5097
|
+
var FLOATING_REACTION_LIFETIME_MS, useCallReactions;
|
|
5098
|
+
var init_useCallReactions = __esm({
|
|
5099
|
+
"src/call/useCallReactions.ts"() {
|
|
5100
|
+
FLOATING_REACTION_LIFETIME_MS = 3e3;
|
|
5101
|
+
useCallReactions = (sdk) => {
|
|
5102
|
+
const [floatingReactions, setFloatingReactions] = useState([]);
|
|
5103
|
+
const [raisedHands, setRaisedHands] = useState({});
|
|
5104
|
+
const [isHandRaised, setIsHandRaised] = useState(false);
|
|
5105
|
+
const nextId = useRef(0);
|
|
5106
|
+
const addFloatingReaction = useCallback((emoji, fromId) => {
|
|
5107
|
+
const id = `${Date.now()}-${nextId.current++}`;
|
|
5108
|
+
setFloatingReactions((prev) => [...prev, { id, emoji, fromId }]);
|
|
5109
|
+
setTimeout(() => {
|
|
5110
|
+
setFloatingReactions((prev) => prev.filter((r) => r.id !== id));
|
|
5111
|
+
}, FLOATING_REACTION_LIFETIME_MS);
|
|
5112
|
+
}, []);
|
|
5113
|
+
const { send, localParticipant } = sdk.useMeeting({
|
|
5114
|
+
onData: (data) => {
|
|
5115
|
+
try {
|
|
5116
|
+
const raw = typeof data.payload === "string" ? data.payload : new TextDecoder().decode(data.payload);
|
|
5117
|
+
const message = JSON.parse(raw);
|
|
5118
|
+
if (message.type === "reaction") {
|
|
5119
|
+
addFloatingReaction(message.emoji, data.from);
|
|
5120
|
+
} else if (message.type === "raise-hand") {
|
|
5121
|
+
setRaisedHands((prev) => ({ ...prev, [data.from]: message.raised }));
|
|
5122
|
+
}
|
|
5123
|
+
} catch {
|
|
5124
|
+
}
|
|
5125
|
+
}
|
|
5126
|
+
});
|
|
5127
|
+
useEffect(() => {
|
|
5128
|
+
setRaisedHands((prev) => {
|
|
5129
|
+
const next = {};
|
|
5130
|
+
for (const id of Object.keys(prev)) {
|
|
5131
|
+
if (prev[id]) next[id] = true;
|
|
5132
|
+
}
|
|
5133
|
+
return next;
|
|
5134
|
+
});
|
|
5135
|
+
}, [localParticipant?.id]);
|
|
5136
|
+
const sendReaction = useCallback(
|
|
5137
|
+
(emoji) => {
|
|
5138
|
+
if (localParticipant?.id) addFloatingReaction(emoji, localParticipant.id);
|
|
5139
|
+
void send(JSON.stringify({ type: "reaction", emoji }));
|
|
5140
|
+
},
|
|
5141
|
+
[send, localParticipant?.id, addFloatingReaction]
|
|
5142
|
+
);
|
|
5143
|
+
const toggleRaiseHand = useCallback(() => {
|
|
5144
|
+
setIsHandRaised((prev) => {
|
|
5145
|
+
const next = !prev;
|
|
5146
|
+
if (localParticipant?.id) {
|
|
5147
|
+
setRaisedHands((hands) => ({ ...hands, [localParticipant.id]: next }));
|
|
5148
|
+
}
|
|
5149
|
+
void send(JSON.stringify({ type: "raise-hand", raised: next }));
|
|
5150
|
+
return next;
|
|
5151
|
+
});
|
|
5152
|
+
}, [send, localParticipant?.id]);
|
|
5153
|
+
return { floatingReactions, raisedHands, isHandRaised, sendReaction, toggleRaiseHand };
|
|
5154
|
+
};
|
|
5155
|
+
}
|
|
5156
|
+
});
|
|
4564
5157
|
var MeetingView, MeetingView_default;
|
|
4565
5158
|
var init_MeetingView = __esm({
|
|
4566
5159
|
"src/call/MeetingView.tsx"() {
|
|
4567
5160
|
init_ParticipantView();
|
|
5161
|
+
init_AudioCallView();
|
|
5162
|
+
init_ScreenShareView();
|
|
4568
5163
|
init_CallControls();
|
|
5164
|
+
init_FloatingReactions();
|
|
5165
|
+
init_useCallReactions();
|
|
4569
5166
|
init_call_store();
|
|
4570
|
-
|
|
5167
|
+
init_utils2();
|
|
5168
|
+
MeetingView = ({ sdk, mode, peerName, peerAvatar }) => {
|
|
4571
5169
|
const endCall = useCallStore((s) => s.endCall);
|
|
4572
5170
|
const [hasJoined, setHasJoined] = useState(false);
|
|
4573
5171
|
const hasJoinedRef = useRef(false);
|
|
4574
|
-
const
|
|
5172
|
+
const [groupView, setGroupView] = useState("grid");
|
|
5173
|
+
const {
|
|
5174
|
+
join,
|
|
5175
|
+
participants,
|
|
5176
|
+
localParticipant,
|
|
5177
|
+
localWebcamOn,
|
|
5178
|
+
presenterId,
|
|
5179
|
+
activeSpeakerId
|
|
5180
|
+
} = sdk.useMeeting({
|
|
4575
5181
|
onMeetingJoined: () => {
|
|
4576
5182
|
hasJoinedRef.current = true;
|
|
4577
5183
|
setHasJoined(true);
|
|
@@ -4581,6 +5187,7 @@ var init_MeetingView = __esm({
|
|
|
4581
5187
|
endCall();
|
|
4582
5188
|
}
|
|
4583
5189
|
});
|
|
5190
|
+
const { floatingReactions, raisedHands, isHandRaised, sendReaction, toggleRaiseHand } = useCallReactions(sdk);
|
|
4584
5191
|
useEffect(() => {
|
|
4585
5192
|
join();
|
|
4586
5193
|
}, []);
|
|
@@ -4590,9 +5197,136 @@ var init_MeetingView = __esm({
|
|
|
4590
5197
|
/* @__PURE__ */ jsx("p", { className: "text-sm", children: "Connecting\u2026" })
|
|
4591
5198
|
] });
|
|
4592
5199
|
}
|
|
4593
|
-
const remoteIds = Array.from(participants.keys())
|
|
4594
|
-
|
|
4595
|
-
|
|
5200
|
+
const remoteIds = Array.from(participants.keys()).filter(
|
|
5201
|
+
(id) => id !== localParticipant?.id
|
|
5202
|
+
);
|
|
5203
|
+
const isGroup = remoteIds.length > 1;
|
|
5204
|
+
const controls = /* @__PURE__ */ jsx(
|
|
5205
|
+
CallControls_default,
|
|
5206
|
+
{
|
|
5207
|
+
sdk,
|
|
5208
|
+
onReaction: sendReaction,
|
|
5209
|
+
isHandRaised,
|
|
5210
|
+
onToggleRaiseHand: toggleRaiseHand
|
|
5211
|
+
}
|
|
5212
|
+
);
|
|
5213
|
+
if (presenterId) {
|
|
5214
|
+
return /* @__PURE__ */ jsxs("div", { className: "relative flex flex-1 flex-col", children: [
|
|
5215
|
+
/* @__PURE__ */ jsx(FloatingReactions_default, { reactions: floatingReactions }),
|
|
5216
|
+
/* @__PURE__ */ jsx(
|
|
5217
|
+
ScreenShareView_default,
|
|
5218
|
+
{
|
|
5219
|
+
sdk,
|
|
5220
|
+
presenterId,
|
|
5221
|
+
isLocal: presenterId === localParticipant?.id,
|
|
5222
|
+
className: "flex flex-1 flex-col overflow-hidden p-4 pb-2"
|
|
5223
|
+
}
|
|
5224
|
+
),
|
|
5225
|
+
/* @__PURE__ */ jsxs("div", { className: "flex gap-2 overflow-x-auto px-4 pb-2", children: [
|
|
5226
|
+
localParticipant?.id && /* @__PURE__ */ jsx(
|
|
5227
|
+
ParticipantView_default,
|
|
5228
|
+
{
|
|
5229
|
+
sdk,
|
|
5230
|
+
participantId: localParticipant.id,
|
|
5231
|
+
isLocal: true,
|
|
5232
|
+
isHandRaised: !!raisedHands[localParticipant.id],
|
|
5233
|
+
className: "aspect-video h-20 min-h-0 w-32 shrink-0"
|
|
5234
|
+
}
|
|
5235
|
+
),
|
|
5236
|
+
remoteIds.map((id) => /* @__PURE__ */ jsx(
|
|
5237
|
+
ParticipantView_default,
|
|
5238
|
+
{
|
|
5239
|
+
sdk,
|
|
5240
|
+
participantId: id,
|
|
5241
|
+
fallbackName: peerName,
|
|
5242
|
+
fallbackImage: peerAvatar,
|
|
5243
|
+
isHandRaised: !!raisedHands[id],
|
|
5244
|
+
className: "aspect-video h-20 min-h-0 w-32 shrink-0"
|
|
5245
|
+
},
|
|
5246
|
+
id
|
|
5247
|
+
))
|
|
5248
|
+
] }),
|
|
5249
|
+
controls
|
|
5250
|
+
] });
|
|
5251
|
+
}
|
|
5252
|
+
const showVideoGrid = mode === "video" || localWebcamOn;
|
|
5253
|
+
if (!showVideoGrid) {
|
|
5254
|
+
return /* @__PURE__ */ jsxs("div", { className: "relative flex flex-1 flex-col", children: [
|
|
5255
|
+
/* @__PURE__ */ jsx(FloatingReactions_default, { reactions: floatingReactions }),
|
|
5256
|
+
/* @__PURE__ */ jsx(
|
|
5257
|
+
AudioCallView_default,
|
|
5258
|
+
{
|
|
5259
|
+
sdk,
|
|
5260
|
+
remoteParticipantId: remoteIds[0],
|
|
5261
|
+
peerName,
|
|
5262
|
+
peerAvatar,
|
|
5263
|
+
isRemoteHandRaised: !!(remoteIds[0] && raisedHands[remoteIds[0]])
|
|
5264
|
+
}
|
|
5265
|
+
),
|
|
5266
|
+
controls
|
|
5267
|
+
] });
|
|
5268
|
+
}
|
|
5269
|
+
const focusedId = groupView === "speaker" ? activeSpeakerId && activeSpeakerId !== localParticipant?.id ? activeSpeakerId : remoteIds[0] || localParticipant?.id : null;
|
|
5270
|
+
const stripIds = focusedId ? [localParticipant?.id, ...remoteIds].filter(
|
|
5271
|
+
(id) => !!id && id !== focusedId
|
|
5272
|
+
) : [];
|
|
5273
|
+
return /* @__PURE__ */ jsxs("div", { className: "relative flex flex-1 flex-col", children: [
|
|
5274
|
+
/* @__PURE__ */ jsx(FloatingReactions_default, { reactions: floatingReactions }),
|
|
5275
|
+
isGroup && /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-end gap-1 px-4 pt-2", children: [
|
|
5276
|
+
/* @__PURE__ */ jsx(
|
|
5277
|
+
"button",
|
|
5278
|
+
{
|
|
5279
|
+
type: "button",
|
|
5280
|
+
"aria-label": "Grid view",
|
|
5281
|
+
onClick: () => setGroupView("grid"),
|
|
5282
|
+
className: cn(
|
|
5283
|
+
"flex size-8 items-center justify-center rounded-lg transition-colors",
|
|
5284
|
+
groupView === "grid" ? "bg-white/15 text-white" : "text-white/50 hover:text-white/80"
|
|
5285
|
+
),
|
|
5286
|
+
children: /* @__PURE__ */ jsx(LayoutGrid, { size: 16 })
|
|
5287
|
+
}
|
|
5288
|
+
),
|
|
5289
|
+
/* @__PURE__ */ jsx(
|
|
5290
|
+
"button",
|
|
5291
|
+
{
|
|
5292
|
+
type: "button",
|
|
5293
|
+
"aria-label": "Speaker view",
|
|
5294
|
+
onClick: () => setGroupView("speaker"),
|
|
5295
|
+
className: cn(
|
|
5296
|
+
"flex size-8 items-center justify-center rounded-lg transition-colors",
|
|
5297
|
+
groupView === "speaker" ? "bg-white/15 text-white" : "text-white/50 hover:text-white/80"
|
|
5298
|
+
),
|
|
5299
|
+
children: /* @__PURE__ */ jsx(Rows3, { size: 16 })
|
|
5300
|
+
}
|
|
5301
|
+
)
|
|
5302
|
+
] }),
|
|
5303
|
+
focusedId ? /* @__PURE__ */ jsxs("div", { className: "flex flex-1 flex-col gap-3 overflow-y-auto p-4", children: [
|
|
5304
|
+
/* @__PURE__ */ jsx(
|
|
5305
|
+
ParticipantView_default,
|
|
5306
|
+
{
|
|
5307
|
+
sdk,
|
|
5308
|
+
participantId: focusedId,
|
|
5309
|
+
isLocal: focusedId === localParticipant?.id,
|
|
5310
|
+
fallbackName: peerName,
|
|
5311
|
+
fallbackImage: peerAvatar,
|
|
5312
|
+
isHandRaised: !!raisedHands[focusedId],
|
|
5313
|
+
className: "min-h-[240px] flex-1"
|
|
5314
|
+
}
|
|
5315
|
+
),
|
|
5316
|
+
stripIds.length > 0 && /* @__PURE__ */ jsx("div", { className: "flex gap-2 overflow-x-auto", children: stripIds.map((id) => /* @__PURE__ */ jsx(
|
|
5317
|
+
ParticipantView_default,
|
|
5318
|
+
{
|
|
5319
|
+
sdk,
|
|
5320
|
+
participantId: id,
|
|
5321
|
+
isLocal: id === localParticipant?.id,
|
|
5322
|
+
fallbackName: peerName,
|
|
5323
|
+
fallbackImage: peerAvatar,
|
|
5324
|
+
isHandRaised: !!raisedHands[id],
|
|
5325
|
+
className: "aspect-video h-20 min-h-0 w-32 shrink-0"
|
|
5326
|
+
},
|
|
5327
|
+
id
|
|
5328
|
+
)) })
|
|
5329
|
+
] }) : /* @__PURE__ */ jsxs(
|
|
4596
5330
|
"div",
|
|
4597
5331
|
{
|
|
4598
5332
|
className: "grid flex-1 auto-rows-fr gap-3 overflow-y-auto p-4",
|
|
@@ -4603,12 +5337,30 @@ var init_MeetingView = __esm({
|
|
|
4603
5337
|
)}, minmax(0, 1fr))`
|
|
4604
5338
|
},
|
|
4605
5339
|
children: [
|
|
4606
|
-
localParticipant?.id && /* @__PURE__ */ jsx(
|
|
4607
|
-
|
|
5340
|
+
localParticipant?.id && /* @__PURE__ */ jsx(
|
|
5341
|
+
ParticipantView_default,
|
|
5342
|
+
{
|
|
5343
|
+
sdk,
|
|
5344
|
+
participantId: localParticipant.id,
|
|
5345
|
+
isLocal: true,
|
|
5346
|
+
isHandRaised: !!raisedHands[localParticipant.id]
|
|
5347
|
+
}
|
|
5348
|
+
),
|
|
5349
|
+
remoteIds.map((id) => /* @__PURE__ */ jsx(
|
|
5350
|
+
ParticipantView_default,
|
|
5351
|
+
{
|
|
5352
|
+
sdk,
|
|
5353
|
+
participantId: id,
|
|
5354
|
+
fallbackName: peerName,
|
|
5355
|
+
fallbackImage: peerAvatar,
|
|
5356
|
+
isHandRaised: !!raisedHands[id]
|
|
5357
|
+
},
|
|
5358
|
+
id
|
|
5359
|
+
))
|
|
4608
5360
|
]
|
|
4609
5361
|
}
|
|
4610
5362
|
),
|
|
4611
|
-
|
|
5363
|
+
controls
|
|
4612
5364
|
] });
|
|
4613
5365
|
};
|
|
4614
5366
|
MeetingView_default = MeetingView;
|
|
@@ -4626,7 +5378,14 @@ var init_CallSession = __esm({
|
|
|
4626
5378
|
init_useStore();
|
|
4627
5379
|
init_videosdk_loader();
|
|
4628
5380
|
init_MeetingView();
|
|
4629
|
-
CallSession = ({
|
|
5381
|
+
CallSession = ({
|
|
5382
|
+
meetingId,
|
|
5383
|
+
token,
|
|
5384
|
+
mode,
|
|
5385
|
+
peerName,
|
|
5386
|
+
peerAvatar,
|
|
5387
|
+
onError
|
|
5388
|
+
}) => {
|
|
4630
5389
|
const [sdk, setSdk] = useState(null);
|
|
4631
5390
|
const userId = useStore((s) => s.loggedUserDetails?._id);
|
|
4632
5391
|
const userName = useStore((s) => s.loggedUserDetails?.name);
|
|
@@ -4667,7 +5426,7 @@ var init_CallSession = __esm({
|
|
|
4667
5426
|
token,
|
|
4668
5427
|
reinitialiseMeetingOnConfigChange: false,
|
|
4669
5428
|
joinWithoutUserInteraction: true,
|
|
4670
|
-
children: /* @__PURE__ */ jsx(MeetingView_default, { sdk, mode })
|
|
5429
|
+
children: /* @__PURE__ */ jsx(MeetingView_default, { sdk, mode, peerName, peerAvatar })
|
|
4671
5430
|
}
|
|
4672
5431
|
);
|
|
4673
5432
|
};
|
|
@@ -5619,6 +6378,172 @@ function useEffectiveSettingsOptional() {
|
|
|
5619
6378
|
};
|
|
5620
6379
|
}
|
|
5621
6380
|
|
|
6381
|
+
// src/call/CallOverlay.tsx
|
|
6382
|
+
init_avatar();
|
|
6383
|
+
init_button();
|
|
6384
|
+
init_call_store();
|
|
6385
|
+
|
|
6386
|
+
// src/call/useRingCountdown.ts
|
|
6387
|
+
init_call_types();
|
|
6388
|
+
var useRingCountdown = (active, callId) => {
|
|
6389
|
+
const [secondsLeft, setSecondsLeft] = useState(
|
|
6390
|
+
Math.ceil(CALL_RING_TIMEOUT_MS / 1e3)
|
|
6391
|
+
);
|
|
6392
|
+
useEffect(() => {
|
|
6393
|
+
if (!active) return;
|
|
6394
|
+
const startedAt = Date.now();
|
|
6395
|
+
setSecondsLeft(Math.ceil(CALL_RING_TIMEOUT_MS / 1e3));
|
|
6396
|
+
const id = setInterval(() => {
|
|
6397
|
+
const remainingMs = CALL_RING_TIMEOUT_MS - (Date.now() - startedAt);
|
|
6398
|
+
setSecondsLeft(Math.max(0, Math.ceil(remainingMs / 1e3)));
|
|
6399
|
+
}, 1e3);
|
|
6400
|
+
return () => clearInterval(id);
|
|
6401
|
+
}, [active, callId]);
|
|
6402
|
+
return secondsLeft;
|
|
6403
|
+
};
|
|
6404
|
+
var useCallDuration = (startedAt) => {
|
|
6405
|
+
const [elapsedMs, setElapsedMs] = useState(0);
|
|
6406
|
+
useEffect(() => {
|
|
6407
|
+
if (!startedAt) {
|
|
6408
|
+
setElapsedMs(0);
|
|
6409
|
+
return;
|
|
6410
|
+
}
|
|
6411
|
+
setElapsedMs(Date.now() - startedAt);
|
|
6412
|
+
const id = setInterval(() => setElapsedMs(Date.now() - startedAt), 1e3);
|
|
6413
|
+
return () => clearInterval(id);
|
|
6414
|
+
}, [startedAt]);
|
|
6415
|
+
const totalSeconds = Math.max(0, Math.floor(elapsedMs / 1e3));
|
|
6416
|
+
const hours = Math.floor(totalSeconds / 3600);
|
|
6417
|
+
const minutes = Math.floor(totalSeconds % 3600 / 60);
|
|
6418
|
+
const seconds = totalSeconds % 60;
|
|
6419
|
+
const pad = (n) => String(n).padStart(2, "0");
|
|
6420
|
+
return hours > 0 ? `${hours}:${pad(minutes)}:${pad(seconds)}` : `${pad(minutes)}:${pad(seconds)}`;
|
|
6421
|
+
};
|
|
6422
|
+
var CallSession2 = React11__default.lazy(() => Promise.resolve().then(() => (init_CallSession(), CallSession_exports)));
|
|
6423
|
+
var CallOverlay = () => {
|
|
6424
|
+
const uiStatus = useCallStore((s) => s.uiStatus);
|
|
6425
|
+
const mode = useCallStore((s) => s.mode);
|
|
6426
|
+
const callId = useCallStore((s) => s.callId);
|
|
6427
|
+
const meetingId = useCallStore((s) => s.meetingId);
|
|
6428
|
+
const token = useCallStore((s) => s.token);
|
|
6429
|
+
const error = useCallStore((s) => s.error);
|
|
6430
|
+
const peer = useCallStore((s) => s.peer);
|
|
6431
|
+
const caller = useCallStore((s) => s.caller);
|
|
6432
|
+
const isGroupCall = useCallStore((s) => s.isGroupCall);
|
|
6433
|
+
const callStartedAt = useCallStore((s) => s.callStartedAt);
|
|
6434
|
+
const cancelCall = useCallStore((s) => s.cancelCall);
|
|
6435
|
+
const reset = useCallStore((s) => s.reset);
|
|
6436
|
+
const notifyServerCallFailed = useCallStore((s) => s.notifyServerCallFailed);
|
|
6437
|
+
const [sessionError, setSessionError] = useState(null);
|
|
6438
|
+
const secondsLeft = useRingCountdown(uiStatus === "ringing-out", callId);
|
|
6439
|
+
const duration = useCallDuration(callStartedAt);
|
|
6440
|
+
useEffect(() => {
|
|
6441
|
+
setSessionError(null);
|
|
6442
|
+
}, [callId]);
|
|
6443
|
+
useEffect(() => {
|
|
6444
|
+
if (sessionError) notifyServerCallFailed();
|
|
6445
|
+
}, [sessionError, notifyServerCallFailed]);
|
|
6446
|
+
const isOpen = uiStatus !== "idle" && uiStatus !== "incoming";
|
|
6447
|
+
if (!isOpen || typeof document === "undefined") return null;
|
|
6448
|
+
const displayError = error || sessionError;
|
|
6449
|
+
const peerName = peer?.name || caller?.name || "";
|
|
6450
|
+
const peerAvatar = peer?.avatar || caller?.profileImage || void 0;
|
|
6451
|
+
const isVideo = mode === "video";
|
|
6452
|
+
return createPortal(
|
|
6453
|
+
/* @__PURE__ */ jsx(
|
|
6454
|
+
"div",
|
|
6455
|
+
{
|
|
6456
|
+
style: {
|
|
6457
|
+
position: "fixed",
|
|
6458
|
+
inset: 0,
|
|
6459
|
+
width: "100vw",
|
|
6460
|
+
height: "100vh",
|
|
6461
|
+
zIndex: 2147483647
|
|
6462
|
+
},
|
|
6463
|
+
className: "flex flex-col overflow-hidden bg-gradient-to-b from-neutral-900 via-neutral-950 to-black text-white",
|
|
6464
|
+
children: displayError ? /* @__PURE__ */ jsxs("div", { className: "flex flex-1 flex-col items-center justify-center gap-4 p-6 text-center", children: [
|
|
6465
|
+
/* @__PURE__ */ jsx("p", { className: "max-w-sm text-sm text-white/80", children: displayError }),
|
|
6466
|
+
/* @__PURE__ */ jsxs(Button, { variant: "secondary", onClick: reset, children: [
|
|
6467
|
+
/* @__PURE__ */ jsx(X, { size: 16, className: "mr-1" }),
|
|
6468
|
+
" Close"
|
|
6469
|
+
] })
|
|
6470
|
+
] }) : uiStatus === "in-call" && meetingId && token && mode ? /* @__PURE__ */ jsxs("div", { className: "flex flex-1 flex-col", children: [
|
|
6471
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-center gap-2 px-4 py-3 text-sm text-white/70", children: [
|
|
6472
|
+
isGroupCall && /* @__PURE__ */ jsx(Users, { size: 14, className: "text-white/50" }),
|
|
6473
|
+
peerName && /* @__PURE__ */ jsx("span", { className: "font-medium text-white/90", children: peerName }),
|
|
6474
|
+
/* @__PURE__ */ jsx("span", { "aria-hidden": true, className: "text-white/30", children: "\xB7" }),
|
|
6475
|
+
/* @__PURE__ */ jsx("span", { className: "tabular-nums", children: duration })
|
|
6476
|
+
] }),
|
|
6477
|
+
/* @__PURE__ */ jsx(
|
|
6478
|
+
React11__default.Suspense,
|
|
6479
|
+
{
|
|
6480
|
+
fallback: /* @__PURE__ */ jsxs("div", { className: "flex flex-1 flex-col items-center justify-center gap-3 text-white/80", children: [
|
|
6481
|
+
/* @__PURE__ */ jsx(Loader2, { className: "size-8 animate-spin" }),
|
|
6482
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm", children: "Loading call\u2026" })
|
|
6483
|
+
] }),
|
|
6484
|
+
children: /* @__PURE__ */ jsx(
|
|
6485
|
+
CallSession2,
|
|
6486
|
+
{
|
|
6487
|
+
meetingId,
|
|
6488
|
+
token,
|
|
6489
|
+
mode,
|
|
6490
|
+
peerName,
|
|
6491
|
+
peerAvatar,
|
|
6492
|
+
onError: setSessionError
|
|
6493
|
+
}
|
|
6494
|
+
)
|
|
6495
|
+
}
|
|
6496
|
+
)
|
|
6497
|
+
] }) : /* @__PURE__ */ jsxs("div", { className: "flex flex-1 flex-col items-center justify-center gap-6 p-6 text-center", children: [
|
|
6498
|
+
/* @__PURE__ */ jsxs("div", { className: "relative flex items-center justify-center", children: [
|
|
6499
|
+
uiStatus === "ringing-out" && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
6500
|
+
/* @__PURE__ */ jsx("span", { className: "absolute inline-flex size-32 animate-ping rounded-full bg-white/10" }),
|
|
6501
|
+
/* @__PURE__ */ jsx(
|
|
6502
|
+
"span",
|
|
6503
|
+
{
|
|
6504
|
+
className: "absolute inline-flex size-32 animate-ping rounded-full bg-white/5",
|
|
6505
|
+
style: { animationDelay: "0.6s" }
|
|
6506
|
+
}
|
|
6507
|
+
)
|
|
6508
|
+
] }),
|
|
6509
|
+
peerName ? /* @__PURE__ */ jsxs(Avatar, { className: "relative size-28 ring-4 ring-white/10", children: [
|
|
6510
|
+
/* @__PURE__ */ jsx(AvatarImage, { src: peerAvatar, alt: peerName }),
|
|
6511
|
+
/* @__PURE__ */ jsx(AvatarFallback, { className: "bg-neutral-700 text-3xl font-medium text-white", children: peerName.charAt(0).toUpperCase() })
|
|
6512
|
+
] }) : /* @__PURE__ */ jsx("div", { className: "relative flex size-28 items-center justify-center rounded-full bg-white/10", children: isVideo ? /* @__PURE__ */ jsx(Video, { size: 36 }) : /* @__PURE__ */ jsx(Phone, { size: 36 }) })
|
|
6513
|
+
] }),
|
|
6514
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
6515
|
+
peerName && /* @__PURE__ */ jsxs("p", { className: "flex items-center justify-center gap-1.5 text-xl font-semibold tracking-tight", children: [
|
|
6516
|
+
isGroupCall && /* @__PURE__ */ jsx(Users, { size: 16, className: "text-white/50" }),
|
|
6517
|
+
peerName
|
|
6518
|
+
] }),
|
|
6519
|
+
/* @__PURE__ */ jsxs("p", { className: "mt-1 text-sm text-white/60", children: [
|
|
6520
|
+
uiStatus === "creating" && "Starting call\u2026",
|
|
6521
|
+
uiStatus === "ringing-out" && `${isVideo ? "Video calling" : "Calling"}\u2026 \xB7 ${secondsLeft}s`,
|
|
6522
|
+
uiStatus === "joining" && "Connecting\u2026"
|
|
6523
|
+
] })
|
|
6524
|
+
] }),
|
|
6525
|
+
uiStatus === "ringing-out" && /* @__PURE__ */ jsxs("div", { className: "mt-1 flex flex-col items-center gap-2", children: [
|
|
6526
|
+
/* @__PURE__ */ jsx(
|
|
6527
|
+
Button,
|
|
6528
|
+
{
|
|
6529
|
+
variant: "ghost",
|
|
6530
|
+
size: "icon",
|
|
6531
|
+
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",
|
|
6532
|
+
"aria-label": "Cancel call",
|
|
6533
|
+
onClick: cancelCall,
|
|
6534
|
+
children: /* @__PURE__ */ jsx(PhoneOff, { size: 24 })
|
|
6535
|
+
}
|
|
6536
|
+
),
|
|
6537
|
+
/* @__PURE__ */ jsx("span", { className: "text-xs font-medium text-white/60", children: "Cancel" })
|
|
6538
|
+
] })
|
|
6539
|
+
] })
|
|
6540
|
+
}
|
|
6541
|
+
),
|
|
6542
|
+
document.body
|
|
6543
|
+
);
|
|
6544
|
+
};
|
|
6545
|
+
var CallOverlay_default = CallOverlay;
|
|
6546
|
+
|
|
5622
6547
|
// src/ui/dialog.tsx
|
|
5623
6548
|
init_utils2();
|
|
5624
6549
|
function Dialog({
|
|
@@ -6157,161 +7082,7 @@ var ChatAlertDialogContent = ({
|
|
|
6157
7082
|
);
|
|
6158
7083
|
};
|
|
6159
7084
|
|
|
6160
|
-
// src/call/
|
|
6161
|
-
init_avatar();
|
|
6162
|
-
init_button();
|
|
6163
|
-
init_utils2();
|
|
6164
|
-
init_call_store();
|
|
6165
|
-
|
|
6166
|
-
// src/call/useRingCountdown.ts
|
|
6167
|
-
init_call_types();
|
|
6168
|
-
var useRingCountdown = (active, callId) => {
|
|
6169
|
-
const [secondsLeft, setSecondsLeft] = useState(
|
|
6170
|
-
Math.ceil(CALL_RING_TIMEOUT_MS / 1e3)
|
|
6171
|
-
);
|
|
6172
|
-
useEffect(() => {
|
|
6173
|
-
if (!active) return;
|
|
6174
|
-
const startedAt = Date.now();
|
|
6175
|
-
setSecondsLeft(Math.ceil(CALL_RING_TIMEOUT_MS / 1e3));
|
|
6176
|
-
const id = setInterval(() => {
|
|
6177
|
-
const remainingMs = CALL_RING_TIMEOUT_MS - (Date.now() - startedAt);
|
|
6178
|
-
setSecondsLeft(Math.max(0, Math.ceil(remainingMs / 1e3)));
|
|
6179
|
-
}, 1e3);
|
|
6180
|
-
return () => clearInterval(id);
|
|
6181
|
-
}, [active, callId]);
|
|
6182
|
-
return secondsLeft;
|
|
6183
|
-
};
|
|
6184
|
-
var useCallDuration = (startedAt) => {
|
|
6185
|
-
const [elapsedMs, setElapsedMs] = useState(0);
|
|
6186
|
-
useEffect(() => {
|
|
6187
|
-
if (!startedAt) {
|
|
6188
|
-
setElapsedMs(0);
|
|
6189
|
-
return;
|
|
6190
|
-
}
|
|
6191
|
-
setElapsedMs(Date.now() - startedAt);
|
|
6192
|
-
const id = setInterval(() => setElapsedMs(Date.now() - startedAt), 1e3);
|
|
6193
|
-
return () => clearInterval(id);
|
|
6194
|
-
}, [startedAt]);
|
|
6195
|
-
const totalSeconds = Math.max(0, Math.floor(elapsedMs / 1e3));
|
|
6196
|
-
const hours = Math.floor(totalSeconds / 3600);
|
|
6197
|
-
const minutes = Math.floor(totalSeconds % 3600 / 60);
|
|
6198
|
-
const seconds = totalSeconds % 60;
|
|
6199
|
-
const pad = (n) => String(n).padStart(2, "0");
|
|
6200
|
-
return hours > 0 ? `${hours}:${pad(minutes)}:${pad(seconds)}` : `${pad(minutes)}:${pad(seconds)}`;
|
|
6201
|
-
};
|
|
6202
|
-
var CallSession2 = React9__default.lazy(() => Promise.resolve().then(() => (init_CallSession(), CallSession_exports)));
|
|
6203
|
-
var CallOverlay = () => {
|
|
6204
|
-
const uiStatus = useCallStore((s) => s.uiStatus);
|
|
6205
|
-
const mode = useCallStore((s) => s.mode);
|
|
6206
|
-
const callId = useCallStore((s) => s.callId);
|
|
6207
|
-
const meetingId = useCallStore((s) => s.meetingId);
|
|
6208
|
-
const token = useCallStore((s) => s.token);
|
|
6209
|
-
const error = useCallStore((s) => s.error);
|
|
6210
|
-
const callee = useCallStore((s) => s.callee);
|
|
6211
|
-
const caller = useCallStore((s) => s.caller);
|
|
6212
|
-
const callStartedAt = useCallStore((s) => s.callStartedAt);
|
|
6213
|
-
const cancelCall = useCallStore((s) => s.cancelCall);
|
|
6214
|
-
const reset = useCallStore((s) => s.reset);
|
|
6215
|
-
const notifyServerCallFailed = useCallStore((s) => s.notifyServerCallFailed);
|
|
6216
|
-
const [sessionError, setSessionError] = useState(null);
|
|
6217
|
-
const secondsLeft = useRingCountdown(uiStatus === "ringing-out", callId);
|
|
6218
|
-
const duration = useCallDuration(callStartedAt);
|
|
6219
|
-
useEffect(() => {
|
|
6220
|
-
setSessionError(null);
|
|
6221
|
-
}, [callId]);
|
|
6222
|
-
useEffect(() => {
|
|
6223
|
-
if (sessionError) notifyServerCallFailed();
|
|
6224
|
-
}, [sessionError, notifyServerCallFailed]);
|
|
6225
|
-
const isOpen = uiStatus !== "idle" && uiStatus !== "incoming";
|
|
6226
|
-
if (!isOpen) return null;
|
|
6227
|
-
const displayError = error || sessionError;
|
|
6228
|
-
const peerName = callee?.name || caller?.name || "";
|
|
6229
|
-
const peerAvatar = callee?.avatar || caller?.profileImage || void 0;
|
|
6230
|
-
const isVideo = mode === "video";
|
|
6231
|
-
return /* @__PURE__ */ jsx(Dialog, { open: isOpen, onOpenChange: () => void 0, children: /* @__PURE__ */ jsx(
|
|
6232
|
-
ChatDialogContent,
|
|
6233
|
-
{
|
|
6234
|
-
showCloseButton: false,
|
|
6235
|
-
onEscapeKeyDown: (e) => e.preventDefault(),
|
|
6236
|
-
onPointerDownOutside: (e) => e.preventDefault(),
|
|
6237
|
-
className: cn(
|
|
6238
|
-
"fixed inset-0 top-0 left-0 z-100 flex h-screen w-screen max-w-none",
|
|
6239
|
-
"translate-x-0 translate-y-0 flex-col rounded-none border-0 bg-gradient-to-b from-neutral-900 via-neutral-950 to-black p-0 text-white"
|
|
6240
|
-
),
|
|
6241
|
-
children: displayError ? /* @__PURE__ */ jsxs("div", { className: "flex flex-1 flex-col items-center justify-center gap-4 p-6 text-center", children: [
|
|
6242
|
-
/* @__PURE__ */ jsx("p", { className: "max-w-sm text-sm text-white/80", children: displayError }),
|
|
6243
|
-
/* @__PURE__ */ jsxs(Button, { variant: "secondary", onClick: reset, children: [
|
|
6244
|
-
/* @__PURE__ */ jsx(X, { size: 16, className: "mr-1" }),
|
|
6245
|
-
" Close"
|
|
6246
|
-
] })
|
|
6247
|
-
] }) : uiStatus === "in-call" && meetingId && token && mode ? /* @__PURE__ */ jsxs("div", { className: "flex flex-1 flex-col", children: [
|
|
6248
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-center gap-2 px-4 py-3 text-sm text-white/70", children: [
|
|
6249
|
-
peerName && /* @__PURE__ */ jsx("span", { className: "font-medium text-white/90", children: peerName }),
|
|
6250
|
-
/* @__PURE__ */ jsx("span", { "aria-hidden": true, className: "text-white/30", children: "\xB7" }),
|
|
6251
|
-
/* @__PURE__ */ jsx("span", { className: "tabular-nums", children: duration })
|
|
6252
|
-
] }),
|
|
6253
|
-
/* @__PURE__ */ jsx(
|
|
6254
|
-
React9__default.Suspense,
|
|
6255
|
-
{
|
|
6256
|
-
fallback: /* @__PURE__ */ jsxs("div", { className: "flex flex-1 flex-col items-center justify-center gap-3 text-white/80", children: [
|
|
6257
|
-
/* @__PURE__ */ jsx(Loader2, { className: "size-8 animate-spin" }),
|
|
6258
|
-
/* @__PURE__ */ jsx("p", { className: "text-sm", children: "Loading call\u2026" })
|
|
6259
|
-
] }),
|
|
6260
|
-
children: /* @__PURE__ */ jsx(
|
|
6261
|
-
CallSession2,
|
|
6262
|
-
{
|
|
6263
|
-
meetingId,
|
|
6264
|
-
token,
|
|
6265
|
-
mode,
|
|
6266
|
-
onError: setSessionError
|
|
6267
|
-
}
|
|
6268
|
-
)
|
|
6269
|
-
}
|
|
6270
|
-
)
|
|
6271
|
-
] }) : /* @__PURE__ */ jsxs("div", { className: "flex flex-1 flex-col items-center justify-center gap-6 p-6 text-center", children: [
|
|
6272
|
-
/* @__PURE__ */ jsxs("div", { className: "relative flex items-center justify-center", children: [
|
|
6273
|
-
uiStatus === "ringing-out" && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
6274
|
-
/* @__PURE__ */ jsx("span", { className: "absolute inline-flex size-32 animate-ping rounded-full bg-white/10" }),
|
|
6275
|
-
/* @__PURE__ */ jsx(
|
|
6276
|
-
"span",
|
|
6277
|
-
{
|
|
6278
|
-
className: "absolute inline-flex size-32 animate-ping rounded-full bg-white/5",
|
|
6279
|
-
style: { animationDelay: "0.6s" }
|
|
6280
|
-
}
|
|
6281
|
-
)
|
|
6282
|
-
] }),
|
|
6283
|
-
peerName ? /* @__PURE__ */ jsxs(Avatar, { className: "relative size-28 ring-4 ring-white/10", children: [
|
|
6284
|
-
/* @__PURE__ */ jsx(AvatarImage, { src: peerAvatar, alt: peerName }),
|
|
6285
|
-
/* @__PURE__ */ jsx(AvatarFallback, { className: "bg-neutral-700 text-3xl font-medium text-white", children: peerName.charAt(0).toUpperCase() })
|
|
6286
|
-
] }) : /* @__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 }) })
|
|
6287
|
-
] }),
|
|
6288
|
-
/* @__PURE__ */ jsxs("div", { children: [
|
|
6289
|
-
peerName && /* @__PURE__ */ jsx("p", { className: "text-xl font-semibold tracking-tight", children: peerName }),
|
|
6290
|
-
/* @__PURE__ */ jsxs("p", { className: "mt-1 text-sm text-white/60", children: [
|
|
6291
|
-
uiStatus === "creating" && "Starting call\u2026",
|
|
6292
|
-
uiStatus === "ringing-out" && `${isVideo ? "Video calling" : "Calling"}\u2026 \xB7 ${secondsLeft}s`,
|
|
6293
|
-
uiStatus === "joining" && "Connecting\u2026"
|
|
6294
|
-
] })
|
|
6295
|
-
] }),
|
|
6296
|
-
uiStatus === "ringing-out" && /* @__PURE__ */ jsxs("div", { className: "mt-1 flex flex-col items-center gap-2", children: [
|
|
6297
|
-
/* @__PURE__ */ jsx(
|
|
6298
|
-
Button,
|
|
6299
|
-
{
|
|
6300
|
-
variant: "ghost",
|
|
6301
|
-
size: "icon",
|
|
6302
|
-
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",
|
|
6303
|
-
"aria-label": "Cancel call",
|
|
6304
|
-
onClick: cancelCall,
|
|
6305
|
-
children: /* @__PURE__ */ jsx(PhoneOff, { size: 24 })
|
|
6306
|
-
}
|
|
6307
|
-
),
|
|
6308
|
-
/* @__PURE__ */ jsx("span", { className: "text-xs font-medium text-white/60", children: "Cancel" })
|
|
6309
|
-
] })
|
|
6310
|
-
] })
|
|
6311
|
-
}
|
|
6312
|
-
) });
|
|
6313
|
-
};
|
|
6314
|
-
var CallOverlay_default = CallOverlay;
|
|
7085
|
+
// src/call/IncomingCallDialog.tsx
|
|
6315
7086
|
init_avatar();
|
|
6316
7087
|
init_button();
|
|
6317
7088
|
init_utils2();
|
|
@@ -6320,6 +7091,8 @@ var IncomingCallDialog = ({ ringtoneUrl }) => {
|
|
|
6320
7091
|
const uiStatus = useCallStore((s) => s.uiStatus);
|
|
6321
7092
|
const callId = useCallStore((s) => s.callId);
|
|
6322
7093
|
const caller = useCallStore((s) => s.caller);
|
|
7094
|
+
const peer = useCallStore((s) => s.peer);
|
|
7095
|
+
const isGroupCall = useCallStore((s) => s.isGroupCall);
|
|
6323
7096
|
const mode = useCallStore((s) => s.mode);
|
|
6324
7097
|
const acceptCall = useCallStore((s) => s.acceptCall);
|
|
6325
7098
|
const rejectCall = useCallStore((s) => s.rejectCall);
|
|
@@ -6337,7 +7110,9 @@ var IncomingCallDialog = ({ ringtoneUrl }) => {
|
|
|
6337
7110
|
}
|
|
6338
7111
|
}, [isOpen, ringtoneUrl]);
|
|
6339
7112
|
if (!isOpen) return null;
|
|
6340
|
-
const
|
|
7113
|
+
const callerName = caller?.name || "Unknown caller";
|
|
7114
|
+
const name = isGroupCall ? peer?.name || callerName : callerName;
|
|
7115
|
+
const avatar = isGroupCall ? peer?.avatar || void 0 : caller?.profileImage;
|
|
6341
7116
|
const isVideo = mode === "video";
|
|
6342
7117
|
return /* @__PURE__ */ jsxs(Dialog, { open: isOpen, onOpenChange: () => void 0, children: [
|
|
6343
7118
|
ringtoneUrl && /* @__PURE__ */ jsx("audio", { ref: audioRef, src: ringtoneUrl, loop: true }),
|
|
@@ -6359,7 +7134,7 @@ var IncomingCallDialog = ({ ringtoneUrl }) => {
|
|
|
6359
7134
|
}
|
|
6360
7135
|
),
|
|
6361
7136
|
/* @__PURE__ */ jsxs(Avatar, { className: "relative size-24 ring-4 ring-white/10", children: [
|
|
6362
|
-
/* @__PURE__ */ jsx(AvatarImage, { src:
|
|
7137
|
+
/* @__PURE__ */ jsx(AvatarImage, { src: avatar, alt: name }),
|
|
6363
7138
|
/* @__PURE__ */ jsx(AvatarFallback, { className: "bg-neutral-700 text-2xl font-medium text-white", children: name.charAt(0).toUpperCase() })
|
|
6364
7139
|
] }),
|
|
6365
7140
|
/* @__PURE__ */ jsx(
|
|
@@ -6375,13 +7150,7 @@ var IncomingCallDialog = ({ ringtoneUrl }) => {
|
|
|
6375
7150
|
] }),
|
|
6376
7151
|
/* @__PURE__ */ jsxs("div", { className: "text-center", children: [
|
|
6377
7152
|
/* @__PURE__ */ jsx("p", { className: "text-xl font-semibold tracking-tight", children: name }),
|
|
6378
|
-
/* @__PURE__ */
|
|
6379
|
-
"Incoming ",
|
|
6380
|
-
isVideo ? "video" : "voice",
|
|
6381
|
-
" call \xB7 ",
|
|
6382
|
-
secondsLeft,
|
|
6383
|
-
"s"
|
|
6384
|
-
] })
|
|
7153
|
+
/* @__PURE__ */ jsx("p", { className: "mt-1 text-sm text-white/50", children: isGroupCall ? `${callerName} is calling \xB7 ${isVideo ? "video" : "voice"} call \xB7 ${secondsLeft}s` : `Incoming ${isVideo ? "video" : "voice"} call \xB7 ${secondsLeft}s` })
|
|
6385
7154
|
] }),
|
|
6386
7155
|
/* @__PURE__ */ jsxs("div", { className: "mt-1 flex items-center justify-center gap-10", children: [
|
|
6387
7156
|
/* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center gap-2", children: [
|
|
@@ -6468,7 +7237,7 @@ var Chat = ({
|
|
|
6468
7237
|
const status = useStore((s) => s.status);
|
|
6469
7238
|
const error = useStore((s) => s.error);
|
|
6470
7239
|
useChatSync();
|
|
6471
|
-
|
|
7240
|
+
React11__default.useEffect(() => {
|
|
6472
7241
|
const clientId = client?.id;
|
|
6473
7242
|
const initializeChat = async () => {
|
|
6474
7243
|
if (!clientId || !loggedUserDetails) return;
|
|
@@ -6558,28 +7327,28 @@ var Chat = ({
|
|
|
6558
7327
|
queryParamApis: queryParamApis ?? ["get"],
|
|
6559
7328
|
queryParamsByApi: queryParamsByApi ?? {}
|
|
6560
7329
|
});
|
|
6561
|
-
|
|
7330
|
+
React11__default.useEffect(() => {
|
|
6562
7331
|
setChatQueryParams(queryParams);
|
|
6563
7332
|
setChatQueryParamApis(queryParamApis);
|
|
6564
7333
|
setChatQueryParamsByApi(queryParamsByApi);
|
|
6565
7334
|
}, [queryConfigKey]);
|
|
6566
|
-
|
|
7335
|
+
React11__default.useEffect(() => {
|
|
6567
7336
|
if (lastDM && onMessageReceived) {
|
|
6568
7337
|
onMessageReceived(lastDM);
|
|
6569
7338
|
}
|
|
6570
7339
|
}, [lastDM, onMessageReceived]);
|
|
6571
|
-
|
|
7340
|
+
React11__default.useEffect(() => {
|
|
6572
7341
|
if (status === "connected" && onSocketConnected) {
|
|
6573
7342
|
const socketId = useStore.getState().socketId;
|
|
6574
7343
|
if (socketId) onSocketConnected(socketId);
|
|
6575
7344
|
}
|
|
6576
7345
|
}, [status, onSocketConnected]);
|
|
6577
|
-
|
|
7346
|
+
React11__default.useEffect(() => {
|
|
6578
7347
|
if (status === "error" && error && onSocketError) {
|
|
6579
7348
|
onSocketError(error);
|
|
6580
7349
|
}
|
|
6581
7350
|
}, [status, error, onSocketError]);
|
|
6582
|
-
const contextValue =
|
|
7351
|
+
const contextValue = React11__default.useMemo(
|
|
6583
7352
|
() => ({
|
|
6584
7353
|
onSendMessage,
|
|
6585
7354
|
client,
|
|
@@ -6599,7 +7368,7 @@ var Chat = ({
|
|
|
6599
7368
|
messagesData
|
|
6600
7369
|
]
|
|
6601
7370
|
);
|
|
6602
|
-
const customization =
|
|
7371
|
+
const customization = React11__default.useMemo(
|
|
6603
7372
|
() => ({
|
|
6604
7373
|
components: uiConfig.components,
|
|
6605
7374
|
classNames: uiConfig.classNames,
|
|
@@ -6619,277 +7388,7 @@ init_utils2();
|
|
|
6619
7388
|
// src/hooks/useChatController.ts
|
|
6620
7389
|
init_useStore();
|
|
6621
7390
|
init_store_helpers();
|
|
6622
|
-
|
|
6623
|
-
// src/notifications/open-conversation.ts
|
|
6624
|
-
init_store_helpers();
|
|
6625
|
-
var OPEN_CONVERSATION_EVENT = "realtimex:open-conversation";
|
|
6626
|
-
var NOTIFICATION_CLICK_SW_TYPE = "realtimex:notification-click";
|
|
6627
|
-
var PUSH_BROADCAST_CHANNEL = "realtimex-push";
|
|
6628
|
-
var PUSH_DATA_CACHE_NAME = "realtimex-push-data-v1";
|
|
6629
|
-
var PENDING_STORAGE_KEY = "realtimex:pending-open-conversation";
|
|
6630
|
-
var PENDING_OPEN_CACHE_PATH = "pending-open";
|
|
6631
|
-
var resolveConversationIdFromPushData = (data) => {
|
|
6632
|
-
if (!data || typeof data !== "object") return "";
|
|
6633
|
-
let fcmMsg = data.FCM_MSG;
|
|
6634
|
-
if (typeof fcmMsg === "string") {
|
|
6635
|
-
try {
|
|
6636
|
-
fcmMsg = JSON.parse(fcmMsg);
|
|
6637
|
-
} catch {
|
|
6638
|
-
fcmMsg = void 0;
|
|
6639
|
-
}
|
|
6640
|
-
}
|
|
6641
|
-
const nested = fcmMsg && typeof fcmMsg === "object" ? fcmMsg.data : void 0;
|
|
6642
|
-
const source = { ...nested || {}, ...data };
|
|
6643
|
-
return String(
|
|
6644
|
-
source.conversationId || source.conversation_id || source.conversationID || ""
|
|
6645
|
-
).trim();
|
|
6646
|
-
};
|
|
6647
|
-
var normalizePushMeta = (meta) => {
|
|
6648
|
-
if (!meta || typeof meta !== "object") return {};
|
|
6649
|
-
return {
|
|
6650
|
-
senderId: String(
|
|
6651
|
-
meta.senderId || meta.sender_id || ""
|
|
6652
|
-
).trim() || void 0,
|
|
6653
|
-
kind: String(meta.kind || "").trim() || void 0,
|
|
6654
|
-
messageId: String(
|
|
6655
|
-
meta.messageId || meta.message_id || ""
|
|
6656
|
-
).trim() || void 0,
|
|
6657
|
-
title: String(meta.title || "").trim() || void 0
|
|
6658
|
-
};
|
|
6659
|
-
};
|
|
6660
|
-
var parsePendingRaw = (raw) => {
|
|
6661
|
-
if (!raw) return null;
|
|
6662
|
-
try {
|
|
6663
|
-
const parsed = JSON.parse(raw);
|
|
6664
|
-
if (parsed?.conversationId) {
|
|
6665
|
-
return {
|
|
6666
|
-
conversationId: String(parsed.conversationId).trim(),
|
|
6667
|
-
...normalizePushMeta(parsed)
|
|
6668
|
-
};
|
|
6669
|
-
}
|
|
6670
|
-
} catch {
|
|
6671
|
-
}
|
|
6672
|
-
const id = String(raw).trim();
|
|
6673
|
-
return id ? { conversationId: id } : null;
|
|
6674
|
-
};
|
|
6675
|
-
var peekPendingOpenConversation = () => peekPendingOpenRequest()?.conversationId ?? null;
|
|
6676
|
-
var peekPendingOpenRequest = () => {
|
|
6677
|
-
try {
|
|
6678
|
-
return parsePendingRaw(sessionStorage.getItem(PENDING_STORAGE_KEY));
|
|
6679
|
-
} catch {
|
|
6680
|
-
return null;
|
|
6681
|
-
}
|
|
6682
|
-
};
|
|
6683
|
-
var consumePendingOpenConversation = () => consumePendingOpenRequest()?.conversationId ?? null;
|
|
6684
|
-
var consumePendingOpenRequest = () => {
|
|
6685
|
-
const request = peekPendingOpenRequest();
|
|
6686
|
-
try {
|
|
6687
|
-
sessionStorage.removeItem(PENDING_STORAGE_KEY);
|
|
6688
|
-
} catch {
|
|
6689
|
-
}
|
|
6690
|
-
return request;
|
|
6691
|
-
};
|
|
6692
|
-
var requestOpenConversation = (conversationId, meta) => {
|
|
6693
|
-
const id = String(conversationId || "").trim();
|
|
6694
|
-
if (!id) return;
|
|
6695
|
-
const request = {
|
|
6696
|
-
conversationId: id,
|
|
6697
|
-
...normalizePushMeta(meta)
|
|
6698
|
-
};
|
|
6699
|
-
console.log("[realtimex-push] requestOpenConversation:", request);
|
|
6700
|
-
try {
|
|
6701
|
-
sessionStorage.setItem(PENDING_STORAGE_KEY, JSON.stringify(request));
|
|
6702
|
-
} catch {
|
|
6703
|
-
}
|
|
6704
|
-
try {
|
|
6705
|
-
window.dispatchEvent(
|
|
6706
|
-
new CustomEvent(OPEN_CONVERSATION_EVENT, { detail: request })
|
|
6707
|
-
);
|
|
6708
|
-
} catch {
|
|
6709
|
-
}
|
|
6710
|
-
};
|
|
6711
|
-
var buildChannelFromPushRequest = (request, loggedUserId, allUsers) => {
|
|
6712
|
-
const conversationId = request.conversationId;
|
|
6713
|
-
const isGroup = request.kind === "group";
|
|
6714
|
-
const senderId = String(request.senderId || "").trim();
|
|
6715
|
-
if (isGroup) {
|
|
6716
|
-
const rawName2 = request.title || "Group Chat";
|
|
6717
|
-
return {
|
|
6718
|
-
id: conversationId,
|
|
6719
|
-
conversationId,
|
|
6720
|
-
name: rawName2.charAt(0).toUpperCase() + rawName2.slice(1),
|
|
6721
|
-
isGroup: true
|
|
6722
|
-
};
|
|
6723
|
-
}
|
|
6724
|
-
const senderUser = senderId ? (allUsers || []).find((user) => String(user._id) === senderId) : null;
|
|
6725
|
-
const rawName = senderUser?.name || request.title || "New message";
|
|
6726
|
-
return {
|
|
6727
|
-
id: senderId || conversationId,
|
|
6728
|
-
conversationId,
|
|
6729
|
-
name: rawName.charAt(0).toUpperCase() + rawName.slice(1),
|
|
6730
|
-
image: senderUser?.image || void 0,
|
|
6731
|
-
email: senderUser?.email,
|
|
6732
|
-
isGroup: false
|
|
6733
|
-
};
|
|
6734
|
-
};
|
|
6735
|
-
var mapConversationToChannel = (conversation, loggedUserId, allUsers) => {
|
|
6736
|
-
const conversationId = String(conversation?._id || conversation?.id || "");
|
|
6737
|
-
const isGroup = isGroupConversation(conversation);
|
|
6738
|
-
if (isGroup) {
|
|
6739
|
-
const rawName2 = conversation.groupName || conversation.name || "Group Chat";
|
|
6740
|
-
return {
|
|
6741
|
-
id: conversationId,
|
|
6742
|
-
conversationId,
|
|
6743
|
-
name: rawName2.charAt(0).toUpperCase() + rawName2.slice(1),
|
|
6744
|
-
image: resolveGroupImage(conversation),
|
|
6745
|
-
isGroup: true,
|
|
6746
|
-
isLeft: !!conversation.isLeft,
|
|
6747
|
-
isBlocked: !!conversation.isBlocked,
|
|
6748
|
-
pinnedCount: conversation.pinnedCount,
|
|
6749
|
-
starredCount: conversation.starredCount
|
|
6750
|
-
};
|
|
6751
|
-
}
|
|
6752
|
-
const { id: otherId, user } = resolveOtherParticipant(
|
|
6753
|
-
conversation,
|
|
6754
|
-
loggedUserId,
|
|
6755
|
-
allUsers
|
|
6756
|
-
);
|
|
6757
|
-
const rawName = user?.name || "Unknown User";
|
|
6758
|
-
return {
|
|
6759
|
-
id: otherId || conversationId,
|
|
6760
|
-
conversationId,
|
|
6761
|
-
name: rawName.charAt(0).toUpperCase() + rawName.slice(1),
|
|
6762
|
-
image: user?.image || void 0,
|
|
6763
|
-
email: user?.email,
|
|
6764
|
-
isGroup: false,
|
|
6765
|
-
isLeft: !!conversation.isLeft,
|
|
6766
|
-
isBlocked: !!conversation.isBlocked,
|
|
6767
|
-
pinnedCount: conversation.pinnedCount,
|
|
6768
|
-
starredCount: conversation.starredCount
|
|
6769
|
-
};
|
|
6770
|
-
};
|
|
6771
|
-
var stripConversationIdFromUrl = () => {
|
|
6772
|
-
if (typeof window === "undefined") return;
|
|
6773
|
-
try {
|
|
6774
|
-
const url = new URL(window.location.href);
|
|
6775
|
-
if (!url.searchParams.has("conversationId")) return;
|
|
6776
|
-
url.searchParams.delete("conversationId");
|
|
6777
|
-
const next = `${url.pathname}${url.search}${url.hash}`;
|
|
6778
|
-
window.history.replaceState(window.history.state, "", next);
|
|
6779
|
-
} catch {
|
|
6780
|
-
}
|
|
6781
|
-
};
|
|
6782
|
-
var cacheRequestForPath = (path) => new Request(new URL(path, window.location.origin).toString());
|
|
6783
|
-
var consumePendingOpenFromPushCache = async () => {
|
|
6784
|
-
if (typeof caches === "undefined") return null;
|
|
6785
|
-
try {
|
|
6786
|
-
const cache = await caches.open(PUSH_DATA_CACHE_NAME);
|
|
6787
|
-
const req = cacheRequestForPath(PENDING_OPEN_CACHE_PATH);
|
|
6788
|
-
const res = await cache.match(req);
|
|
6789
|
-
if (!res) return null;
|
|
6790
|
-
await cache.delete(req);
|
|
6791
|
-
const data = await res.json();
|
|
6792
|
-
const conversationId = resolveConversationIdFromPushData(data);
|
|
6793
|
-
if (!conversationId) return null;
|
|
6794
|
-
return {
|
|
6795
|
-
conversationId,
|
|
6796
|
-
...normalizePushMeta(data)
|
|
6797
|
-
};
|
|
6798
|
-
} catch {
|
|
6799
|
-
return null;
|
|
6800
|
-
}
|
|
6801
|
-
};
|
|
6802
|
-
var readConversationIdFromLocation = () => {
|
|
6803
|
-
try {
|
|
6804
|
-
return new URLSearchParams(window.location.search).get("conversationId") || "";
|
|
6805
|
-
} catch {
|
|
6806
|
-
return "";
|
|
6807
|
-
}
|
|
6808
|
-
};
|
|
6809
|
-
var consumedUrlConversationIds = /* @__PURE__ */ new Set();
|
|
6810
|
-
var lastHandledOpenId = "";
|
|
6811
|
-
var lastHandledOpenAt = 0;
|
|
6812
|
-
var shouldHandleOpen = (conversationId) => {
|
|
6813
|
-
const id = String(conversationId || "").trim();
|
|
6814
|
-
if (!id) return false;
|
|
6815
|
-
const now = Date.now();
|
|
6816
|
-
if (lastHandledOpenId === id && now - lastHandledOpenAt < 2500) {
|
|
6817
|
-
return false;
|
|
6818
|
-
}
|
|
6819
|
-
lastHandledOpenId = id;
|
|
6820
|
-
lastHandledOpenAt = now;
|
|
6821
|
-
return true;
|
|
6822
|
-
};
|
|
6823
|
-
var handleNotificationOpen = (conversationId, pushData, options) => {
|
|
6824
|
-
const id = String(conversationId || "").trim();
|
|
6825
|
-
if (!id) {
|
|
6826
|
-
console.warn("[realtimex-push] notification open ignored \u2014 no conversationId", pushData);
|
|
6827
|
-
return;
|
|
6828
|
-
}
|
|
6829
|
-
if (!shouldHandleOpen(id)) {
|
|
6830
|
-
stripConversationIdFromUrl();
|
|
6831
|
-
return;
|
|
6832
|
-
}
|
|
6833
|
-
requestOpenConversation(id, pushData);
|
|
6834
|
-
const chatPath = options.chatPath || "/chat";
|
|
6835
|
-
stripConversationIdFromUrl();
|
|
6836
|
-
options.onNavigate?.(id, chatPath);
|
|
6837
|
-
};
|
|
6838
|
-
var installNotificationClickBridge = (options = {}) => {
|
|
6839
|
-
if (typeof window === "undefined") return () => void 0;
|
|
6840
|
-
const onSwMessage = (event) => {
|
|
6841
|
-
const data = event.data;
|
|
6842
|
-
console.log("[realtimex-push] SW \u2192 page message:", data);
|
|
6843
|
-
if (!data || data.type !== NOTIFICATION_CLICK_SW_TYPE) return;
|
|
6844
|
-
const pushData = data.data && typeof data.data === "object" ? data.data : void 0;
|
|
6845
|
-
const conversationId = String(data.conversationId || "") || resolveConversationIdFromPushData(pushData);
|
|
6846
|
-
console.log("[realtimex-push] notification click \u2192 open:", {
|
|
6847
|
-
conversationId,
|
|
6848
|
-
pushData
|
|
6849
|
-
});
|
|
6850
|
-
handleNotificationOpen(conversationId, pushData, options);
|
|
6851
|
-
};
|
|
6852
|
-
navigator.serviceWorker?.addEventListener("message", onSwMessage);
|
|
6853
|
-
let broadcast = null;
|
|
6854
|
-
try {
|
|
6855
|
-
broadcast = new BroadcastChannel(PUSH_BROADCAST_CHANNEL);
|
|
6856
|
-
broadcast.onmessage = onSwMessage;
|
|
6857
|
-
} catch {
|
|
6858
|
-
}
|
|
6859
|
-
const fromUrl = readConversationIdFromLocation();
|
|
6860
|
-
if (fromUrl && !consumedUrlConversationIds.has(fromUrl)) {
|
|
6861
|
-
consumedUrlConversationIds.add(fromUrl);
|
|
6862
|
-
handleNotificationOpen(fromUrl, void 0, options);
|
|
6863
|
-
} else {
|
|
6864
|
-
stripConversationIdFromUrl();
|
|
6865
|
-
}
|
|
6866
|
-
void consumePendingOpenFromPushCache().then((request) => {
|
|
6867
|
-
if (!request?.conversationId) return;
|
|
6868
|
-
handleNotificationOpen(
|
|
6869
|
-
request.conversationId,
|
|
6870
|
-
request,
|
|
6871
|
-
options
|
|
6872
|
-
);
|
|
6873
|
-
});
|
|
6874
|
-
return () => {
|
|
6875
|
-
navigator.serviceWorker?.removeEventListener("message", onSwMessage);
|
|
6876
|
-
broadcast?.close();
|
|
6877
|
-
};
|
|
6878
|
-
};
|
|
6879
|
-
|
|
6880
|
-
// src/hooks/useChatController.ts
|
|
6881
|
-
init_call_types();
|
|
6882
|
-
function formatCallLogText(entry) {
|
|
6883
|
-
const label = entry.mode === "video" ? "video" : "voice";
|
|
6884
|
-
if (entry.outcome === "missed") return `Missed ${label} call`;
|
|
6885
|
-
if (entry.outcome === "declined") return `${label === "video" ? "Video" : "Voice"} call declined`;
|
|
6886
|
-
if (entry.outcome === "busy") return `${label === "video" ? "Video" : "Voice"} call \xB7 not answered`;
|
|
6887
|
-
const totalSeconds = Math.max(0, Math.floor(entry.durationSeconds));
|
|
6888
|
-
const minutes = Math.floor(totalSeconds / 60);
|
|
6889
|
-
const seconds = totalSeconds % 60;
|
|
6890
|
-
const duration = `${minutes}:${String(seconds).padStart(2, "0")}`;
|
|
6891
|
-
return `${label === "video" ? "Video" : "Voice"} call \xB7 ${duration}`;
|
|
6892
|
-
}
|
|
7391
|
+
init_open_conversation();
|
|
6893
7392
|
function resolveMessageType(content, attachments) {
|
|
6894
7393
|
if (attachments.length === 0) return "text";
|
|
6895
7394
|
if (!content.trim() && attachments.length === 1) {
|
|
@@ -6947,28 +7446,6 @@ var useChatController = () => {
|
|
|
6947
7446
|
);
|
|
6948
7447
|
}, []);
|
|
6949
7448
|
const { localMessages, setLocalMessages } = useChatSync();
|
|
6950
|
-
useEffect(() => {
|
|
6951
|
-
const handleCallLog = (event) => {
|
|
6952
|
-
const entry = event.detail;
|
|
6953
|
-
if (!entry?.conversationId) return;
|
|
6954
|
-
const logMessage = {
|
|
6955
|
-
id: `call-log-${Date.now()}-${Math.random().toString(36).slice(2)}`,
|
|
6956
|
-
content: formatCallLogText(entry),
|
|
6957
|
-
sender: "system",
|
|
6958
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
6959
|
-
isOwnMessage: false,
|
|
6960
|
-
isSystemMessage: true,
|
|
6961
|
-
channelId: entry.conversationId,
|
|
6962
|
-
conversationId: entry.conversationId
|
|
6963
|
-
};
|
|
6964
|
-
setLocalMessages((prev) => ({
|
|
6965
|
-
...prev,
|
|
6966
|
-
[entry.conversationId]: [...prev[entry.conversationId] || [], logMessage]
|
|
6967
|
-
}));
|
|
6968
|
-
};
|
|
6969
|
-
window.addEventListener(CALL_LOG_EVENT, handleCallLog);
|
|
6970
|
-
return () => window.removeEventListener(CALL_LOG_EVENT, handleCallLog);
|
|
6971
|
-
}, [setLocalMessages]);
|
|
6972
7449
|
const emitDmSend = useStore((s) => s.emitDmSend);
|
|
6973
7450
|
const emitGroupMessageSend2 = useStore((s) => s.emitGroupMessageSend);
|
|
6974
7451
|
const emitMarkAsRead = useStore((s) => s.emitMarkAsRead);
|
|
@@ -7874,9 +8351,9 @@ function AdminPermissionTooltip({
|
|
|
7874
8351
|
if (!disabled || !message) {
|
|
7875
8352
|
return /* @__PURE__ */ jsx(Fragment, { children });
|
|
7876
8353
|
}
|
|
7877
|
-
const gatedChildren =
|
|
7878
|
-
if (!
|
|
7879
|
-
return
|
|
8354
|
+
const gatedChildren = React11__default.Children.map(children, (child) => {
|
|
8355
|
+
if (!React11__default.isValidElement(child)) return child;
|
|
8356
|
+
return React11__default.cloneElement(child, {
|
|
7880
8357
|
className: cn(child.props.className, "pointer-events-none")
|
|
7881
8358
|
});
|
|
7882
8359
|
});
|
|
@@ -9816,41 +10293,7 @@ var ChatSocketStatus_default = ChatSocketStatus;
|
|
|
9816
10293
|
|
|
9817
10294
|
// src/chat/header/HeaderSearchPopover.tsx
|
|
9818
10295
|
init_button();
|
|
9819
|
-
|
|
9820
|
-
// src/ui/popover.tsx
|
|
9821
|
-
init_utils2();
|
|
9822
|
-
function Popover({
|
|
9823
|
-
...props
|
|
9824
|
-
}) {
|
|
9825
|
-
return /* @__PURE__ */ jsx(Popover$1.Root, { "data-slot": "popover", ...props });
|
|
9826
|
-
}
|
|
9827
|
-
function PopoverTrigger({
|
|
9828
|
-
...props
|
|
9829
|
-
}) {
|
|
9830
|
-
return /* @__PURE__ */ jsx(Popover$1.Trigger, { "data-slot": "popover-trigger", ...props });
|
|
9831
|
-
}
|
|
9832
|
-
function PopoverContent({
|
|
9833
|
-
className,
|
|
9834
|
-
align = "center",
|
|
9835
|
-
sideOffset = 4,
|
|
9836
|
-
...props
|
|
9837
|
-
}) {
|
|
9838
|
-
return /* @__PURE__ */ jsx(Popover$1.Portal, { children: /* @__PURE__ */ jsx(
|
|
9839
|
-
Popover$1.Content,
|
|
9840
|
-
{
|
|
9841
|
-
"data-slot": "popover-content",
|
|
9842
|
-
align,
|
|
9843
|
-
sideOffset,
|
|
9844
|
-
className: cn(
|
|
9845
|
-
"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",
|
|
9846
|
-
className
|
|
9847
|
-
),
|
|
9848
|
-
...props
|
|
9849
|
-
}
|
|
9850
|
-
) });
|
|
9851
|
-
}
|
|
9852
|
-
|
|
9853
|
-
// src/chat/header/HeaderSearchPopover.tsx
|
|
10296
|
+
init_popover();
|
|
9854
10297
|
init_utils2();
|
|
9855
10298
|
function escapeRegExp(value) {
|
|
9856
10299
|
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
@@ -9867,7 +10310,7 @@ function highlightSearchMatch(text, query) {
|
|
|
9867
10310
|
children: part
|
|
9868
10311
|
},
|
|
9869
10312
|
`${part}-${index}`
|
|
9870
|
-
) : /* @__PURE__ */ jsx(
|
|
10313
|
+
) : /* @__PURE__ */ jsx(React11__default.Fragment, { children: part }, `${part}-${index}`)
|
|
9871
10314
|
);
|
|
9872
10315
|
}
|
|
9873
10316
|
function getMessagePreview(message) {
|
|
@@ -10265,20 +10708,20 @@ var HeaderRightSide = ({
|
|
|
10265
10708
|
const startCall = useCallStore((s) => s.startCall);
|
|
10266
10709
|
const showPhoneCall = features.showPhoneCall ?? defaultChatFeatures.showPhoneCall;
|
|
10267
10710
|
const showVideoCall = features.showVideoCall ?? defaultChatFeatures.showVideoCall;
|
|
10268
|
-
const
|
|
10711
|
+
const peer = { name: activeChannel.name, avatar: activeChannel.avatar || activeChannel.image };
|
|
10269
10712
|
const handlePhoneCall = () => {
|
|
10270
10713
|
if (features.onPhoneCall) {
|
|
10271
10714
|
features.onPhoneCall({ activeChannel, conversationId });
|
|
10272
10715
|
return;
|
|
10273
10716
|
}
|
|
10274
|
-
if (conversationId) startCall(conversationId, "audio",
|
|
10717
|
+
if (conversationId) startCall(conversationId, "audio", peer, activeChannel.isGroup);
|
|
10275
10718
|
};
|
|
10276
10719
|
const handleVideoCall = () => {
|
|
10277
10720
|
if (features.onVideoCall) {
|
|
10278
10721
|
features.onVideoCall({ activeChannel, conversationId });
|
|
10279
10722
|
return;
|
|
10280
10723
|
}
|
|
10281
|
-
if (conversationId) startCall(conversationId, "video",
|
|
10724
|
+
if (conversationId) startCall(conversationId, "video", peer, activeChannel.isGroup);
|
|
10282
10725
|
};
|
|
10283
10726
|
const headerItemGapClass = "gap-0.5";
|
|
10284
10727
|
const headerIconBtnClass = "inline-flex size-6 shrink-0 items-center justify-center rounded-lg p-0 text-(--chat-muted) transition-colors hover:bg-(--chat-hover) hover:text-(--chat-text) has-[>svg]:p-0";
|
|
@@ -15923,6 +16366,7 @@ var MessageReactions = ({
|
|
|
15923
16366
|
var MessageReactions_default = MessageReactions;
|
|
15924
16367
|
init_utils2();
|
|
15925
16368
|
init_button();
|
|
16369
|
+
init_popover();
|
|
15926
16370
|
var MessageToolbar = ({
|
|
15927
16371
|
isSender,
|
|
15928
16372
|
isRecent,
|
|
@@ -18073,6 +18517,9 @@ function useActiveChannelMessages({
|
|
|
18073
18517
|
};
|
|
18074
18518
|
}
|
|
18075
18519
|
|
|
18520
|
+
// src/chat/active-channel-messages/ChatDateJumpMenu.tsx
|
|
18521
|
+
init_popover();
|
|
18522
|
+
|
|
18076
18523
|
// src/chat/ui/calendar.tsx
|
|
18077
18524
|
init_utils2();
|
|
18078
18525
|
init_button();
|
|
@@ -18229,8 +18676,8 @@ function CalendarDayButton({
|
|
|
18229
18676
|
...props
|
|
18230
18677
|
}) {
|
|
18231
18678
|
const defaultClassNames = getDefaultClassNames();
|
|
18232
|
-
const ref =
|
|
18233
|
-
|
|
18679
|
+
const ref = React11.useRef(null);
|
|
18680
|
+
React11.useEffect(() => {
|
|
18234
18681
|
if (modifiers.focused) ref.current?.focus();
|
|
18235
18682
|
}, [modifiers.focused]);
|
|
18236
18683
|
return /* @__PURE__ */ jsx(
|
|
@@ -18606,6 +19053,9 @@ function ComposerStatusBanner({
|
|
|
18606
19053
|
] }) });
|
|
18607
19054
|
}
|
|
18608
19055
|
|
|
19056
|
+
// src/chat/channel-message-box/ChannelMessageBoxView.tsx
|
|
19057
|
+
init_popover();
|
|
19058
|
+
|
|
18609
19059
|
// src/chat/composer-attachment/ComposerAttachmentPreview.tsx
|
|
18610
19060
|
init_utils2();
|
|
18611
19061
|
init_button();
|
|
@@ -19202,10 +19652,10 @@ function useChannelMessageBox({
|
|
|
19202
19652
|
const composerDisabledReason = conversationId ? composerDisabledByConversation[conversationId] : void 0;
|
|
19203
19653
|
const fileInputRef = useRef(null);
|
|
19204
19654
|
const attachmentsCountRef = useRef(0);
|
|
19205
|
-
|
|
19655
|
+
React11__default.useEffect(() => {
|
|
19206
19656
|
attachmentsCountRef.current = state.attachments.length;
|
|
19207
19657
|
}, [state.attachments.length]);
|
|
19208
|
-
|
|
19658
|
+
React11__default.useEffect(() => {
|
|
19209
19659
|
const draft = {
|
|
19210
19660
|
messageInput: state.messageInput,
|
|
19211
19661
|
attachments: state.attachments
|
|
@@ -19213,7 +19663,7 @@ function useChannelMessageBox({
|
|
|
19213
19663
|
syncComposerRef(draft);
|
|
19214
19664
|
updateConversationDraft(conversationIdRef.current, draft);
|
|
19215
19665
|
}, [state.messageInput, state.attachments, syncComposerRef]);
|
|
19216
|
-
|
|
19666
|
+
React11__default.useEffect(() => {
|
|
19217
19667
|
if (typingTimeoutRef.current) {
|
|
19218
19668
|
clearTimeout(typingTimeoutRef.current);
|
|
19219
19669
|
typingTimeoutRef.current = null;
|
|
@@ -20274,6 +20724,7 @@ var ChatViewport_default = ChatViewport;
|
|
|
20274
20724
|
init_utils2();
|
|
20275
20725
|
init_normalize_helpers();
|
|
20276
20726
|
init_useStore();
|
|
20727
|
+
init_open_conversation();
|
|
20277
20728
|
var RETRY_DELAYS_MS = [1e3, 3e3, 8e3, 2e4];
|
|
20278
20729
|
var recentToastIds = /* @__PURE__ */ new Set();
|
|
20279
20730
|
var rememberToastId = (id) => {
|
|
@@ -20574,8 +21025,8 @@ var ChatMain = ({
|
|
|
20574
21025
|
viewportHeight = "full",
|
|
20575
21026
|
viewportClassName
|
|
20576
21027
|
}) => {
|
|
20577
|
-
const clientObj =
|
|
20578
|
-
const uiConfigObj =
|
|
21028
|
+
const clientObj = React11__default.useMemo(() => ({ id: clientId }), [clientId]);
|
|
21029
|
+
const uiConfigObj = React11__default.useMemo(
|
|
20579
21030
|
() => ({
|
|
20580
21031
|
...uiConfig || {},
|
|
20581
21032
|
components: { ...uiConfig?.components, ...components },
|
|
@@ -20952,6 +21403,9 @@ var packageDefaultComponents = {
|
|
|
20952
21403
|
ChatSocketStatus: ChatSocketStatus_default
|
|
20953
21404
|
};
|
|
20954
21405
|
|
|
21406
|
+
// src/index.ts
|
|
21407
|
+
init_open_conversation();
|
|
21408
|
+
|
|
20955
21409
|
// src/hooks/useAdminFeatureGate.ts
|
|
20956
21410
|
function useAdminFeatureGate(feature) {
|
|
20957
21411
|
const ctx = useEffectiveSettingsOptional();
|