@samparkchat/sampark-web-chat 0.1.2 → 0.1.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.
Files changed (84) hide show
  1. package/package.json +1 -1
  2. package/sampark/sampark-chat/components/GroupVideoCall.d.ts +10 -0
  3. package/sampark/sampark-chat/components/GroupVideoCall.js +162 -67
  4. package/sampark/sampark-chat/components/Headers/ChatHeader.d.ts +3 -0
  5. package/sampark/sampark-chat/components/Headers/ChatHeader.js +20 -72
  6. package/sampark/sampark-chat/components/Headers/GroupChatHeader.js +16 -8
  7. package/sampark/sampark-chat/components/Headers/HeaderPanel/CallDetailsPanel.d.ts +11 -1
  8. package/sampark/sampark-chat/components/Headers/HeaderPanel/CallDetailsPanel.js +127 -73
  9. package/sampark/sampark-chat/components/Headers/HeaderPanel/CallHistory.d.ts +9 -0
  10. package/sampark/sampark-chat/components/Headers/HeaderPanel/CallHistory.js +17 -41
  11. package/sampark/sampark-chat/components/Headers/HeaderPanel/GroupList.js +81 -21
  12. package/sampark/sampark-chat/components/Headers/HeaderPanel/UserList.d.ts +1 -0
  13. package/sampark/sampark-chat/components/Headers/HeaderPanel/UserList.js +6 -17
  14. package/sampark/sampark-chat/components/Headers/SamparkHeader.d.ts +5 -0
  15. package/sampark/sampark-chat/components/Headers/SamparkHeader.js +39 -28
  16. package/sampark/sampark-chat/components/OnlineIndicator.d.ts +4 -0
  17. package/sampark/sampark-chat/components/OnlineIndicator.js +37 -3
  18. package/sampark/sampark-chat/components/ProfileModal.d.ts +7 -0
  19. package/sampark/sampark-chat/components/ProfileModal.js +397 -64
  20. package/sampark/sampark-chat/components/VideoCall.js +19 -11
  21. package/sampark/sampark-chat/components/bottomsheets/GroupBottomSheet.d.ts +3 -0
  22. package/sampark/sampark-chat/components/bottomsheets/GroupBottomSheet.js +104 -43
  23. package/sampark/sampark-chat/components/bottomsheets/GroupOptionBottomSheet.d.ts +2 -0
  24. package/sampark/sampark-chat/components/bottomsheets/GroupOptionBottomSheet.js +12 -3
  25. package/sampark/sampark-chat/components/lists/CommonList.d.ts +8 -0
  26. package/sampark/sampark-chat/components/lists/CommonList.js +125 -38
  27. package/sampark/sampark-chat/components/lists/ParticipantList.d.ts +2 -0
  28. package/sampark/sampark-chat/components/lists/ParticipantList.js +8 -5
  29. package/sampark/sampark-chat/components/lists/UserInfo.d.ts +9 -0
  30. package/sampark/sampark-chat/components/lists/UserInfo.js +207 -30
  31. package/sampark/sampark-chat/components/lists/ViewParticipents.d.ts +9 -0
  32. package/sampark/sampark-chat/components/lists/ViewParticipents.js +630 -230
  33. package/sampark/sampark-chat/components/modals/CustomStatusModal.d.ts +10 -0
  34. package/sampark/sampark-chat/components/modals/CustomStatusModal.js +225 -0
  35. package/sampark/sampark-chat/components/modals/MeetingAdminTransferModal.d.ts +18 -0
  36. package/sampark/sampark-chat/components/modals/MeetingAdminTransferModal.js +264 -0
  37. package/sampark/sampark-chat/components/modals/ProfileAvatar.d.ts +31 -0
  38. package/sampark/sampark-chat/components/modals/ProfileAvatar.js +80 -0
  39. package/sampark/sampark-chat/components/modals/UnbanRequestModal.d.ts +32 -0
  40. package/sampark/sampark-chat/components/modals/UnbanRequestModal.js +343 -0
  41. package/sampark/sampark-chat/models/ConfirmActionModal.d.ts +6 -0
  42. package/sampark/sampark-chat/models/ConfirmActionModal.js +39 -2
  43. package/sampark/sampark-chat/screens/PeerChatScreen.d.ts +3 -0
  44. package/sampark/sampark-chat/screens/PeerChatScreen.js +37 -31
  45. package/sampark/sampark-chat/screens/SamparkChatsScreen.js +860 -271
  46. package/sampark/sampark-chat/screens/groupChatScreen.d.ts +5 -0
  47. package/sampark/sampark-chat/screens/groupChatScreen.js +343 -69
  48. package/sampark/sampark-chat/sdk/client/GroupCall.d.ts +3 -0
  49. package/sampark/sampark-chat/sdk/client/GroupCall.js +66 -0
  50. package/sampark/sampark-chat/sdk/client/Groupchat.d.ts +141 -1
  51. package/sampark/sampark-chat/sdk/client/Groupchat.js +793 -45
  52. package/sampark/sampark-chat/sdk/client/JanusClient.js +1 -1
  53. package/sampark/sampark-chat/sdk/client/PeerChat.d.ts +27 -0
  54. package/sampark/sampark-chat/sdk/client/PeerChat.js +74 -0
  55. package/sampark/sampark-chat/sdk/enum/ActionTypes.d.ts +4 -1
  56. package/sampark/sampark-chat/sdk/enum/ActionTypes.js +3 -0
  57. package/sampark/sampark-chat/sdk/interface/SamparkGroupCallService.d.ts +1 -0
  58. package/sampark/sampark-chat/sdk/interface/SamparkGroupChatService.d.ts +14 -0
  59. package/sampark/sampark-chat/sdk/types/BanParticipantRequest.d.ts +12 -0
  60. package/sampark/sampark-chat/sdk/types/BanParticipantRequest.js +15 -0
  61. package/sampark/sampark-chat/sdk/types/GetAllParticipantStatusesRequest.d.ts +9 -0
  62. package/sampark/sampark-chat/sdk/types/GetAllParticipantStatusesRequest.js +19 -0
  63. package/sampark/sampark-chat/sdk/types/GetGroupsRequest.d.ts +2 -1
  64. package/sampark/sampark-chat/sdk/types/GetGroupsRequest.js +2 -1
  65. package/sampark/sampark-chat/sdk/types/MuteParticipantRequest.d.ts +12 -0
  66. package/sampark/sampark-chat/sdk/types/MuteParticipantRequest.js +15 -0
  67. package/sampark/sampark-chat/sdk/types/ProcessUnbanRequest.d.ts +13 -0
  68. package/sampark/sampark-chat/sdk/types/ProcessUnbanRequest.js +16 -0
  69. package/sampark/sampark-chat/sdk/types/SetParticipantApplicationDataRequest.d.ts +14 -0
  70. package/sampark/sampark-chat/sdk/types/SetParticipantApplicationDataRequest.js +19 -0
  71. package/sampark/sampark-chat/sdk/types/SubmitUnbanRequest.d.ts +12 -0
  72. package/sampark/sampark-chat/sdk/types/SubmitUnbanRequest.js +16 -0
  73. package/sampark/sampark-chat/sdk/types/TransferOwnershipRequest.d.ts +12 -0
  74. package/sampark/sampark-chat/sdk/types/TransferOwnershipRequest.js +15 -0
  75. package/sampark/sampark-chat/sdk/types/UpdateParticipantRoleRequest.d.ts +12 -0
  76. package/sampark/sampark-chat/sdk/types/UpdateParticipantRoleRequest.js +16 -0
  77. package/sampark/sampark-chat/theme/colors.d.ts +2 -0
  78. package/sampark/sampark-chat/theme/colors.js +3 -1
  79. package/sampark/sampark-chat/theme/fonts.d.ts +8 -0
  80. package/sampark/sampark-chat/theme/fonts.js +8 -0
  81. package/sampark/sampark-chat/theme/icons.d.ts +30 -0
  82. package/sampark/sampark-chat/theme/icons.js +8 -2
  83. /package/sampark/sampark-chat/components/{models → modals}/ReactionModal.d.ts +0 -0
  84. /package/sampark/sampark-chat/components/{models → modals}/ReactionModal.js +0 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@samparkchat/sampark-web-chat",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
4
4
  "main": "sampark/sampark-web-chat/index.js",
5
5
  "types": "sampark/sampark-web-chat/index.d.ts",
6
6
  "private": false,
@@ -16,11 +16,21 @@ interface GroupVideoCallProps {
16
16
  clientUsers: User[];
17
17
  primaryColor: any;
18
18
  secondaryColor: any;
19
+ callSettings: CallSettings;
19
20
  }
20
21
  interface User {
21
22
  user_id: string;
22
23
  user_name: string;
23
24
  profile_pic?: string;
24
25
  }
26
+ interface CallSettings {
27
+ is_one_to_one_calls: boolean;
28
+ is_group_calls: boolean;
29
+ is_invite_users: boolean;
30
+ is_shared_screen: boolean;
31
+ is_remove_participants: boolean;
32
+ is_record_calls: boolean;
33
+ is_microphone_control: boolean;
34
+ }
25
35
  declare const _default: React.NamedExoticComponent<GroupVideoCallProps>;
26
36
  export default _default;
@@ -60,8 +60,9 @@ import { faVideo, faVideoSlash, faMicrophone, faMicrophoneSlash, faPhoneSlash, f
60
60
  import CustomIcons from "../theme/icons";
61
61
  import colors from "../theme/colors";
62
62
  import fonts from "../theme/fonts";
63
+ import MeetingAdminTransferModal from "./modals/MeetingAdminTransferModal";
63
64
  var GroupVideoCall = function (_a) {
64
- var client = _a.client, userId = _a.userId, userName = _a.userName, onClose = _a.onClose, isInitiator = _a.isInitiator, callingMode = _a.callingMode, janusClient = _a.janusClient, janusHandleId = _a.janusHandleId, createSubscriberHandle = _a.createSubscriberHandle, roomId = _a.roomId, janusCallGroupId = _a.janusCallGroupId, janusCallGroupName = _a.janusCallGroupName, _b = _a.clientUsers, clientUsers = _b === void 0 ? [] : _b, primaryColor = _a.primaryColor, secondaryColor = _a.secondaryColor;
65
+ var client = _a.client, userId = _a.userId, userName = _a.userName, onClose = _a.onClose, isInitiator = _a.isInitiator, callingMode = _a.callingMode, janusClient = _a.janusClient, janusHandleId = _a.janusHandleId, createSubscriberHandle = _a.createSubscriberHandle, roomId = _a.roomId, janusCallGroupId = _a.janusCallGroupId, janusCallGroupName = _a.janusCallGroupName, _b = _a.clientUsers, clientUsers = _b === void 0 ? [] : _b, primaryColor = _a.primaryColor, secondaryColor = _a.secondaryColor, callSettings = _a.callSettings;
65
66
  var getHeaderGradient = function () {
66
67
  var primary = primaryColor || colors.blue;
67
68
  var secondary = secondaryColor || colors.blueLight;
@@ -126,6 +127,8 @@ var GroupVideoCall = function (_a) {
126
127
  var subscriberCreationQueue = useRef(Promise.resolve());
127
128
  var activeSubscriberCreations = useRef(0);
128
129
  var maxConcurrentCreations = 2; // Limit concurrent attach operations
130
+ var _17 = useState(false), showOwnershipTransferModal = _17[0], setShowOwnershipTransferModal = _17[1];
131
+ var previousIsInitiatorRef = useRef(isInitiator);
129
132
  var logICEDiagnostics = function (type, pc) { return __awaiter(void 0, void 0, void 0, function () {
130
133
  var stats, err_1;
131
134
  return __generator(this, function (_a) {
@@ -414,6 +417,12 @@ var GroupVideoCall = function (_a) {
414
417
  onClose();
415
418
  }
416
419
  }, [exitedByAdmin]);
420
+ useEffect(function () {
421
+ if (!previousIsInitiatorRef.current && isInitiator) {
422
+ showJoinNotification("Now you are owner of this call");
423
+ }
424
+ previousIsInitiatorRef.current = isInitiator;
425
+ }, [isInitiator]);
417
426
  useEffect(function () {
418
427
  if (isScreenSharing) {
419
428
  busyUsers.forEach(function (busyUserId) {
@@ -575,7 +584,7 @@ var GroupVideoCall = function (_a) {
575
584
  });
576
585
  }); };
577
586
  var recreatePublisherConnection = function () { return __awaiter(void 0, void 0, void 0, function () {
578
- var pc_1, candidatesSent_1, offer, hasAudio, hasVideo, response, err_4;
587
+ var pc_1, candidatesSent_1, offer, hasAudio, hasVideo, uniqueFileName, response, updateKafkaResponse, err_4;
579
588
  return __generator(this, function (_a) {
580
589
  switch (_a.label) {
581
590
  case 0:
@@ -585,7 +594,7 @@ var GroupVideoCall = function (_a) {
585
594
  }
586
595
  _a.label = 1;
587
596
  case 1:
588
- _a.trys.push([1, 7, 8, 9]);
597
+ _a.trys.push([1, 8, 9, 10]);
589
598
  isPublishingRef.current = true;
590
599
  setPublisherState('joining');
591
600
  if (pcRef.current) {
@@ -597,7 +606,7 @@ var GroupVideoCall = function (_a) {
597
606
  return [4 /*yield*/, new Promise(function (resolve) { return setTimeout(resolve, 300); })];
598
607
  case 2:
599
608
  _a.sent();
600
- if (!(janusClient && janusHandleId && localStream && publisherState !== 'published')) return [3 /*break*/, 6];
609
+ if (!(janusClient && janusHandleId && localStream && publisherState !== 'published')) return [3 /*break*/, 7];
601
610
  setIsPublishing(true);
602
611
  setPublisherState('publishing');
603
612
  pc_1 = new RTCPeerConnection(pcConfig);
@@ -699,22 +708,27 @@ var GroupVideoCall = function (_a) {
699
708
  _a.sent();
700
709
  hasAudio = localStream.getAudioTracks().length > 0 && micEnabled;
701
710
  hasVideo = localStream.getVideoTracks().length > 0 && cameraEnabled;
702
- return [4 /*yield*/, janusClient.sendMessage(janusHandleId, { request: "publish", audio: hasAudio, video: hasVideo }, offer)];
711
+ uniqueFileName = "".concat(roomId, "_").concat(userId, "_").concat(Date.now());
712
+ return [4 /*yield*/, janusClient.sendMessage(janusHandleId, { request: "publish", audio: hasAudio, video: hasVideo, record: callSettings.is_record_calls, filename: uniqueFileName }, offer)];
703
713
  case 5:
704
714
  response = _a.sent();
705
715
  setPublisherState('published');
706
- _a.label = 6;
707
- case 6: return [3 /*break*/, 9];
708
- case 7:
716
+ if (!callSettings.is_record_calls) return [3 /*break*/, 7];
717
+ return [4 /*yield*/, client.groupVideoCall.updateKafkaHandler(roomId, janusHandleId, uniqueFileName, uniqueFileName)];
718
+ case 6:
719
+ updateKafkaResponse = _a.sent();
720
+ _a.label = 7;
721
+ case 7: return [3 /*break*/, 10];
722
+ case 8:
709
723
  err_4 = _a.sent();
710
724
  console.error("Error recreating publisher connection:", err_4);
711
725
  setIsPublishing(false);
712
726
  setPublisherState('idle');
713
- return [3 /*break*/, 9];
714
- case 8:
727
+ return [3 /*break*/, 10];
728
+ case 9:
715
729
  isPublishingRef.current = false;
716
730
  return [7 /*endfinally*/];
717
- case 9: return [2 /*return*/];
731
+ case 10: return [2 /*return*/];
718
732
  }
719
733
  });
720
734
  }); };
@@ -1464,30 +1478,43 @@ var GroupVideoCall = function (_a) {
1464
1478
  pd = event.plugindata;
1465
1479
  if (!(event.janus === "event" && ((_c = pd === null || pd === void 0 ? void 0 : pd.data) === null || _c === void 0 ? void 0 : _c.videoroom))) return [3 /*break*/, 3];
1466
1480
  handlePublishers = function (publishers) { return __awaiter(void 0, void 0, void 0, function () {
1467
- var myPubId, _i, publishers_1, pub, pubId;
1481
+ var myPubId, _loop_2, _i, publishers_1, pub;
1468
1482
  return __generator(this, function (_a) {
1469
1483
  switch (_a.label) {
1470
1484
  case 0:
1471
1485
  if (!Array.isArray(publishers)) return [3 /*break*/, 4];
1472
1486
  myPubId = myPublisherIdRef.current;
1487
+ _loop_2 = function (pub) {
1488
+ var pubId, isNewPublisher;
1489
+ return __generator(this, function (_b) {
1490
+ switch (_b.label) {
1491
+ case 0:
1492
+ pubId = Number(pub.id);
1493
+ if (!(pubId !== myPubId)) return [3 /*break*/, 2];
1494
+ isNewPublisher = !notifiedPublishersRef.current.has(pubId) &&
1495
+ !remoteStreams.some(function (s) { return s.id === pubId; }) &&
1496
+ !Array.from(remoteSubscribersRef.current.values()).some(function (sub) { return sub.publisherId === pubId; });
1497
+ return [4 /*yield*/, subscribeToPublisher(pub.id, pub.display)];
1498
+ case 1:
1499
+ _b.sent();
1500
+ if (pd.data.videoroom === "event" && pub.display && isNewPublisher) {
1501
+ notifiedPublishersRef.current.add(pubId); // Mark as notified
1502
+ showJoinNotification("".concat(pub.display, " has joined the call"));
1503
+ }
1504
+ return [3 /*break*/, 2];
1505
+ case 2: return [2 /*return*/];
1506
+ }
1507
+ });
1508
+ };
1473
1509
  _i = 0, publishers_1 = publishers;
1474
1510
  _a.label = 1;
1475
1511
  case 1:
1476
1512
  if (!(_i < publishers_1.length)) return [3 /*break*/, 4];
1477
1513
  pub = publishers_1[_i];
1478
- pubId = Number(pub.id);
1479
- if (!(pubId !== myPubId)) return [3 /*break*/, 3];
1480
- return [4 /*yield*/, subscribeToPublisher(pub.id, pub.display)];
1514
+ return [5 /*yield**/, _loop_2(pub)];
1481
1515
  case 2:
1482
1516
  _a.sent();
1483
- // Show notification when new user joins (only for event, not initial join)
1484
- // Check if we haven't already notified about this publisher
1485
- if (pd.data.videoroom === "event" && pub.display && !notifiedPublishersRef.current.has(pubId)) {
1486
- console.log("🎉 New user joined! Showing notification for:", pub.display);
1487
- notifiedPublishersRef.current.add(pubId); // Mark as notified
1488
- showJoinNotification(pub.display);
1489
- }
1490
- return [3 /*break*/, 3];
1517
+ _a.label = 3;
1491
1518
  case 3:
1492
1519
  _i++;
1493
1520
  return [3 /*break*/, 1];
@@ -1922,7 +1949,7 @@ var GroupVideoCall = function (_a) {
1922
1949
  }); };
1923
1950
  useEffect(function () {
1924
1951
  var publishStream = function () { return __awaiter(void 0, void 0, void 0, function () {
1925
- var pc_3, candidatesGenerated_1, offer, hasAudio, hasVideo, response, err_27;
1952
+ var pc_3, candidatesGenerated_1, offer, hasAudio, hasVideo, uniqueFileName, response, updateKafkaResponse, err_27;
1926
1953
  return __generator(this, function (_a) {
1927
1954
  switch (_a.label) {
1928
1955
  case 0:
@@ -1933,10 +1960,10 @@ var GroupVideoCall = function (_a) {
1933
1960
  !isPublishing &&
1934
1961
  streamInitialized &&
1935
1962
  hasJoinedRoom &&
1936
- publisherState !== 'published')) return [3 /*break*/, 6];
1963
+ publisherState !== 'published')) return [3 /*break*/, 8];
1937
1964
  _a.label = 1;
1938
1965
  case 1:
1939
- _a.trys.push([1, 5, , 6]);
1966
+ _a.trys.push([1, 7, , 8]);
1940
1967
  setIsPublishing(true);
1941
1968
  setPublisherState('publishing');
1942
1969
  pc_3 = new RTCPeerConnection(pcConfig);
@@ -2004,14 +2031,21 @@ var GroupVideoCall = function (_a) {
2004
2031
  _a.sent();
2005
2032
  hasAudio = localStream.getAudioTracks().length > 0 && micEnabled;
2006
2033
  hasVideo = localStream.getVideoTracks().length > 0 && cameraEnabled;
2007
- return [4 /*yield*/, janusClient.sendMessage(janusHandleId, { request: "publish", audio: hasAudio, video: hasVideo }, offer)];
2034
+ uniqueFileName = "".concat(roomId, "_").concat(userId, "_").concat(Date.now());
2035
+ return [4 /*yield*/, janusClient.sendMessage(janusHandleId, { request: "publish", audio: hasAudio, video: hasVideo, record: callSettings.is_record_calls, filename: uniqueFileName }, offer)];
2008
2036
  case 4:
2009
2037
  response = _a.sent();
2038
+ if (!callSettings.is_record_calls) return [3 /*break*/, 6];
2039
+ return [4 /*yield*/, client.groupVideoCall.updateKafkaHandler(roomId, janusHandleId, uniqueFileName, uniqueFileName)];
2040
+ case 5:
2041
+ updateKafkaResponse = _a.sent();
2042
+ _a.label = 6;
2043
+ case 6:
2010
2044
  if (response === null || response === void 0 ? void 0 : response.error) {
2011
2045
  throw new Error("Janus publish error: ".concat(response.error));
2012
2046
  }
2013
- return [3 /*break*/, 6];
2014
- case 5:
2047
+ return [3 /*break*/, 8];
2048
+ case 7:
2015
2049
  err_27 = _a.sent();
2016
2050
  console.error("Error publishing initial stream:", err_27);
2017
2051
  setIsPublishing(false);
@@ -2022,8 +2056,8 @@ var GroupVideoCall = function (_a) {
2022
2056
  setIsPublishing(false);
2023
2057
  }, 2000 * (reconnectAttempts + 1));
2024
2058
  }
2025
- return [3 /*break*/, 6];
2026
- case 6: return [2 /*return*/];
2059
+ return [3 /*break*/, 8];
2060
+ case 8: return [2 /*return*/];
2027
2061
  }
2028
2062
  });
2029
2063
  }); };
@@ -2176,23 +2210,37 @@ var GroupVideoCall = function (_a) {
2176
2210
  return map;
2177
2211
  }, [remoteStreams, speakingUsers]);
2178
2212
  var filteredAvailableUsers = React.useMemo(function () {
2179
- if (!searchQuery.trim())
2180
- return availableUsers;
2181
- var query = searchQuery.toLowerCase();
2182
- return availableUsers.filter(function (user) {
2213
+ var users = availableUsers;
2214
+ if (searchQuery.trim()) {
2215
+ var query_1 = searchQuery.toLowerCase();
2216
+ users = availableUsers.filter(function (user) {
2217
+ var _a, _b;
2218
+ return ((_a = user.user_name) === null || _a === void 0 ? void 0 : _a.toLowerCase().includes(query_1)) ||
2219
+ ((_b = user.user_id) === null || _b === void 0 ? void 0 : _b.toLowerCase().includes(query_1));
2220
+ });
2221
+ }
2222
+ return users.sort(function (a, b) {
2183
2223
  var _a, _b;
2184
- return ((_a = user.user_name) === null || _a === void 0 ? void 0 : _a.toLowerCase().includes(query)) ||
2185
- ((_b = user.user_id) === null || _b === void 0 ? void 0 : _b.toLowerCase().includes(query));
2224
+ var nameA = ((_a = a.user_name) === null || _a === void 0 ? void 0 : _a.toLowerCase()) || '';
2225
+ var nameB = ((_b = b.user_name) === null || _b === void 0 ? void 0 : _b.toLowerCase()) || '';
2226
+ return nameA.localeCompare(nameB);
2186
2227
  });
2187
2228
  }, [availableUsers, searchQuery]);
2188
2229
  var filteredCurrentCallUsers = React.useMemo(function () {
2189
- if (!searchQuery.trim())
2190
- return usersInCurrentCall;
2191
- var query = searchQuery.toLowerCase();
2192
- return usersInCurrentCall.filter(function (user) {
2230
+ var users = usersInCurrentCall;
2231
+ if (searchQuery.trim()) {
2232
+ var query_2 = searchQuery.toLowerCase();
2233
+ users = usersInCurrentCall.filter(function (user) {
2234
+ var _a, _b;
2235
+ return ((_a = user.user_name) === null || _a === void 0 ? void 0 : _a.toLowerCase().includes(query_2)) ||
2236
+ ((_b = user.user_id) === null || _b === void 0 ? void 0 : _b.toLowerCase().includes(query_2));
2237
+ });
2238
+ }
2239
+ return users.sort(function (a, b) {
2193
2240
  var _a, _b;
2194
- return ((_a = user.user_name) === null || _a === void 0 ? void 0 : _a.toLowerCase().includes(query)) ||
2195
- ((_b = user.user_id) === null || _b === void 0 ? void 0 : _b.toLowerCase().includes(query));
2241
+ var nameA = ((_a = a.user_name) === null || _a === void 0 ? void 0 : _a.toLowerCase()) || '';
2242
+ var nameB = ((_b = b.user_name) === null || _b === void 0 ? void 0 : _b.toLowerCase()) || '';
2243
+ return nameA.localeCompare(nameB);
2196
2244
  });
2197
2245
  }, [usersInCurrentCall, searchQuery]);
2198
2246
  var getStreamForUserId = function (targetUserId) {
@@ -2212,14 +2260,14 @@ var GroupVideoCall = function (_a) {
2212
2260
  var gridCols = Math.ceil(Math.sqrt(Math.max(1, count)));
2213
2261
  var gridRows = Math.ceil(count / gridCols);
2214
2262
  var forceRepublish = function () { return __awaiter(void 0, void 0, void 0, function () {
2215
- var hasAudio, hasVideo, offer, response, err_28;
2263
+ var hasAudio, hasVideo, offer, uniqueFileName, response, updateKafkaResponse, err_28;
2216
2264
  return __generator(this, function (_a) {
2217
2265
  switch (_a.label) {
2218
2266
  case 0:
2219
- if (!(pcRef.current && janusClient && janusHandleId && localStream)) return [3 /*break*/, 7];
2267
+ if (!(pcRef.current && janusClient && janusHandleId && localStream)) return [3 /*break*/, 9];
2220
2268
  _a.label = 1;
2221
2269
  case 1:
2222
- _a.trys.push([1, 6, , 7]);
2270
+ _a.trys.push([1, 8, , 9]);
2223
2271
  hasAudio = localStream.getAudioTracks().length > 0 && micEnabled;
2224
2272
  hasVideo = localStream.getVideoTracks().length > 0 && cameraEnabled;
2225
2273
  return [4 /*yield*/, janusClient.sendMessage(janusHandleId, {
@@ -2238,15 +2286,21 @@ var GroupVideoCall = function (_a) {
2238
2286
  return [4 /*yield*/, pcRef.current.setLocalDescription(offer)];
2239
2287
  case 4:
2240
2288
  _a.sent();
2241
- return [4 /*yield*/, janusClient.sendMessage(janusHandleId, { request: "publish", audio: hasAudio, video: hasVideo }, offer)];
2289
+ uniqueFileName = "".concat(roomId, "_").concat(userId, "_").concat(Date.now());
2290
+ return [4 /*yield*/, janusClient.sendMessage(janusHandleId, { request: "publish", audio: hasAudio, video: hasVideo, record: callSettings.is_record_calls, filename: uniqueFileName }, offer)];
2242
2291
  case 5:
2243
2292
  response = _a.sent();
2244
- return [3 /*break*/, 7];
2293
+ if (!callSettings.is_record_calls) return [3 /*break*/, 7];
2294
+ return [4 /*yield*/, client.groupVideoCall.updateKafkaHandler(roomId, janusHandleId, uniqueFileName, uniqueFileName)];
2245
2295
  case 6:
2296
+ updateKafkaResponse = _a.sent();
2297
+ _a.label = 7;
2298
+ case 7: return [3 /*break*/, 9];
2299
+ case 8:
2246
2300
  err_28 = _a.sent();
2247
2301
  console.error("Error in force republish:", err_28);
2248
- return [3 /*break*/, 7];
2249
- case 7: return [2 /*return*/];
2302
+ return [3 /*break*/, 9];
2303
+ case 9: return [2 /*return*/];
2250
2304
  }
2251
2305
  });
2252
2306
  }); };
@@ -2289,6 +2343,50 @@ var GroupVideoCall = function (_a) {
2289
2343
  }
2290
2344
  });
2291
2345
  }); };
2346
+ var handleEndCall = function () {
2347
+ if (isInitiator && usersInCurrentCall.length > 1) {
2348
+ // Show ownership transfer modal if initiator and there are other participants
2349
+ setShowOwnershipTransferModal(true);
2350
+ }
2351
+ else {
2352
+ // Directly end the call if not initiator or alone in the call
2353
+ cleanupSession();
2354
+ onClose();
2355
+ }
2356
+ };
2357
+ var handleOwnershipTransfer = function (newOwnerId) { return __awaiter(void 0, void 0, void 0, function () {
2358
+ var error_3;
2359
+ return __generator(this, function (_a) {
2360
+ switch (_a.label) {
2361
+ case 0:
2362
+ _a.trys.push([0, 3, , 4]);
2363
+ if (!(client && roomId)) return [3 /*break*/, 2];
2364
+ return [4 /*yield*/, client.groupVideoCall.transferOwnership(roomId, newOwnerId)];
2365
+ case 1:
2366
+ _a.sent();
2367
+ _a.label = 2;
2368
+ case 2:
2369
+ // Close modal and end call
2370
+ setShowOwnershipTransferModal(false);
2371
+ cleanupSession();
2372
+ onClose();
2373
+ return [3 /*break*/, 4];
2374
+ case 3:
2375
+ error_3 = _a.sent();
2376
+ console.error("Error transferring ownership:", error_3);
2377
+ return [3 /*break*/, 4];
2378
+ case 4: return [2 /*return*/];
2379
+ }
2380
+ });
2381
+ }); };
2382
+ var handleCancelOwnershipTransfer = function () {
2383
+ setShowOwnershipTransferModal(false);
2384
+ };
2385
+ var handleEndCallAnyway = function () {
2386
+ setShowOwnershipTransferModal(false);
2387
+ cleanupSession();
2388
+ onClose();
2389
+ };
2292
2390
  useEffect(function () {
2293
2391
  window.forceRepublish = forceRepublish;
2294
2392
  window.recreateConnection = recreatePublisherConnection;
@@ -2436,11 +2534,11 @@ var GroupVideoCall = function (_a) {
2436
2534
  React.createElement("div", { className: "control-button-wrapper" },
2437
2535
  React.createElement("button", { style: __assign(__assign({}, styles.controlButton), { color: cameraEnabled ? '#fff' : '#e50d0dff' }), onClick: toggleCamera },
2438
2536
  React.createElement(FontAwesomeIcon, { style: { fontSize: '20px' }, icon: cameraEnabled ? faVideo : faVideoSlash })),
2439
- React.createElement("span", { className: "custom-tooltip" }, cameraEnabled ? "Turn off camera" : "Turn on camera")),
2537
+ React.createElement("span", { className: "custom-tooltip tooltip-top" }, cameraEnabled ? "Turn off camera" : "Turn on camera")),
2440
2538
  React.createElement("div", { className: "control-button-wrapper" },
2441
2539
  React.createElement("button", { style: __assign(__assign({}, styles.controlButton), { color: micEnabled ? '#fff' : '#e50d0dff' }), onClick: toggleMic },
2442
2540
  React.createElement(FontAwesomeIcon, { style: { fontSize: '20px' }, icon: micEnabled ? faMicrophone : faMicrophoneSlash })),
2443
- React.createElement("span", { className: "custom-tooltip" }, micEnabled ? "Mute microphone" : "Unmute microphone")),
2541
+ React.createElement("span", { className: "custom-tooltip tooltip-top" }, micEnabled ? "Mute microphone" : "Unmute microphone")),
2444
2542
  React.createElement("div", { className: "control-button-wrapper" },
2445
2543
  React.createElement("button", { style: isScreenSharing ? __assign(__assign({}, styles.controlButton), styles.screenShareActive) : styles.controlButton, ref: screenShareButtonRef, onClick: function () {
2446
2544
  if (isScreenSharing) {
@@ -2458,28 +2556,25 @@ var GroupVideoCall = function (_a) {
2458
2556
  }
2459
2557
  } },
2460
2558
  React.createElement(FontAwesomeIcon, { style: { fontSize: '20px' }, icon: faDesktop })),
2461
- React.createElement("span", { className: "custom-tooltip" }, isScreenSharing ? "Screen share options" : "Start screen share")),
2559
+ React.createElement("span", { className: "custom-tooltip tooltip-top" }, isScreenSharing ? "Screen share options" : "Start screen share")),
2462
2560
  React.createElement("div", { className: "control-button-wrapper" },
2463
2561
  React.createElement("button", { style: styles.controlButton, onClick: function () {
2464
2562
  setIsMinimized(true);
2465
2563
  setWasDragged(false);
2466
2564
  } },
2467
2565
  React.createElement(FontAwesomeIcon, { style: { fontSize: '20px' }, icon: faCompress })),
2468
- React.createElement("span", { className: "custom-tooltip" }, "Minimize call")),
2566
+ React.createElement("span", { className: "custom-tooltip tooltip-top" }, "Minimize call")),
2469
2567
  React.createElement("div", { className: "control-button-wrapper" },
2470
2568
  React.createElement("button", { style: styles.controlButton, onClick: function () {
2471
2569
  setShowInvitePanel(true);
2472
2570
  setShowAvailableUsers(false);
2473
2571
  } },
2474
2572
  React.createElement(FontAwesomeIcon, { style: { fontSize: '20px' }, icon: faUserPlus })),
2475
- React.createElement("span", { className: "custom-tooltip" }, "Invite participants")),
2573
+ React.createElement("span", { className: "custom-tooltip tooltip-top" }, "Invite participants")),
2476
2574
  React.createElement("div", { className: "control-button-wrapper" },
2477
- React.createElement("button", { style: __assign(__assign({}, styles.controlButton), styles.endCallButton), onClick: function () {
2478
- cleanupSession();
2479
- onClose();
2480
- } },
2575
+ React.createElement("button", { style: __assign(__assign({}, styles.controlButton), styles.endCallButton), onClick: handleEndCall },
2481
2576
  React.createElement(FontAwesomeIcon, { style: { fontSize: '20px' }, icon: faPhoneSlash })),
2482
- React.createElement("span", { className: "custom-tooltip" }, "End call")))),
2577
+ React.createElement("span", { className: "custom-tooltip tooltip-top" }, "End call")))),
2483
2578
  showScreenSharePopup && (React.createElement("div", { style: styles.screenSharePopupOverlay, onClick: function () { return setShowScreenSharePopup(false); } },
2484
2579
  React.createElement("div", { style: __assign(__assign({}, styles.screenSharePopup), { top: popupPosition.top, left: popupPosition.left }), onClick: function (e) { return e.stopPropagation(); } },
2485
2580
  React.createElement("button", { style: styles.popupButton, onClick: function () {
@@ -2695,8 +2790,8 @@ var GroupVideoCall = function (_a) {
2695
2790
  } },
2696
2791
  React.createElement("span", { style: { fontSize: '20px' } }, "\uD83D\uDC4B"),
2697
2792
  React.createElement("span", null,
2698
- React.createElement("strong", null, notification.userName),
2699
- " joined the call"))); }))));
2793
+ React.createElement("strong", null, notification.userName)))); })),
2794
+ React.createElement(MeetingAdminTransferModal, { open: showOwnershipTransferModal, onClose: handleCancelOwnershipTransfer, onTransfer: handleOwnershipTransfer, onEndCallAnyway: handleEndCallAnyway, participants: usersInCurrentCall, currentUserId: userId, primaryColor: primaryColor, secondaryColor: secondaryColor })));
2700
2795
  };
2701
2796
  var styles = {
2702
2797
  container: {
@@ -2884,7 +2979,7 @@ var styles = {
2884
2979
  display: "grid",
2885
2980
  gap: "10px",
2886
2981
  padding: "20px",
2887
- height: "calc(100vh - 120px)",
2982
+ height: "calc(100vh - 100px)",
2888
2983
  },
2889
2984
  videoWrapper: {
2890
2985
  position: "relative",
@@ -2941,8 +3036,8 @@ var styles = {
2941
3036
  position: "absolute",
2942
3037
  top: "8px",
2943
3038
  right: "8px",
2944
- width: "20px",
2945
- height: "20px",
3039
+ width: "30px",
3040
+ height: "30px",
2946
3041
  borderRadius: "50%",
2947
3042
  background: "rgba(34, 197, 94, 0.8)",
2948
3043
  border: "2px solid rgba(34, 197, 94, 0.4)",
@@ -2990,7 +3085,7 @@ var styles = {
2990
3085
  controlsContainer: {
2991
3086
  position: "absolute",
2992
3087
  left: "50%",
2993
- bottom: "32px",
3088
+ bottom: "12px",
2994
3089
  transform: "translateX(-50%)",
2995
3090
  display: "flex",
2996
3091
  gap: "24px",
@@ -3438,5 +3533,5 @@ var styles = {
3438
3533
  fontSize: "14px",
3439
3534
  },
3440
3535
  };
3441
- var inviteKeyframes = "\n/* Custom Tooltip Styling - Hide native tooltip and show custom one */\n.control-button-wrapper {\n position: relative;\n display: inline-block;\n}\n\n.control-button-wrapper .custom-tooltip {\n visibility: hidden;\n opacity: 0;\n position: absolute;\n bottom: calc(100% + 8px);\n left: 50%;\n transform: translateX(-50%);\n background-color: #000;\n color: #fff;\n padding: 6px 12px;\n border-radius: 6px;\n font-size: 12px;\n white-space: nowrap;\n z-index: 10000;\n pointer-events: none;\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);\n transition: opacity 0.2s ease-in-out, visibility 0.2s ease-in-out;\n}\n\n.control-button-wrapper .custom-tooltip::after {\n content: '';\n position: absolute;\n top: 100%;\n left: 50%;\n transform: translateX(-50%);\n border: 6px solid transparent;\n border-top-color: #000;\n}\n\n.control-button-wrapper:hover .custom-tooltip {\n visibility: visible;\n opacity: 1;\n}\n\n@keyframes fadeIn {\n 0% {\n opacity: 0;\n }\n 100% {\n opacity: 1;\n }\n}\n\n@keyframes slideInRight {\n 0% {\n transform: translateX(100%);\n opacity: 0;\n }\n 100% {\n transform: translateX(0);\n opacity: 1;\n }\n}\n\n@keyframes slideDown {\n 0% {\n opacity: 0;\n transform: translateY(-12px) scale(0.95);\n }\n 100% {\n opacity: 1;\n transform: translateY(0) scale(1);\n }\n}\n\n@keyframes invitePulse {\n 0% {\n transform: scale(1.05);\n box-shadow: 0 0 20px rgba(16,185,129,0.4), inset 0 1px 0 rgba(255,255,255,0.1);\n }\n 50% {\n transform: scale(1.08);\n box-shadow: 0 0 30px rgba(16,185,129,0.6), inset 0 1px 0 rgba(255,255,255,0.2);\n }\n 100% {\n transform: scale(1.05);\n box-shadow: 0 0 20px rgba(16,185,129,0.4), inset 0 1px 0 rgba(255,255,255,0.1);\n }\n}\n\n@keyframes speakingPulse {\n 0% {\n transform: scale(1);\n box-shadow: 0 0 10px rgba(16, 185, 129, 0.6);\n }\n 50% {\n transform: scale(1.2);\n box-shadow: 0 0 20px rgba(16, 185, 129, 0.8);\n }\n 100% {\n transform: scale(1);\n box-shadow: 0 0 10px rgba(16, 185, 129, 0.6);\n }\n}\n\n@keyframes microphoneGreenBlink {\n 0% {\n color: #22c55e;\n background: rgba(34, 197, 94, 0.2);\n text-shadow: 0 0 5px rgba(34, 197, 94, 0.6);\n box-shadow: 0 0 8px rgba(34, 197, 94, 0.4);\n }\n 50% {\n color: #16a34a;\n background: rgba(34, 197, 94, 0.4);\n text-shadow: 0 0 10px rgba(34, 197, 94, 0.8);\n transform: scale(1.1);\n box-shadow: 0 0 15px rgba(34, 197, 94, 0.7);\n }\n 100% {\n color: #22c55e;\n background: rgba(34, 197, 94, 0.2);\n text-shadow: 0 0 5px rgba(34, 197, 94, 0.6);\n box-shadow: 0 0 8px rgba(34, 197, 94, 0.4);\n }\n}\n\n@keyframes microphoneSilentBlink {\n 0% {\n background: rgba(107, 114, 128, 0.1);\n box-shadow: 0 0 5px rgba(107, 114, 128, 0.3);\n }\n 50% {\n background: rgba(107, 114, 128, 0.3);\n transform: scale(1.05);\n box-shadow: 0 0 10px rgba(107, 114, 128, 0.5);\n }\n 100% {\n background: rgba(107, 114, 128, 0.1);\n box-shadow: 0 0 5px rgba(107, 114, 128, 0.3);\n }\n}\n\n@keyframes fadeOut {\n 0% {\n opacity: 1;\n transform: translateX(0);\n }\n 100% {\n opacity: 0;\n transform: translateX(20px);\n }\n}\n\n@media (prefers-reduced-motion: reduce) {\n * {\n animation: none !important;\n transition: none !important;\n }\n}\n\n/* Search Input Interactions */\ninput[type=\"text\"]:focus {\n border-color: #007bff !important;\n box-shadow: 0 0 0 3px rgba(0, 123, 255, 0.1);\n}\n\nbutton[aria-label=\"Clear search\"]:hover {\n color: #333 !important;\n}\n";
3536
+ var inviteKeyframes = "\n@keyframes fadeIn {\n 0% {\n opacity: 0;\n }\n 100% {\n opacity: 1;\n }\n}\n\n@keyframes slideInRight {\n 0% {\n transform: translateX(100%);\n opacity: 0;\n }\n 100% {\n transform: translateX(0);\n opacity: 1;\n }\n}\n\n@keyframes slideDown {\n 0% {\n opacity: 0;\n transform: translateY(-12px) scale(0.95);\n }\n 100% {\n opacity: 1;\n transform: translateY(0) scale(1);\n }\n}\n\n@keyframes invitePulse {\n 0% {\n transform: scale(1.05);\n box-shadow: 0 0 20px rgba(16,185,129,0.4), inset 0 1px 0 rgba(255,255,255,0.1);\n }\n 50% {\n transform: scale(1.08);\n box-shadow: 0 0 30px rgba(16,185,129,0.6), inset 0 1px 0 rgba(255,255,255,0.2);\n }\n 100% {\n transform: scale(1.05);\n box-shadow: 0 0 20px rgba(16,185,129,0.4), inset 0 1px 0 rgba(255,255,255,0.1);\n }\n}\n\n@keyframes speakingPulse {\n 0% {\n transform: scale(1);\n box-shadow: 0 0 10px rgba(16, 185, 129, 0.6);\n }\n 50% {\n transform: scale(1.2);\n box-shadow: 0 0 20px rgba(16, 185, 129, 0.8);\n }\n 100% {\n transform: scale(1);\n box-shadow: 0 0 10px rgba(16, 185, 129, 0.6);\n }\n}\n\n@keyframes microphoneGreenBlink {\n 0% {\n color: #22c55e;\n background: rgba(34, 197, 94, 0.2);\n text-shadow: 0 0 5px rgba(34, 197, 94, 0.6);\n box-shadow: 0 0 8px rgba(34, 197, 94, 0.4);\n }\n 50% {\n color: #16a34a;\n background: rgba(34, 197, 94, 0.4);\n text-shadow: 0 0 10px rgba(34, 197, 94, 0.8);\n transform: scale(1.1);\n box-shadow: 0 0 15px rgba(34, 197, 94, 0.7);\n }\n 100% {\n color: #22c55e;\n background: rgba(34, 197, 94, 0.2);\n text-shadow: 0 0 5px rgba(34, 197, 94, 0.6);\n box-shadow: 0 0 8px rgba(34, 197, 94, 0.4);\n }\n}\n\n@keyframes microphoneSilentBlink {\n 0% {\n background: rgba(107, 114, 128, 0.1);\n box-shadow: 0 0 5px rgba(107, 114, 128, 0.3);\n }\n 50% {\n background: rgba(107, 114, 128, 0.3);\n transform: scale(1.05);\n box-shadow: 0 0 10px rgba(107, 114, 128, 0.5);\n }\n 100% {\n background: rgba(107, 114, 128, 0.1);\n box-shadow: 0 0 5px rgba(107, 114, 128, 0.3);\n }\n}\n\n@keyframes fadeOut {\n 0% {\n opacity: 1;\n transform: translateX(0);\n }\n 100% {\n opacity: 0;\n transform: translateX(20px);\n }\n}\n\n@media (prefers-reduced-motion: reduce) {\n * {\n animation: none !important;\n transition: none !important;\n }\n}\n\n/* Search Input Interactions */\ninput[type=\"text\"]:focus {\n border-color: #007bff !important;\n box-shadow: 0 0 0 3px rgba(0, 123, 255, 0.1);\n}\n\nbutton[aria-label=\"Clear search\"]:hover {\n color: #333 !important;\n}\n";
3442
3537
  export default React.memo(GroupVideoCall);
@@ -18,6 +18,9 @@ type PeerChatHeaderProps = {
18
18
  secondaryColor?: string;
19
19
  busyCallusersList?: string[];
20
20
  peerUserId?: string;
21
+ participantStatuses?: {
22
+ [userId: string]: string;
23
+ };
21
24
  };
22
25
  declare const PeerChatHeader: React.FC<PeerChatHeaderProps>;
23
26
  export default PeerChatHeader;
@@ -11,13 +11,12 @@ var __assign = (this && this.__assign) || function () {
11
11
  };
12
12
  import React from "react";
13
13
  import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
14
- import { getInitials } from "../../services/Common";
15
14
  import CustomIcons from "../../theme/icons";
16
15
  import colors from "../../theme/colors";
17
16
  import fonts from "../../theme/fonts";
18
- import OnlineIndicator from "../OnlineIndicator";
17
+ import ProfileAvatar from "../modals/ProfileAvatar";
19
18
  var PeerChatHeader = function (_a) {
20
- var userProfilePic = _a.userProfilePic, onclick = _a.onclick, _b = _a.showBackButton, showBackButton = _b === void 0 ? true : _b, mainHeading = _a.mainHeading, _c = _a.headingStyle, headingStyle = _c === void 0 ? {} : _c, isPeerOnline = _a.isPeerOnline, _d = _a.showActionButtons, showActionButtons = _d === void 0 ? true : _d, onVideoCall = _a.onVideoCall, onAudioCall = _a.onAudioCall, onUserClick = _a.onUserClick, _e = _a.showVideoCall, showVideoCall = _e === void 0 ? false : _e, onSearch = _a.onSearch, _f = _a.showOptions, showOptions = _f === void 0 ? false : _f, onOptionsClick = _a.onOptionsClick, primaryColor = _a.primaryColor, secondaryColor = _a.secondaryColor, busyCallusersList = _a.busyCallusersList, peerUserId = _a.peerUserId;
19
+ var userProfilePic = _a.userProfilePic, onclick = _a.onclick, _b = _a.showBackButton, showBackButton = _b === void 0 ? true : _b, mainHeading = _a.mainHeading, _c = _a.headingStyle, headingStyle = _c === void 0 ? {} : _c, isPeerOnline = _a.isPeerOnline, _d = _a.showActionButtons, showActionButtons = _d === void 0 ? true : _d, onVideoCall = _a.onVideoCall, onAudioCall = _a.onAudioCall, onUserClick = _a.onUserClick, _e = _a.showVideoCall, showVideoCall = _e === void 0 ? false : _e, onSearch = _a.onSearch, _f = _a.showOptions, showOptions = _f === void 0 ? false : _f, onOptionsClick = _a.onOptionsClick, primaryColor = _a.primaryColor, secondaryColor = _a.secondaryColor, busyCallusersList = _a.busyCallusersList, peerUserId = _a.peerUserId, _g = _a.participantStatuses, participantStatuses = _g === void 0 ? {} : _g;
21
20
  var getHeaderGradient = function () {
22
21
  var primary = primaryColor || colors.blue;
23
22
  var secondary = secondaryColor || colors.blueLight;
@@ -27,22 +26,8 @@ var PeerChatHeader = function (_a) {
27
26
  showBackButton && (React.createElement("button", { onClick: onclick, style: styles.backButton },
28
27
  React.createElement(FontAwesomeIcon, { icon: CustomIcons.BackArrow.icon, style: { fontSize: CustomIcons.BackArrow.size, color: CustomIcons.BackArrow.color } }))),
29
28
  React.createElement("div", { style: styles.userInfo },
30
- React.createElement("div", { style: styles.profilePicContainer }, userProfilePic && userProfilePic.trim() ? (React.createElement("div", { style: styles.profileWrapper },
31
- React.createElement("img", { src: userProfilePic, alt: "User Profile", style: styles.profilePic, onError: function (e) {
32
- try {
33
- var img = e.currentTarget;
34
- // console.debug('[PeerChatHeader] profile image failed to load, falling back to logo192.png', img.src);
35
- img.onerror = null;
36
- img.src = '/logo192.png';
37
- }
38
- catch (err) {
39
- // console.debug('[PeerChatHeader] error setting fallback image', err);
40
- }
41
- } }),
42
- React.createElement(OnlineIndicator, { isOnline: isPeerOnline || false, isBusy: busyCallusersList === null || busyCallusersList === void 0 ? void 0 : busyCallusersList.includes(peerUserId || ""), variant: "avatar" }))) : (React.createElement("div", { style: styles.profileWrapper },
43
- React.createElement("div", { style: styles.profilePlaceholder },
44
- React.createElement("span", { style: styles.initials }, getInitials(mainHeading || ''))),
45
- React.createElement(OnlineIndicator, { isOnline: isPeerOnline || false, variant: "avatar" })))),
29
+ React.createElement("div", { style: styles.profilePicContainer },
30
+ React.createElement(ProfileAvatar, { userName: mainHeading || '', profilePic: userProfilePic, size: 45, showOnlineIndicator: true, isOnline: isPeerOnline || false, isBusy: busyCallusersList === null || busyCallusersList === void 0 ? void 0 : busyCallusersList.includes(peerUserId || ""), userId: peerUserId, participantStatuses: participantStatuses, primaryColor: primaryColor, secondaryColor: secondaryColor })),
46
31
  React.createElement("div", { style: styles.userDetails },
47
32
  React.createElement("h2", { style: styles.userName, onClick: onUserClick },
48
33
  " ",
@@ -50,14 +35,22 @@ var PeerChatHeader = function (_a) {
50
35
  React.createElement("span", { style: __assign(__assign({}, styles.onlineStatus), { color: isPeerOnline ? colors.onlineColor : colors.disabledColor }) }))),
51
36
  (showActionButtons || showOptions) && (React.createElement("div", { style: styles.actionButtons },
52
37
  showActionButtons && (React.createElement(React.Fragment, null,
53
- React.createElement("button", { disabled: !isPeerOnline || showVideoCall || (busyCallusersList === null || busyCallusersList === void 0 ? void 0 : busyCallusersList.includes(peerUserId || "")), style: __assign(__assign({}, styles.actionButton), { opacity: (!isPeerOnline || showVideoCall || (busyCallusersList === null || busyCallusersList === void 0 ? void 0 : busyCallusersList.includes(peerUserId || ""))) ? 0.5 : 1 }), "aria-label": "Call", onClick: onAudioCall },
54
- React.createElement(FontAwesomeIcon, { icon: CustomIcons.faPhone.icon, style: { fontSize: CustomIcons.faPhone.size, color: colors.white } })),
55
- React.createElement("button", { disabled: !isPeerOnline || showVideoCall || (busyCallusersList === null || busyCallusersList === void 0 ? void 0 : busyCallusersList.includes(peerUserId || "")), style: __assign(__assign({}, styles.actionButton), { opacity: (!isPeerOnline || showVideoCall || (busyCallusersList === null || busyCallusersList === void 0 ? void 0 : busyCallusersList.includes(peerUserId || ""))) ? 0.5 : 1 }), "aria-label": "Video Call", onClick: onVideoCall },
56
- React.createElement(FontAwesomeIcon, { icon: CustomIcons.faVideo.icon, style: { fontSize: CustomIcons.faVideo.size, color: colors.white } })),
57
- React.createElement("button", { style: styles.actionButton, "aria-label": "Search", onClick: onSearch },
58
- React.createElement(FontAwesomeIcon, { icon: CustomIcons.faSearch.icon, style: { fontSize: CustomIcons.faSearch.size, color: colors.white } })))),
59
- showOptions && (React.createElement("button", { style: styles.actionButton, "aria-label": "Options", onClick: onOptionsClick },
60
- React.createElement(FontAwesomeIcon, { icon: CustomIcons.faEllipsisV.icon, style: { fontSize: CustomIcons.faEllipsisV.size, color: colors.white } })))))));
38
+ React.createElement("div", { className: "control-button-wrapper" },
39
+ React.createElement("button", { disabled: !isPeerOnline || showVideoCall || (busyCallusersList === null || busyCallusersList === void 0 ? void 0 : busyCallusersList.includes(peerUserId || "")), style: __assign(__assign({}, styles.actionButton), { opacity: (!isPeerOnline || showVideoCall || (busyCallusersList === null || busyCallusersList === void 0 ? void 0 : busyCallusersList.includes(peerUserId || ""))) ? 0.5 : 1 }), "aria-label": "Call", onClick: onAudioCall },
40
+ React.createElement(FontAwesomeIcon, { icon: CustomIcons.faPhone.icon, style: { fontSize: CustomIcons.faPhone.size, color: colors.white } })),
41
+ React.createElement("span", { className: "custom-tooltip" }, "Audio Call")),
42
+ React.createElement("div", { className: "control-button-wrapper" },
43
+ React.createElement("button", { disabled: !isPeerOnline || showVideoCall || (busyCallusersList === null || busyCallusersList === void 0 ? void 0 : busyCallusersList.includes(peerUserId || "")), style: __assign(__assign({}, styles.actionButton), { opacity: (!isPeerOnline || showVideoCall || (busyCallusersList === null || busyCallusersList === void 0 ? void 0 : busyCallusersList.includes(peerUserId || ""))) ? 0.5 : 1 }), "aria-label": "Video Call", onClick: onVideoCall },
44
+ React.createElement(FontAwesomeIcon, { icon: CustomIcons.faVideo.icon, style: { fontSize: CustomIcons.faVideo.size, color: colors.white } })),
45
+ React.createElement("span", { className: "custom-tooltip" }, "Video Call")),
46
+ React.createElement("div", { className: "control-button-wrapper" },
47
+ React.createElement("button", { style: styles.actionButton, "aria-label": "Search", onClick: onSearch },
48
+ React.createElement(FontAwesomeIcon, { icon: CustomIcons.faSearch.icon, style: { fontSize: CustomIcons.faSearch.size, color: colors.white } })),
49
+ React.createElement("span", { className: "custom-tooltip" }, "Search")))),
50
+ showOptions && (React.createElement("div", { className: "control-button-wrapper" },
51
+ React.createElement("button", { style: styles.actionButton, "aria-label": "Options", onClick: onOptionsClick },
52
+ React.createElement(FontAwesomeIcon, { icon: CustomIcons.faEllipsisV.icon, style: { fontSize: CustomIcons.faEllipsisV.size, color: colors.white } })),
53
+ React.createElement("span", { className: "custom-tooltip" }, "Options")))))));
61
54
  };
62
55
  var styles = {
63
56
  headerContainer: {
@@ -108,51 +101,6 @@ var styles = {
108
101
  justifyContent: "center",
109
102
  flexShrink: 0,
110
103
  },
111
- profileWrapper: {
112
- position: "relative",
113
- display: "inline-block",
114
- width: "clamp(36px, 6vw, 46px)",
115
- height: "clamp(36px, 6vw, 46px)",
116
- flexShrink: 0,
117
- },
118
- profilePic: {
119
- width: "100%",
120
- height: "100%",
121
- borderRadius: "50%",
122
- objectFit: "cover",
123
- border: "2px solid rgba(255, 255, 255, 0.16)",
124
- boxShadow: "0 4px 10px rgba(0,0,0,0.12)",
125
- display: "block",
126
- },
127
- profilePlaceholder: {
128
- width: "90%",
129
- height: "90%",
130
- borderRadius: "50%",
131
- backgroundColor: "rgba(255,255,255,0.12)",
132
- display: "flex",
133
- alignItems: "center",
134
- justifyContent: "center",
135
- border: "2px solid rgba(255, 255, 255, 0.16)",
136
- boxShadow: "0 4px 8px rgba(0,0,0,0.08)",
137
- },
138
- statusDot: {
139
- position: "absolute",
140
- right: "-2px",
141
- bottom: "0px",
142
- width: "10px",
143
- height: "10px",
144
- borderRadius: "50%",
145
- border: "2px solid white",
146
- boxShadow: "0 1px 2px rgba(0,0,0,0.22)",
147
- transform: "none",
148
- backgroundColor: colors.onlineColor,
149
- },
150
- initials: {
151
- color: colors.white,
152
- fontSize: "14px",
153
- fontWeight: 700,
154
- letterSpacing: "0.2px",
155
- },
156
104
  userDetails: {
157
105
  display: "flex",
158
106
  flexDirection: "column",