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.
- package/dist/components/BlockList/index.d.ts +1 -0
- package/dist/components/BlockList/index.d.ts.map +1 -0
- package/dist/components/BlockList/index.js +55 -28
- package/dist/components/BlockList/index.js.map +1 -0
- package/dist/components/CallScreen/index.d.ts +1 -0
- package/dist/components/CallScreen/index.d.ts.map +1 -0
- package/dist/components/CallScreen/index.js +107 -39
- package/dist/components/CallScreen/index.js.map +1 -0
- package/dist/components/ChatScreen/index.d.ts +1 -0
- package/dist/components/ChatScreen/index.d.ts.map +1 -0
- package/dist/components/ChatScreen/index.js +493 -294
- package/dist/components/ChatScreen/index.js.map +1 -0
- package/dist/components/ChatWidget.d.ts +1 -0
- package/dist/components/ChatWidget.d.ts.map +1 -0
- package/dist/components/ChatWidget.js +499 -367
- package/dist/components/ChatWidget.js.map +1 -0
- package/dist/components/EmojiPicker/index.d.ts +1 -0
- package/dist/components/EmojiPicker/index.d.ts.map +1 -0
- package/dist/components/EmojiPicker/index.js +19 -7
- package/dist/components/EmojiPicker/index.js.map +1 -0
- package/dist/components/ErrorBoundary/index.d.ts +20 -0
- package/dist/components/ErrorBoundary/index.d.ts.map +1 -0
- package/dist/components/ErrorBoundary/index.js +76 -0
- package/dist/components/ErrorBoundary/index.js.map +1 -0
- package/dist/components/HomeScreen/index.d.ts +1 -0
- package/dist/components/HomeScreen/index.d.ts.map +1 -0
- package/dist/components/HomeScreen/index.js +236 -158
- package/dist/components/HomeScreen/index.js.map +1 -0
- package/dist/components/MaintenanceView/index.d.ts +1 -0
- package/dist/components/MaintenanceView/index.d.ts.map +1 -0
- package/dist/components/MaintenanceView/index.js +28 -12
- package/dist/components/MaintenanceView/index.js.map +1 -0
- package/dist/components/MiniCallBar/index.d.ts +1 -0
- package/dist/components/MiniCallBar/index.d.ts.map +1 -0
- package/dist/components/MiniCallBar/index.js +85 -37
- package/dist/components/MiniCallBar/index.js.map +1 -0
- package/dist/components/PermissionsGateScreen/index.d.ts +1 -0
- package/dist/components/PermissionsGateScreen/index.d.ts.map +1 -0
- package/dist/components/PermissionsGateScreen/index.js +82 -28
- package/dist/components/PermissionsGateScreen/index.js.map +1 -0
- package/dist/components/RecentChatsScreen/index.d.ts +1 -0
- package/dist/components/RecentChatsScreen/index.d.ts.map +1 -0
- package/dist/components/RecentChatsScreen/index.js +79 -19
- package/dist/components/RecentChatsScreen/index.js.map +1 -0
- package/dist/components/SlideNavMenu.d.ts +1 -0
- package/dist/components/SlideNavMenu.d.ts.map +1 -0
- package/dist/components/SlideNavMenu.js +82 -63
- package/dist/components/SlideNavMenu.js.map +1 -0
- package/dist/components/Tabs/BottomTabs.d.ts +1 -0
- package/dist/components/Tabs/BottomTabs.d.ts.map +1 -0
- package/dist/components/Tabs/BottomTabs.js +34 -19
- package/dist/components/Tabs/BottomTabs.js.map +1 -0
- package/dist/components/TicketDetailScreen/index.d.ts +1 -0
- package/dist/components/TicketDetailScreen/index.d.ts.map +1 -0
- package/dist/components/TicketDetailScreen/index.js +66 -27
- package/dist/components/TicketDetailScreen/index.js.map +1 -0
- package/dist/components/TicketFormScreen/index.d.ts +1 -0
- package/dist/components/TicketFormScreen/index.d.ts.map +1 -0
- package/dist/components/TicketFormScreen/index.js +99 -49
- package/dist/components/TicketFormScreen/index.js.map +1 -0
- package/dist/components/TicketScreen/index.d.ts +1 -0
- package/dist/components/TicketScreen/index.d.ts.map +1 -0
- package/dist/components/TicketScreen/index.js +95 -26
- package/dist/components/TicketScreen/index.js.map +1 -0
- package/dist/components/UserListScreen/index.d.ts +1 -0
- package/dist/components/UserListScreen/index.d.ts.map +1 -0
- package/dist/components/UserListScreen/index.js +127 -53
- package/dist/components/UserListScreen/index.js.map +1 -0
- package/dist/components/ViewerBlockedScreen/index.d.ts +1 -0
- package/dist/components/ViewerBlockedScreen/index.d.ts.map +1 -0
- package/dist/components/ViewerBlockedScreen/index.js +113 -61
- package/dist/components/ViewerBlockedScreen/index.js.map +1 -0
- package/dist/config/index.d.ts +7 -3
- package/dist/config/index.d.ts.map +1 -0
- package/dist/config/index.js +73 -22
- package/dist/config/index.js.map +1 -0
- package/dist/hooks/useChat.d.ts +9 -1
- package/dist/hooks/useChat.d.ts.map +1 -0
- package/dist/hooks/useChat.js +60 -18
- package/dist/hooks/useChat.js.map +1 -0
- package/dist/hooks/useRemoteConfig.d.ts +1 -0
- package/dist/hooks/useRemoteConfig.d.ts.map +1 -0
- package/dist/hooks/useRemoteConfig.js +22 -15
- package/dist/hooks/useRemoteConfig.js.map +1 -0
- package/dist/hooks/useSocket.d.ts +59 -0
- package/dist/hooks/useSocket.d.ts.map +1 -0
- package/dist/hooks/useSocket.js +203 -0
- package/dist/hooks/useSocket.js.map +1 -0
- package/dist/hooks/useWebRTC.d.ts +10 -2
- package/dist/hooks/useWebRTC.d.ts.map +1 -0
- package/dist/hooks/useWebRTC.js +101 -69
- package/dist/hooks/useWebRTC.js.map +1 -0
- package/dist/index.d.ts +7 -1
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +67 -21
- package/dist/index.js.map +1 -0
- package/dist/types/index.d.ts +129 -48
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +4 -1
- package/dist/types/index.js.map +1 -0
- package/dist/utils/chat.d.ts +1 -0
- package/dist/utils/chat.d.ts.map +1 -0
- package/dist/utils/chat.js +17 -7
- package/dist/utils/chat.js.map +1 -0
- package/dist/utils/fileName.d.ts +1 -0
- package/dist/utils/fileName.d.ts.map +1 -0
- package/dist/utils/fileName.js +5 -1
- package/dist/utils/fileName.js.map +1 -0
- package/dist/utils/messageSound.d.ts +1 -0
- package/dist/utils/messageSound.d.ts.map +1 -0
- package/dist/utils/messageSound.js +9 -3
- package/dist/utils/messageSound.js.map +1 -0
- package/dist/utils/presenceStatus.d.ts +1 -0
- package/dist/utils/presenceStatus.d.ts.map +1 -0
- package/dist/utils/presenceStatus.js +11 -4
- package/dist/utils/presenceStatus.js.map +1 -0
- package/dist/utils/privacyConsent.d.ts +1 -0
- package/dist/utils/privacyConsent.d.ts.map +1 -0
- package/dist/utils/privacyConsent.js +9 -3
- package/dist/utils/privacyConsent.js.map +1 -0
- package/dist/utils/reenableRequest.d.ts +1 -0
- package/dist/utils/reenableRequest.d.ts.map +1 -0
- package/dist/utils/reenableRequest.js +5 -1
- package/dist/utils/reenableRequest.js.map +1 -0
- package/dist/utils/theme.d.ts +1 -0
- package/dist/utils/theme.d.ts.map +1 -0
- package/dist/utils/theme.js +10 -4
- package/dist/utils/theme.js.map +1 -0
- package/dist/utils/widgetPermissions.d.ts +1 -0
- package/dist/utils/widgetPermissions.d.ts.map +1 -0
- package/dist/utils/widgetPermissions.js +13 -5
- package/dist/utils/widgetPermissions.js.map +1 -0
- package/dist/utils/widgetSession.d.ts +1 -0
- package/dist/utils/widgetSession.d.ts.map +1 -0
- package/dist/utils/widgetSession.js +9 -3
- package/dist/utils/widgetSession.js.map +1 -0
- package/package.json +8 -4
- package/src/components/ChatWidget.tsx +643 -622
- package/src/components/ErrorBoundary/index.tsx +62 -0
- package/src/config/index.ts +87 -26
- package/src/hooks/useChat.ts +59 -12
- package/src/hooks/useRemoteConfig.ts +8 -3
- package/src/hooks/useSocket.ts +249 -0
- package/src/hooks/useWebRTC.ts +99 -64
- package/src/index.ts +14 -3
- package/src/types/index.ts +177 -143
package/src/hooks/useWebRTC.ts
CHANGED
|
@@ -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:
|
|
12
|
-
peer:
|
|
13
|
-
startedAt:
|
|
14
|
-
isMuted:
|
|
20
|
+
state: 'idle',
|
|
21
|
+
peer: null,
|
|
22
|
+
startedAt: null,
|
|
23
|
+
isMuted: false,
|
|
15
24
|
isCameraOn: false,
|
|
16
25
|
});
|
|
17
26
|
|
|
18
|
-
const pcRef
|
|
19
|
-
const localStream
|
|
20
|
-
const localVideoRef
|
|
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()
|
|
44
|
+
updateSession({ state: 'connected', startedAt: new Date() });
|
|
45
45
|
};
|
|
46
46
|
|
|
47
47
|
pc.onicecandidate = e => {
|
|
48
|
-
if (e.candidate) {
|
|
49
|
-
|
|
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
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
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
|
-
|
|
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 =
|
|
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
|
-
|
|
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
|
|
103
|
-
localStream.current.getAudioTracks().forEach(t => { t.enabled =
|
|
104
|
-
updateSession({ 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
|
|
110
|
-
localStream.current.getVideoTracks().forEach(t => { t.enabled =
|
|
111
|
-
updateSession({ isCameraOn:
|
|
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
|
-
|
|
123
|
-
|
|
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
|
-
|
|
33
|
-
|
|
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';
|
package/src/types/index.ts
CHANGED
|
@@ -1,208 +1,242 @@
|
|
|
1
|
-
// ───
|
|
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:
|
|
4
|
-
apiKey:
|
|
5
|
-
status:
|
|
6
|
-
chatType:
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
branch?:
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
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
|
-
|
|
37
|
-
|
|
38
|
-
|
|
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:
|
|
44
|
-
allowEmoji:
|
|
45
|
-
allowWebCall:
|
|
46
|
-
maxEmojiCount:
|
|
111
|
+
allowAttachment: boolean;
|
|
112
|
+
allowEmoji: boolean;
|
|
113
|
+
allowWebCall: boolean;
|
|
114
|
+
maxEmojiCount: number;
|
|
47
115
|
allowTranscriptDownload: boolean;
|
|
48
|
-
allowReport:
|
|
49
|
-
allowBlock:
|
|
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:
|
|
76
|
-
developers:
|
|
77
|
-
users:
|
|
78
|
-
sampleChats:
|
|
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
|
-
| '
|
|
92
|
-
| '
|
|
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:
|
|
108
|
-
name:
|
|
109
|
-
email:
|
|
110
|
-
mobile:
|
|
111
|
-
project:
|
|
112
|
-
type:
|
|
113
|
-
avatar:
|
|
114
|
-
status:
|
|
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:
|
|
126
|
-
senderId:
|
|
127
|
-
receiverId:
|
|
128
|
-
text:
|
|
129
|
-
timestamp:
|
|
130
|
-
type:
|
|
131
|
-
status:
|
|
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
|
-
|
|
135
|
-
attachmentUrl?: string;
|
|
136
|
-
/** e.g. image/png — used for inline image preview in bubbles */
|
|
176
|
+
attachmentUrl?: string;
|
|
137
177
|
attachmentMime?: string;
|
|
138
|
-
voiceDuration?:
|
|
139
|
-
|
|
140
|
-
voiceUrl?: string;
|
|
178
|
+
voiceDuration?: number;
|
|
179
|
+
voiceUrl?: string;
|
|
141
180
|
}
|
|
142
181
|
|
|
143
|
-
// ─── Ticket
|
|
182
|
+
// ─── Ticket ──────────────────────────────────────────────────────────────────
|
|
144
183
|
export interface Ticket {
|
|
145
|
-
id:
|
|
146
|
-
title:
|
|
184
|
+
id: string;
|
|
185
|
+
title: string;
|
|
147
186
|
description: string;
|
|
148
|
-
status:
|
|
149
|
-
priority:
|
|
150
|
-
createdAt:
|
|
151
|
-
updatedAt:
|
|
152
|
-
assignedTo:
|
|
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:
|
|
158
|
-
user:
|
|
196
|
+
id: string;
|
|
197
|
+
user: ChatUser;
|
|
159
198
|
lastMessage: string;
|
|
160
|
-
lastTime:
|
|
161
|
-
unread:
|
|
162
|
-
isPaused:
|
|
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:
|
|
170
|
-
peer:
|
|
171
|
-
startedAt:
|
|
172
|
-
isMuted:
|
|
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:
|
|
216
|
+
apiKey: string;
|
|
179
217
|
widgetId: string;
|
|
218
|
+
socketUrl: string;
|
|
180
219
|
}
|
|
181
220
|
|
|
182
|
-
// ─── Theme prop
|
|
221
|
+
// ─── Theme prop ──────────────────────────────────────────────────────────────
|
|
183
222
|
export interface ChatWidgetTheme {
|
|
184
|
-
primaryColor?:
|
|
185
|
-
fontFamily?:
|
|
186
|
-
buttonColor?:
|
|
223
|
+
primaryColor?: string;
|
|
224
|
+
fontFamily?: string;
|
|
225
|
+
buttonColor?: string;
|
|
187
226
|
buttonTextColor?: string;
|
|
188
|
-
buttonLabel?:
|
|
189
|
-
buttonPosition?:
|
|
190
|
-
borderRadius?:
|
|
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:
|
|
199
|
-
name:
|
|
200
|
-
type:
|
|
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?:
|
|
240
|
+
theme?: ChatWidgetTheme;
|
|
207
241
|
viewer?: ChatWidgetViewer;
|
|
208
242
|
}
|