ajaxter-chat 1.0.3 → 3.0.1
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/README.md +124 -241
- package/dist/components/BlockList/index.d.ts +10 -0
- package/dist/components/BlockList/index.js +33 -0
- package/dist/components/CallScreen/index.d.ts +13 -0
- package/dist/components/CallScreen/index.js +48 -0
- package/dist/components/ChatScreen/index.d.ts +19 -0
- package/dist/components/ChatScreen/index.js +168 -0
- package/dist/components/ChatWidget.d.ts +0 -24
- package/dist/components/ChatWidget.js +228 -43
- package/dist/components/EmojiPicker/index.d.ts +8 -0
- package/dist/components/EmojiPicker/index.js +18 -0
- package/dist/components/HomeScreen/index.d.ts +8 -0
- package/dist/components/HomeScreen/index.js +55 -0
- package/dist/components/MaintenanceView/index.d.ts +0 -1
- package/dist/components/MaintenanceView/index.js +13 -52
- package/dist/components/RecentChatsScreen/index.d.ts +17 -0
- package/dist/components/RecentChatsScreen/index.js +8 -0
- package/dist/components/Tabs/BottomTabs.d.ts +10 -0
- package/dist/components/Tabs/BottomTabs.js +34 -0
- package/dist/components/TicketScreen/index.d.ts +9 -0
- package/dist/components/TicketScreen/index.js +54 -0
- package/dist/components/UserListScreen/index.d.ts +11 -0
- package/dist/components/UserListScreen/index.js +35 -0
- package/dist/config/index.d.ts +3 -16
- package/dist/config/index.js +20 -103
- package/dist/hooks/useChat.d.ts +10 -9
- package/dist/hooks/useChat.js +22 -40
- package/dist/hooks/useRemoteConfig.d.ts +6 -0
- package/dist/hooks/useRemoteConfig.js +22 -0
- package/dist/hooks/useWebRTC.d.ts +11 -0
- package/dist/hooks/useWebRTC.js +112 -0
- package/dist/index.d.ts +16 -11
- package/dist/index.js +15 -16
- package/dist/types/index.d.ts +66 -38
- package/dist/utils/chat.d.ts +13 -0
- package/dist/utils/chat.js +62 -0
- package/dist/utils/theme.d.ts +3 -2
- package/dist/utils/theme.js +13 -21
- package/package.json +10 -20
- package/public/chatData.json +162 -0
- package/src/components/BlockList/index.tsx +94 -0
- package/src/components/CallScreen/index.tsx +144 -0
- package/src/components/ChatScreen/index.tsx +469 -0
- package/src/components/ChatWidget.tsx +471 -0
- package/src/components/EmojiPicker/index.tsx +48 -0
- package/src/components/HomeScreen/index.tsx +106 -0
- package/src/components/MaintenanceView/index.tsx +38 -0
- package/src/components/RecentChatsScreen/index.tsx +63 -0
- package/src/components/Tabs/BottomTabs.tsx +90 -0
- package/src/components/TicketScreen/index.tsx +124 -0
- package/src/components/UserListScreen/index.tsx +103 -0
- package/src/config/index.ts +40 -0
- package/src/hooks/useChat.ts +48 -0
- package/src/hooks/useRemoteConfig.ts +20 -0
- package/src/hooks/useWebRTC.ts +130 -0
- package/src/index.ts +29 -0
- package/src/types/index.ts +127 -0
- package/src/utils/chat.ts +70 -0
- package/src/utils/theme.ts +27 -0
- package/dist/components/BottomNav/index.d.ts +0 -10
- package/dist/components/BottomNav/index.js +0 -32
- package/dist/components/ChatBox/index.d.ts +0 -15
- package/dist/components/ChatBox/index.js +0 -228
- package/dist/components/ChatButton/index.d.ts +0 -9
- package/dist/components/ChatButton/index.js +0 -17
- package/dist/components/ChatWindow/index.d.ts +0 -10
- package/dist/components/ChatWindow/index.js +0 -286
- package/dist/components/HomeView/index.d.ts +0 -12
- package/dist/components/HomeView/index.js +0 -51
- package/dist/components/UserList/index.d.ts +0 -13
- package/dist/components/UserList/index.js +0 -136
- package/dist/hooks/useUsers.d.ts +0 -14
- package/dist/hooks/useUsers.js +0 -32
- package/dist/services/userService.d.ts +0 -7
- package/dist/services/userService.js +0 -18
|
@@ -1,53 +1,14 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
export const MaintenanceView = ({
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
height: '72px',
|
|
16
|
-
borderRadius: '50%',
|
|
17
|
-
backgroundColor: `${primaryColor}15`,
|
|
18
|
-
display: 'flex',
|
|
19
|
-
alignItems: 'center',
|
|
20
|
-
justifyContent: 'center',
|
|
21
|
-
marginBottom: '8px',
|
|
22
|
-
}, children: _jsx("svg", { width: "32", height: "32", viewBox: "0 0 24 24", fill: "none", children: _jsx("path", { d: "M12 9v4M12 17h.01M10.29 3.86L1.82 18a2 2 0 001.71 3h16.94a2 2 0 001.71-3L13.71 3.86a2 2 0 00-3.42 0z", stroke: primaryColor, strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }) }) }), _jsx("h3", { style: {
|
|
23
|
-
margin: 0,
|
|
24
|
-
fontSize: '17px',
|
|
25
|
-
fontWeight: 700,
|
|
26
|
-
color: '#1a1a2e',
|
|
27
|
-
letterSpacing: '-0.02em',
|
|
28
|
-
}, children: "Under Maintenance" }), _jsx("p", { style: {
|
|
29
|
-
margin: 0,
|
|
30
|
-
fontSize: '14px',
|
|
31
|
-
color: '#888',
|
|
32
|
-
lineHeight: 1.6,
|
|
33
|
-
maxWidth: '220px',
|
|
34
|
-
}, children: "Chat is under maintenance. We'll be back shortly. Thank you for your patience!" }), _jsxs("span", { style: {
|
|
35
|
-
display: 'inline-flex',
|
|
36
|
-
alignItems: 'center',
|
|
37
|
-
gap: '6px',
|
|
38
|
-
padding: '6px 14px',
|
|
39
|
-
borderRadius: '20px',
|
|
40
|
-
backgroundColor: '#fff3cd',
|
|
41
|
-
color: '#856404',
|
|
42
|
-
fontSize: '12px',
|
|
43
|
-
fontWeight: 600,
|
|
44
|
-
marginTop: '8px',
|
|
45
|
-
border: '1px solid #ffc10730',
|
|
46
|
-
}, children: [_jsx("span", { style: {
|
|
47
|
-
width: '6px',
|
|
48
|
-
height: '6px',
|
|
49
|
-
borderRadius: '50%',
|
|
50
|
-
backgroundColor: '#ffc107',
|
|
51
|
-
display: 'inline-block',
|
|
52
|
-
} }), "Temporarily Unavailable"] })] }));
|
|
53
|
-
};
|
|
2
|
+
export const MaintenanceView = ({ primaryColor }) => (_jsxs("div", { style: {
|
|
3
|
+
display: 'flex', flexDirection: 'column', alignItems: 'center',
|
|
4
|
+
justifyContent: 'center', height: '100%', padding: '32px', textAlign: 'center', gap: 16,
|
|
5
|
+
}, children: [_jsx("div", { style: {
|
|
6
|
+
width: 72, height: 72, borderRadius: '50%',
|
|
7
|
+
backgroundColor: `${primaryColor}15`,
|
|
8
|
+
display: 'flex', alignItems: 'center', justifyContent: 'center',
|
|
9
|
+
}, children: _jsx("svg", { width: "32", height: "32", viewBox: "0 0 24 24", fill: "none", children: _jsx("path", { d: "M12 9v4M12 17h.01M10.29 3.86L1.82 18a2 2 0 001.71 3h16.94a2 2 0 001.71-3L13.71 3.86a2 2 0 00-3.42 0z", stroke: primaryColor, strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }) }) }), _jsx("h3", { style: { margin: 0, fontSize: 17, fontWeight: 800, color: '#1a2332', letterSpacing: '-0.02em' }, children: "Under Maintenance" }), _jsx("p", { style: { margin: 0, fontSize: 14, color: '#7b8fa1', lineHeight: 1.6, maxWidth: 220 }, children: "Chat is under maintenance. We'll be back shortly!" }), _jsxs("span", { style: {
|
|
10
|
+
display: 'inline-flex', alignItems: 'center', gap: 6,
|
|
11
|
+
padding: '6px 14px', borderRadius: 20,
|
|
12
|
+
backgroundColor: '#fff3cd', color: '#856404',
|
|
13
|
+
fontSize: 12, fontWeight: 700, border: '1px solid #ffc10730',
|
|
14
|
+
}, children: [_jsx("span", { style: { width: 6, height: 6, borderRadius: '50%', backgroundColor: '#ffc107', display: 'inline-block' } }), "Temporarily Unavailable"] })] }));
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { ChatUser, WidgetConfig } from '../../types';
|
|
3
|
+
interface RecentChat {
|
|
4
|
+
id: string;
|
|
5
|
+
user: ChatUser;
|
|
6
|
+
lastMessage: string;
|
|
7
|
+
lastTime: string;
|
|
8
|
+
unread: number;
|
|
9
|
+
isPaused: boolean;
|
|
10
|
+
}
|
|
11
|
+
interface RecentChatsScreenProps {
|
|
12
|
+
chats: RecentChat[];
|
|
13
|
+
config: WidgetConfig;
|
|
14
|
+
onSelectChat: (user: ChatUser) => void;
|
|
15
|
+
}
|
|
16
|
+
export declare const RecentChatsScreen: React.FC<RecentChatsScreenProps>;
|
|
17
|
+
export {};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { avatarColor, initials, formatTime } from '../../utils/chat';
|
|
3
|
+
export const RecentChatsScreen = ({ chats, config, onSelectChat }) => (_jsxs("div", { style: { display: 'flex', flexDirection: 'column', height: '100%' }, children: [_jsxs("div", { style: { background: `linear-gradient(135deg,${config.primaryColor},${config.primaryColor}cc)`, padding: '18px 18px 22px', flexShrink: 0 }, children: [_jsx("h2", { style: { margin: 0, fontSize: 20, fontWeight: 800, color: '#fff', letterSpacing: '-0.02em' }, children: "Recent Chats" }), _jsx("p", { style: { margin: '3px 0 0', fontSize: 12, color: 'rgba(255,255,255,0.8)' }, children: "Your conversation history" })] }), _jsx("div", { style: { flex: 1, overflowY: 'auto' }, children: chats.length === 0 ? (_jsxs("div", { style: { padding: '50px 24px', textAlign: 'center' }, children: [_jsx("div", { style: { fontSize: 36, marginBottom: 10 }, children: "\uD83D\uDCAC" }), _jsx("div", { style: { fontWeight: 700, color: '#1a2332', marginBottom: 6 }, children: "No chats yet" }), _jsx("div", { style: { fontSize: 13, color: '#7b8fa1' }, children: "Start a conversation from home" })] })) : chats.map((chat, i) => (_jsxs("button", { onClick: () => onSelectChat(chat.user), style: {
|
|
4
|
+
width: '100%', padding: '13px 16px', display: 'flex', alignItems: 'center', gap: 13,
|
|
5
|
+
background: 'transparent', border: 'none', borderBottom: '1px solid #f0f2f5',
|
|
6
|
+
cursor: 'pointer', textAlign: 'left', animation: `cw-fadeUp 0.28s ease both`, animationDelay: `${i * 0.05}s`,
|
|
7
|
+
transition: 'background 0.14s',
|
|
8
|
+
}, onMouseEnter: e => e.currentTarget.style.background = '#f8faff', onMouseLeave: e => e.currentTarget.style.background = 'transparent', children: [_jsxs("div", { style: { position: 'relative', flexShrink: 0 }, children: [_jsx("div", { style: { width: 46, height: 46, borderRadius: '50%', backgroundColor: avatarColor(chat.user.name), display: 'flex', alignItems: 'center', justifyContent: 'center', color: '#fff', fontWeight: 700, fontSize: 15 }, children: initials(chat.user.name) }), chat.unread > 0 && (_jsx("span", { style: { position: 'absolute', top: -2, right: -2, width: 18, height: 18, borderRadius: '50%', background: '#ef4444', color: '#fff', fontSize: 10, fontWeight: 700, display: 'flex', alignItems: 'center', justifyContent: 'center', border: '2px solid #fff' }, children: chat.unread }))] }), _jsxs("div", { style: { flex: 1, minWidth: 0 }, children: [_jsxs("div", { style: { display: 'flex', justifyContent: 'space-between', marginBottom: 3 }, children: [_jsx("span", { style: { fontWeight: 700, fontSize: 14, color: '#1a2332' }, children: chat.user.name }), _jsx("span", { style: { fontSize: 11, color: '#b0bec5' }, children: formatTime(chat.lastTime) })] }), _jsxs("div", { style: { fontSize: 13, color: '#7b8fa1', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', display: 'flex', alignItems: 'center', gap: 5 }, children: [chat.isPaused && _jsx("span", { style: { fontSize: 10, background: '#fef3c7', color: '#92400e', padding: '1px 5px', borderRadius: 4, fontWeight: 700 }, children: "PAUSED" }), chat.lastMessage] })] })] }, chat.id))) })] }));
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { BottomTab } from '../../types';
|
|
3
|
+
interface BottomTabsProps {
|
|
4
|
+
active: BottomTab;
|
|
5
|
+
onChange: (tab: BottomTab) => void;
|
|
6
|
+
primaryColor: string;
|
|
7
|
+
onBlockList: () => void;
|
|
8
|
+
}
|
|
9
|
+
export declare const BottomTabs: React.FC<BottomTabsProps>;
|
|
10
|
+
export {};
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
export const BottomTabs = ({ active, onChange, primaryColor, onBlockList }) => {
|
|
3
|
+
const tabs = [
|
|
4
|
+
{ key: 'home', label: 'Home', Icon: HomeIcon },
|
|
5
|
+
{ key: 'chats', label: 'Chats', Icon: ChatsIcon },
|
|
6
|
+
{ key: 'tickets', label: 'Tickets', Icon: TicketsIcon },
|
|
7
|
+
];
|
|
8
|
+
return (_jsxs("div", { style: {
|
|
9
|
+
display: 'flex', borderTop: '1px solid #eef0f5',
|
|
10
|
+
backgroundColor: '#fff', flexShrink: 0,
|
|
11
|
+
}, children: [tabs.map(tab => {
|
|
12
|
+
const isActive = active === tab.key;
|
|
13
|
+
return (_jsxs("button", { onClick: () => onChange(tab.key), style: {
|
|
14
|
+
flex: 1, padding: '10px 0 8px',
|
|
15
|
+
display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 3,
|
|
16
|
+
background: 'transparent', border: 'none', cursor: 'pointer',
|
|
17
|
+
fontSize: '10px', fontWeight: isActive ? 700 : 500,
|
|
18
|
+
color: isActive ? primaryColor : '#9aa3af',
|
|
19
|
+
borderTop: isActive ? `2px solid ${primaryColor}` : '2px solid transparent',
|
|
20
|
+
transition: 'color 0.15s',
|
|
21
|
+
fontFamily: 'inherit',
|
|
22
|
+
}, children: [_jsx(tab.Icon, { a: isActive, c: isActive ? primaryColor : '#b0bec5' }), tab.label] }, tab.key));
|
|
23
|
+
}), _jsxs("button", { onClick: onBlockList, style: {
|
|
24
|
+
padding: '10px 14px 8px',
|
|
25
|
+
display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 3,
|
|
26
|
+
background: 'transparent', border: 'none', cursor: 'pointer',
|
|
27
|
+
fontSize: '10px', fontWeight: 500, color: '#9aa3af',
|
|
28
|
+
borderTop: '2px solid transparent',
|
|
29
|
+
transition: 'color 0.15s', fontFamily: 'inherit',
|
|
30
|
+
}, title: "Block List", children: [_jsxs("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", children: [_jsx("circle", { cx: "12", cy: "12", r: "10", stroke: "#b0bec5", strokeWidth: "1.8" }), _jsx("line", { x1: "4.93", y1: "4.93", x2: "19.07", y2: "19.07", stroke: "#b0bec5", strokeWidth: "1.8", strokeLinecap: "round" })] }), "Blocked"] })] }));
|
|
31
|
+
};
|
|
32
|
+
const HomeIcon = ({ a, c }) => (_jsxs("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", children: [_jsx("path", { d: "M3 9.5L12 3l9 6.5V20a1 1 0 01-1 1H4a1 1 0 01-1-1V9.5z", stroke: c, strokeWidth: a ? 2.2 : 1.8, fill: a ? `${c}20` : 'none', strokeLinecap: "round", strokeLinejoin: "round" }), _jsx("path", { d: "M9 21V12h6v9", stroke: c, strokeWidth: a ? 2.2 : 1.8, strokeLinecap: "round", strokeLinejoin: "round" })] }));
|
|
33
|
+
const ChatsIcon = ({ a, c }) => (_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: c, strokeWidth: a ? 2.2 : 1.8, fill: a ? `${c}20` : 'none', strokeLinecap: "round", strokeLinejoin: "round" }) }));
|
|
34
|
+
const TicketsIcon = ({ a, c }) => (_jsxs("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", children: [_jsx("path", { d: "M15 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V9l-4-4z", stroke: c, strokeWidth: a ? 2.2 : 1.8, fill: a ? `${c}20` : 'none', strokeLinecap: "round", strokeLinejoin: "round" }), _jsx("path", { d: "M15 5v4h4M9 13h6M9 17h4", stroke: c, strokeWidth: a ? 2.2 : 1.8, strokeLinecap: "round" })] }));
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { Ticket, WidgetConfig } from '../../types';
|
|
3
|
+
interface TicketScreenProps {
|
|
4
|
+
tickets: Ticket[];
|
|
5
|
+
config: WidgetConfig;
|
|
6
|
+
onRaiseTicket: (title: string, desc: string, priority: Ticket['priority']) => void;
|
|
7
|
+
}
|
|
8
|
+
export declare const TicketScreen: React.FC<TicketScreenProps>;
|
|
9
|
+
export {};
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useState } from 'react';
|
|
3
|
+
export const TicketScreen = ({ tickets, config, onRaiseTicket }) => {
|
|
4
|
+
const [showForm, setShowForm] = useState(false);
|
|
5
|
+
const [title, setTitle] = useState('');
|
|
6
|
+
const [desc, setDesc] = useState('');
|
|
7
|
+
const [priority, setPriority] = useState('medium');
|
|
8
|
+
const handleSubmit = () => {
|
|
9
|
+
if (!title.trim())
|
|
10
|
+
return;
|
|
11
|
+
onRaiseTicket(title.trim(), desc.trim(), priority);
|
|
12
|
+
setTitle('');
|
|
13
|
+
setDesc('');
|
|
14
|
+
setPriority('medium');
|
|
15
|
+
setShowForm(false);
|
|
16
|
+
};
|
|
17
|
+
const sm = {
|
|
18
|
+
open: { label: 'Open', bg: `${config.primaryColor}14`, color: config.primaryColor },
|
|
19
|
+
'in-progress': { label: 'In Progress', bg: '#fef3c7', color: '#d97706' },
|
|
20
|
+
resolved: { label: 'Resolved', bg: '#f0fdf4', color: '#16a34a' },
|
|
21
|
+
closed: { label: 'Closed', bg: '#f3f4f6', color: '#6b7280' },
|
|
22
|
+
};
|
|
23
|
+
const pm = {
|
|
24
|
+
low: { label: 'Low', color: '#6b7280' },
|
|
25
|
+
medium: { label: 'Medium', color: '#d97706' },
|
|
26
|
+
high: { label: 'High', color: '#ef4444' },
|
|
27
|
+
};
|
|
28
|
+
return (_jsxs("div", { style: { display: 'flex', flexDirection: 'column', height: '100%' }, children: [_jsx("div", { style: {
|
|
29
|
+
background: `linear-gradient(135deg,${config.primaryColor},${config.primaryColor}cc)`,
|
|
30
|
+
padding: '18px 18px 22px', flexShrink: 0, position: 'relative',
|
|
31
|
+
}, children: _jsxs("div", { style: { display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start' }, children: [_jsxs("div", { children: [_jsx("h2", { style: { margin: 0, fontSize: 20, fontWeight: 800, color: '#fff', letterSpacing: '-0.02em' }, children: "Tickets" }), _jsxs("p", { style: { margin: '3px 0 0', fontSize: 12, color: 'rgba(255,255,255,0.8)' }, children: [tickets.length, " ticket", tickets.length !== 1 ? 's' : '', " raised"] })] }), _jsx("button", { onClick: () => setShowForm(v => !v), style: {
|
|
32
|
+
background: 'rgba(255,255,255,0.22)', border: 'none', borderRadius: 20,
|
|
33
|
+
padding: '7px 14px', color: '#fff', fontWeight: 700, fontSize: 13,
|
|
34
|
+
cursor: 'pointer', display: 'flex', alignItems: 'center', gap: 5,
|
|
35
|
+
}, children: showForm ? '✕ Cancel' : '+ New' })] }) }), showForm && (_jsxs("div", { style: { padding: '16px', borderBottom: '1px solid #eef0f5', background: '#fafcff', flexShrink: 0, animation: 'cw-fadeUp 0.2s ease' }, children: [_jsx("input", { placeholder: "Title *", value: title, onChange: e => setTitle(e.target.value), style: inputStyle(config.primaryColor), onFocus: e => (e.target.style.borderColor = config.primaryColor), onBlur: e => (e.target.style.borderColor = '#e5e7eb') }), _jsx("textarea", { placeholder: "Describe the issue\u2026", value: desc, onChange: e => setDesc(e.target.value), rows: 3, style: Object.assign(Object.assign({}, inputStyle(config.primaryColor)), { resize: 'none', marginTop: 8 }), onFocus: e => (e.target.style.borderColor = config.primaryColor), onBlur: e => (e.target.style.borderColor = '#e5e7eb') }), _jsx("div", { style: { display: 'flex', gap: 8, marginTop: 8, marginBottom: 12 }, children: ['low', 'medium', 'high'].map(p => (_jsx("button", { onClick: () => setPriority(p), style: {
|
|
36
|
+
flex: 1, padding: '6px', border: `1.5px solid ${priority === p ? pm[p].color : '#e5e7eb'}`,
|
|
37
|
+
borderRadius: 8, background: priority === p ? pm[p].color + '15' : '#fff',
|
|
38
|
+
color: pm[p].color, fontWeight: 700, fontSize: 12, cursor: 'pointer', textTransform: 'capitalize',
|
|
39
|
+
transition: 'all 0.15s',
|
|
40
|
+
}, children: pm[p].label }, p))) }), _jsx("button", { onClick: handleSubmit, disabled: !title.trim(), style: {
|
|
41
|
+
width: '100%', padding: '10px', borderRadius: 10, border: 'none',
|
|
42
|
+
background: title.trim() ? config.primaryColor : '#e5e7eb',
|
|
43
|
+
color: title.trim() ? '#fff' : '#9ca3af',
|
|
44
|
+
fontWeight: 700, fontSize: 14, cursor: title.trim() ? 'pointer' : 'not-allowed',
|
|
45
|
+
}, children: "Submit Ticket" })] })), _jsx("div", { style: { flex: 1, overflowY: 'auto' }, children: tickets.length === 0 ? (_jsxs("div", { style: { padding: '50px 24px', textAlign: 'center' }, children: [_jsx("div", { style: { fontSize: 36, marginBottom: 10 }, children: "\uD83C\uDFAB" }), _jsx("div", { style: { fontWeight: 700, color: '#1a2332', marginBottom: 6 }, children: "No tickets yet" }), _jsx("div", { style: { fontSize: 13, color: '#7b8fa1' }, children: "Raise a ticket for major issues" })] })) : tickets.map((t, i) => (_jsxs("div", { style: { padding: '14px 16px', borderBottom: '1px solid #f0f2f5', animation: `cw-fadeUp 0.3s ease both`, animationDelay: `${i * 0.05}s` }, children: [_jsxs("div", { style: { display: 'flex', alignItems: 'flex-start', justifyContent: 'space-between', marginBottom: 5 }, children: [_jsx("span", { style: { fontWeight: 700, fontSize: 14, color: '#1a2332', flex: 1, paddingRight: 10 }, children: t.title }), _jsx("span", { style: { fontSize: 10, fontWeight: 700, padding: '3px 9px', borderRadius: 20, backgroundColor: sm[t.status].bg, color: sm[t.status].color, whiteSpace: 'nowrap', textTransform: 'uppercase', letterSpacing: '0.04em', flexShrink: 0 }, children: sm[t.status].label })] }), t.description && _jsx("p", { style: { margin: '0 0 7px', fontSize: 13, color: '#7b8fa1', lineHeight: 1.5 }, children: t.description }), _jsxs("div", { style: { display: 'flex', gap: 10, fontSize: 11, color: '#b0bec5' }, children: [_jsxs("span", { style: { color: pm[t.priority].color, fontWeight: 700 }, children: ["\u25CF ", pm[t.priority].label] }), _jsxs("span", { children: ["#", t.id] }), _jsx("span", { children: new Date(t.createdAt).toLocaleDateString([], { month: 'short', day: 'numeric' }) })] })] }, t.id))) })] }));
|
|
46
|
+
};
|
|
47
|
+
function inputStyle(primaryColor) {
|
|
48
|
+
return {
|
|
49
|
+
width: '100%', padding: '9px 13px', borderRadius: 10,
|
|
50
|
+
border: '1.5px solid #e5e7eb', outline: 'none',
|
|
51
|
+
fontSize: 14, color: '#1a2332', boxSizing: 'border-box',
|
|
52
|
+
fontFamily: 'inherit', transition: 'border-color 0.2s',
|
|
53
|
+
};
|
|
54
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { ChatUser, UserListContext } from '../../types';
|
|
3
|
+
interface UserListScreenProps {
|
|
4
|
+
context: UserListContext;
|
|
5
|
+
users: ChatUser[];
|
|
6
|
+
primaryColor: string;
|
|
7
|
+
onBack: () => void;
|
|
8
|
+
onSelectUser: (user: ChatUser) => void;
|
|
9
|
+
}
|
|
10
|
+
export declare const UserListScreen: React.FC<UserListScreenProps>;
|
|
11
|
+
export {};
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { avatarColor, initials } from '../../utils/chat';
|
|
3
|
+
export const UserListScreen = ({ context, users, primaryColor, onBack, onSelectUser, }) => {
|
|
4
|
+
const title = context === 'support' ? 'Need Support' : 'New Conversation';
|
|
5
|
+
const subtitle = context === 'support' ? 'Choose a support agent' : 'Choose a colleague';
|
|
6
|
+
return (_jsxs("div", { style: { display: 'flex', flexDirection: 'column', height: '100%', animation: 'cw-slideIn 0.22s ease' }, children: [_jsxs("div", { style: { background: `linear-gradient(135deg,${primaryColor},${primaryColor}cc)`, padding: '14px 18px', display: 'flex', alignItems: 'center', gap: 12, flexShrink: 0 }, children: [_jsx(BackBtn, { onClick: onBack }), _jsxs("div", { children: [_jsx("div", { style: { fontWeight: 700, fontSize: 16, color: '#fff' }, children: title }), _jsx("div", { style: { fontSize: 12, color: 'rgba(255,255,255,0.8)' }, children: subtitle })] })] }), _jsx("div", { style: { flex: 1, overflowY: 'auto' }, children: users.length === 0 ? (_jsx(Empty, {})) : users.map((u, i) => (_jsxs("button", { onClick: () => onSelectUser(u), style: {
|
|
7
|
+
width: '100%', padding: '13px 18px', display: 'flex',
|
|
8
|
+
alignItems: 'center', gap: 13, background: 'transparent',
|
|
9
|
+
border: 'none', borderBottom: '1px solid #f0f2f5',
|
|
10
|
+
cursor: 'pointer', textAlign: 'left',
|
|
11
|
+
animation: `cw-fadeUp 0.28s ease both`, animationDelay: `${i * 0.05}s`,
|
|
12
|
+
transition: 'background 0.14s',
|
|
13
|
+
}, onMouseEnter: e => e.currentTarget.style.background = '#f8faff', onMouseLeave: e => e.currentTarget.style.background = 'transparent', children: [_jsxs("div", { style: { position: 'relative', flexShrink: 0 }, children: [_jsx("div", { style: {
|
|
14
|
+
width: 44, height: 44, borderRadius: '50%',
|
|
15
|
+
backgroundColor: avatarColor(u.name),
|
|
16
|
+
display: 'flex', alignItems: 'center', justifyContent: 'center',
|
|
17
|
+
color: '#fff', fontWeight: 700, fontSize: 14,
|
|
18
|
+
}, children: initials(u.name) }), _jsx("span", { style: {
|
|
19
|
+
position: 'absolute', bottom: 1, right: 1,
|
|
20
|
+
width: 11, height: 11, borderRadius: '50%', border: '2px solid #fff',
|
|
21
|
+
backgroundColor: u.status === 'online' ? '#22c55e' : u.status === 'away' ? '#f59e0b' : '#d1d5db',
|
|
22
|
+
} })] }), _jsxs("div", { style: { flex: 1, minWidth: 0 }, children: [_jsx("div", { style: { fontWeight: 700, fontSize: 14, color: '#1a2332', marginBottom: 2 }, children: u.name }), _jsxs("div", { style: { fontSize: 12, color: '#7b8fa1', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }, children: [u.designation, " \u00B7 ", u.project] })] }), _jsx("span", { style: {
|
|
23
|
+
fontSize: 10, fontWeight: 700, padding: '3px 9px', borderRadius: 20,
|
|
24
|
+
textTransform: 'uppercase', letterSpacing: '0.05em', flexShrink: 0,
|
|
25
|
+
background: u.type === 'developer' ? `${primaryColor}15` : '#f0fdf4',
|
|
26
|
+
color: u.type === 'developer' ? primaryColor : '#16a34a',
|
|
27
|
+
border: `1px solid ${u.type === 'developer' ? primaryColor + '30' : '#16a34a30'}`,
|
|
28
|
+
}, children: u.type === 'developer' ? 'Dev' : 'User' })] }, u.uid))) })] }));
|
|
29
|
+
};
|
|
30
|
+
const BackBtn = ({ onClick }) => (_jsx("button", { onClick: onClick, style: {
|
|
31
|
+
background: 'rgba(255,255,255,0.22)', border: 'none', borderRadius: '50%',
|
|
32
|
+
width: 32, height: 32, display: 'flex', alignItems: 'center', justifyContent: 'center',
|
|
33
|
+
cursor: 'pointer', flexShrink: 0,
|
|
34
|
+
}, children: _jsx("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", children: _jsx("path", { d: "M19 12H5M5 12L12 19M5 12L12 5", stroke: "#fff", strokeWidth: "2.2", strokeLinecap: "round", strokeLinejoin: "round" }) }) }));
|
|
35
|
+
const Empty = () => (_jsxs("div", { style: { padding: '50px 24px', textAlign: 'center' }, children: [_jsx("div", { style: { fontSize: 36, marginBottom: 10 }, children: "\uD83D\uDC65" }), _jsx("div", { style: { fontWeight: 700, color: '#1a2332', marginBottom: 6 }, children: "No users available" }), _jsx("div", { style: { fontSize: 13, color: '#7b8fa1' }, children: "Check back later" })] }));
|
package/dist/config/index.d.ts
CHANGED
|
@@ -1,16 +1,3 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export declare function
|
|
3
|
-
|
|
4
|
-
* Resolves the URL used by `fetchUsers`.
|
|
5
|
-
*
|
|
6
|
-
* **Same-origin / internal proxy (recommended):** If `CHAT_USER_LIST` starts with `/`
|
|
7
|
-
* (e.g. `/api/v1/chat/users`), the browser only requests your app origin. The real
|
|
8
|
-
* backend URL stays on the server (Next.js route, BFF, etc.) and does not appear in
|
|
9
|
-
* the client Network tab as a cross-origin call.
|
|
10
|
-
*
|
|
11
|
-
* **Absolute URL:** If `CHAT_USER_LIST` is a full `http(s)://...` string, it is used as-is
|
|
12
|
-
* (visible in Network as that host).
|
|
13
|
-
*
|
|
14
|
-
* **Legacy:** Otherwise the path is joined with `CHAT_HOST_URL` and optional `CHAT_HOST_PORT`.
|
|
15
|
-
*/
|
|
16
|
-
export declare function buildUserListUrl(config: ChatConfig): string;
|
|
1
|
+
import { LocalEnvConfig, RemoteChatData } from '../types';
|
|
2
|
+
export declare function loadLocalConfig(): LocalEnvConfig;
|
|
3
|
+
export declare function fetchRemoteChatData(apiKey: string, widgetId: string): Promise<RemoteChatData>;
|
package/dist/config/index.js
CHANGED
|
@@ -1,113 +1,30 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
*/
|
|
6
|
-
function getEnvVar(key) {
|
|
1
|
+
const REMOTE_URL = 'https://window.mscorpres.com/TEST/chatData.json';
|
|
2
|
+
const DEMO_API_KEY = 'demo1234';
|
|
3
|
+
const DEMO_WIDGET_ID = 'demo';
|
|
4
|
+
function getEnv(key) {
|
|
7
5
|
var _a, _b, _c;
|
|
8
|
-
const nextKey = `NEXT_PUBLIC_${key}`;
|
|
9
|
-
const reactKey = `REACT_APP_${key}`;
|
|
10
|
-
// Next.js / Node.js environment
|
|
11
6
|
if (typeof process !== 'undefined' && process.env) {
|
|
12
|
-
return ((_c = (_b = (_a = process.env[
|
|
7
|
+
return ((_c = (_b = (_a = process.env[`NEXT_PUBLIC_${key}`]) !== null && _a !== void 0 ? _a : process.env[`REACT_APP_${key}`]) !== null && _b !== void 0 ? _b : process.env[key]) !== null && _c !== void 0 ? _c : undefined);
|
|
13
8
|
}
|
|
14
9
|
return undefined;
|
|
15
10
|
}
|
|
16
|
-
function
|
|
17
|
-
const valid = ['ACTIVE', 'DISABLE', 'MAINTENANCE'];
|
|
18
|
-
if (value && valid.includes(value)) {
|
|
19
|
-
return value;
|
|
20
|
-
}
|
|
21
|
-
console.warn(`[ChatWidget] Invalid CHAT_STATUS "${value}". Defaulting to "DISABLE".`);
|
|
22
|
-
return 'DISABLE';
|
|
23
|
-
}
|
|
24
|
-
function validateChatType(value) {
|
|
25
|
-
const valid = ['SUPPORT', 'CHAT', 'BOTH'];
|
|
26
|
-
if (value && valid.includes(value)) {
|
|
27
|
-
return value;
|
|
28
|
-
}
|
|
29
|
-
console.warn(`[ChatWidget] Invalid CHAT_TYPE "${value}". Defaulting to "SUPPORT".`);
|
|
30
|
-
return 'SUPPORT';
|
|
31
|
-
}
|
|
32
|
-
function parseBool(value, defaultValue) {
|
|
33
|
-
if (value === undefined || value === '')
|
|
34
|
-
return defaultValue;
|
|
35
|
-
const v = value.toLowerCase().trim();
|
|
36
|
-
if (v === 'true' || v === '1' || v === 'yes')
|
|
37
|
-
return true;
|
|
38
|
-
if (v === 'false' || v === '0' || v === 'no')
|
|
39
|
-
return false;
|
|
40
|
-
return defaultValue;
|
|
41
|
-
}
|
|
42
|
-
function parsePositiveInt(value, fallback) {
|
|
43
|
-
if (value === undefined || value === '')
|
|
44
|
-
return fallback;
|
|
45
|
-
const n = parseInt(value, 10);
|
|
46
|
-
return Number.isFinite(n) && n > 0 ? n : fallback;
|
|
47
|
-
}
|
|
48
|
-
function parseOptionalPort(value) {
|
|
49
|
-
if (value === undefined || value.trim() === '')
|
|
50
|
-
return undefined;
|
|
51
|
-
const n = parseInt(value, 10);
|
|
52
|
-
return Number.isFinite(n) && n > 0 ? n : undefined;
|
|
53
|
-
}
|
|
54
|
-
function parseSizeRatio(value, fallback) {
|
|
55
|
-
if (value === undefined || value === '')
|
|
56
|
-
return fallback;
|
|
57
|
-
const n = parseFloat(value);
|
|
58
|
-
if (!Number.isFinite(n))
|
|
59
|
-
return fallback;
|
|
60
|
-
return Math.min(1, Math.max(0, n));
|
|
61
|
-
}
|
|
62
|
-
export function loadChatConfig() {
|
|
11
|
+
export function loadLocalConfig() {
|
|
63
12
|
var _a, _b;
|
|
64
|
-
const hostUrl = (_a = getEnvVar('CHAT_HOST_URL')) !== null && _a !== void 0 ? _a : 'http://localhost';
|
|
65
|
-
const hostPort = parseOptionalPort(getEnvVar('CHAT_HOST_PORT'));
|
|
66
|
-
const userListEndpoint = (_b = getEnvVar('CHAT_USER_LIST')) !== null && _b !== void 0 ? _b : 'api/users';
|
|
67
|
-
const status = validateStatus(getEnvVar('CHAT_STATUS'));
|
|
68
|
-
const chatType = validateChatType(getEnvVar('CHAT_TYPE'));
|
|
69
|
-
const showNeedSupport = parseBool(getEnvVar('CHAT_SHOW_NEED_SUPPORT'), true);
|
|
70
|
-
const showNewConversation = parseBool(getEnvVar('CHAT_SHOW_NEW_CONVERSATION'), true);
|
|
71
13
|
return {
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
userListEndpoint,
|
|
75
|
-
status,
|
|
76
|
-
chatType,
|
|
77
|
-
showNeedSupport,
|
|
78
|
-
showNewConversation,
|
|
79
|
-
widgetMinWidth: parsePositiveInt(getEnvVar('CHAT_WIDGET_MIN_WIDTH'), 320),
|
|
80
|
-
widgetMaxWidth: parsePositiveInt(getEnvVar('CHAT_WIDGET_MAX_WIDTH'), 720),
|
|
81
|
-
widgetMinHeight: parsePositiveInt(getEnvVar('CHAT_WIDGET_MIN_HEIGHT'), 420),
|
|
82
|
-
widgetMaxHeight: parsePositiveInt(getEnvVar('CHAT_WIDGET_MAX_HEIGHT'), 720),
|
|
83
|
-
widgetDefaultSize: parseSizeRatio(getEnvVar('CHAT_WIDGET_DEFAULT_SIZE'), 0.45),
|
|
14
|
+
apiKey: (_a = getEnv('CHAT_API_KEY')) !== null && _a !== void 0 ? _a : DEMO_API_KEY,
|
|
15
|
+
widgetId: (_b = getEnv('CHAT_WIDGET_ID')) !== null && _b !== void 0 ? _b : DEMO_WIDGET_ID,
|
|
84
16
|
};
|
|
85
17
|
}
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
*/
|
|
99
|
-
export function buildUserListUrl(config) {
|
|
100
|
-
const raw = config.userListEndpoint.trim();
|
|
101
|
-
if (raw.startsWith('/')) {
|
|
102
|
-
return raw;
|
|
103
|
-
}
|
|
104
|
-
if (/^https?:\/\//i.test(raw)) {
|
|
105
|
-
return raw;
|
|
106
|
-
}
|
|
107
|
-
const base = config.hostUrl.replace(/\/$/, '');
|
|
108
|
-
const endpoint = raw.replace(/^\//, '');
|
|
109
|
-
if (config.hostPort !== undefined) {
|
|
110
|
-
return `${base}:${config.hostPort}/${endpoint}`;
|
|
111
|
-
}
|
|
112
|
-
return `${base}/${endpoint}`;
|
|
18
|
+
export async function fetchRemoteChatData(apiKey, widgetId) {
|
|
19
|
+
const url = REMOTE_URL;
|
|
20
|
+
const res = await fetch(url, {
|
|
21
|
+
headers: {
|
|
22
|
+
'X-Chat-Api-Key': apiKey,
|
|
23
|
+
'X-Chat-Widget-Id': widgetId,
|
|
24
|
+
'Content-Type': 'application/json',
|
|
25
|
+
},
|
|
26
|
+
});
|
|
27
|
+
if (!res.ok)
|
|
28
|
+
throw new Error(`Failed to load chat config: ${res.status}`);
|
|
29
|
+
return res.json();
|
|
113
30
|
}
|
package/dist/hooks/useChat.d.ts
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
|
-
import { ChatMessage, ChatUser
|
|
2
|
-
|
|
1
|
+
import { ChatMessage, ChatUser } from '../types';
|
|
2
|
+
export declare function useChat(initialMessages?: ChatMessage[]): {
|
|
3
3
|
messages: ChatMessage[];
|
|
4
4
|
activeUser: ChatUser | null;
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
isPaused: boolean;
|
|
6
|
+
isReported: boolean;
|
|
7
|
+
selectUser: (user: ChatUser, history?: ChatMessage[]) => void;
|
|
8
|
+
sendMessage: (text: string, type?: ChatMessage["type"], extra?: Partial<ChatMessage>) => void;
|
|
9
|
+
togglePause: () => void;
|
|
10
|
+
reportChat: () => void;
|
|
8
11
|
clearChat: () => void;
|
|
9
|
-
|
|
10
|
-
}
|
|
11
|
-
export declare function useChat(): UseChatReturn;
|
|
12
|
-
export {};
|
|
12
|
+
setMessages: import("react").Dispatch<import("react").SetStateAction<ChatMessage[]>>;
|
|
13
|
+
};
|
package/dist/hooks/useChat.js
CHANGED
|
@@ -1,48 +1,30 @@
|
|
|
1
1
|
import { useState, useCallback } from 'react';
|
|
2
|
-
export function useChat() {
|
|
3
|
-
const [messages, setMessages] = useState(
|
|
2
|
+
export function useChat(initialMessages = []) {
|
|
3
|
+
const [messages, setMessages] = useState(initialMessages);
|
|
4
4
|
const [activeUser, setActiveUser] = useState(null);
|
|
5
|
-
const [
|
|
6
|
-
const
|
|
7
|
-
|
|
8
|
-
setRecentChats((prev) => {
|
|
9
|
-
const rest = prev.filter((r) => r.user.uid !== user.uid);
|
|
10
|
-
return [{ user, lastMessage, updatedAt }, ...rest].slice(0, 50);
|
|
11
|
-
});
|
|
12
|
-
}, []);
|
|
13
|
-
const selectUser = useCallback((user) => {
|
|
14
|
-
setActiveUser(user);
|
|
15
|
-
setMessages([]);
|
|
16
|
-
}, []);
|
|
17
|
-
const openRecent = useCallback((user) => {
|
|
5
|
+
const [isPaused, setIsPaused] = useState(false);
|
|
6
|
+
const [isReported, setIsReported] = useState(false);
|
|
7
|
+
const selectUser = useCallback((user, history = []) => {
|
|
18
8
|
setActiveUser(user);
|
|
19
|
-
setMessages(
|
|
9
|
+
setMessages(history);
|
|
10
|
+
setIsPaused(false);
|
|
11
|
+
setIsReported(false);
|
|
12
|
+
// TODO: socket.emit('join', { roomId: user.uid });
|
|
13
|
+
// TODO: socket.on('message', msg => setMessages(prev => [...prev, msg]));
|
|
20
14
|
}, []);
|
|
21
|
-
const sendMessage = useCallback((text) => {
|
|
22
|
-
if (!activeUser ||
|
|
15
|
+
const sendMessage = useCallback((text, type = 'text', extra = {}) => {
|
|
16
|
+
if (!activeUser || isPaused)
|
|
23
17
|
return;
|
|
24
|
-
const
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
};
|
|
32
|
-
setMessages((prev) => [...prev, newMsg]);
|
|
33
|
-
upsertRecent(activeUser, text.trim());
|
|
34
|
-
}, [activeUser, upsertRecent]);
|
|
35
|
-
const clearChat = useCallback(() => {
|
|
36
|
-
setMessages([]);
|
|
37
|
-
setActiveUser(null);
|
|
38
|
-
}, []);
|
|
18
|
+
const msg = Object.assign({ id: `msg_${Date.now()}_${Math.random().toString(36).slice(2)}`, senderId: 'me', receiverId: activeUser.uid, text, timestamp: new Date().toISOString(), type, status: 'sent' }, extra);
|
|
19
|
+
setMessages(prev => [...prev, msg]);
|
|
20
|
+
// TODO: socket.emit('message', msg);
|
|
21
|
+
}, [activeUser, isPaused]);
|
|
22
|
+
const togglePause = useCallback(() => setIsPaused(p => !p), []);
|
|
23
|
+
const reportChat = useCallback(() => { setIsReported(true); /* TODO: API call */ }, []);
|
|
24
|
+
const clearChat = useCallback(() => { setMessages([]); setActiveUser(null); }, []);
|
|
39
25
|
return {
|
|
40
|
-
messages,
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
selectUser,
|
|
44
|
-
sendMessage,
|
|
45
|
-
clearChat,
|
|
46
|
-
openRecent,
|
|
26
|
+
messages, activeUser, isPaused, isReported,
|
|
27
|
+
selectUser, sendMessage, togglePause, reportChat, clearChat,
|
|
28
|
+
setMessages,
|
|
47
29
|
};
|
|
48
30
|
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { useState, useEffect } from 'react';
|
|
2
|
+
import { fetchRemoteChatData } from '../config';
|
|
3
|
+
export function useRemoteConfig(apiKey, widgetId) {
|
|
4
|
+
const [data, setData] = useState(null);
|
|
5
|
+
const [loading, setLoading] = useState(true);
|
|
6
|
+
const [error, setError] = useState(null);
|
|
7
|
+
useEffect(() => {
|
|
8
|
+
let cancelled = false;
|
|
9
|
+
setLoading(true);
|
|
10
|
+
fetchRemoteChatData(apiKey, widgetId)
|
|
11
|
+
.then(d => { if (!cancelled) {
|
|
12
|
+
setData(d);
|
|
13
|
+
setLoading(false);
|
|
14
|
+
} })
|
|
15
|
+
.catch(e => { if (!cancelled) {
|
|
16
|
+
setError(e.message);
|
|
17
|
+
setLoading(false);
|
|
18
|
+
} });
|
|
19
|
+
return () => { cancelled = true; };
|
|
20
|
+
}, [apiKey, widgetId]);
|
|
21
|
+
return { data, loading, error };
|
|
22
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { CallSession, ChatUser } from '../types';
|
|
2
|
+
export declare function useWebRTC(): {
|
|
3
|
+
session: CallSession;
|
|
4
|
+
localVideoRef: import("react").RefObject<HTMLVideoElement | null>;
|
|
5
|
+
remoteVideoRef: import("react").RefObject<HTMLVideoElement | null>;
|
|
6
|
+
startCall: (peer: ChatUser, withVideo?: boolean) => Promise<void>;
|
|
7
|
+
acceptCall: (offer: RTCSessionDescriptionInit, peer: ChatUser, withVideo?: boolean) => Promise<void>;
|
|
8
|
+
endCall: () => void;
|
|
9
|
+
toggleMute: () => void;
|
|
10
|
+
toggleCamera: () => void;
|
|
11
|
+
};
|