ajaxter-chat 3.0.17 → 3.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (143) hide show
  1. package/dist/components/BlockList/index.d.ts +1 -0
  2. package/dist/components/BlockList/index.d.ts.map +1 -0
  3. package/dist/components/BlockList/index.js +55 -28
  4. package/dist/components/BlockList/index.js.map +1 -0
  5. package/dist/components/CallScreen/index.d.ts +1 -0
  6. package/dist/components/CallScreen/index.d.ts.map +1 -0
  7. package/dist/components/CallScreen/index.js +107 -39
  8. package/dist/components/CallScreen/index.js.map +1 -0
  9. package/dist/components/ChatScreen/index.d.ts +1 -0
  10. package/dist/components/ChatScreen/index.d.ts.map +1 -0
  11. package/dist/components/ChatScreen/index.js +493 -294
  12. package/dist/components/ChatScreen/index.js.map +1 -0
  13. package/dist/components/ChatWidget.d.ts +1 -0
  14. package/dist/components/ChatWidget.d.ts.map +1 -0
  15. package/dist/components/ChatWidget.js +350 -255
  16. package/dist/components/ChatWidget.js.map +1 -0
  17. package/dist/components/EmojiPicker/index.d.ts +1 -0
  18. package/dist/components/EmojiPicker/index.d.ts.map +1 -0
  19. package/dist/components/EmojiPicker/index.js +19 -7
  20. package/dist/components/EmojiPicker/index.js.map +1 -0
  21. package/dist/components/ErrorBoundary/index.d.ts +20 -0
  22. package/dist/components/ErrorBoundary/index.d.ts.map +1 -0
  23. package/dist/components/ErrorBoundary/index.js +76 -0
  24. package/dist/components/ErrorBoundary/index.js.map +1 -0
  25. package/dist/components/HomeScreen/index.d.ts +1 -0
  26. package/dist/components/HomeScreen/index.d.ts.map +1 -0
  27. package/dist/components/HomeScreen/index.js +236 -158
  28. package/dist/components/HomeScreen/index.js.map +1 -0
  29. package/dist/components/MaintenanceView/index.d.ts +1 -0
  30. package/dist/components/MaintenanceView/index.d.ts.map +1 -0
  31. package/dist/components/MaintenanceView/index.js +28 -12
  32. package/dist/components/MaintenanceView/index.js.map +1 -0
  33. package/dist/components/MiniCallBar/index.d.ts +1 -0
  34. package/dist/components/MiniCallBar/index.d.ts.map +1 -0
  35. package/dist/components/MiniCallBar/index.js +85 -37
  36. package/dist/components/MiniCallBar/index.js.map +1 -0
  37. package/dist/components/PermissionsGateScreen/index.d.ts +1 -0
  38. package/dist/components/PermissionsGateScreen/index.d.ts.map +1 -0
  39. package/dist/components/PermissionsGateScreen/index.js +82 -28
  40. package/dist/components/PermissionsGateScreen/index.js.map +1 -0
  41. package/dist/components/RecentChatsScreen/index.d.ts +1 -0
  42. package/dist/components/RecentChatsScreen/index.d.ts.map +1 -0
  43. package/dist/components/RecentChatsScreen/index.js +79 -19
  44. package/dist/components/RecentChatsScreen/index.js.map +1 -0
  45. package/dist/components/SlideNavMenu.d.ts +1 -0
  46. package/dist/components/SlideNavMenu.d.ts.map +1 -0
  47. package/dist/components/SlideNavMenu.js +82 -63
  48. package/dist/components/SlideNavMenu.js.map +1 -0
  49. package/dist/components/Tabs/BottomTabs.d.ts +1 -0
  50. package/dist/components/Tabs/BottomTabs.d.ts.map +1 -0
  51. package/dist/components/Tabs/BottomTabs.js +34 -19
  52. package/dist/components/Tabs/BottomTabs.js.map +1 -0
  53. package/dist/components/TicketDetailScreen/index.d.ts +1 -0
  54. package/dist/components/TicketDetailScreen/index.d.ts.map +1 -0
  55. package/dist/components/TicketDetailScreen/index.js +66 -27
  56. package/dist/components/TicketDetailScreen/index.js.map +1 -0
  57. package/dist/components/TicketFormScreen/index.d.ts +1 -0
  58. package/dist/components/TicketFormScreen/index.d.ts.map +1 -0
  59. package/dist/components/TicketFormScreen/index.js +99 -49
  60. package/dist/components/TicketFormScreen/index.js.map +1 -0
  61. package/dist/components/TicketScreen/index.d.ts +1 -0
  62. package/dist/components/TicketScreen/index.d.ts.map +1 -0
  63. package/dist/components/TicketScreen/index.js +95 -26
  64. package/dist/components/TicketScreen/index.js.map +1 -0
  65. package/dist/components/UserListScreen/index.d.ts +1 -0
  66. package/dist/components/UserListScreen/index.d.ts.map +1 -0
  67. package/dist/components/UserListScreen/index.js +127 -53
  68. package/dist/components/UserListScreen/index.js.map +1 -0
  69. package/dist/components/ViewerBlockedScreen/index.d.ts +1 -0
  70. package/dist/components/ViewerBlockedScreen/index.d.ts.map +1 -0
  71. package/dist/components/ViewerBlockedScreen/index.js +113 -61
  72. package/dist/components/ViewerBlockedScreen/index.js.map +1 -0
  73. package/dist/config/index.d.ts +1 -0
  74. package/dist/config/index.d.ts.map +1 -0
  75. package/dist/config/index.js +7 -2
  76. package/dist/config/index.js.map +1 -0
  77. package/dist/hooks/useChat.d.ts +9 -1
  78. package/dist/hooks/useChat.d.ts.map +1 -0
  79. package/dist/hooks/useChat.js +60 -18
  80. package/dist/hooks/useChat.js.map +1 -0
  81. package/dist/hooks/useRemoteConfig.d.ts +1 -0
  82. package/dist/hooks/useRemoteConfig.d.ts.map +1 -0
  83. package/dist/hooks/useRemoteConfig.js +12 -8
  84. package/dist/hooks/useRemoteConfig.js.map +1 -0
  85. package/dist/hooks/useSocket.d.ts +40 -0
  86. package/dist/hooks/useSocket.d.ts.map +1 -0
  87. package/dist/hooks/useSocket.js +190 -0
  88. package/dist/hooks/useSocket.js.map +1 -0
  89. package/dist/hooks/useWebRTC.d.ts +10 -2
  90. package/dist/hooks/useWebRTC.d.ts.map +1 -0
  91. package/dist/hooks/useWebRTC.js +101 -69
  92. package/dist/hooks/useWebRTC.js.map +1 -0
  93. package/dist/index.d.ts +6 -0
  94. package/dist/index.d.ts.map +1 -0
  95. package/dist/index.js +67 -21
  96. package/dist/index.js.map +1 -0
  97. package/dist/types/index.d.ts +1 -0
  98. package/dist/types/index.d.ts.map +1 -0
  99. package/dist/types/index.js +3 -1
  100. package/dist/types/index.js.map +1 -0
  101. package/dist/utils/chat.d.ts +1 -0
  102. package/dist/utils/chat.d.ts.map +1 -0
  103. package/dist/utils/chat.js +17 -7
  104. package/dist/utils/chat.js.map +1 -0
  105. package/dist/utils/fileName.d.ts +1 -0
  106. package/dist/utils/fileName.d.ts.map +1 -0
  107. package/dist/utils/fileName.js +5 -1
  108. package/dist/utils/fileName.js.map +1 -0
  109. package/dist/utils/messageSound.d.ts +1 -0
  110. package/dist/utils/messageSound.d.ts.map +1 -0
  111. package/dist/utils/messageSound.js +9 -3
  112. package/dist/utils/messageSound.js.map +1 -0
  113. package/dist/utils/presenceStatus.d.ts +1 -0
  114. package/dist/utils/presenceStatus.d.ts.map +1 -0
  115. package/dist/utils/presenceStatus.js +11 -4
  116. package/dist/utils/presenceStatus.js.map +1 -0
  117. package/dist/utils/privacyConsent.d.ts +1 -0
  118. package/dist/utils/privacyConsent.d.ts.map +1 -0
  119. package/dist/utils/privacyConsent.js +9 -3
  120. package/dist/utils/privacyConsent.js.map +1 -0
  121. package/dist/utils/reenableRequest.d.ts +1 -0
  122. package/dist/utils/reenableRequest.d.ts.map +1 -0
  123. package/dist/utils/reenableRequest.js +5 -1
  124. package/dist/utils/reenableRequest.js.map +1 -0
  125. package/dist/utils/theme.d.ts +1 -0
  126. package/dist/utils/theme.d.ts.map +1 -0
  127. package/dist/utils/theme.js +10 -4
  128. package/dist/utils/theme.js.map +1 -0
  129. package/dist/utils/widgetPermissions.d.ts +1 -0
  130. package/dist/utils/widgetPermissions.d.ts.map +1 -0
  131. package/dist/utils/widgetPermissions.js +13 -5
  132. package/dist/utils/widgetPermissions.js.map +1 -0
  133. package/dist/utils/widgetSession.d.ts +1 -0
  134. package/dist/utils/widgetSession.d.ts.map +1 -0
  135. package/dist/utils/widgetSession.js +9 -3
  136. package/dist/utils/widgetSession.js.map +1 -0
  137. package/package.json +3 -3
  138. package/src/components/ChatWidget.tsx +291 -269
  139. package/src/components/ErrorBoundary/index.tsx +62 -0
  140. package/src/hooks/useChat.ts +59 -12
  141. package/src/hooks/useSocket.ts +228 -0
  142. package/src/hooks/useWebRTC.ts +99 -64
  143. package/src/index.ts +7 -2
@@ -1,61 +1,102 @@
1
+ "use strict";
1
2
  'use client';
2
- import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
- import { useState, useEffect, useCallback, useRef, useMemo } from 'react';
4
- import { loadLocalConfig } from '../config';
5
- import { mergeTheme } from '../utils/theme';
6
- import { useRemoteConfig } from '../hooks/useRemoteConfig';
7
- import { useChat } from '../hooks/useChat';
8
- import { useWebRTC } from '../hooks/useWebRTC';
9
- import { saveSession, loadSession } from '../utils/widgetSession';
10
- import { playMessageSound, getMessageSoundEnabled, setMessageSoundEnabled } from '../utils/messageSound';
11
- import { HomeScreen } from './HomeScreen';
12
- import { UserListScreen } from './UserListScreen';
13
- import { ChatScreen } from './ChatScreen';
14
- import { RecentChatsScreen } from './RecentChatsScreen';
15
- import { TicketScreen } from './TicketScreen';
16
- import { TicketDetailScreen } from './TicketDetailScreen';
17
- import { TicketFormScreen } from './TicketFormScreen';
18
- import { BlockListScreen } from './BlockList';
19
- import { CallScreen } from './CallScreen';
20
- import { MiniCallBar } from './MiniCallBar';
21
- import { MaintenanceView } from './MaintenanceView';
22
- import { BottomTabs } from './Tabs/BottomTabs';
23
- import { ViewerBlockedScreen } from './ViewerBlockedScreen';
24
- import { PermissionsGateScreen } from './PermissionsGateScreen';
25
- import { hasStoredPermissionsGrant } from '../utils/widgetPermissions';
26
- export const ChatWidget = ({ theme: localTheme, viewer }) => {
27
- var _a, _b, _c, _d, _e;
3
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
4
+ if (k2 === undefined) k2 = k;
5
+ var desc = Object.getOwnPropertyDescriptor(m, k);
6
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
7
+ desc = { enumerable: true, get: function() { return m[k]; } };
8
+ }
9
+ Object.defineProperty(o, k2, desc);
10
+ }) : (function(o, m, k, k2) {
11
+ if (k2 === undefined) k2 = k;
12
+ o[k2] = m[k];
13
+ }));
14
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
15
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
16
+ }) : function(o, v) {
17
+ o["default"] = v;
18
+ });
19
+ var __importStar = (this && this.__importStar) || (function () {
20
+ var ownKeys = function(o) {
21
+ ownKeys = Object.getOwnPropertyNames || function (o) {
22
+ var ar = [];
23
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
24
+ return ar;
25
+ };
26
+ return ownKeys(o);
27
+ };
28
+ return function (mod) {
29
+ if (mod && mod.__esModule) return mod;
30
+ var result = {};
31
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
32
+ __setModuleDefault(result, mod);
33
+ return result;
34
+ };
35
+ })();
36
+ Object.defineProperty(exports, "__esModule", { value: true });
37
+ exports.ChatWidget = void 0;
38
+ const react_1 = __importStar(require("react"));
39
+ const config_1 = require("../config");
40
+ const theme_1 = require("../utils/theme");
41
+ const useRemoteConfig_1 = require("../hooks/useRemoteConfig");
42
+ const useChat_1 = require("../hooks/useChat");
43
+ const useWebRTC_1 = require("../hooks/useWebRTC");
44
+ const useSocket_1 = require("../hooks/useSocket");
45
+ const widgetSession_1 = require("../utils/widgetSession");
46
+ const messageSound_1 = require("../utils/messageSound");
47
+ const ErrorBoundary_1 = require("./ErrorBoundary");
48
+ const HomeScreen_1 = require("./HomeScreen");
49
+ const UserListScreen_1 = require("./UserListScreen");
50
+ const ChatScreen_1 = require("./ChatScreen");
51
+ const RecentChatsScreen_1 = require("./RecentChatsScreen");
52
+ const TicketScreen_1 = require("./TicketScreen");
53
+ const TicketDetailScreen_1 = require("./TicketDetailScreen");
54
+ const TicketFormScreen_1 = require("./TicketFormScreen");
55
+ const BlockList_1 = require("./BlockList");
56
+ const CallScreen_1 = require("./CallScreen");
57
+ const MiniCallBar_1 = require("./MiniCallBar");
58
+ const MaintenanceView_1 = require("./MaintenanceView");
59
+ const BottomTabs_1 = require("./Tabs/BottomTabs");
60
+ const ViewerBlockedScreen_1 = require("./ViewerBlockedScreen");
61
+ const PermissionsGateScreen_1 = require("./PermissionsGateScreen");
62
+ const widgetPermissions_1 = require("../utils/widgetPermissions");
63
+ /** WebSocket server URL — override via env NEXT_PUBLIC_SOCKET_URL / REACT_APP_SOCKET_URL */
64
+ function getSocketUrl() {
65
+ var _a, _b;
66
+ if (typeof process !== 'undefined' && process.env) {
67
+ return ((_b = (_a = process.env['NEXT_PUBLIC_SOCKET_URL']) !== null && _a !== void 0 ? _a : process.env['REACT_APP_SOCKET_URL']) !== null && _b !== void 0 ? _b : 'http://localhost:3005');
68
+ }
69
+ return 'http://localhost:3005';
70
+ }
71
+ const ChatWidget = ({ theme: localTheme, viewer }) => {
72
+ var _a, _b, _c, _d, _e, _f, _g, _h;
28
73
  /* SSR guard */
29
- const [mounted, setMounted] = useState(false);
30
- useEffect(() => { setMounted(true); }, []);
31
- /* Env config */
32
- const { apiKey, widgetId } = loadLocalConfig();
33
- /* Remote config */
34
- const { data, loading: cfgLoading, error: cfgError } = useRemoteConfig(apiKey, widgetId);
35
- /* Merged theme — remote config overrides defaults, local prop overrides both */
36
- const theme = mergeTheme((data === null || data === void 0 ? void 0 : data.widget) ? { primaryColor: data.widget.primaryColor, buttonLabel: data.widget.buttonLabel, buttonPosition: data.widget.buttonPosition } : undefined, localTheme);
37
- /* Drawer open state */
38
- const [isOpen, setIsOpen] = useState(false);
39
- const [closing, setClosing] = useState(false); // for slide-out animation
40
- /** True when user hid the drawer during ringing/connected call; WebRTC session stays active. */
41
- const [callMinimized, setCallMinimized] = useState(false);
42
- /* Navigation */
43
- const [activeTab, setActiveTab] = useState('home');
44
- const [screen, setScreen] = useState('home');
45
- const [userListCtx, setUserListCtx] = useState('support');
46
- const [chatReturnCtx, setChatReturnCtx] = useState('conversation');
47
- const [viewingTicketId, setViewingTicketId] = useState(null);
48
- const [messageSoundEnabled, setMessageSoundEnabledState] = useState(true);
49
- /** Stagger list animation only when opening from home burger menu */
50
- const [listEntranceAnimation, setListEntranceAnimation] = useState(false);
51
- /** Microphone, geolocation, and screen capture granted for this tab */
52
- const [permissionsOk, setPermissionsOk] = useState(false);
53
- /* App state */
54
- const [tickets, setTickets] = useState((_a = data === null || data === void 0 ? void 0 : data.sampleTickets) !== null && _a !== void 0 ? _a : []);
55
- const [recentChats, setRecentChats] = useState([]);
56
- const [blockedUids, setBlockedUids] = useState((_b = data === null || data === void 0 ? void 0 : data.blockedUsers) !== null && _b !== void 0 ? _b : []);
74
+ const [mounted, setMounted] = (0, react_1.useState)(false);
75
+ (0, react_1.useEffect)(() => { setMounted(true); }, []);
76
+ const { apiKey, widgetId } = (0, config_1.loadLocalConfig)();
77
+ const { data, loading: cfgLoading, error: cfgError } = (0, useRemoteConfig_1.useRemoteConfig)(apiKey, widgetId);
78
+ const theme = (0, theme_1.mergeTheme)((data === null || data === void 0 ? void 0 : data.widget) ? {
79
+ primaryColor: data.widget.primaryColor,
80
+ buttonLabel: data.widget.buttonLabel,
81
+ buttonPosition: data.widget.buttonPosition,
82
+ } : undefined, localTheme);
83
+ const [isOpen, setIsOpen] = (0, react_1.useState)(false);
84
+ const [closing, setClosing] = (0, react_1.useState)(false);
85
+ const [callMinimized, setCallMinimized] = (0, react_1.useState)(false);
86
+ const [activeTab, setActiveTab] = (0, react_1.useState)('home');
87
+ const [screen, setScreen] = (0, react_1.useState)('home');
88
+ const [userListCtx, setUserListCtx] = (0, react_1.useState)('support');
89
+ const [chatReturnCtx, setChatReturnCtx] = (0, react_1.useState)('conversation');
90
+ const [viewingTicketId, setViewingTicketId] = (0, react_1.useState)(null);
91
+ const [messageSoundEnabled, setMessageSoundEnabledState] = (0, react_1.useState)(true);
92
+ const [listEntranceAnimation, setListEntranceAnimation] = (0, react_1.useState)(false);
93
+ const [permissionsOk, setPermissionsOk] = (0, react_1.useState)(false);
94
+ const [socketStatus, setSocketStatus] = (0, react_1.useState)('disconnected');
95
+ const [tickets, setTickets] = (0, react_1.useState)((_a = data === null || data === void 0 ? void 0 : data.sampleTickets) !== null && _a !== void 0 ? _a : []);
96
+ const [recentChats, setRecentChats] = (0, react_1.useState)([]);
97
+ const [blockedUids, setBlockedUids] = (0, react_1.useState)((_b = data === null || data === void 0 ? void 0 : data.blockedUsers) !== null && _b !== void 0 ? _b : []);
57
98
  /* Sync remote data into local state once loaded */
58
- useEffect(() => {
99
+ (0, react_1.useEffect)(() => {
59
100
  var _a, _b, _c, _d;
60
101
  if (data) {
61
102
  setTickets(data.sampleTickets);
@@ -81,58 +122,109 @@ export const ChatWidget = ({ theme: localTheme, viewer }) => {
81
122
  setRecentChats(recents);
82
123
  }
83
124
  }, [data, viewer === null || viewer === void 0 ? void 0 : viewer.projectId]);
84
- /* Chat hook */
85
- const { messages, activeUser, isPaused, isReported, selectUser, sendMessage, togglePause, reportChat, clearChat, setMessages, } = useChat();
86
- /* WebRTC hook */
87
- const { session: callSession, localVideoRef, remoteVideoRef, startCall, endCall, toggleMute, toggleCamera } = useWebRTC();
125
+ /* ── Socket setup ───────────────────────────────────────────────────── */
126
+ const viewerUidForSocket = ((_e = (_c = viewer === null || viewer === void 0 ? void 0 : viewer.uid) !== null && _c !== void 0 ? _c : (_d = data === null || data === void 0 ? void 0 : data.widget) === null || _d === void 0 ? void 0 : _d.viewerUid) !== null && _e !== void 0 ? _e : '').trim();
127
+ const { joinRoom, leaveRoom, emitMessage, emitPauseToggle, emitReport, emitBlock, emitUnblock, emitTransfer, emitCallOffer, emitCallAnswer, emitIceCandidate, emitCallEnd, emitAddParticipant, status: _socketStatus, } = (0, useSocket_1.useSocket)({
128
+ widgetId,
129
+ viewerUid: viewerUidForSocket,
130
+ serverUrl: getSocketUrl(),
131
+ onMessage: (msg) => {
132
+ receiveMessage(msg);
133
+ if (messageSoundEnabled && msg.senderId !== 'me')
134
+ (0, messageSound_1.playMessageSound)();
135
+ // Update recent chats last message
136
+ setRecentChats(prev => prev.map(r => r.user.uid === msg.senderId || r.user.uid === msg.receiverId
137
+ ? Object.assign(Object.assign({}, r), { lastMessage: msg.text, lastTime: msg.timestamp, unread: r.unread + 1 }) : r));
138
+ },
139
+ onMessageAck: ({ messageId, status }) => {
140
+ updateMessageStatus(messageId, status);
141
+ },
142
+ onChatPaused: (_roomId, paused) => {
143
+ // Sync pause state from remote
144
+ setRecentChats(prev => prev.map(r => activeUser && r.user.uid === activeUser.uid ? Object.assign(Object.assign({}, r), { isPaused: paused }) : r));
145
+ },
146
+ onCallOffer: async (offer, fromUid, callId) => {
147
+ var _a, _b;
148
+ // Find peer user
149
+ const allUsers = [...((_a = data === null || data === void 0 ? void 0 : data.developers) !== null && _a !== void 0 ? _a : []), ...((_b = data === null || data === void 0 ? void 0 : data.users) !== null && _b !== void 0 ? _b : [])];
150
+ const peer = allUsers.find(u => u.uid === fromUid);
151
+ if (peer) {
152
+ await acceptCall(offer, peer, callId);
153
+ setScreen('call');
154
+ setIsOpen(true);
155
+ }
156
+ },
157
+ onCallAnswer: async (answer, _fromUid, _callId) => {
158
+ await addIceCandidate({ sdpMid: '0', sdpMLineIndex: 0, candidate: '' });
159
+ // WebRTC: set remote description
160
+ void answer;
161
+ },
162
+ onIceCandidate: async (candidate) => {
163
+ await addIceCandidate(candidate);
164
+ },
165
+ onCallEnd: () => { _endCall(); },
166
+ onError: (code, message) => {
167
+ console.error('[ChatWidget] Socket error:', code, message);
168
+ },
169
+ onUserStatus: (uid, status) => {
170
+ setRecentChats(prev => prev.map(r => r.user.uid === uid ? Object.assign(Object.assign({}, r), { user: Object.assign(Object.assign({}, r.user), { status }) }) : r));
171
+ },
172
+ });
173
+ (0, react_1.useEffect)(() => { setSocketStatus(_socketStatus); }, [_socketStatus]);
174
+ /* ── Chat hook ──────────────────────────────────────────────────────── */
175
+ const { messages, activeUser, isPaused, isReported, selectUser, sendMessage, receiveMessage, updateMessageStatus, togglePause: _togglePause, reportChat: _reportChat, clearChat, setMessages, } = (0, useChat_1.useChat)([], {
176
+ onEmitMessage: (msg) => { emitMessage(msg); },
177
+ onEmitPause: (roomId, targetUid, paused) => { emitPauseToggle(roomId, targetUid, paused); },
178
+ onEmitReport: (roomId) => { emitReport(roomId); },
179
+ });
180
+ /* ── WebRTC hook ────────────────────────────────────────────────────── */
181
+ const { session: callSession, localVideoRef, remoteVideoRef, startCall: _startCall, acceptCall, addIceCandidate, endCall: _endCall, toggleMute, toggleCamera, } = (0, useWebRTC_1.useWebRTC)({
182
+ onOfferReady: (offer, toUid, callId) => { emitCallOffer(offer, toUid, callId); },
183
+ onAnswerReady: (answer, toUid, callId) => { emitCallAnswer(answer, toUid, callId); },
184
+ onIceCandidateReady: (candidate, toUid) => { emitIceCandidate(candidate, toUid); },
185
+ onCallEnded: (callId, toUid) => { emitCallEnd(callId, toUid); },
186
+ });
88
187
  const callInProgress = callSession.state === 'calling' || callSession.state === 'connected';
89
- useEffect(() => {
188
+ (0, react_1.useEffect)(() => {
90
189
  if (!callInProgress)
91
190
  setCallMinimized(false);
92
191
  }, [callInProgress]);
93
- /* ── Drawer open/close with slide animation ───────────────────────────── */
192
+ /* ── Drawer open/close ──────────────────────────────────────────────── */
94
193
  const openDrawer = () => {
95
194
  setClosing(false);
96
195
  setIsOpen(true);
97
196
  setCallMinimized(false);
98
197
  };
99
- const persistWidgetState = useCallback(() => {
198
+ const persistWidgetState = (0, react_1.useCallback)(() => {
100
199
  var _a;
101
200
  const w = data === null || data === void 0 ? void 0 : data.widget;
102
201
  if (!w)
103
202
  return;
104
- saveSession(w.id, {
105
- screen,
106
- activeTab,
107
- userListCtx,
203
+ (0, widgetSession_1.saveSession)(w.id, {
204
+ screen, activeTab, userListCtx,
108
205
  activeUserUid: (_a = activeUser === null || activeUser === void 0 ? void 0 : activeUser.uid) !== null && _a !== void 0 ? _a : null,
109
- messages,
110
- viewingTicketId,
111
- chatReturnCtx,
206
+ messages, viewingTicketId, chatReturnCtx,
112
207
  });
113
208
  }, [data === null || data === void 0 ? void 0 : data.widget, screen, activeTab, userListCtx, activeUser === null || activeUser === void 0 ? void 0 : activeUser.uid, messages, viewingTicketId, chatReturnCtx]);
114
- const closeDrawer = useCallback(() => {
209
+ const closeDrawer = (0, react_1.useCallback)(() => {
115
210
  persistWidgetState();
116
211
  setClosing(true);
117
- setTimeout(() => {
118
- setIsOpen(false);
119
- setClosing(false);
120
- }, 300);
212
+ setTimeout(() => { setIsOpen(false); setClosing(false); }, 300);
121
213
  }, [persistWidgetState]);
122
- useEffect(() => {
214
+ (0, react_1.useEffect)(() => {
123
215
  var _a;
124
216
  const id = (_a = data === null || data === void 0 ? void 0 : data.widget) === null || _a === void 0 ? void 0 : _a.id;
125
217
  if (!id)
126
218
  return;
127
- setPermissionsOk(hasStoredPermissionsGrant(id));
128
- }, [(_c = data === null || data === void 0 ? void 0 : data.widget) === null || _c === void 0 ? void 0 : _c.id]);
129
- const restoredRef = useRef(false);
130
- useEffect(() => {
219
+ setPermissionsOk((0, widgetPermissions_1.hasStoredPermissionsGrant)(id));
220
+ }, [(_f = data === null || data === void 0 ? void 0 : data.widget) === null || _f === void 0 ? void 0 : _f.id]);
221
+ const restoredRef = (0, react_1.useRef)(false);
222
+ (0, react_1.useEffect)(() => {
131
223
  var _a, _b, _c, _d, _e, _f;
132
224
  if (!(data === null || data === void 0 ? void 0 : data.widget) || restoredRef.current)
133
225
  return;
134
226
  const w = data.widget;
135
- setMessageSoundEnabledState(getMessageSoundEnabled(w.id));
227
+ setMessageSoundEnabledState((0, messageSound_1.getMessageSoundEnabled)(w.id));
136
228
  const uidForBlock = (_b = ((_a = viewer === null || viewer === void 0 ? void 0 : viewer.uid) !== null && _a !== void 0 ? _a : w.viewerUid)) === null || _b === void 0 ? void 0 : _b.trim();
137
229
  let viewerIsBlocked = w.viewerBlocked === true;
138
230
  if (!viewerIsBlocked && uidForBlock) {
@@ -147,7 +239,7 @@ export const ChatWidget = ({ theme: localTheme, viewer }) => {
147
239
  restoredRef.current = true;
148
240
  return;
149
241
  }
150
- const p = loadSession(w.id);
242
+ const p = (0, widgetSession_1.loadSession)(w.id);
151
243
  if (p) {
152
244
  setScreen(p.screen);
153
245
  setActiveTab(p.activeTab);
@@ -165,12 +257,15 @@ export const ChatWidget = ({ theme: localTheme, viewer }) => {
165
257
  ? p.messages
166
258
  : ((_f = data.sampleChats[u.uid]) !== null && _f !== void 0 ? _f : []);
167
259
  selectUser(u, hist);
260
+ // Rejoin socket room
261
+ const roomId = [u.uid, viewerUidForSocket].sort().join('_');
262
+ joinRoom(roomId);
168
263
  }
169
264
  }
170
265
  }
171
266
  restoredRef.current = true;
172
- }, [data, selectUser, clearChat, viewer === null || viewer === void 0 ? void 0 : viewer.projectId, viewer === null || viewer === void 0 ? void 0 : viewer.uid]);
173
- useEffect(() => {
267
+ }, [data, selectUser, clearChat, viewer === null || viewer === void 0 ? void 0 : viewer.projectId, viewer === null || viewer === void 0 ? void 0 : viewer.uid, viewerUidForSocket, joinRoom]);
268
+ (0, react_1.useEffect)(() => {
174
269
  var _a, _b;
175
270
  if (!(data === null || data === void 0 ? void 0 : data.widget))
176
271
  return;
@@ -188,36 +283,22 @@ export const ChatWidget = ({ theme: localTheme, viewer }) => {
188
283
  setActiveTab('home');
189
284
  setViewingTicketId(null);
190
285
  }, [data === null || data === void 0 ? void 0 : data.widget, data === null || data === void 0 ? void 0 : data.developers, data === null || data === void 0 ? void 0 : data.users, viewer === null || viewer === void 0 ? void 0 : viewer.uid, clearChat]);
191
- useEffect(() => {
286
+ (0, react_1.useEffect)(() => {
192
287
  if (!(data === null || data === void 0 ? void 0 : data.widget))
193
288
  return;
194
289
  persistWidgetState();
195
- }, [(_d = data === null || data === void 0 ? void 0 : data.widget) === null || _d === void 0 ? void 0 : _d.id, screen, activeTab, userListCtx, activeUser === null || activeUser === void 0 ? void 0 : activeUser.uid, messages, viewingTicketId, chatReturnCtx, persistWidgetState]);
196
- const incomingSoundRef = useRef(0);
197
- useEffect(() => {
198
- incomingSoundRef.current = messages.length;
199
- }, [activeUser === null || activeUser === void 0 ? void 0 : activeUser.uid]);
200
- useEffect(() => {
201
- if (!messageSoundEnabled || !activeUser || !(data === null || data === void 0 ? void 0 : data.widget))
202
- return;
203
- if (messages.length < incomingSoundRef.current) {
204
- incomingSoundRef.current = messages.length;
205
- return;
206
- }
207
- const added = messages.slice(incomingSoundRef.current);
208
- incomingSoundRef.current = messages.length;
209
- if (added.some(m => m.senderId !== 'me'))
210
- playMessageSound();
211
- }, [messages, messageSoundEnabled, activeUser, data === null || data === void 0 ? void 0 : data.widget]);
212
- const toggleMessageSound = useCallback((enabled) => {
290
+ }, [(_g = data === null || data === void 0 ? void 0 : data.widget) === null || _g === void 0 ? void 0 : _g.id, screen, activeTab, userListCtx, activeUser === null || activeUser === void 0 ? void 0 : activeUser.uid, messages, viewingTicketId, chatReturnCtx, persistWidgetState]);
291
+ const incomingSoundRef = (0, react_1.useRef)(0);
292
+ (0, react_1.useEffect)(() => { incomingSoundRef.current = messages.length; }, [activeUser === null || activeUser === void 0 ? void 0 : activeUser.uid]);
293
+ const toggleMessageSound = (0, react_1.useCallback)((enabled) => {
213
294
  const w = data === null || data === void 0 ? void 0 : data.widget;
214
295
  if (!w)
215
296
  return;
216
- setMessageSoundEnabled(w.id, enabled);
297
+ (0, messageSound_1.setMessageSoundEnabled)(w.id, enabled);
217
298
  setMessageSoundEnabledState(enabled);
218
299
  }, [data === null || data === void 0 ? void 0 : data.widget]);
219
- /* ── Navigation ──────────────────────────────────────────────────────── */
220
- const handleCardClick = useCallback((ctx, options) => {
300
+ /* ── Navigation ─────────────────────────────────────────────────────── */
301
+ const handleCardClick = (0, react_1.useCallback)((ctx, options) => {
221
302
  setListEntranceAnimation(!!(options === null || options === void 0 ? void 0 : options.fromMenu));
222
303
  if (ctx === 'ticket') {
223
304
  setActiveTab('tickets');
@@ -228,7 +309,7 @@ export const ChatWidget = ({ theme: localTheme, viewer }) => {
228
309
  setScreen('user-list');
229
310
  }
230
311
  }, []);
231
- const handleNavFromMenu = useCallback((ctx) => {
312
+ const handleNavFromMenu = (0, react_1.useCallback)((ctx) => {
232
313
  setListEntranceAnimation(false);
233
314
  clearChat();
234
315
  if (ctx === 'ticket') {
@@ -240,62 +321,71 @@ export const ChatWidget = ({ theme: localTheme, viewer }) => {
240
321
  setScreen('user-list');
241
322
  }
242
323
  }, [clearChat]);
243
- const listCtxForUser = useCallback((user, viewerIsDev) => {
324
+ const listCtxForUser = (0, react_1.useCallback)((user, viewerIsDev) => {
244
325
  if (viewerIsDev)
245
326
  return user.type === 'user' ? 'support' : 'conversation';
246
327
  return user.type === 'developer' ? 'support' : 'conversation';
247
328
  }, []);
248
- const handleSelectUser = useCallback((user, returnCtxOverride) => {
329
+ const handleSelectUser = (0, react_1.useCallback)((user, returnCtxOverride) => {
249
330
  var _a;
250
331
  setListEntranceAnimation(false);
251
332
  setChatReturnCtx(returnCtxOverride !== null && returnCtxOverride !== void 0 ? returnCtxOverride : userListCtx);
252
333
  const history = (_a = data === null || data === void 0 ? void 0 : data.sampleChats[user.uid]) !== null && _a !== void 0 ? _a : [];
253
334
  selectUser(user, history);
254
335
  setScreen('chat');
336
+ // Join socket room for real-time messages
337
+ const roomId = [user.uid, viewerUidForSocket].sort().join('_');
338
+ joinRoom(roomId);
255
339
  setRecentChats(prev => {
256
340
  const exists = prev.find(r => r.user.uid === user.uid);
257
341
  if (exists)
258
- return prev;
342
+ return prev.map(r => r.user.uid === user.uid ? Object.assign(Object.assign({}, r), { unread: 0 }) : r);
259
343
  return [{ id: `rc_${user.uid}`, user, lastMessage: '', lastTime: new Date().toISOString(), unread: 0, isPaused: false }, ...prev];
260
344
  });
261
- }, [data, selectUser, userListCtx]);
262
- const handleBackFromChat = useCallback(() => {
345
+ }, [data, selectUser, userListCtx, viewerUidForSocket, joinRoom]);
346
+ const handleBackFromChat = (0, react_1.useCallback)(() => {
263
347
  setListEntranceAnimation(false);
348
+ if (activeUser) {
349
+ const roomId = [activeUser.uid, viewerUidForSocket].sort().join('_');
350
+ leaveRoom(roomId);
351
+ }
264
352
  clearChat();
265
353
  setUserListCtx(chatReturnCtx);
266
354
  setScreen('user-list');
267
- }, [clearChat, chatReturnCtx]);
268
- const handleOpenTicket = useCallback((id) => {
355
+ }, [clearChat, chatReturnCtx, activeUser, viewerUidForSocket, leaveRoom]);
356
+ const handleOpenTicket = (0, react_1.useCallback)((id) => {
269
357
  setListEntranceAnimation(false);
270
358
  setViewingTicketId(id);
271
359
  setScreen('ticket-detail');
272
360
  setActiveTab('tickets');
273
361
  }, []);
274
- const handleTabChange = useCallback((tab) => {
362
+ const handleTabChange = (0, react_1.useCallback)((tab) => {
275
363
  setListEntranceAnimation(false);
276
364
  setActiveTab(tab);
277
365
  setScreen(tab === 'home' ? 'home' : tab === 'chats' ? 'recent-chats' : 'tickets');
278
366
  }, []);
279
- useEffect(() => {
367
+ (0, react_1.useEffect)(() => {
280
368
  if (!listEntranceAnimation)
281
369
  return;
282
370
  const t = window.setTimeout(() => setListEntranceAnimation(false), 520);
283
371
  return () => window.clearTimeout(t);
284
372
  }, [listEntranceAnimation]);
285
- /* ── Block/Unblock ───────────────────────────────────────────────────── */
286
- const handleBlock = useCallback(() => {
373
+ /* ── Block/Unblock ──────────────────────────────────────────────────── */
374
+ const handleBlock = (0, react_1.useCallback)(() => {
287
375
  if (!activeUser)
288
376
  return;
289
377
  setBlockedUids(prev => [...prev, activeUser.uid]);
378
+ emitBlock(activeUser.uid);
290
379
  clearChat();
291
380
  setScreen('block-list');
292
381
  setActiveTab('home');
293
- }, [activeUser, clearChat]);
294
- const handleUnblock = useCallback((uid) => {
382
+ }, [activeUser, clearChat, emitBlock]);
383
+ const handleUnblock = (0, react_1.useCallback)((uid) => {
295
384
  setBlockedUids(prev => prev.filter(id => id !== uid));
296
- }, []);
297
- /* ── Tickets ─────────────────────────────────────────────────────────── */
298
- const handleRaiseTicket = useCallback((title, desc, priority) => {
385
+ emitUnblock(uid);
386
+ }, [emitUnblock]);
387
+ /* ── Tickets ────────────────────────────────────────────────────────── */
388
+ const handleRaiseTicket = (0, react_1.useCallback)((title, desc, priority) => {
299
389
  const t = {
300
390
  id: `TKT-${String(Date.now()).slice(-4)}`,
301
391
  title, description: desc, status: 'open', priority,
@@ -308,32 +398,31 @@ export const ChatWidget = ({ theme: localTheme, viewer }) => {
308
398
  setScreen('ticket-detail');
309
399
  setActiveTab('tickets');
310
400
  }, []);
311
- /* ── Pause sync back into recent chats ──────────────────────────────── */
312
- const handleTogglePause = useCallback(() => {
313
- togglePause();
401
+ /* ── Pause sync back into recent chats ─────────────────────────────── */
402
+ const handleTogglePause = (0, react_1.useCallback)(() => {
403
+ _togglePause();
314
404
  if (activeUser) {
315
405
  setRecentChats(prev => prev.map(r => r.user.uid === activeUser.uid ? Object.assign(Object.assign({}, r), { isPaused: !isPaused }) : r));
316
406
  }
317
- }, [togglePause, activeUser, isPaused]);
318
- /* ── Call ────────────────────────────────────────────────────────────── */
319
- const handleStartCall = useCallback((withVideo) => {
407
+ }, [_togglePause, activeUser, isPaused]);
408
+ /* ── Call ───────────────────────────────────────────────────────────── */
409
+ const handleStartCall = (0, react_1.useCallback)((withVideo) => {
320
410
  if (!activeUser)
321
411
  return;
322
- startCall(activeUser, withVideo);
412
+ _startCall(activeUser, withVideo);
323
413
  setScreen('call');
324
- }, [activeUser, startCall]);
325
- const handleEndCall = useCallback(() => {
326
- endCall();
414
+ }, [activeUser, _startCall]);
415
+ const handleEndCall = (0, react_1.useCallback)(() => {
416
+ _endCall();
327
417
  setCallMinimized(false);
328
418
  setScreen('chat');
329
- }, [endCall]);
330
- const minimizeCall = useCallback(() => {
419
+ }, [_endCall]);
420
+ const minimizeCall = (0, react_1.useCallback)(() => {
331
421
  setCallMinimized(true);
332
422
  closeDrawer();
333
423
  }, [closeDrawer]);
334
- /* ── Derived ─────────────────────────────────────────────────────────── */
335
- const isBlocked = activeUser ? blockedUids.includes(activeUser.uid) : false;
336
- const widgetConfig = useMemo(() => {
424
+ /* ── Derived config (must be declared before callbacks that use it) ── */
425
+ const widgetConfig = (0, react_1.useMemo)(() => {
337
426
  var _a;
338
427
  if (!(data === null || data === void 0 ? void 0 : data.widget))
339
428
  return undefined;
@@ -347,9 +436,36 @@ export const ChatWidget = ({ theme: localTheme, viewer }) => {
347
436
  }
348
437
  return w;
349
438
  }, [data === null || data === void 0 ? void 0 : data.widget, viewer]);
439
+ /* ── Transfer (developer feature) ──────────────────────────────────── */
440
+ const handleTransferToDeveloper = (0, react_1.useCallback)((dev) => {
441
+ var _a;
442
+ if (!activeUser || !widgetConfig)
443
+ return;
444
+ const agent = ((_a = widgetConfig.viewerName) === null || _a === void 0 ? void 0 : _a.trim()) || 'Agent';
445
+ const roomId = [activeUser.uid, viewerUidForSocket].sort().join('_');
446
+ const transferNote = {
447
+ id: `tr_${Date.now()}_${Math.random().toString(36).slice(2)}`,
448
+ senderId: 'me',
449
+ receiverId: dev.uid,
450
+ text: `— ${agent} transferred this conversation from ${activeUser.name} to ${dev.name} —`,
451
+ timestamp: new Date().toISOString(),
452
+ type: 'text',
453
+ status: 'sent',
454
+ };
455
+ emitTransfer(roomId, dev.uid, transferNote.text);
456
+ selectUser(dev, [...messages, transferNote]);
457
+ }, [activeUser, messages, selectUser, widgetConfig, viewerUidForSocket, emitTransfer]);
458
+ /* ── Add participant (developer feature) ────────────────────────────── */
459
+ const handleAddParticipant = (0, react_1.useCallback)((uid) => {
460
+ if (!activeUser)
461
+ return;
462
+ const roomId = [activeUser.uid, viewerUidForSocket].sort().join('_');
463
+ emitAddParticipant(roomId, uid);
464
+ }, [activeUser, viewerUidForSocket, emitAddParticipant]);
465
+ /* ── Derived ────────────────────────────────────────────────────────── */
466
+ const isBlocked = activeUser ? blockedUids.includes(activeUser.uid) : false;
350
467
  const primaryColor = theme.primaryColor;
351
- /** All developers are listed; only end-`user` rows are filtered by `viewer.projectId`. */
352
- const allUsers = useMemo(() => {
468
+ const allUsers = (0, react_1.useMemo)(() => {
353
469
  var _a, _b;
354
470
  if (!data)
355
471
  return [];
@@ -357,10 +473,9 @@ export const ChatWidget = ({ theme: localTheme, viewer }) => {
357
473
  const devs = (_b = data.developers) !== null && _b !== void 0 ? _b : [];
358
474
  if (!pid)
359
475
  return [...devs, ...data.users];
360
- const usersInProject = data.users.filter(u => u.project === pid);
361
- return [...devs, ...usersInProject];
476
+ return [...devs, ...data.users.filter(u => u.project === pid)];
362
477
  }, [data, viewer === null || viewer === void 0 ? void 0 : viewer.projectId]);
363
- const effectiveViewerBlocked = useMemo(() => {
478
+ const effectiveViewerBlocked = (0, react_1.useMemo)(() => {
364
479
  var _a, _b;
365
480
  if (!widgetConfig)
366
481
  return false;
@@ -377,61 +492,28 @@ export const ChatWidget = ({ theme: localTheme, viewer }) => {
377
492
  const filteredUsers = screen === 'user-list'
378
493
  ? allUsers.filter(u => {
379
494
  if (userListCtx === 'support') {
380
- if (viewerIsDev)
381
- return u.type === 'user';
382
- return u.type === 'developer';
495
+ return viewerIsDev ? u.type === 'user' : u.type === 'developer';
383
496
  }
384
- if (viewerIsDev) {
497
+ if (viewerIsDev)
385
498
  return u.type === 'developer' && u.uid !== viewerUid;
386
- }
387
499
  return u.type === 'user';
388
500
  })
389
501
  : [];
390
- const otherDevelopers = useMemo(() => allUsers.filter(u => u.type === 'developer' && u.uid !== viewerUid), [allUsers, viewerUid]);
502
+ const otherDevelopers = (0, react_1.useMemo)(() => allUsers.filter(u => u.type === 'developer' && u.uid !== viewerUid), [allUsers, viewerUid]);
391
503
  const blockedUsers = allUsers.filter(u => blockedUids.includes(u.uid));
392
- const totalUnread = useMemo(() => recentChats.reduce((sum, c) => { var _a; return sum + Math.max(0, (_a = c.unread) !== null && _a !== void 0 ? _a : 0); }, 0), [recentChats]);
393
- const handleTransferToDeveloper = useCallback((dev) => {
394
- var _a;
395
- if (!activeUser || !widgetConfig)
396
- return;
397
- const agent = ((_a = widgetConfig.viewerName) === null || _a === void 0 ? void 0 : _a.trim()) || 'Agent';
398
- const transferNote = {
399
- id: `tr_${Date.now()}_${Math.random().toString(36).slice(2)}`,
400
- senderId: 'me',
401
- receiverId: dev.uid,
402
- text: `— ${agent} transferred this conversation from ${activeUser.name} to ${dev.name} —`,
403
- timestamp: new Date().toISOString(),
404
- type: 'text',
405
- status: 'sent',
406
- };
407
- selectUser(dev, [...messages, transferNote]);
408
- }, [activeUser, messages, selectUser, widgetConfig]);
409
- /* Position */
504
+ const totalUnread = (0, react_1.useMemo)(() => recentChats.reduce((sum, c) => { var _a; return sum + Math.max(0, (_a = c.unread) !== null && _a !== void 0 ? _a : 0); }, 0), [recentChats]);
410
505
  const posStyle = theme.buttonPosition === 'bottom-left'
411
506
  ? { left: 24, right: 'auto' }
412
507
  : { right: 24, left: 'auto' };
413
- /* No radius on top-left / bottom-left; left-docked panel keeps inner TR/BR curve */
414
508
  const drawerPosStyle = theme.buttonPosition === 'bottom-left'
415
- ? {
416
- left: 0,
417
- borderTopLeftRadius: 0,
418
- borderBottomLeftRadius: 0,
419
- borderTopRightRadius: 16,
420
- borderBottomRightRadius: 16,
421
- }
422
- : {
423
- right: 0,
424
- borderTopLeftRadius: 0,
425
- borderBottomLeftRadius: 0,
426
- };
427
- /* ── Don't render until mounted (SSR safe) ──────────────────────────── */
509
+ ? { left: 0, borderTopLeftRadius: 0, borderBottomLeftRadius: 0, borderTopRightRadius: 16, borderBottomRightRadius: 16 }
510
+ : { right: 0, borderTopLeftRadius: 0, borderBottomLeftRadius: 0 };
428
511
  if (!mounted)
429
512
  return null;
430
- return (_jsxs(_Fragment, { children: [_jsx("style", { children: `
513
+ return (react_1.default.createElement(ErrorBoundary_1.ErrorBoundary, { primaryColor: primaryColor },
514
+ react_1.default.createElement("style", null, `
431
515
  @import url('https://fonts.googleapis.com/css2?family=DM+Sans:wght@400;500;600;700;800&display=swap');
432
-
433
516
  .cw-root * { box-sizing: border-box; font-family: 'DM Sans', 'Segoe UI', sans-serif; }
434
-
435
517
  @keyframes cw-slideInRight { from { transform: translateX(100%); opacity: 0; } to { transform: translateX(0); opacity: 1; } }
436
518
  @keyframes cw-slideOutRight { from { transform: translateX(0); opacity: 1; } to { transform: translateX(100%); opacity: 0; } }
437
519
  @keyframes cw-slideInLeft { from { transform: translateX(-100%); opacity: 0; } to { transform: translateX(0); opacity: 1; } }
@@ -440,82 +522,95 @@ export const ChatWidget = ({ theme: localTheme, viewer }) => {
440
522
  @keyframes cw-slideIn { from { opacity: 0; transform: translateX(18px); } to { opacity: 1; transform: translateX(0); } }
441
523
  @keyframes cw-pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.4; } }
442
524
  @keyframes cw-btnPop { from { transform: scale(0.8); opacity: 0; } to { transform: scale(1); opacity: 1; } }
443
-
525
+ @keyframes spin { to { transform: rotate(360deg); } }
444
526
  .cw-scroll::-webkit-scrollbar { width: 4px; }
445
527
  .cw-scroll::-webkit-scrollbar-track { background: transparent; }
446
528
  .cw-scroll::-webkit-scrollbar-thumb { background: #e0e0e0; border-radius: 4px; }
447
-
448
529
  .cw-drawer-enter { animation: ${theme.buttonPosition === 'bottom-left' ? 'cw-slideInLeft' : 'cw-slideInRight'} 0.32s cubic-bezier(0.22,1,0.36,1) both; }
449
530
  .cw-drawer-exit { animation: ${theme.buttonPosition === 'bottom-left' ? 'cw-slideOutLeft' : 'cw-slideOutRight'} 0.28s cubic-bezier(0.55,0,1,0.45) both; }
450
-
451
- .cw-drawer-panel {
452
- width: 30%;
453
- max-width: 100vw;
454
- min-width: 0;
455
- }
456
- @media (max-width: 1024px) {
457
- .cw-drawer-panel { width: 100%; }
458
- }
459
- ` }), !isOpen && callMinimized && callInProgress && callSession.peer && (_jsx(MiniCallBar, { session: callSession, primaryColor: primaryColor, buttonPosition: theme.buttonPosition, onExpand: openDrawer, onEnd: handleEndCall })), !isOpen && (_jsxs("button", { className: "cw-root", type: "button", onClick: openDrawer, "aria-label": totalUnread > 0 ? `${theme.buttonLabel}, ${totalUnread} unread` : theme.buttonLabel, title: totalUnread > 0 ? `${totalUnread} unread message${totalUnread === 1 ? '' : 's'}` : theme.buttonLabel, style: Object.assign(Object.assign({ position: 'fixed', bottom: 24, zIndex: 9999 }, posStyle), { display: 'flex', alignItems: 'center', gap: 10, padding: '13px 22px', backgroundColor: theme.buttonColor, color: theme.buttonTextColor, border: 'none', borderRadius: 50, cursor: 'pointer', fontSize: 15, fontWeight: 700, boxShadow: `0 8px 28px ${theme.buttonColor}55`, animation: 'cw-btnPop 0.4s cubic-bezier(0.34,1.56,0.64,1)', transition: 'transform 0.2s, box-shadow 0.2s' }), onMouseEnter: e => {
460
- e.currentTarget.style.transform = 'scale(1.06) translateY(-2px)';
461
- e.currentTarget.style.boxShadow = `0 14px 36px ${theme.buttonColor}66`;
462
- }, onMouseLeave: e => {
463
- e.currentTarget.style.transform = 'scale(1)';
464
- e.currentTarget.style.boxShadow = `0 8px 28px ${theme.buttonColor}55`;
465
- }, children: [_jsxs("span", { style: { position: 'relative', display: 'inline-flex', alignItems: 'center', justifyContent: 'center' }, children: [_jsx("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", children: _jsx("path", { d: "M21 15a2 2 0 01-2 2H7l-4 4V5a2 2 0 012-2h14a2 2 0 012 2z", stroke: theme.buttonTextColor, strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }) }), totalUnread > 0 && (_jsx("span", { style: {
466
- position: 'absolute',
467
- top: -8,
468
- right: -10,
469
- minWidth: 20,
470
- height: 20,
471
- padding: '0 5px',
472
- borderRadius: 999,
473
- background: '#ef4444',
474
- color: '#fff',
475
- fontSize: 11,
476
- fontWeight: 800,
477
- lineHeight: '20px',
478
- textAlign: 'center',
479
- border: '2px solid #fff',
480
- boxSizing: 'border-box',
481
- }, children: totalUnread > 99 ? '99+' : totalUnread }))] }), _jsx("span", { children: theme.buttonLabel })] })), isOpen && (_jsx("div", { "aria-hidden": true, style: {
482
- position: 'fixed', inset: 0, zIndex: 9997,
483
- backgroundColor: 'rgba(0,0,0,0.35)',
484
- opacity: closing ? 0 : 1,
485
- transition: 'opacity 0.3s',
486
- } })), isOpen && (_jsxs("div", { className: `cw-root cw-drawer-panel ${closing ? 'cw-drawer-exit' : 'cw-drawer-enter'}`, style: Object.assign(Object.assign({ position: 'fixed', top: 0, bottom: 0 }, drawerPosStyle), { zIndex: 9998, backgroundColor: '#fff', boxShadow: theme.buttonPosition === 'bottom-left'
487
- ? '4px 0 40px rgba(0,0,0,0.18)'
488
- : '-4px 0 40px rgba(0,0,0,0.18)', display: 'flex', flexDirection: 'column', overflow: 'hidden' }), children: [cfgLoading && (_jsxs("div", { style: { display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', height: '100%', gap: 16 }, children: [_jsx("div", { style: {
489
- width: 40, height: 40, borderRadius: '50%',
490
- border: `3px solid ${primaryColor}30`,
491
- borderTopColor: primaryColor,
492
- animation: 'spin 0.8s linear infinite',
493
- } }), _jsx("style", { children: `@keyframes spin { to { transform: rotate(360deg); } }` }), _jsx("p", { style: { fontSize: 14, color: '#7b8fa1' }, children: "Loading chat\u2026" })] })), cfgError && !cfgLoading && (_jsxs("div", { style: { display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', height: '100%', gap: 12, padding: 32, textAlign: 'center' }, children: [_jsx("div", { style: { fontSize: 40 }, children: "\u26A0\uFE0F" }), _jsx("p", { style: { fontWeight: 700, color: '#1a2332' }, children: "Could not load chat configuration" }), _jsx("p", { style: { fontSize: 13, color: '#7b8fa1', lineHeight: 1.6 }, children: cfgError }), _jsx("button", { onClick: closeDrawer, style: { padding: '9px 20px', borderRadius: 10, border: 'none', background: primaryColor, color: '#fff', cursor: 'pointer', fontWeight: 700 }, children: "Close" })] })), !cfgLoading && !cfgError && widgetConfig && (_jsxs(_Fragment, { children: [screen !== 'chat' && screen !== 'call' && !effectiveViewerBlocked && (_jsx("div", { style: {
494
- position: 'absolute', top: 12,
495
- right: theme.buttonPosition === 'bottom-left' ? 'auto' : 12,
496
- left: theme.buttonPosition === 'bottom-left' ? 12 : 'auto',
497
- zIndex: 20, display: 'flex', gap: 6,
498
- }, children: _jsx(CornerBtn, { onClick: closeDrawer, title: "Close", children: _jsx("svg", { width: "12", height: "12", viewBox: "0 0 24 24", fill: "none", children: _jsx("path", { d: "M18 6L6 18M6 6l12 12", stroke: "#fff", strokeWidth: "2.5", strokeLinecap: "round" }) }) }) })), widgetConfig.status === 'MAINTENANCE' && (_jsx(MaintenanceView, { primaryColor: primaryColor })), widgetConfig.status === 'DISABLE' && (_jsxs("div", { style: { display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', height: '100%', padding: 32, textAlign: 'center', gap: 12 }, children: [_jsx("div", { style: { fontSize: 40 }, children: "\uD83D\uDD12" }), _jsx("p", { style: { fontWeight: 700, color: '#1a2332' }, children: "Chat is disabled" }), _jsx("button", { onClick: closeDrawer, style: { padding: '9px 20px', borderRadius: 10, border: 'none', background: primaryColor, color: '#fff', cursor: 'pointer', fontWeight: 700 }, children: "Close" })] })), widgetConfig.status === 'ACTIVE' && effectiveViewerBlocked && (_jsx(ViewerBlockedScreen, { config: widgetConfig, apiKey: apiKey, onClose: closeDrawer })), widgetConfig.status === 'ACTIVE' && !effectiveViewerBlocked && !permissionsOk && (_jsx(PermissionsGateScreen, { primaryColor: primaryColor, widgetId: widgetConfig.id, onGranted: () => setPermissionsOk(true) })), widgetConfig.status === 'ACTIVE' && !effectiveViewerBlocked && permissionsOk && (_jsxs("div", { className: "cw-scroll", style: { flex: 1, display: 'flex', flexDirection: 'column', overflow: 'hidden' }, children: [screen === 'home' && (_jsx(HomeScreen, { config: widgetConfig, apiKey: apiKey, onNavigate: handleCardClick, onOpenTicket: handleOpenTicket, tickets: tickets })), screen === 'user-list' && (_jsx(UserListScreen, { context: userListCtx, users: filteredUsers, primaryColor: primaryColor, viewerType: (_e = widgetConfig.viewerType) !== null && _e !== void 0 ? _e : 'user', onBack: () => { setListEntranceAnimation(false); setScreen('home'); }, onSelectUser: handleSelectUser, onBlockList: userListCtx === 'conversation' ? () => setScreen('block-list') : undefined, useHomeHeader: userListCtx === 'support' && widgetConfig.viewerType !== 'developer', animateEntrance: listEntranceAnimation })), screen === 'chat' && activeUser && (_jsx(ChatScreen, { activeUser: activeUser, messages: messages, config: widgetConfig, isPaused: isPaused, isReported: isReported, isBlocked: isBlocked, onSend: sendMessage, onBack: handleBackFromChat, onClose: closeDrawer, onTogglePause: handleTogglePause, onReport: reportChat, onBlock: handleBlock, onStartCall: handleStartCall, onNavAction: handleNavFromMenu, otherDevelopers: otherDevelopers, onTransferToDeveloper: handleTransferToDeveloper, messageSoundEnabled: messageSoundEnabled, onToggleMessageSound: toggleMessageSound })), screen === 'call' && callSession.peer && (_jsx(CallScreen, { session: callSession, localVideoRef: localVideoRef, remoteVideoRef: remoteVideoRef, onEnd: handleEndCall, onToggleMute: toggleMute, onToggleCamera: toggleCamera, primaryColor: primaryColor, onMinimize: minimizeCall })), screen === 'recent-chats' && (_jsx(RecentChatsScreen, { chats: recentChats, config: widgetConfig, onSelectChat: u => handleSelectUser(u, listCtxForUser(u, viewerIsDev)), animateEntrance: listEntranceAnimation })), screen === 'tickets' && (_jsx(TicketScreen, { tickets: tickets, config: widgetConfig, onNewTicket: () => { setListEntranceAnimation(false); setScreen('ticket-new'); }, onSelectTicket: id => {
499
- setListEntranceAnimation(false);
500
- setViewingTicketId(id);
501
- setScreen('ticket-detail');
502
- }, animateEntrance: listEntranceAnimation })), screen === 'ticket-new' && (_jsx(TicketFormScreen, { config: widgetConfig, onSubmit: handleRaiseTicket, onCancel: () => setScreen('tickets') })), screen === 'ticket-detail' && viewingTicketId && ((() => {
503
- const t = tickets.find(x => x.id === viewingTicketId);
504
- return t ? (_jsx(TicketDetailScreen, { ticket: t, config: widgetConfig, onBack: () => { setViewingTicketId(null); setScreen('tickets'); } })) : null;
505
- })()), screen === 'block-list' && (_jsx(BlockListScreen, { blockedUsers: blockedUsers, config: widgetConfig, onUnblock: handleUnblock, onBack: () => { setScreen('home'); setActiveTab('home'); } }))] })), widgetConfig.status === 'ACTIVE' &&
506
- !effectiveViewerBlocked &&
507
- permissionsOk &&
508
- screen !== 'chat' &&
509
- screen !== 'call' &&
510
- screen !== 'user-list' &&
511
- screen !== 'block-list' &&
512
- screen !== 'ticket-detail' &&
513
- screen !== 'ticket-new' && (_jsx(BottomTabs, { active: activeTab, onChange: handleTabChange, primaryColor: primaryColor }))] }))] }))] }));
531
+ .cw-drawer-panel { width: 30%; max-width: 100vw; min-width: 0; }
532
+ @media (max-width: 1024px) { .cw-drawer-panel { width: 100%; } }
533
+ `),
534
+ socketStatus !== 'connected' && socketStatus !== 'disconnected' && (react_1.default.createElement("div", { style: Object.assign(Object.assign({ position: 'fixed', bottom: 80 }, posStyle), { zIndex: 9996, background: socketStatus === 'connecting' ? '#f59e0b' : '#ef4444', color: '#fff', fontSize: 11, fontWeight: 700, padding: '3px 10px', borderRadius: 20, animation: 'cw-fadeUp 0.3s ease' }) }, socketStatus === 'connecting' ? '● Connecting…' : '● Offline')),
535
+ !isOpen && callMinimized && callInProgress && callSession.peer && (react_1.default.createElement(MiniCallBar_1.MiniCallBar, { session: callSession, primaryColor: primaryColor, buttonPosition: theme.buttonPosition, onExpand: openDrawer, onEnd: handleEndCall })),
536
+ !isOpen && (react_1.default.createElement("button", { className: "cw-root", type: "button", onClick: openDrawer, "aria-label": totalUnread > 0 ? `${theme.buttonLabel}, ${totalUnread} unread` : theme.buttonLabel, title: totalUnread > 0 ? `${totalUnread} unread message${totalUnread === 1 ? '' : 's'}` : theme.buttonLabel, style: Object.assign(Object.assign({ position: 'fixed', bottom: 24, zIndex: 9999 }, posStyle), { display: 'flex', alignItems: 'center', gap: 10, padding: '13px 22px', backgroundColor: theme.buttonColor, color: theme.buttonTextColor, border: 'none', borderRadius: 50, cursor: 'pointer', fontSize: 15, fontWeight: 700, boxShadow: `0 8px 28px ${theme.buttonColor}55`, animation: 'cw-btnPop 0.4s cubic-bezier(0.34,1.56,0.64,1)', transition: 'transform 0.2s, box-shadow 0.2s' }), onMouseEnter: e => {
537
+ e.currentTarget.style.transform = 'scale(1.06) translateY(-2px)';
538
+ e.currentTarget.style.boxShadow = `0 14px 36px ${theme.buttonColor}66`;
539
+ }, onMouseLeave: e => {
540
+ e.currentTarget.style.transform = 'scale(1)';
541
+ e.currentTarget.style.boxShadow = `0 8px 28px ${theme.buttonColor}55`;
542
+ } },
543
+ react_1.default.createElement("span", { style: { position: 'relative', display: 'inline-flex', alignItems: 'center', justifyContent: 'center' } },
544
+ react_1.default.createElement("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none" },
545
+ react_1.default.createElement("path", { d: "M21 15a2 2 0 01-2 2H7l-4 4V5a2 2 0 012-2h14a2 2 0 012 2z", stroke: theme.buttonTextColor, strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" })),
546
+ totalUnread > 0 && (react_1.default.createElement("span", { style: {
547
+ position: 'absolute', top: -8, right: -10,
548
+ minWidth: 20, height: 20, padding: '0 5px', borderRadius: 999,
549
+ background: '#ef4444', color: '#fff', fontSize: 11, fontWeight: 800,
550
+ lineHeight: '20px', textAlign: 'center', border: '2px solid #fff', boxSizing: 'border-box',
551
+ } }, totalUnread > 99 ? '99+' : totalUnread))),
552
+ react_1.default.createElement("span", null, theme.buttonLabel))),
553
+ isOpen && (react_1.default.createElement("div", { "aria-hidden": true, style: {
554
+ position: 'fixed', inset: 0, zIndex: 9997,
555
+ backgroundColor: 'rgba(0,0,0,0.35)',
556
+ opacity: closing ? 0 : 1,
557
+ transition: 'opacity 0.3s',
558
+ } })),
559
+ isOpen && (react_1.default.createElement("div", { className: `cw-root cw-drawer-panel ${closing ? 'cw-drawer-exit' : 'cw-drawer-enter'}`, style: Object.assign(Object.assign({ position: 'fixed', top: 0, bottom: 0 }, drawerPosStyle), { zIndex: 9998, backgroundColor: '#fff', boxShadow: theme.buttonPosition === 'bottom-left'
560
+ ? '4px 0 40px rgba(0,0,0,0.18)'
561
+ : '-4px 0 40px rgba(0,0,0,0.18)', display: 'flex', flexDirection: 'column', overflow: 'hidden' }) },
562
+ cfgLoading && (react_1.default.createElement("div", { style: { display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', height: '100%', gap: 16 } },
563
+ react_1.default.createElement("div", { style: { width: 40, height: 40, borderRadius: '50%', border: `3px solid ${primaryColor}30`, borderTopColor: primaryColor, animation: 'spin 0.8s linear infinite' } }),
564
+ react_1.default.createElement("p", { style: { fontSize: 14, color: '#7b8fa1' } }, "Loading chat\u2026"))),
565
+ cfgError && !cfgLoading && (react_1.default.createElement("div", { style: { display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', height: '100%', gap: 12, padding: 32, textAlign: 'center' } },
566
+ react_1.default.createElement("div", { style: { fontSize: 40 } }, "\u26A0\uFE0F"),
567
+ react_1.default.createElement("p", { style: { fontWeight: 700, color: '#1a2332' } }, "Could not load chat configuration"),
568
+ react_1.default.createElement("p", { style: { fontSize: 13, color: '#7b8fa1', lineHeight: 1.6 } }, cfgError),
569
+ react_1.default.createElement("button", { onClick: closeDrawer, style: { padding: '9px 20px', borderRadius: 10, border: 'none', background: primaryColor, color: '#fff', cursor: 'pointer', fontWeight: 700 } }, "Close"))),
570
+ !cfgLoading && !cfgError && widgetConfig && (react_1.default.createElement(ErrorBoundary_1.ErrorBoundary, { primaryColor: primaryColor },
571
+ screen !== 'chat' && screen !== 'call' && !effectiveViewerBlocked && (react_1.default.createElement("div", { style: {
572
+ position: 'absolute', top: 12,
573
+ right: theme.buttonPosition === 'bottom-left' ? 'auto' : 12,
574
+ left: theme.buttonPosition === 'bottom-left' ? 12 : 'auto',
575
+ zIndex: 20, display: 'flex', gap: 6,
576
+ } },
577
+ react_1.default.createElement(CornerBtn, { onClick: closeDrawer, title: "Close" },
578
+ react_1.default.createElement("svg", { width: "12", height: "12", viewBox: "0 0 24 24", fill: "none" },
579
+ react_1.default.createElement("path", { d: "M18 6L6 18M6 6l12 12", stroke: "#fff", strokeWidth: "2.5", strokeLinecap: "round" }))))),
580
+ widgetConfig.status === 'MAINTENANCE' && react_1.default.createElement(MaintenanceView_1.MaintenanceView, { primaryColor: primaryColor }),
581
+ widgetConfig.status === 'DISABLE' && (react_1.default.createElement("div", { style: { display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', height: '100%', padding: 32, textAlign: 'center', gap: 12 } },
582
+ react_1.default.createElement("div", { style: { fontSize: 40 } }, "\uD83D\uDD12"),
583
+ react_1.default.createElement("p", { style: { fontWeight: 700, color: '#1a2332' } }, "Chat is disabled"),
584
+ react_1.default.createElement("button", { onClick: closeDrawer, style: { padding: '9px 20px', borderRadius: 10, border: 'none', background: primaryColor, color: '#fff', cursor: 'pointer', fontWeight: 700 } }, "Close"))),
585
+ widgetConfig.status === 'ACTIVE' && effectiveViewerBlocked && (react_1.default.createElement(ViewerBlockedScreen_1.ViewerBlockedScreen, { config: widgetConfig, apiKey: apiKey, onClose: closeDrawer })),
586
+ widgetConfig.status === 'ACTIVE' && !effectiveViewerBlocked && !permissionsOk && (react_1.default.createElement(PermissionsGateScreen_1.PermissionsGateScreen, { primaryColor: primaryColor, widgetId: widgetConfig.id, onGranted: () => setPermissionsOk(true) })),
587
+ widgetConfig.status === 'ACTIVE' && !effectiveViewerBlocked && permissionsOk && (react_1.default.createElement("div", { className: "cw-scroll", style: { flex: 1, display: 'flex', flexDirection: 'column', overflow: 'hidden' } },
588
+ screen === 'home' && (react_1.default.createElement(HomeScreen_1.HomeScreen, { config: widgetConfig, apiKey: apiKey, onNavigate: handleCardClick, onOpenTicket: handleOpenTicket, tickets: tickets })),
589
+ screen === 'user-list' && (react_1.default.createElement(UserListScreen_1.UserListScreen, { context: userListCtx, users: filteredUsers, primaryColor: primaryColor, viewerType: (_h = widgetConfig.viewerType) !== null && _h !== void 0 ? _h : 'user', onBack: () => { setListEntranceAnimation(false); setScreen('home'); }, onSelectUser: handleSelectUser, onBlockList: userListCtx === 'conversation' ? () => setScreen('block-list') : undefined, useHomeHeader: userListCtx === 'support' && widgetConfig.viewerType !== 'developer', animateEntrance: listEntranceAnimation })),
590
+ screen === 'chat' && activeUser && (react_1.default.createElement(ChatScreen_1.ChatScreen, { activeUser: activeUser, messages: messages, config: widgetConfig, isPaused: isPaused, isReported: isReported, isBlocked: isBlocked, onSend: sendMessage, onBack: handleBackFromChat, onClose: closeDrawer, onTogglePause: handleTogglePause, onReport: _reportChat, onBlock: handleBlock, onStartCall: handleStartCall, onNavAction: handleNavFromMenu, otherDevelopers: otherDevelopers, onTransferToDeveloper: handleTransferToDeveloper, messageSoundEnabled: messageSoundEnabled, onToggleMessageSound: toggleMessageSound })),
591
+ screen === 'call' && callSession.peer && (react_1.default.createElement(CallScreen_1.CallScreen, { session: callSession, localVideoRef: localVideoRef, remoteVideoRef: remoteVideoRef, onEnd: handleEndCall, onToggleMute: toggleMute, onToggleCamera: toggleCamera, primaryColor: primaryColor, onMinimize: minimizeCall })),
592
+ screen === 'recent-chats' && (react_1.default.createElement(RecentChatsScreen_1.RecentChatsScreen, { chats: recentChats, config: widgetConfig, onSelectChat: u => handleSelectUser(u, listCtxForUser(u, viewerIsDev)), animateEntrance: listEntranceAnimation })),
593
+ screen === 'tickets' && (react_1.default.createElement(TicketScreen_1.TicketScreen, { tickets: tickets, config: widgetConfig, onNewTicket: () => { setListEntranceAnimation(false); setScreen('ticket-new'); }, onSelectTicket: id => {
594
+ setListEntranceAnimation(false);
595
+ setViewingTicketId(id);
596
+ setScreen('ticket-detail');
597
+ }, animateEntrance: listEntranceAnimation })),
598
+ screen === 'ticket-new' && (react_1.default.createElement(TicketFormScreen_1.TicketFormScreen, { config: widgetConfig, onSubmit: handleRaiseTicket, onCancel: () => setScreen('tickets') })),
599
+ screen === 'ticket-detail' && viewingTicketId && (() => {
600
+ const t = tickets.find(x => x.id === viewingTicketId);
601
+ return t ? (react_1.default.createElement(TicketDetailScreen_1.TicketDetailScreen, { ticket: t, config: widgetConfig, onBack: () => { setViewingTicketId(null); setScreen('tickets'); } })) : null;
602
+ })(),
603
+ screen === 'block-list' && (react_1.default.createElement(BlockList_1.BlockListScreen, { blockedUsers: blockedUsers, config: widgetConfig, onUnblock: handleUnblock, onBack: () => { setScreen('home'); setActiveTab('home'); } })))),
604
+ widgetConfig.status === 'ACTIVE' &&
605
+ !effectiveViewerBlocked && permissionsOk &&
606
+ screen !== 'chat' && screen !== 'call' &&
607
+ screen !== 'user-list' && screen !== 'block-list' &&
608
+ screen !== 'ticket-detail' && screen !== 'ticket-new' && (react_1.default.createElement(BottomTabs_1.BottomTabs, { active: activeTab, onChange: handleTabChange, primaryColor: primaryColor }))))))));
514
609
  };
515
- export default ChatWidget;
516
- /* ── Tiny corner button ────────────────────────────────────────────────────── */
517
- const CornerBtn = ({ onClick, title, children }) => (_jsx("button", { onClick: onClick, title: title, style: {
518
- width: 26, height: 26, borderRadius: '50%',
519
- background: 'rgba(0,0,0,0.25)', border: 'none',
610
+ exports.ChatWidget = ChatWidget;
611
+ exports.default = exports.ChatWidget;
612
+ const CornerBtn = ({ onClick, title, children }) => (react_1.default.createElement("button", { onClick: onClick, title: title, style: {
613
+ width: 26, height: 26, borderRadius: '50%', background: 'rgba(0,0,0,0.25)', border: 'none',
520
614
  display: 'flex', alignItems: 'center', justifyContent: 'center', cursor: 'pointer',
521
- }, children: children }));
615
+ } }, children));
616
+ //# sourceMappingURL=ChatWidget.js.map