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.
Files changed (75) hide show
  1. package/README.md +124 -241
  2. package/dist/components/BlockList/index.d.ts +10 -0
  3. package/dist/components/BlockList/index.js +33 -0
  4. package/dist/components/CallScreen/index.d.ts +13 -0
  5. package/dist/components/CallScreen/index.js +48 -0
  6. package/dist/components/ChatScreen/index.d.ts +19 -0
  7. package/dist/components/ChatScreen/index.js +168 -0
  8. package/dist/components/ChatWidget.d.ts +0 -24
  9. package/dist/components/ChatWidget.js +228 -43
  10. package/dist/components/EmojiPicker/index.d.ts +8 -0
  11. package/dist/components/EmojiPicker/index.js +18 -0
  12. package/dist/components/HomeScreen/index.d.ts +8 -0
  13. package/dist/components/HomeScreen/index.js +55 -0
  14. package/dist/components/MaintenanceView/index.d.ts +0 -1
  15. package/dist/components/MaintenanceView/index.js +13 -52
  16. package/dist/components/RecentChatsScreen/index.d.ts +17 -0
  17. package/dist/components/RecentChatsScreen/index.js +8 -0
  18. package/dist/components/Tabs/BottomTabs.d.ts +10 -0
  19. package/dist/components/Tabs/BottomTabs.js +34 -0
  20. package/dist/components/TicketScreen/index.d.ts +9 -0
  21. package/dist/components/TicketScreen/index.js +54 -0
  22. package/dist/components/UserListScreen/index.d.ts +11 -0
  23. package/dist/components/UserListScreen/index.js +35 -0
  24. package/dist/config/index.d.ts +3 -16
  25. package/dist/config/index.js +20 -103
  26. package/dist/hooks/useChat.d.ts +10 -9
  27. package/dist/hooks/useChat.js +22 -40
  28. package/dist/hooks/useRemoteConfig.d.ts +6 -0
  29. package/dist/hooks/useRemoteConfig.js +22 -0
  30. package/dist/hooks/useWebRTC.d.ts +11 -0
  31. package/dist/hooks/useWebRTC.js +112 -0
  32. package/dist/index.d.ts +16 -11
  33. package/dist/index.js +15 -16
  34. package/dist/types/index.d.ts +66 -38
  35. package/dist/utils/chat.d.ts +13 -0
  36. package/dist/utils/chat.js +62 -0
  37. package/dist/utils/theme.d.ts +3 -2
  38. package/dist/utils/theme.js +13 -21
  39. package/package.json +10 -20
  40. package/public/chatData.json +162 -0
  41. package/src/components/BlockList/index.tsx +94 -0
  42. package/src/components/CallScreen/index.tsx +144 -0
  43. package/src/components/ChatScreen/index.tsx +469 -0
  44. package/src/components/ChatWidget.tsx +471 -0
  45. package/src/components/EmojiPicker/index.tsx +48 -0
  46. package/src/components/HomeScreen/index.tsx +106 -0
  47. package/src/components/MaintenanceView/index.tsx +38 -0
  48. package/src/components/RecentChatsScreen/index.tsx +63 -0
  49. package/src/components/Tabs/BottomTabs.tsx +90 -0
  50. package/src/components/TicketScreen/index.tsx +124 -0
  51. package/src/components/UserListScreen/index.tsx +103 -0
  52. package/src/config/index.ts +40 -0
  53. package/src/hooks/useChat.ts +48 -0
  54. package/src/hooks/useRemoteConfig.ts +20 -0
  55. package/src/hooks/useWebRTC.ts +130 -0
  56. package/src/index.ts +29 -0
  57. package/src/types/index.ts +127 -0
  58. package/src/utils/chat.ts +70 -0
  59. package/src/utils/theme.ts +27 -0
  60. package/dist/components/BottomNav/index.d.ts +0 -10
  61. package/dist/components/BottomNav/index.js +0 -32
  62. package/dist/components/ChatBox/index.d.ts +0 -15
  63. package/dist/components/ChatBox/index.js +0 -228
  64. package/dist/components/ChatButton/index.d.ts +0 -9
  65. package/dist/components/ChatButton/index.js +0 -17
  66. package/dist/components/ChatWindow/index.d.ts +0 -10
  67. package/dist/components/ChatWindow/index.js +0 -286
  68. package/dist/components/HomeView/index.d.ts +0 -12
  69. package/dist/components/HomeView/index.js +0 -51
  70. package/dist/components/UserList/index.d.ts +0 -13
  71. package/dist/components/UserList/index.js +0 -136
  72. package/dist/hooks/useUsers.d.ts +0 -14
  73. package/dist/hooks/useUsers.js +0 -32
  74. package/dist/services/userService.d.ts +0 -7
  75. package/dist/services/userService.js +0 -18
package/README.md CHANGED
@@ -1,303 +1,186 @@
1
- # Chat Widget
1
+ # react-chat-widget-extension v3
2
2
 
3
- A reusable, fully configurable floating chat widget for **React.js** and **Next.js** applications.
4
-
5
- - ✅ Environment-variable-driven behavior (status, type, API endpoint)
6
- - ✅ Fully themeable via a `theme` prop (colors, fonts, button, position)
7
- - ✅ SSR-safe — works with Next.js App Router and Pages Router
8
- - ✅ TypeScript-first
9
- - ✅ WebSocket-ready architecture
10
- - ✅ Loading skeletons, empty states, error handling built in
11
-
12
- ---
13
-
14
- ## Folder Structure
15
-
16
- ```
17
- my_first_project/
18
- ├── src/
19
- │ ├── index.ts # Public API exports
20
- │ ├── types/
21
- │ │ └── index.ts # All TypeScript types & interfaces
22
- │ ├── config/
23
- │ │ └── index.ts # Env variable loader (Next.js + React safe)
24
- │ ├── services/
25
- │ │ └── userService.ts # Fetch users from API
26
- │ ├── hooks/
27
- │ │ ├── useUsers.ts # Fetch & filter users hook
28
- │ │ └── useChat.ts # Chat state & message management hook
29
- │ ├── utils/
30
- │ │ └── theme.ts # Theme merging & CSS variable utilities
31
- │ └── components/
32
- │ ├── ChatWidget.tsx # 🏠 Root widget — mount this in your app
33
- │ ├── ChatButton/
34
- │ │ └── index.tsx # Floating action button
35
- │ ├── ChatWindow/
36
- │ │ └── index.tsx # Expandable chat panel
37
- │ ├── UserList/
38
- │ │ └── index.tsx # User list with loading/error/empty states
39
- │ ├── ChatBox/
40
- │ │ └── index.tsx # Conversation panel + message input
41
- │ └── MaintenanceView/
42
- │ └── index.tsx # Shown when CHAT_STATUS=MAINTENANCE
43
- ├── examples/
44
- │ ├── react-app/
45
- │ │ ├── .env.example # React env variables template
46
- │ │ └── App.tsx # React usage example
47
- │ └── nextjs-app/
48
- │ ├── .env.local.example # Next.js env variables template
49
- │ ├── app/
50
- │ │ ├── layout.tsx # App Router: root layout
51
- │ │ ├── ChatWidgetWrapper.tsx # App Router: 'use client' boundary
52
- │ │ └── page.tsx # App Router: home page
53
- │ └── pages/
54
- │ ├── _app.tsx # Pages Router: global app
55
- │ └── index.tsx # Pages Router: index page
56
- ├── package.json
57
- ├── tsconfig.json
58
- └── README.md
59
- ```
3
+ A production-ready **drawer/slider-based** chat widget for React.js and Next.js.
4
+ All configuration is loaded remotely from your hosted `chatData.json`.
60
5
 
61
6
  ---
62
7
 
63
- ## Installation
64
-
65
- ```bash
66
- npm install ajaxter-chat
67
- # or
68
- yarn add ajaxter-chat
69
- ```
70
-
71
- ---
72
-
73
- ## Environment Variables
74
-
75
- ### React.js (.env)
8
+ ## Setup (2 env vars only)
76
9
 
10
+ ### React (.env)
77
11
  ```env
78
- REACT_APP_CHAT_HOST_URL=http://your-api.com
79
- REACT_APP_CHAT_HOST_PORT=4000
80
- REACT_APP_CHAT_USER_LIST=api/v1/chat/users
81
- REACT_APP_CHAT_STATUS=ACTIVE
82
- REACT_APP_CHAT_TYPE=BOTH
12
+ REACT_APP_CHAT_API_KEY=demo1234
13
+ REACT_APP_CHAT_WIDGET_ID=demo
83
14
  ```
84
15
 
85
16
  ### Next.js (.env.local)
86
-
87
17
  ```env
88
- NEXT_PUBLIC_CHAT_HOST_URL=http://your-api.com
89
- NEXT_PUBLIC_CHAT_HOST_PORT=4000
90
- NEXT_PUBLIC_CHAT_USER_LIST=api/v1/chat/users
91
- NEXT_PUBLIC_CHAT_STATUS=ACTIVE
92
- NEXT_PUBLIC_CHAT_TYPE=BOTH
18
+ NEXT_PUBLIC_CHAT_API_KEY=demo1234
19
+ NEXT_PUBLIC_CHAT_WIDGET_ID=demo
93
20
  ```
94
21
 
95
- ### Variable Reference
96
-
97
- | Variable | Type | Description |
98
- |--------------------|---------------------------------------|------------------------------------------|
99
- | `CHAT_HOST_URL` | string | Base URL of your chat/user API |
100
- | `CHAT_HOST_PORT` | number | Port for your API server |
101
- | `CHAT_USER_LIST` | string | User list URL — see **User List API** below |
102
- | `CHAT_STATUS` | `ACTIVE` \| `DISABLE` \| `MAINTENANCE` | Controls widget visibility & state |
103
- | `CHAT_TYPE` | `SUPPORT` \| `CHAT` \| `BOTH` | Controls which users are shown |
104
-
105
- ---
106
-
107
- ## CHAT_STATUS Behavior
108
-
109
- | Value | Behavior |
110
- |-----------------|---------------------------------------------------------------|
111
- | `ACTIVE` | Widget is fully enabled |
112
- | `DISABLE` | Widget is **not rendered at all** — zero DOM footprint |
113
- | `MAINTENANCE` | Widget opens but shows a maintenance message (non-interactive)|
114
-
115
22
  ---
116
23
 
117
- ## CHAT_TYPE Behavior
118
-
119
- | Value | User List Shown | UI |
120
- |-----------|------------------------------------------|-----------------------------|
121
- | `SUPPORT` | Only `type: "developer"` users | Single panel |
122
- | `CHAT` | Only `type: "user"` users | Single panel |
123
- | `BOTH` | Both developers and users | Two tabs (Support / Users) |
124
-
125
- ---
24
+ ## Remote Config URL
126
25
 
127
- ## User List API
128
-
129
- The widget calls `CHAT_USER_LIST` in three ways:
130
-
131
- 1. **Same-origin / BFF (recommended)** — value starts with `/`, e.g. `/api/v1/chat/users`. The browser only shows a request to **your** app; your route handler proxies to the real API server-side, so the upstream URL (e.g. `http://your-api.com:4000/...`) does not appear as the client request URL in DevTools.
132
- 2. **Full URL** — value starts with `http://` or `https://`; that exact URL is fetched (visible in Network).
133
- 3. **Legacy** — otherwise it is built as
134
- `CHAT_HOST_URL` + optional `:CHAT_HOST_PORT` + path.
135
-
136
- Example (Next.js route at `app/api/v1/chat/users/route.ts` that forwards to your backend):
137
-
138
- ```env
139
- NEXT_PUBLIC_CHAT_USER_LIST=/api/v1/chat/users
26
+ The widget fetches all config from:
140
27
  ```
141
-
142
- Legacy example:
143
-
28
+ GET https://window.mscorpres.com/TEST/chatData.json
144
29
  ```
145
- GET ${CHAT_HOST_URL}:${CHAT_HOST_PORT}/${CHAT_USER_LIST}
30
+ With headers:
146
31
  ```
32
+ X-Chat-Api-Key: demo1234
33
+ X-Chat-Widget-Id: demo
34
+ ```
35
+
36
+ ---
37
+
38
+ ## chatData.json Schema
147
39
 
148
- Expected response:
40
+ Host this file at the URL above:
149
41
 
150
42
  ```json
151
- [
152
- {
153
- "name": "Alice Dev",
154
- "uid": "uid_001",
155
- "email": "alice@company.com",
156
- "mobile": "+1234567890",
157
- "project": "Platform Team",
158
- "type": "developer"
43
+ {
44
+ "widget": {
45
+ "id": "demo",
46
+ "apiKey": "demo1234",
47
+ "status": "ACTIVE",
48
+ "chatType": "BOTH",
49
+ "primaryColor": "#2563EB",
50
+ "buttonLabel": "Support",
51
+ "buttonPosition": "bottom-right",
52
+ "welcomeTitle": "Hi there 👋",
53
+ "welcomeSubtitle": "Need help? Start a conversation:",
54
+ "allowVoiceMessage": true,
55
+ "allowAttachment": true,
56
+ "allowEmoji": true,
57
+ "allowWebCall": true,
58
+ "maxEmojiCount": 20,
59
+ "allowTranscriptDownload": true,
60
+ "allowReport": true,
61
+ "allowBlock": true
159
62
  },
160
- {
161
- "name": "Bob Smith",
162
- "uid": "uid_002",
163
- "email": "bob@client.com",
164
- "mobile": "+0987654321",
165
- "project": "Client Portal",
166
- "type": "user"
167
- }
168
- ]
63
+ "developers": [ { "uid":"dev_001","name":"...","type":"developer","status":"online", ... } ],
64
+ "users": [ { "uid":"usr_001","name":"...","type":"user","status":"online", ... } ],
65
+ "sampleChats": { "dev_001": [ { "id":"msg_001","senderId":"me","text":"Hi!","type":"text", ... } ] },
66
+ "sampleTickets": [ { "id":"TKT-0001","title":"...","status":"open","priority":"high", ... } ],
67
+ "blockedUsers": []
68
+ }
169
69
  ```
170
70
 
171
71
  ---
172
72
 
173
73
  ## Usage
174
74
 
175
- ### React.js
176
-
75
+ ### React
177
76
  ```tsx
178
- // App.tsx
179
- import { ChatWidget } from 'ajaxter-chat';
180
-
181
- function App() {
182
- return (
183
- <div>
184
- <main>Your app content</main>
185
-
186
- <ChatWidget
187
- theme={{
188
- primaryColor: '#6C63FF',
189
- buttonColor: '#6C63FF',
190
- buttonTextColor: '#ffffff',
191
- buttonLabel: 'Chat with us',
192
- buttonPosition: 'bottom-right',
193
- fontFamily: "'DM Sans', sans-serif",
194
- borderRadius: '16px',
195
- }}
196
- />
197
- </div>
198
- );
77
+ import { ChatWidget } from 'react-chat-widget-extension';
78
+ export default function App() {
79
+ return <><main>App</main><ChatWidget /></>;
199
80
  }
200
81
  ```
201
82
 
202
- ### Next.js App Router
203
-
83
+ ### Next.js App Router
204
84
  ```tsx
205
85
  // app/ChatWidgetWrapper.tsx
206
86
  'use client';
207
- import { ChatWidget } from 'ajaxter-chat';
208
-
209
- export function ChatWidgetWrapper() {
210
- return <ChatWidget theme={{ primaryColor: '#0ea5e9' }} />;
211
- }
212
- ```
87
+ import { ChatWidget } from 'react-chat-widget-extension';
88
+ export function ChatWidgetWrapper() { return <ChatWidget />; }
213
89
 
214
- ```tsx
215
90
  // app/layout.tsx
216
91
  import { ChatWidgetWrapper } from './ChatWidgetWrapper';
217
-
218
- export default function RootLayout({ children }) {
219
- return (
220
- <html lang="en">
221
- <body>
222
- {children}
223
- <ChatWidgetWrapper />
224
- </body>
225
- </html>
226
- );
92
+ export default function Layout({ children }) {
93
+ return <html><body>{children}<ChatWidgetWrapper /></body></html>;
227
94
  }
228
95
  ```
229
96
 
230
- ### Next.js Pages Router
231
-
97
+ ### Next.js Pages Router
232
98
  ```tsx
233
99
  // pages/_app.tsx
234
- import { ChatWidget } from 'ajaxter-chat';
235
-
100
+ import { ChatWidget } from 'react-chat-widget-extension';
236
101
  export default function MyApp({ Component, pageProps }) {
237
- return (
238
- <>
239
- <Component {...pageProps} />
240
- <ChatWidget theme={{ primaryColor: '#10b981' }} />
241
- </>
242
- );
102
+ return <><Component {...pageProps}/><ChatWidget /></>;
243
103
  }
244
104
  ```
245
105
 
246
106
  ---
247
107
 
248
- ## Theme Props
249
-
250
- All theme properties are **optional**. Defaults are used when omitted.
251
-
252
- ```tsx
253
- interface ChatWidgetTheme {
254
- fontFamily?: string; // Default: "'DM Sans', 'Segoe UI', sans-serif"
255
- primaryColor?: string; // Default: '#6C63FF' header, accents, active states
256
- backgroundColor?: string; // Default: '#ffffff' — widget panel background
257
- buttonColor?: string; // Default: '#6C63FF' — floating button background
258
- buttonTextColor?: string; // Default: '#ffffff' — floating button text/icon color
259
- buttonLabel?: string; // Default: 'Chat with us'
260
- buttonPosition?: 'bottom-right' | 'bottom-left'; // Default: 'bottom-right'
261
- borderRadius?: string; // Default: '16px'
262
- }
263
- ```
108
+ ## Features
109
+
110
+ | Feature | Details |
111
+ |---|---|
112
+ | **Drawer/Slider UI** | Slides in from right (or left) with smooth animation. Backdrop closes it. |
113
+ | **Home Screen** | "Hi there 👋" hero, cards for Support / New Conversation / Raise Ticket |
114
+ | **User List** | Slide-in panel with online status dot, designation, project |
115
+ | **Chat Screen** | Matches the UI image hamburger back, title "Support", phone + fullscreen icons |
116
+ | **Enter Your Details** | Blue card at top of chat (ticket chat body placeholder) |
117
+ | **Voice Messages** | Record via MediaRecorder API, shows duration |
118
+ | **Attachments** | File picker, shows filename + size as message |
119
+ | **Emoji Picker** | 20 curated emojis, limited count via `maxEmojiCount` |
120
+ | **Pause Chat** | Developer can pause — user cannot send messages |
121
+ | **Resume Chat** | One-click resume from banner or menu |
122
+ | **Report Chat** | Flags chat, shows warning banner |
123
+ | **Block User** | Blocks users only (not developers), with confirm dialog |
124
+ | **Block List Tab** | View all blocked users, one-click unblock |
125
+ | **Download Transcript** | Saves plain-text .txt file of full conversation |
126
+ | **WebRTC Calls** | Secure P2P voice/video call with mute + camera toggle |
127
+ | **Ticket Screen** | Raise tickets with title, description, priority selector |
128
+ | **Ticket History** | Shows all tickets with status badge and priority color |
129
+ | **Recent Chats Tab** | Shows past conversations with unread badges |
130
+ | **Maximize/Minimize** | Toggle drawer width 380px ↔ 480px |
131
+ | **Slide Close** | Smooth slide-out animation on close |
132
+ | **SSR Safe** | isMounted guard, works in Next.js App Router and Pages Router |
133
+ | **CHAT_STATUS** | ACTIVE / DISABLE / MAINTENANCE handled |
264
134
 
265
135
  ---
266
136
 
267
- ## WebSocket Integration
268
-
269
- The `useChat` hook is ready for WebSocket integration. Look for the `TODO` comments in:
270
-
271
- - `src/hooks/useChat.ts` → Add `socket.emit('message', newMsg)` in `sendMessage`
272
- - `src/hooks/useChat.ts` → Add `socket.on('message', ...)` listener in `selectUser`
273
- - `src/components/ChatWindow/index.tsx` → Initialize socket connection on mount
274
-
275
- Example with socket.io:
137
+ ## WebSocket Integration Points
276
138
 
139
+ In `src/hooks/useChat.ts` — find the `// TODO:` comments:
277
140
  ```ts
278
- // In useChat.ts — sendMessage
279
- socket.emit('chat:message', { to: activeUser.uid, text });
141
+ // In selectUser:
142
+ // socket.emit('join', { roomId: user.uid });
143
+ // socket.on('message', msg => setMessages(prev => [...prev, msg]));
280
144
 
281
- // In useChat.ts — selectUser
282
- socket.on('chat:message', (msg: ChatMessage) => {
283
- setMessages((prev) => [...prev, msg]);
284
- });
145
+ // In sendMessage:
146
+ // socket.emit('message', msg);
285
147
  ```
286
148
 
287
- ---
288
-
289
- ## Build
290
-
291
- ```bash
292
- cd my_first_project
293
- npm install
294
- npm run build # Compile TypeScript → dist/
295
- npm run type-check # Verify types without emitting
296
- npm run dev # Watch mode
149
+ In `src/hooks/useWebRTC.ts` — find the `// TODO:` comments:
150
+ ```ts
151
+ // ICE candidate: socket.emit('ice-candidate', { candidate, to: peer.uid });
152
+ // Offer: socket.emit('call-offer', { offer, to: peer.uid });
153
+ // Answer: socket.emit('call-answer', { answer, to: peer.uid });
297
154
  ```
298
155
 
299
156
  ---
300
157
 
301
- ## License
158
+ ## Folder Structure
302
159
 
303
- MIT
160
+ ```
161
+ src/
162
+ ├── index.ts
163
+ ├── types/index.ts
164
+ ├── config/index.ts ← reads CHAT_API_KEY + CHAT_WIDGET_ID, fetches chatData.json
165
+ ├── utils/theme.ts
166
+ ├── utils/chat.ts ← transcript, avatarColor, formatTime, downloadText
167
+ ├── hooks/
168
+ │ ├── useRemoteConfig.ts ← fetches chatData.json
169
+ │ ├── useChat.ts ← messages, pause, report
170
+ │ └── useWebRTC.ts ← P2P WebRTC calling
171
+ └── components/
172
+ ├── ChatWidget.tsx ← 🏠 Root drawer orchestrator
173
+ ├── HomeScreen/
174
+ ├── UserListScreen/
175
+ ├── ChatScreen/ ← voice, attach, emoji, pause, report, block, transcript, call
176
+ ├── CallScreen/ ← WebRTC video/audio UI
177
+ ├── EmojiPicker/
178
+ ├── TicketScreen/
179
+ ├── RecentChatsScreen/
180
+ ├── BlockList/
181
+ ├── MaintenanceView/
182
+ └── Tabs/BottomTabs.tsx ← Home / Chats / Tickets / Blocked
183
+
184
+ public/
185
+ └── chatData.json ← Sample JSON — host at window.mscorpres.com/TEST/chatData.json
186
+ ```
@@ -0,0 +1,10 @@
1
+ import React from 'react';
2
+ import { ChatUser, WidgetConfig } from '../../types';
3
+ interface BlockListScreenProps {
4
+ blockedUsers: ChatUser[];
5
+ config: WidgetConfig;
6
+ onUnblock: (uid: string) => void;
7
+ onBack: () => void;
8
+ }
9
+ export declare const BlockListScreen: React.FC<BlockListScreenProps>;
10
+ export {};
@@ -0,0 +1,33 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { avatarColor, initials } from '../../utils/chat';
3
+ export const BlockListScreen = ({ blockedUsers, config, onUnblock, onBack, }) => (_jsxs("div", { style: { display: 'flex', flexDirection: 'column', height: '100%', animation: 'cw-slideIn 0.22s ease' }, children: [_jsxs("div", { style: {
4
+ background: `linear-gradient(135deg,${config.primaryColor},${config.primaryColor}cc)`,
5
+ padding: '14px 18px', display: 'flex', alignItems: 'center', gap: 12, flexShrink: 0,
6
+ }, children: [_jsx("button", { onClick: onBack, style: backBtnStyle, 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" }) }) }), _jsxs("div", { children: [_jsx("div", { style: { fontWeight: 700, fontSize: 16, color: '#fff' }, children: "Block List" }), _jsxs("div", { style: { fontSize: 12, color: 'rgba(255,255,255,0.8)' }, children: [blockedUsers.length, " blocked user", blockedUsers.length !== 1 ? 's' : ''] })] })] }), _jsx("div", { style: { flex: 1, overflowY: 'auto' }, children: blockedUsers.length === 0 ? (_jsxs("div", { style: { padding: '50px 24px', textAlign: 'center' }, children: [_jsx("div", { style: { fontSize: 36, marginBottom: 10 }, children: "\u2705" }), _jsx("div", { style: { fontWeight: 700, color: '#1a2332', marginBottom: 6 }, children: "No blocked users" }), _jsx("div", { style: { fontSize: 13, color: '#7b8fa1' }, children: "Users you block will appear here" })] })) : (blockedUsers.map((user, i) => (_jsxs("div", { style: {
7
+ padding: '13px 16px', display: 'flex', alignItems: 'center', gap: 13,
8
+ borderBottom: '1px solid #f0f2f5',
9
+ animation: `cw-fadeUp 0.28s ease both`, animationDelay: `${i * 0.05}s`,
10
+ }, children: [_jsx("div", { style: {
11
+ width: 44, height: 44, borderRadius: '50%',
12
+ backgroundColor: avatarColor(user.name),
13
+ display: 'flex', alignItems: 'center', justifyContent: 'center',
14
+ color: '#fff', fontWeight: 700, fontSize: 14, flexShrink: 0,
15
+ filter: 'grayscale(0.6)', opacity: 0.7,
16
+ }, children: initials(user.name) }), _jsxs("div", { style: { flex: 1, minWidth: 0 }, children: [_jsx("div", { style: { fontWeight: 700, fontSize: 14, color: '#6b7280' }, children: user.name }), _jsx("div", { style: { fontSize: 12, color: '#9ca3af', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }, children: user.email })] }), _jsx("button", { onClick: () => onUnblock(user.uid), style: {
17
+ padding: '6px 14px', borderRadius: 20,
18
+ border: `1.5px solid ${config.primaryColor}`,
19
+ background: 'transparent', color: config.primaryColor,
20
+ fontSize: 12, fontWeight: 700, cursor: 'pointer',
21
+ transition: 'all 0.15s', flexShrink: 0,
22
+ }, onMouseEnter: e => {
23
+ e.currentTarget.style.background = config.primaryColor;
24
+ e.currentTarget.style.color = '#fff';
25
+ }, onMouseLeave: e => {
26
+ e.currentTarget.style.background = 'transparent';
27
+ e.currentTarget.style.color = config.primaryColor;
28
+ }, children: "Unblock" })] }, user.uid)))) })] }));
29
+ const backBtnStyle = {
30
+ background: 'rgba(255,255,255,0.22)', border: 'none', borderRadius: '50%',
31
+ width: 32, height: 32, display: 'flex', alignItems: 'center', justifyContent: 'center',
32
+ cursor: 'pointer', flexShrink: 0,
33
+ };
@@ -0,0 +1,13 @@
1
+ import React from 'react';
2
+ import { CallSession } from '../../types';
3
+ interface CallScreenProps {
4
+ session: CallSession;
5
+ localVideoRef: React.RefObject<HTMLVideoElement | null>;
6
+ remoteVideoRef: React.RefObject<HTMLVideoElement | null>;
7
+ onEnd: () => void;
8
+ onToggleMute: () => void;
9
+ onToggleCamera: () => void;
10
+ primaryColor: string;
11
+ }
12
+ export declare const CallScreen: React.FC<CallScreenProps>;
13
+ export {};
@@ -0,0 +1,48 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { useEffect, useState } from 'react';
3
+ import { avatarColor, initials } from '../../utils/chat';
4
+ export const CallScreen = ({ session, localVideoRef, remoteVideoRef, onEnd, onToggleMute, onToggleCamera, primaryColor, }) => {
5
+ const [duration, setDuration] = useState(0);
6
+ const peer = session.peer;
7
+ useEffect(() => {
8
+ if (session.state !== 'connected' || !session.startedAt)
9
+ return;
10
+ const t = setInterval(() => {
11
+ setDuration(Math.floor((Date.now() - session.startedAt.getTime()) / 1000));
12
+ }, 1000);
13
+ return () => clearInterval(t);
14
+ }, [session.state, session.startedAt]);
15
+ const mins = String(Math.floor(duration / 60)).padStart(2, '0');
16
+ const secs = String(duration % 60).padStart(2, '0');
17
+ return (_jsxs("div", { style: {
18
+ display: 'flex', flexDirection: 'column', height: '100%',
19
+ background: session.isCameraOn ? '#000' : `linear-gradient(145deg,${primaryColor}dd,#0f172a)`,
20
+ color: '#fff', animation: 'cw-slideIn 0.22s ease',
21
+ position: 'relative', overflow: 'hidden',
22
+ }, children: [_jsx("video", { ref: remoteVideoRef, autoPlay: true, playsInline: true, style: { position: 'absolute', inset: 0, width: '100%', height: '100%', objectFit: 'cover', opacity: session.state === 'connected' ? 1 : 0 } }), _jsx("video", { ref: localVideoRef, autoPlay: true, playsInline: true, muted: true, style: {
23
+ position: 'absolute', bottom: 120, right: 14,
24
+ width: 90, height: 120, borderRadius: 10,
25
+ objectFit: 'cover', border: '2px solid rgba(255,255,255,0.3)',
26
+ display: session.isCameraOn ? 'block' : 'none',
27
+ zIndex: 10,
28
+ } }), _jsxs("div", { style: { position: 'relative', zIndex: 5, display: 'flex', flexDirection: 'column', height: '100%', background: 'rgba(0,0,0,0.35)' }, children: [_jsx("div", { style: { padding: '16px 18px', display: 'flex', alignItems: 'center', gap: 10 }, children: _jsx("div", { style: { flex: 1 }, children: _jsxs("div", { style: { fontWeight: 700, fontSize: 15, color: '#fff' }, children: [session.state === 'calling' && 'Calling...', session.state === 'connected' && 'Connected', session.state === 'ended' && 'Call Ended'] }) }) }), _jsx("div", { style: { flex: 1, display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', gap: 16 }, children: peer && (_jsxs(_Fragment, { children: [_jsx("div", { style: {
29
+ width: 90, height: 90, borderRadius: '50%',
30
+ backgroundColor: avatarColor(peer.name),
31
+ display: 'flex', alignItems: 'center', justifyContent: 'center',
32
+ fontSize: 28, fontWeight: 700, color: '#fff',
33
+ boxShadow: '0 0 0 4px rgba(255,255,255,0.2)',
34
+ animation: session.state === 'calling' ? 'cw-pulse 1.5s ease infinite' : 'none',
35
+ }, children: initials(peer.name) }), _jsxs("div", { style: { textAlign: 'center' }, children: [_jsx("div", { style: { fontSize: 20, fontWeight: 800 }, children: peer.name }), _jsxs("div", { style: { fontSize: 13, opacity: 0.8, marginTop: 4 }, children: [session.state === 'calling' && 'Ringing...', session.state === 'connected' && `${mins}:${secs}`, session.state === 'ended' && 'Call ended'] })] })] })) }), _jsxs("div", { style: { padding: '24px', display: 'flex', justifyContent: 'center', alignItems: 'center', gap: 20 }, children: [_jsx(CallBtn, { active: session.isMuted, activeColor: "#374151", onClick: onToggleMute, title: session.isMuted ? 'Unmute' : 'Mute', children: _jsx("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", children: session.isMuted
36
+ ? _jsxs(_Fragment, { children: [_jsx("path", { d: "M12 1a3 3 0 00-3 3v8a3 3 0 006 0V4a3 3 0 00-3-3z", stroke: "#fff", strokeWidth: "2" }), _jsx("line", { x1: "1", y1: "1", x2: "23", y2: "23", stroke: "#ef4444", strokeWidth: "2" })] })
37
+ : _jsxs(_Fragment, { children: [_jsx("path", { d: "M12 1a3 3 0 00-3 3v8a3 3 0 006 0V4a3 3 0 00-3-3z", stroke: "#fff", strokeWidth: "2", strokeLinecap: "round" }), _jsx("path", { d: "M19 10v2a7 7 0 01-14 0v-2M12 19v4", stroke: "#fff", strokeWidth: "2", strokeLinecap: "round" })] }) }) }), _jsx("button", { onClick: onEnd, style: {
38
+ width: 60, height: 60, borderRadius: '50%', backgroundColor: '#ef4444',
39
+ border: 'none', cursor: 'pointer', display: 'flex', alignItems: 'center', justifyContent: 'center',
40
+ boxShadow: '0 4px 16px rgba(239,68,68,0.5)', flexShrink: 0,
41
+ }, children: _jsx("svg", { width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", children: _jsx("path", { d: "M22 16.92v3a2 2 0 01-2.18 2 19.79 19.79 0 01-8.63-3.07A19.5 19.5 0 013.07 10.8a19.79 19.79 0 01-3.07-8.68A2 2 0 012 0h3a2 2 0 012 1.72c.127.96.361 1.903.7 2.81a2 2 0 01-.45 2.11L6.09 7.91a16 16 0 006 6l1.27-1.27a2 2 0 012.11-.45c.907.339 1.85.573 2.81.7A2 2 0 0122 14.92v2z", fill: "#fff", transform: "rotate(135 12 12)" }) }) }), _jsx(CallBtn, { active: session.isCameraOn, activeColor: primaryColor, onClick: onToggleCamera, title: "Camera", children: _jsx("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", children: _jsx("path", { d: "M23 7l-7 5 7 5V7zM1 5h13a2 2 0 012 2v10a2 2 0 01-2 2H1a2 2 0 01-2-2V7a2 2 0 012-2z", stroke: "#fff", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }) }) })] })] })] }));
42
+ };
43
+ const CallBtn = ({ active, activeColor, onClick, title, children, }) => (_jsx("button", { onClick: onClick, title: title, style: {
44
+ width: 50, height: 50, borderRadius: '50%',
45
+ backgroundColor: active ? activeColor : 'rgba(255,255,255,0.2)',
46
+ border: 'none', cursor: 'pointer',
47
+ display: 'flex', alignItems: 'center', justifyContent: 'center', flexShrink: 0,
48
+ }, children: children }));
@@ -0,0 +1,19 @@
1
+ import React from 'react';
2
+ import { ChatMessage, ChatUser, WidgetConfig } from '../../types';
3
+ interface ChatScreenProps {
4
+ activeUser: ChatUser;
5
+ messages: ChatMessage[];
6
+ config: WidgetConfig;
7
+ isPaused: boolean;
8
+ isReported: boolean;
9
+ isBlocked: boolean;
10
+ onSend: (text: string, type?: ChatMessage['type'], extra?: Partial<ChatMessage>) => void;
11
+ onBack: () => void;
12
+ onClose: () => void;
13
+ onTogglePause: () => void;
14
+ onReport: () => void;
15
+ onBlock: () => void;
16
+ onStartCall: (withVideo: boolean) => void;
17
+ }
18
+ export declare const ChatScreen: React.FC<ChatScreenProps>;
19
+ export {};