ajaxter-chat 3.0.17 → 3.2.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 (146) 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 +499 -367
  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 +7 -3
  74. package/dist/config/index.d.ts.map +1 -0
  75. package/dist/config/index.js +73 -22
  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 +22 -15
  84. package/dist/hooks/useRemoteConfig.js.map +1 -0
  85. package/dist/hooks/useSocket.d.ts +59 -0
  86. package/dist/hooks/useSocket.d.ts.map +1 -0
  87. package/dist/hooks/useSocket.js +203 -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 +7 -1
  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 +129 -48
  98. package/dist/types/index.d.ts.map +1 -0
  99. package/dist/types/index.js +4 -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 +8 -4
  138. package/src/components/ChatWidget.tsx +643 -622
  139. package/src/components/ErrorBoundary/index.tsx +62 -0
  140. package/src/config/index.ts +87 -26
  141. package/src/hooks/useChat.ts +59 -12
  142. package/src/hooks/useRemoteConfig.ts +8 -3
  143. package/src/hooks/useSocket.ts +249 -0
  144. package/src/hooks/useWebRTC.ts +99 -64
  145. package/src/index.ts +14 -3
  146. package/src/types/index.ts +177 -143
@@ -1,130 +1,165 @@
1
1
  import { useState, useRef, useCallback, useEffect } from 'react';
2
2
  import { CallSession, CallState, ChatUser } from '../types';
3
3
 
4
+ export interface UseWebRTCOptions {
5
+ onOfferReady?: (offer: RTCSessionDescriptionInit, toUid: string, callId: string) => void;
6
+ onAnswerReady?: (answer: RTCSessionDescriptionInit, toUid: string, callId: string) => void;
7
+ onIceCandidateReady?: (candidate: RTCIceCandidateInit, toUid: string) => void;
8
+ onCallEnded?: (callId: string, toUid?: string) => void;
9
+ }
10
+
4
11
  const ICE_SERVERS: RTCIceServer[] = [
5
12
  { urls: 'stun:stun.l.google.com:19302' },
6
13
  { urls: 'stun:stun1.l.google.com:19302' },
7
14
  ];
8
15
 
9
- export function useWebRTC() {
16
+ export function useWebRTC(opts: UseWebRTCOptions = {}) {
17
+ const { onOfferReady, onAnswerReady, onIceCandidateReady, onCallEnded } = opts;
18
+
10
19
  const [session, setSession] = useState<CallSession>({
11
- state: 'idle',
12
- peer: null,
13
- startedAt: null,
14
- isMuted: false,
20
+ state: 'idle',
21
+ peer: null,
22
+ startedAt: null,
23
+ isMuted: false,
15
24
  isCameraOn: false,
16
25
  });
17
26
 
18
- const pcRef = useRef<RTCPeerConnection | null>(null);
19
- const localStream = useRef<MediaStream | null>(null);
20
- const localVideoRef = useRef<HTMLVideoElement | null>(null);
27
+ const pcRef = useRef<RTCPeerConnection | null>(null);
28
+ const localStream = useRef<MediaStream | null>(null);
29
+ const localVideoRef = useRef<HTMLVideoElement | null>(null);
21
30
  const remoteVideoRef = useRef<HTMLVideoElement | null>(null);
31
+ const currentCallId = useRef<string | null>(null);
32
+ const peerUidRef = useRef<string | null>(null);
22
33
 
23
- const updateSession = (patch: Partial<CallSession>) =>
24
- setSession(prev => ({ ...prev, ...patch }));
25
-
26
- /** Start an outgoing call */
27
- const startCall = useCallback(async (peer: ChatUser, withVideo = false) => {
28
- updateSession({ state: 'calling', peer });
29
-
30
- const stream = await navigator.mediaDevices.getUserMedia({
31
- audio: true,
32
- video: withVideo,
33
- });
34
- localStream.current = stream;
35
- if (localVideoRef.current) localVideoRef.current.srcObject = stream;
34
+ const updateSession = useCallback((patch: Partial<CallSession>) =>
35
+ setSession(prev => ({ ...prev, ...patch })), []);
36
36
 
37
+ const buildPc = useCallback((peer: ChatUser) => {
37
38
  const pc = new RTCPeerConnection({ iceServers: ICE_SERVERS });
38
39
  pcRef.current = pc;
39
-
40
- stream.getTracks().forEach(t => pc.addTrack(t, stream));
40
+ peerUidRef.current = peer.uid;
41
41
 
42
42
  pc.ontrack = e => {
43
43
  if (remoteVideoRef.current) remoteVideoRef.current.srcObject = e.streams[0];
44
- updateSession({ state: 'connected', startedAt: new Date(), isCameraOn: withVideo });
44
+ updateSession({ state: 'connected', startedAt: new Date() });
45
45
  };
46
46
 
47
47
  pc.onicecandidate = e => {
48
- if (e.candidate) {
49
- // TODO: socket.emit('ice-candidate', { candidate: e.candidate, to: peer.uid });
50
- console.log('[WebRTC] ICE candidate ready — send via signalling:', e.candidate);
48
+ if (e.candidate && peerUidRef.current) {
49
+ onIceCandidateReady?.(e.candidate.toJSON(), peerUidRef.current);
51
50
  }
52
51
  };
53
52
 
54
- const offer = await pc.createOffer();
55
- await pc.setLocalDescription(offer);
56
- // TODO: socket.emit('call-offer', { offer, to: peer.uid, from: 'me' });
57
- console.log('[WebRTC] Offer created send via signalling server:', offer);
58
- }, []);
53
+ pc.onconnectionstatechange = () => {
54
+ if (pc.connectionState === 'failed' || pc.connectionState === 'disconnected') {
55
+ updateSession({ state: 'ended' });
56
+ setTimeout(() => updateSession({ state: 'idle' }), 1800);
57
+ }
58
+ };
59
+
60
+ return pc;
61
+ }, [onIceCandidateReady, updateSession]);
62
+
63
+ const startCall = useCallback(async (peer: ChatUser, withVideo = false) => {
64
+ const callId = `call_${Date.now()}`;
65
+ currentCallId.current = callId;
66
+ updateSession({ state: 'calling', peer });
67
+
68
+ let stream: MediaStream;
69
+ try {
70
+ stream = await navigator.mediaDevices.getUserMedia({ audio: true, video: withVideo });
71
+ } catch (err) {
72
+ console.error('[WebRTC] getUserMedia failed:', err);
73
+ updateSession({ state: 'idle' });
74
+ return;
75
+ }
76
+ localStream.current = stream;
77
+ if (localVideoRef.current) localVideoRef.current.srcObject = stream;
78
+
79
+ const pc = buildPc(peer);
80
+ stream.getTracks().forEach(t => pc.addTrack(t, stream));
81
+
82
+ try {
83
+ const offer = await pc.createOffer();
84
+ await pc.setLocalDescription(offer);
85
+ onOfferReady?.(offer, peer.uid, callId);
86
+ updateSession({ isCameraOn: withVideo });
87
+ } catch (err) {
88
+ console.error('[WebRTC] createOffer failed:', err);
89
+ }
90
+ }, [buildPc, onOfferReady, updateSession]);
59
91
 
60
- /** Accept an incoming call offer */
61
92
  const acceptCall = useCallback(async (
62
93
  offer: RTCSessionDescriptionInit,
63
94
  peer: ChatUser,
95
+ callId: string,
64
96
  withVideo = false
65
97
  ) => {
98
+ currentCallId.current = callId;
66
99
  updateSession({ state: 'connected', peer, startedAt: new Date(), isCameraOn: withVideo });
67
100
 
68
- const stream = await navigator.mediaDevices.getUserMedia({ audio: true, video: withVideo });
101
+ let stream: MediaStream;
102
+ try {
103
+ stream = await navigator.mediaDevices.getUserMedia({ audio: true, video: withVideo });
104
+ } catch (err) {
105
+ console.error('[WebRTC] getUserMedia failed:', err);
106
+ return;
107
+ }
69
108
  localStream.current = stream;
70
109
  if (localVideoRef.current) localVideoRef.current.srcObject = stream;
71
110
 
72
- const pc = new RTCPeerConnection({ iceServers: ICE_SERVERS });
73
- pcRef.current = pc;
111
+ const pc = buildPc(peer);
74
112
  stream.getTracks().forEach(t => pc.addTrack(t, stream));
75
- pc.ontrack = e => {
76
- if (remoteVideoRef.current) remoteVideoRef.current.srcObject = e.streams[0];
77
- };
78
- pc.onicecandidate = e => {
79
- if (e.candidate) {
80
- // TODO: socket.emit('ice-candidate', { candidate: e.candidate, to: peer.uid });
81
- }
82
- };
83
113
 
84
114
  await pc.setRemoteDescription(offer);
85
115
  const answer = await pc.createAnswer();
86
116
  await pc.setLocalDescription(answer);
87
- // TODO: socket.emit('call-answer', { answer, to: peer.uid });
117
+ onAnswerReady?.(answer, peer.uid, callId);
118
+ }, [buildPc, onAnswerReady, updateSession]);
119
+
120
+ const addIceCandidate = useCallback(async (candidate: RTCIceCandidateInit) => {
121
+ try {
122
+ await pcRef.current?.addIceCandidate(new RTCIceCandidate(candidate));
123
+ } catch (err) {
124
+ console.warn('[WebRTC] addIceCandidate failed:', err);
125
+ }
88
126
  }, []);
89
127
 
90
- /** Hang up */
91
128
  const endCall = useCallback(() => {
129
+ const callId = currentCallId.current;
130
+ const peerUid = peerUidRef.current ?? undefined;
92
131
  localStream.current?.getTracks().forEach(t => t.stop());
93
132
  pcRef.current?.close();
94
133
  pcRef.current = null;
95
134
  localStream.current = null;
135
+ currentCallId.current = null;
136
+ if (callId) onCallEnded?.(callId, peerUid);
96
137
  updateSession({ state: 'ended', peer: null, startedAt: null });
97
138
  setTimeout(() => updateSession({ state: 'idle' }), 1800);
98
- }, []);
139
+ }, [onCallEnded, updateSession]);
99
140
 
100
141
  const toggleMute = useCallback(() => {
101
142
  if (!localStream.current) return;
102
- const enabled = !session.isMuted;
103
- localStream.current.getAudioTracks().forEach(t => { t.enabled = enabled; });
104
- updateSession({ isMuted: !session.isMuted });
105
- }, [session.isMuted]);
143
+ const muted = !session.isMuted;
144
+ localStream.current.getAudioTracks().forEach(t => { t.enabled = !muted; });
145
+ updateSession({ isMuted: muted });
146
+ }, [session.isMuted, updateSession]);
106
147
 
107
148
  const toggleCamera = useCallback(() => {
108
149
  if (!localStream.current) return;
109
- const enabled = !session.isCameraOn;
110
- localStream.current.getVideoTracks().forEach(t => { t.enabled = enabled; });
111
- updateSession({ isCameraOn: enabled });
112
- }, [session.isCameraOn]);
150
+ const on = !session.isCameraOn;
151
+ localStream.current.getVideoTracks().forEach(t => { t.enabled = on; });
152
+ updateSession({ isCameraOn: on });
153
+ }, [session.isCameraOn, updateSession]);
113
154
 
114
- // Cleanup on unmount
115
155
  useEffect(() => () => {
116
156
  localStream.current?.getTracks().forEach(t => t.stop());
117
157
  pcRef.current?.close();
118
158
  }, []);
119
159
 
120
160
  return {
121
- session,
122
- localVideoRef,
123
- remoteVideoRef,
124
- startCall,
125
- acceptCall,
126
- endCall,
127
- toggleMute,
128
- toggleCamera,
161
+ session, localVideoRef, remoteVideoRef,
162
+ startCall, acceptCall, addIceCandidate, endCall,
163
+ toggleMute, toggleCamera,
129
164
  };
130
165
  }
package/src/index.ts CHANGED
@@ -10,10 +10,12 @@ export { MaintenanceView } from './components/MaintenanceView';
10
10
  export { BottomTabs } from './components/Tabs/BottomTabs';
11
11
  export { EmojiPicker } from './components/EmojiPicker';
12
12
  export { SlideNavMenu } from './components/SlideNavMenu';
13
+ export { ErrorBoundary } from './components/ErrorBoundary';
13
14
 
14
15
  export { useChat } from './hooks/useChat';
15
16
  export { useWebRTC } from './hooks/useWebRTC';
16
17
  export { useRemoteConfig } from './hooks/useRemoteConfig';
18
+ export { useSocket } from './hooks/useSocket';
17
19
 
18
20
  export { shouldShowPrivacyNotice, dismissPrivacyNotice, getPrivacyDismissedAt } from './utils/privacyConsent';
19
21
  export { submitReenableRequest } from './utils/reenableRequest';
@@ -25,12 +27,21 @@ export type { PresenceSyncPayload } from './utils/presenceStatus';
25
27
  export { avatarColor, initials, formatTime, formatDate, generateTranscript, downloadText, truncateWords } from './utils/chat';
26
28
 
27
29
  export type {
30
+ // Core widget
28
31
  ChatWidgetProps, ChatWidgetTheme, ChatWidgetViewer,
32
+ // Config shapes
29
33
  WidgetConfig, RemoteChatData,
34
+ AjaxterConfigResponse, AjaxterInternalConfig, AjaxterWidgetSettings,
35
+ AjaxterWidgetTheme, AjaxterWidgetFeatures, AjaxterMinimized,
36
+ // Data models
30
37
  ChatUser, ChatMessage, Ticket, RecentChat,
31
38
  CallSession, CallState,
32
- ChatStatus, ChatType, UserType, OnlineStatus,
33
- Screen, BottomTab, UserListContext, MessageType,
39
+ // Enums / literals
40
+ ChatStatus, ChatType, DisplayMode, UserType, OnlineStatus,
41
+ Screen, BottomTab, UserListContext, MessageType, PresenceStatus,
34
42
  LocalEnvConfig,
35
- PresenceStatus,
36
43
  } from './types';
44
+
45
+ export type { UseSocketOptions, SocketStatus, SocketMessageAck } from './hooks/useSocket';
46
+ export type { UseChatOptions } from './hooks/useChat';
47
+ export type { UseWebRTCOptions } from './hooks/useWebRTC';
@@ -1,208 +1,242 @@
1
- // ─── Remote Config (from chatData.json) ────────────────────────────────────
1
+ // ─── Ajaxter Server Config Shape ─────────────────────────────────────────────
2
+
3
+ export interface AjaxterWidgetTheme {
4
+ header: { background: string; text: string };
5
+ agent: { messageBackground: string; messageText: string };
6
+ visitor: { messageBackground: string; messageText: string };
7
+ }
8
+
9
+ export interface AjaxterWidgetFeatures {
10
+ emoji: boolean;
11
+ rating: boolean;
12
+ transcript: boolean;
13
+ voice: boolean;
14
+ attachment: boolean;
15
+ webCall: boolean;
16
+ }
17
+
18
+ export interface AjaxterMinimized {
19
+ desktop: { type: 'slide' | 'round' | 'square'; height?: number; width?: number; borderRadiusTop?: number; borderRadiusBottom?: number };
20
+ mobile: { type: 'round' | 'slide' | 'square' };
21
+ }
22
+
23
+ export interface AjaxterWidgetSettings {
24
+ type: 'inline' | 'popup' | 'embed';
25
+ language: string;
26
+ minimized: AjaxterMinimized;
27
+ maximized: { desktop: { height: number; width: number } };
28
+ theme: AjaxterWidgetTheme;
29
+ notification?: {
30
+ all?: { sound?: boolean; agentTyping?: boolean; visitorTyping?: boolean };
31
+ desktop?: { preview?: boolean };
32
+ mobile?: { preview?: boolean };
33
+ };
34
+ behavior?: { defaultView?: string };
35
+ visibility?: {
36
+ all?: { showWhenOffline?: boolean };
37
+ desktop?: { position?: string; show?: boolean };
38
+ mobile?: { position?: string; show?: boolean };
39
+ };
40
+ features: AjaxterWidgetFeatures;
41
+ scheduler?: { enabled: boolean; config?: unknown };
42
+ states?: Record<string, unknown>;
43
+ }
44
+
45
+ /** Internal fields that drive the npm widget behaviour */
46
+ export interface AjaxterInternalConfig {
47
+ id: string;
48
+ apiKey: string;
49
+ status: ChatStatus;
50
+ chatType: ChatType;
51
+ /** 'slider' = side drawer, 'popup' = centered modal, 'inline' = embedded in page */
52
+ displayMode: DisplayMode;
53
+ primaryColor: string;
54
+ buttonLabel: string;
55
+ brandName: string | null;
56
+ supportPhone: string | null;
57
+ privacyPolicyUrl: string | null;
58
+ showPrivacyNotice: boolean;
59
+ allowVoiceMessage: boolean;
60
+ allowAttachment: boolean;
61
+ allowEmoji: boolean;
62
+ allowWebCall: boolean;
63
+ maxEmojiCount: number;
64
+ allowTranscript: boolean;
65
+ allowReport: boolean;
66
+ allowBlock: boolean;
67
+ }
68
+
69
+ /** Full response shape from GET /api/chat-config */
70
+ export interface AjaxterConfigResponse {
71
+ ok: boolean;
72
+ data: {
73
+ settingsVersion: string;
74
+ propertyName: string;
75
+ branding: { name: string };
76
+ widget: AjaxterWidgetSettings;
77
+ _internal: AjaxterInternalConfig;
78
+ developers: ChatUser[];
79
+ users: ChatUser[];
80
+ sampleChats: Record<string, ChatMessage[]>;
81
+ sampleTickets: Ticket[];
82
+ blockedUsers: string[];
83
+ };
84
+ }
85
+
86
+ /** The merged WidgetConfig shape used throughout the widget components */
2
87
  export interface WidgetConfig {
3
- id: string;
4
- apiKey: string;
5
- status: ChatStatus;
6
- chatType: ChatType;
7
- primaryColor: string;
8
- buttonLabel: string;
9
- buttonPosition: 'bottom-right' | 'bottom-left';
10
- welcomeTitle: string;
11
- welcomeSubtitle: string;
12
- /** Shown in footer (e.g. branch / location name) */
13
- branch?: string;
14
- /** Optional label above branch (e.g. "Answers by") */
15
- footerPoweredBy?: string;
16
- /** Shown on home “Call Us” (tel: link) */
17
- supportPhone?: string;
18
- /**
19
- * Who is using the widget. `developer` = support staff: “Need Support” becomes “Provide Support”
20
- * and lists customers; “New Conversation” lists other developers (excl. viewerUid).
21
- */
22
- viewerType?: 'user' | 'developer';
23
- /** Current user id when viewerType is developer — excluded from developer pick lists */
24
- viewerUid?: string;
25
- /** Display name for transfer notes (optional) */
26
- viewerName?: string;
27
- /**
28
- * Host app project scope (set when embedding passes `viewer.projectId`).
29
- * Use for API calls; lists can be filtered to users in the same project.
30
- */
31
- viewerProjectId?: string;
32
- /** Privacy Policy URL (linked from chat consent banner) */
88
+ id: string;
89
+ apiKey: string;
90
+ status: ChatStatus;
91
+ chatType: ChatType;
92
+ displayMode: DisplayMode;
93
+ primaryColor: string;
94
+ buttonLabel: string;
95
+ buttonPosition: 'bottom-right' | 'bottom-left';
96
+ welcomeTitle: string;
97
+ welcomeSubtitle: string;
98
+ branch?: string;
99
+ footerPoweredBy?: string;
100
+ supportPhone?: string;
101
+ viewerType?: 'user' | 'developer';
102
+ viewerUid?: string;
103
+ viewerName?: string;
104
+ viewerProjectId?: string;
33
105
  privacyPolicyUrl?: string;
34
- /** Set false to hide the consent note above the composer */
35
106
  showPrivacyNotice?: boolean;
36
- /** Product brand (e.g. Ajaxter) */
37
- brandName?: string;
38
- /** Home promotion: “Take a Website Tour” link */
39
- websiteTourUrl?: string;
40
- /** Optional override for the lead line in the promotion card */
41
- promotionLead?: string;
107
+ brandName?: string;
108
+ websiteTourUrl?: string;
109
+ promotionLead?: string;
42
110
  allowVoiceMessage: boolean;
43
- allowAttachment: boolean;
44
- allowEmoji: boolean;
45
- allowWebCall: boolean;
46
- maxEmojiCount: number;
111
+ allowAttachment: boolean;
112
+ allowEmoji: boolean;
113
+ allowWebCall: boolean;
114
+ maxEmojiCount: number;
47
115
  allowTranscriptDownload: boolean;
48
- allowReport: boolean;
49
- allowBlock: boolean;
50
- /**
51
- * When `true` (set by the server if this viewer is spam-blocked or not in allowed user/ticket/chat lists),
52
- * the widget hides all normal navigation and shows only the blocked-user screen with a re-enable request form.
53
- */
54
- viewerBlocked?: boolean;
55
- /** Optional override for the blocked message (default is a fixed spam notice). */
116
+ allowReport: boolean;
117
+ allowBlock: boolean;
118
+ viewerBlocked?: boolean;
56
119
  blockedViewerMessage?: string;
57
- /**
58
- * Absolute URL for `POST` JSON re-enable requests. If omitted, the submit button explains that no endpoint is configured.
59
- * @example https://api.example.com/widgets/reenable-request
60
- */
61
120
  reenableRequestUrl?: string;
62
- /**
63
- * Current presence from your API/DB (include in chatData or a session payload).
64
- * When set, it initializes the status control and overrides session-only cache.
65
- */
66
- presenceStatus?: PresenceStatus;
67
- /**
68
- * Production: `POST` JSON `{ widgetId, apiKey, viewerUid?, status }` to save presence in your database.
69
- * The client still mirrors to sessionStorage as a local fallback.
70
- */
121
+ presenceStatus?: PresenceStatus;
71
122
  presenceUpdateUrl?: string;
123
+ /** Full Ajaxter settings from server (for display-mode, theming, etc.) */
124
+ ajaxterSettings?: AjaxterWidgetSettings;
72
125
  }
73
126
 
74
127
  export interface RemoteChatData {
75
- widget: WidgetConfig;
76
- developers: ChatUser[];
77
- users: ChatUser[];
78
- sampleChats: Record<string, ChatMessage[]>;
128
+ widget: WidgetConfig;
129
+ developers: ChatUser[];
130
+ users: ChatUser[];
131
+ sampleChats: Record<string, ChatMessage[]>;
79
132
  sampleTickets: Ticket[];
80
133
  blockedUsers: string[];
81
134
  }
82
135
 
83
- // ─── Enums ──────────────────────────────────────────────────────────────────
136
+ // ─── Enums ───────────────────────────────────────────────────────────────────
84
137
  export type ChatStatus = 'ACTIVE' | 'DISABLE' | 'MAINTENANCE';
85
138
  export type ChatType = 'SUPPORT' | 'CHAT' | 'BOTH';
139
+ export type DisplayMode = 'slider' | 'popup' | 'inline';
86
140
  export type UserType = 'developer' | 'user';
87
141
  export type OnlineStatus = 'online' | 'away' | 'offline';
88
142
  export type BottomTab = 'home' | 'chats' | 'tickets';
89
- export type Screen =
90
- | 'home'
91
- | 'user-list'
92
- | 'chat'
93
- | 'recent-chats'
94
- | 'tickets'
95
- | 'ticket-new'
96
- | 'ticket-detail'
97
- | 'block-list'
98
- | 'call';
143
+ export type Screen =
144
+ | 'home' | 'user-list' | 'chat' | 'recent-chats'
145
+ | 'tickets' | 'ticket-new' | 'ticket-detail'
146
+ | 'block-list' | 'call';
99
147
  export type UserListContext = 'support' | 'conversation';
100
148
  export type MessageType = 'text' | 'voice' | 'attachment' | 'emoji';
101
-
102
- /** Home status selector; persist via `presenceUpdateUrl` in production */
103
149
  export type PresenceStatus = 'ACTIVE' | 'AWAY' | 'DND';
104
150
 
105
- // ─── User ───────────────────────────────────────────────────────────────────
151
+ // ─── User ────────────────────────────────────────────────────────────────────
106
152
  export interface ChatUser {
107
- uid: string;
108
- name: string;
109
- email: string;
110
- mobile: string;
111
- project: string;
112
- type: UserType;
113
- avatar: string | null;
114
- status: OnlineStatus;
153
+ uid: string;
154
+ name: string;
155
+ email: string;
156
+ mobile: string;
157
+ project: string;
158
+ type: UserType;
159
+ avatar: string | null;
160
+ status: OnlineStatus;
115
161
  designation: string;
116
- /**
117
- * When `true` for the row matching the current viewer (`viewerUid` / `viewer.uid`),
118
- * the widget shows the spam/blocked screen (same as `widget.viewerBlocked`).
119
- */
120
162
  viewerBlocked?: boolean;
121
163
  }
122
164
 
123
- // ─── Message ────────────────────────────────────────────────────────────────
165
+ // ─── Message ─────────────────────────────────────────────────────────────────
124
166
  export interface ChatMessage {
125
- id: string;
126
- senderId: string;
127
- receiverId: string;
128
- text: string;
129
- timestamp: string;
130
- type: MessageType;
131
- status: 'sent' | 'delivered' | 'read';
167
+ id: string;
168
+ senderId: string;
169
+ receiverId: string;
170
+ text: string;
171
+ timestamp: string;
172
+ type: MessageType;
173
+ status: 'sent' | 'delivered' | 'read';
132
174
  attachmentName?: string;
133
175
  attachmentSize?: string;
134
- /** Blob URL for attachment download (local send) */
135
- attachmentUrl?: string;
136
- /** e.g. image/png — used for inline image preview in bubbles */
176
+ attachmentUrl?: string;
137
177
  attachmentMime?: string;
138
- voiceDuration?: number; // seconds
139
- /** Blob URL for in-bubble audio playback (local recording) */
140
- voiceUrl?: string;
178
+ voiceDuration?: number;
179
+ voiceUrl?: string;
141
180
  }
142
181
 
143
- // ─── Ticket ─────────────────────────────────────────────────────────────────
182
+ // ─── Ticket ──────────────────────────────────────────────────────────────────
144
183
  export interface Ticket {
145
- id: string;
146
- title: string;
184
+ id: string;
185
+ title: string;
147
186
  description: string;
148
- status: 'open' | 'in-progress' | 'resolved' | 'closed';
149
- priority: 'low' | 'medium' | 'high';
150
- createdAt: string;
151
- updatedAt: string;
152
- assignedTo: string | null;
187
+ status: 'open' | 'in-progress' | 'resolved' | 'closed';
188
+ priority: 'low' | 'medium' | 'high';
189
+ createdAt: string;
190
+ updatedAt: string;
191
+ assignedTo: string | null;
153
192
  }
154
193
 
155
194
  // ─── Recent Chat ─────────────────────────────────────────────────────────────
156
195
  export interface RecentChat {
157
- id: string;
158
- user: ChatUser;
196
+ id: string;
197
+ user: ChatUser;
159
198
  lastMessage: string;
160
- lastTime: string;
161
- unread: number;
162
- isPaused: boolean;
199
+ lastTime: string;
200
+ unread: number;
201
+ isPaused: boolean;
163
202
  }
164
203
 
165
- // ─── Call ───────────────────────────────────────────────────────────────────
204
+ // ─── Call ────────────────────────────────────────────────────────────────────
166
205
  export type CallState = 'idle' | 'calling' | 'connected' | 'ended';
167
-
168
206
  export interface CallSession {
169
- state: CallState;
170
- peer: ChatUser | null;
171
- startedAt: Date | null;
172
- isMuted: boolean;
207
+ state: CallState;
208
+ peer: ChatUser | null;
209
+ startedAt: Date | null;
210
+ isMuted: boolean;
173
211
  isCameraOn: boolean;
174
212
  }
175
213
 
176
214
  // ─── Local env config ────────────────────────────────────────────────────────
177
215
  export interface LocalEnvConfig {
178
- apiKey: string;
216
+ apiKey: string;
179
217
  widgetId: string;
218
+ socketUrl: string;
180
219
  }
181
220
 
182
- // ─── Theme prop (still overridable) ─────────────────────────────────────────
221
+ // ─── Theme prop ──────────────────────────────────────────────────────────────
183
222
  export interface ChatWidgetTheme {
184
- primaryColor?: string;
185
- fontFamily?: string;
186
- buttonColor?: string;
223
+ primaryColor?: string;
224
+ fontFamily?: string;
225
+ buttonColor?: string;
187
226
  buttonTextColor?: string;
188
- buttonLabel?: string;
189
- buttonPosition?: 'bottom-right' | 'bottom-left';
190
- borderRadius?: string;
227
+ buttonLabel?: string;
228
+ buttonPosition?: 'bottom-right' | 'bottom-left';
229
+ borderRadius?: string;
191
230
  }
192
231
 
193
- /**
194
- * Pass the logged-in user from your React app so the widget matches identity and UI (user vs developer).
195
- * Overrides `viewerUid`, `viewerName`, `viewerType` from remote `chatData.json` when provided.
196
- */
197
232
  export interface ChatWidgetViewer {
198
- uid: string;
199
- name: string;
200
- type: UserType;
201
- /** When set, directory lists only include users whose `ChatUser.project` equals this string (exact match). */
233
+ uid: string;
234
+ name: string;
235
+ type: UserType;
202
236
  projectId?: string;
203
237
  }
204
238
 
205
239
  export interface ChatWidgetProps {
206
- theme?: ChatWidgetTheme;
240
+ theme?: ChatWidgetTheme;
207
241
  viewer?: ChatWidgetViewer;
208
242
  }