@traiyani/chatsdk-react 1.0.1 → 1.0.3
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 +585 -173
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -2,19 +2,47 @@
|
|
|
2
2
|
|
|
3
3
|
React SDK for ChatSDK - Real-time chat solution with product-based conversations, Socket.io, instant messaging, and file sharing.
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- Real-time messaging via Socket.IO
|
|
8
|
+
- Product-based conversations with metadata
|
|
9
|
+
- File/image/video uploads and sharing
|
|
10
|
+
- Block/unblock users
|
|
11
|
+
- Internationalization (English + Arabic with RTL)
|
|
12
|
+
- Dark mode support (light / dark / auto)
|
|
13
|
+
- Typing indicators
|
|
14
|
+
- Read receipts and message status
|
|
15
|
+
- Unread message counts
|
|
16
|
+
- Ready-made UI components (Conversation List + Chat Window)
|
|
17
|
+
- Pre-built — works with any bundler, zero config
|
|
18
|
+
|
|
19
|
+
## Table of Contents
|
|
20
|
+
|
|
21
|
+
1. [Installation](#1-installation)
|
|
22
|
+
2. [SDK Initialization](#2-sdk-initialization)
|
|
23
|
+
3. [User Authentication](#3-user-authentication)
|
|
24
|
+
4. [Start a New Conversation](#4-start-a-new-conversation)
|
|
25
|
+
5. [Using Ready-Made UI Components](#5-using-ready-made-ui-components)
|
|
26
|
+
6. [Sending Messages](#6-sending-messages)
|
|
27
|
+
7. [File Uploads & Attachments](#7-file-uploads--attachments)
|
|
28
|
+
8. [Unread Message Counts](#8-unread-message-counts)
|
|
29
|
+
9. [Block & Unblock Users](#9-block--unblock-users)
|
|
30
|
+
10. [Real-Time Events](#10-real-time-events)
|
|
31
|
+
11. [Theme Support](#11-theme-support)
|
|
32
|
+
12. [Internationalization (i18n)](#12-internationalization-i18n)
|
|
33
|
+
13. [Logout & Cleanup](#13-logout--cleanup)
|
|
34
|
+
14. [TypeScript Support](#14-typescript-support)
|
|
35
|
+
15. [Troubleshooting](#15-troubleshooting)
|
|
36
|
+
|
|
37
|
+
---
|
|
38
|
+
|
|
39
|
+
## 1. Installation
|
|
6
40
|
|
|
7
41
|
```bash
|
|
8
42
|
npm install @traiyani/chatsdk-react axios socket.io-client
|
|
9
43
|
```
|
|
10
44
|
|
|
11
|
-
###
|
|
12
|
-
|
|
13
|
-
Ensure your project has these installed:
|
|
14
|
-
|
|
15
|
-
```bash
|
|
16
|
-
npm install react react-dom axios socket.io-client
|
|
17
|
-
```
|
|
45
|
+
### Requirements
|
|
18
46
|
|
|
19
47
|
| Dependency | Minimum Version |
|
|
20
48
|
|---|---|
|
|
@@ -23,36 +51,343 @@ npm install react react-dom axios socket.io-client
|
|
|
23
51
|
| `axios` | >= 0.21.0 |
|
|
24
52
|
| `socket.io-client` | >= 4.0.0 |
|
|
25
53
|
|
|
26
|
-
|
|
54
|
+
**No build configuration changes needed.** Works with Vite, webpack, Next.js, Create React App, Parcel, and any React bundler out of the box.
|
|
55
|
+
|
|
56
|
+
### Import CSS
|
|
57
|
+
|
|
58
|
+
Import the stylesheet once in your app's entry point:
|
|
59
|
+
|
|
60
|
+
```tsx
|
|
61
|
+
import '@traiyani/chatsdk-react/dist/chatsdk.css';
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
---
|
|
65
|
+
|
|
66
|
+
## 2. SDK Initialization
|
|
67
|
+
|
|
68
|
+
Initialize the SDK once in your app, before any authentication or chat operations.
|
|
69
|
+
|
|
70
|
+
```tsx
|
|
71
|
+
import { ChatSDK } from '@traiyani/chatsdk-react';
|
|
72
|
+
|
|
73
|
+
const sdk = ChatSDK.getInstance();
|
|
74
|
+
|
|
75
|
+
await sdk.init({
|
|
76
|
+
apiBaseUrl: 'https://your-chat-api.com/api', // Your Mzad Chat API URL
|
|
77
|
+
appId: 'your-app-id', // Your application ID
|
|
78
|
+
environment: 'production', // 'development' | 'staging' | 'production'
|
|
79
|
+
enableLogging: true, // Enable console logs (set false in production)
|
|
80
|
+
autoConnect: true, // Auto-connect WebSocket after auth
|
|
81
|
+
});
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### Configuration Options
|
|
85
|
+
|
|
86
|
+
```typescript
|
|
87
|
+
interface ChatSDKConfig {
|
|
88
|
+
apiBaseUrl: string; // Required - API server URL
|
|
89
|
+
appId: string; // Required - Your application ID
|
|
90
|
+
environment: 'development' | 'staging' | 'production'; // Required
|
|
91
|
+
wsUrl?: string; // WebSocket URL (defaults to apiBaseUrl)
|
|
92
|
+
apiKey?: string; // API key (if required by server)
|
|
93
|
+
enableLogging?: boolean; // Enable debug logging (default: false)
|
|
94
|
+
autoConnect?: boolean; // Auto-connect WebSocket (default: true)
|
|
95
|
+
timeout?: number; // HTTP request timeout in ms (default: 30000)
|
|
96
|
+
}
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### Environment Variables
|
|
100
|
+
|
|
101
|
+
Config values can come from any source:
|
|
102
|
+
|
|
103
|
+
```tsx
|
|
104
|
+
// Create React App
|
|
105
|
+
await sdk.init({ apiBaseUrl: process.env.REACT_APP_API_URL, appId: process.env.REACT_APP_APP_ID, ... });
|
|
106
|
+
|
|
107
|
+
// Vite
|
|
108
|
+
await sdk.init({ apiBaseUrl: import.meta.env.VITE_API_URL, appId: import.meta.env.VITE_APP_ID, ... });
|
|
109
|
+
|
|
110
|
+
// Next.js
|
|
111
|
+
await sdk.init({ apiBaseUrl: process.env.NEXT_PUBLIC_API_URL, appId: process.env.NEXT_PUBLIC_APP_ID, ... });
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
---
|
|
115
|
+
|
|
116
|
+
## 3. User Authentication
|
|
117
|
+
|
|
118
|
+
The SDK uses the `chatUsers.authenticate()` method which tries login first and falls back to registration automatically.
|
|
119
|
+
|
|
120
|
+
### 3.1 Login the Current User (`isLoggedinUser = true`)
|
|
121
|
+
|
|
122
|
+
Use `isLoggedinUser = true` when the user is the **currently logged-in user** of your app. This saves the auth token so the SDK can make API calls on behalf of this user.
|
|
123
|
+
|
|
124
|
+
```tsx
|
|
125
|
+
const sdk = ChatSDK.getInstance();
|
|
126
|
+
|
|
127
|
+
// Authenticate the current logged-in user
|
|
128
|
+
const currentUser = await sdk.chatUsers.authenticate(
|
|
129
|
+
'external-user-123', // Your app's user ID
|
|
130
|
+
'your-app-id', // App ID (must match sdk.init)
|
|
131
|
+
'John Doe', // Display name
|
|
132
|
+
'john@example.com', // Email
|
|
133
|
+
true // isLoggedinUser = true → saves token
|
|
134
|
+
);
|
|
135
|
+
|
|
136
|
+
// Connect WebSocket for real-time messaging
|
|
137
|
+
await sdk.connect(currentUser.id);
|
|
138
|
+
|
|
139
|
+
console.log('Logged in:', currentUser.name);
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
**What happens under the hood:**
|
|
143
|
+
1. Tries to **login** the user (if already registered in chat system)
|
|
144
|
+
2. If login fails, **registers** the user automatically
|
|
145
|
+
3. Saves the auth token to localStorage (because `isLoggedinUser = true`)
|
|
146
|
+
4. All subsequent API calls use this token
|
|
147
|
+
|
|
148
|
+
### 3.2 Verify Another User's Existence (`isLoggedinUser = false`)
|
|
149
|
+
|
|
150
|
+
Use `isLoggedinUser = false` when you need to verify or register **another user** (e.g., a seller, the other participant) without switching the current session. This does NOT save the token.
|
|
151
|
+
|
|
152
|
+
```tsx
|
|
153
|
+
// Verify/register the other user (seller) without switching sessions
|
|
154
|
+
const otherUser = await sdk.chatUsers.authenticate(
|
|
155
|
+
'seller-456', // The other user's external ID
|
|
156
|
+
'your-app-id', // App ID
|
|
157
|
+
'Jane Smith', // Their display name
|
|
158
|
+
'jane@example.com', // Their email
|
|
159
|
+
false // isLoggedinUser = false → does NOT save token
|
|
160
|
+
);
|
|
161
|
+
|
|
162
|
+
console.log('Other user verified:', otherUser.name, otherUser.id);
|
|
163
|
+
// Now you can use otherUser.id to start a chat
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
**Why this matters:** Before starting a chat with another user, you must ensure they exist in the chat system. Calling `authenticate` with `isLoggedinUser = false` creates or verifies the user without disrupting your current session.
|
|
167
|
+
|
|
168
|
+
### 3.3 Check Login Status
|
|
169
|
+
|
|
170
|
+
```tsx
|
|
171
|
+
const isLoggedIn = sdk.chatUsers.isAuthenticated();
|
|
172
|
+
const currentUser = sdk.getCurrentUser();
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
---
|
|
176
|
+
|
|
177
|
+
## 4. Start a New Conversation
|
|
178
|
+
|
|
179
|
+
### 4.1 Understanding `externalGroupId` (Required)
|
|
180
|
+
|
|
181
|
+
All `startChat` methods require an `externalGroupId`. This is a unique identifier your app generates to enable fast room lookup (98% faster) and prevent duplicate conversations.
|
|
182
|
+
|
|
183
|
+
**How to generate it:**
|
|
184
|
+
|
|
185
|
+
```tsx
|
|
186
|
+
async function generateExternalGroupId(userId1, userId2, productId) {
|
|
187
|
+
const sorted = [userId1, userId2].sort();
|
|
188
|
+
const base = productId
|
|
189
|
+
? `${sorted[0]}_${sorted[1]}_product_${productId}`
|
|
190
|
+
: `${sorted[0]}_${sorted[1]}`;
|
|
191
|
+
|
|
192
|
+
const encoder = new TextEncoder();
|
|
193
|
+
const data = encoder.encode(base);
|
|
194
|
+
const hashBuffer = await crypto.subtle.digest('SHA-256', data);
|
|
195
|
+
const hashArray = Array.from(new Uint8Array(hashBuffer));
|
|
196
|
+
return hashArray.map(b => b.toString(16).padStart(2, '0')).join('');
|
|
197
|
+
}
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
**Usage:**
|
|
201
|
+
|
|
202
|
+
```tsx
|
|
203
|
+
const currentUserId = sdk.getCurrentUser().id;
|
|
204
|
+
const externalGroupId = await generateExternalGroupId(currentUserId, otherUser.id, 'PRODUCT_123');
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
### 4.2 Simple Direct Chat
|
|
208
|
+
|
|
209
|
+
```tsx
|
|
210
|
+
const conversation = await sdk.chatUsers.startChat(
|
|
211
|
+
externalGroupId, // Generated GUID
|
|
212
|
+
otherUser.id // The other participant's chat user ID
|
|
213
|
+
);
|
|
214
|
+
|
|
215
|
+
console.log('Chat started:', conversation.id);
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
### 4.3 Chat with Product Context & Metadata
|
|
219
|
+
|
|
220
|
+
```tsx
|
|
221
|
+
// Create product context
|
|
222
|
+
const productContext = {
|
|
223
|
+
productId: 'CAR_456',
|
|
224
|
+
productName: 'Toyota Camry 2023',
|
|
225
|
+
productImage: 'https://example.com/car.jpg',
|
|
226
|
+
price: 25000,
|
|
227
|
+
currency: 'QAR',
|
|
228
|
+
category: 'Vehicles',
|
|
229
|
+
productMetadata: {
|
|
230
|
+
mileage: '50000',
|
|
231
|
+
transmission: 'Automatic',
|
|
232
|
+
year: '2023',
|
|
233
|
+
},
|
|
234
|
+
};
|
|
235
|
+
|
|
236
|
+
// Optional: custom chat metadata
|
|
237
|
+
const chatMetadata = {
|
|
238
|
+
dealType: 'sale',
|
|
239
|
+
negotiable: true,
|
|
240
|
+
priority: 'high',
|
|
241
|
+
source: 'featured_listing',
|
|
242
|
+
};
|
|
243
|
+
|
|
244
|
+
// Start product chat
|
|
245
|
+
const conversation = await sdk.chatUsers.startChatWithProduct(
|
|
246
|
+
externalGroupId, // Generated GUID (include productId in generation)
|
|
247
|
+
otherUser.id, // Seller's chat user ID
|
|
248
|
+
productContext, // Product information
|
|
249
|
+
chatMetadata // Optional chat metadata
|
|
250
|
+
);
|
|
251
|
+
|
|
252
|
+
console.log('Product chat started:', conversation.id);
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
### 4.4 Complete Flow: Login → Verify Seller → Start Product Chat
|
|
256
|
+
|
|
257
|
+
```tsx
|
|
258
|
+
import { ChatSDK } from '@traiyani/chatsdk-react';
|
|
259
|
+
|
|
260
|
+
const sdk = ChatSDK.getInstance();
|
|
261
|
+
|
|
262
|
+
// Step 1: Initialize SDK
|
|
263
|
+
await sdk.init({
|
|
264
|
+
apiBaseUrl: 'https://your-chat-api.com/api',
|
|
265
|
+
appId: 'your-app-id',
|
|
266
|
+
environment: 'production',
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
// Step 2: Login the current user (buyer)
|
|
270
|
+
const currentUser = await sdk.chatUsers.authenticate(
|
|
271
|
+
'buyer-external-id',
|
|
272
|
+
'your-app-id',
|
|
273
|
+
'Ahmed',
|
|
274
|
+
'ahmed@example.com',
|
|
275
|
+
true // isLoggedinUser = true → this is the logged-in user
|
|
276
|
+
);
|
|
277
|
+
await sdk.connect(currentUser.id);
|
|
278
|
+
|
|
279
|
+
// Step 3: Verify/register the other user (seller) — does NOT switch session
|
|
280
|
+
const seller = await sdk.chatUsers.authenticate(
|
|
281
|
+
'seller-external-id',
|
|
282
|
+
'your-app-id',
|
|
283
|
+
'Mohammed',
|
|
284
|
+
'mohammed@example.com',
|
|
285
|
+
false // isLoggedinUser = false → just verify existence
|
|
286
|
+
);
|
|
287
|
+
|
|
288
|
+
// Step 4: Generate externalGroupId
|
|
289
|
+
const externalGroupId = await generateExternalGroupId(
|
|
290
|
+
currentUser.id,
|
|
291
|
+
seller.id,
|
|
292
|
+
'PRODUCT_789'
|
|
293
|
+
);
|
|
294
|
+
|
|
295
|
+
// Step 5: Start chat with product
|
|
296
|
+
const conversation = await sdk.chatUsers.startChatWithProduct(
|
|
297
|
+
externalGroupId,
|
|
298
|
+
seller.id,
|
|
299
|
+
{
|
|
300
|
+
productId: 'PRODUCT_789',
|
|
301
|
+
productName: 'iPhone 15 Pro',
|
|
302
|
+
productImage: 'https://example.com/iphone.jpg',
|
|
303
|
+
price: 4999,
|
|
304
|
+
currency: 'QAR',
|
|
305
|
+
category: 'Electronics',
|
|
306
|
+
}
|
|
307
|
+
);
|
|
308
|
+
|
|
309
|
+
// Step 6: Now show ChatWindow component with this conversation
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
---
|
|
313
|
+
|
|
314
|
+
## 5. Using Ready-Made UI Components
|
|
315
|
+
|
|
316
|
+
The SDK includes two ready-made React components with full functionality.
|
|
317
|
+
|
|
318
|
+
### 5.1 Conversation List
|
|
319
|
+
|
|
320
|
+
Displays all conversations with last message preview, unread badges, product context, and real-time updates.
|
|
321
|
+
|
|
322
|
+
```tsx
|
|
323
|
+
import { ConversationList } from '@traiyani/chatsdk-react';
|
|
324
|
+
|
|
325
|
+
<ConversationList
|
|
326
|
+
currentUser={currentUser}
|
|
327
|
+
onSelectConversation={(conversation) => setSelectedConversation(conversation)}
|
|
328
|
+
selectedConversationId={selectedConversation?.id}
|
|
329
|
+
/>
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
**Props:**
|
|
333
|
+
|
|
334
|
+
```typescript
|
|
335
|
+
interface ConversationListProps {
|
|
336
|
+
currentUser: ChatSDKUser; // Required - authenticated user
|
|
337
|
+
onSelectConversation: (conv: Conversation) => void; // Required - selection callback
|
|
338
|
+
selectedConversationId?: string; // Highlights active conversation
|
|
339
|
+
}
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
### 5.2 Chat Window
|
|
343
|
+
|
|
344
|
+
Full chat interface with messaging, file uploads, typing indicators, message status, block/unblock, and product context bar.
|
|
345
|
+
|
|
346
|
+
```tsx
|
|
347
|
+
import { ChatWindow } from '@traiyani/chatsdk-react';
|
|
348
|
+
|
|
349
|
+
<ChatWindow
|
|
350
|
+
conversation={selectedConversation}
|
|
351
|
+
currentUser={currentUser}
|
|
352
|
+
onClose={() => setSelectedConversation(null)}
|
|
353
|
+
onBack={() => setSelectedConversation(null)}
|
|
354
|
+
/>
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
**Props:**
|
|
358
|
+
|
|
359
|
+
```typescript
|
|
360
|
+
interface ChatWindowProps {
|
|
361
|
+
conversation: Conversation | null; // Required - active conversation
|
|
362
|
+
currentUser: ChatSDKUser; // Required - authenticated user
|
|
363
|
+
onClose?: () => void; // Called when close button pressed
|
|
364
|
+
onBack?: () => void; // Called when back button pressed (mobile)
|
|
365
|
+
}
|
|
366
|
+
```
|
|
367
|
+
|
|
368
|
+
### 5.3 Complete App Example
|
|
27
369
|
|
|
28
370
|
```tsx
|
|
29
371
|
import React, { useState, useEffect } from 'react';
|
|
30
|
-
import { ChatSDK,
|
|
372
|
+
import { ChatSDK, ConversationList, ChatWindow } from '@traiyani/chatsdk-react';
|
|
31
373
|
import '@traiyani/chatsdk-react/dist/chatsdk.css';
|
|
32
374
|
|
|
33
|
-
function
|
|
375
|
+
function ChatApp() {
|
|
34
376
|
const [currentUser, setCurrentUser] = useState(null);
|
|
35
377
|
const [selectedConversation, setSelectedConversation] = useState(null);
|
|
36
378
|
|
|
37
379
|
useEffect(() => {
|
|
38
380
|
const init = async () => {
|
|
39
381
|
const sdk = ChatSDK.getInstance();
|
|
40
|
-
|
|
41
382
|
await sdk.init({
|
|
42
|
-
apiBaseUrl:
|
|
43
|
-
appId:
|
|
383
|
+
apiBaseUrl: process.env.REACT_APP_API_URL,
|
|
384
|
+
appId: process.env.REACT_APP_APP_ID,
|
|
44
385
|
environment: 'production',
|
|
45
|
-
enableLogging: false,
|
|
46
|
-
autoConnect: false,
|
|
47
386
|
});
|
|
48
387
|
|
|
49
388
|
const user = await sdk.chatUsers.authenticate(
|
|
50
|
-
'user-123',
|
|
51
|
-
'your-app-id', // your ChatSDK app ID
|
|
52
|
-
'John Doe', // display name
|
|
53
|
-
'john@example.com' // email
|
|
389
|
+
'user-123', 'your-app-id', 'John Doe', 'john@example.com', true
|
|
54
390
|
);
|
|
55
|
-
|
|
56
391
|
await sdk.connect(user.id);
|
|
57
392
|
setCurrentUser(user);
|
|
58
393
|
};
|
|
@@ -63,241 +398,318 @@ function App() {
|
|
|
63
398
|
|
|
64
399
|
return (
|
|
65
400
|
<div style={{ display: 'flex', height: '100vh' }}>
|
|
66
|
-
<
|
|
67
|
-
|
|
68
|
-
onSelectConversation={setSelectedConversation}
|
|
69
|
-
selectedConversationId={selectedConversation?.id}
|
|
70
|
-
/>
|
|
71
|
-
{selectedConversation && (
|
|
72
|
-
<ChatWindow
|
|
73
|
-
conversation={selectedConversation}
|
|
401
|
+
<div style={{ width: '350px', borderRight: '1px solid #e0e0e0' }}>
|
|
402
|
+
<ConversationList
|
|
74
403
|
currentUser={currentUser}
|
|
75
|
-
|
|
76
|
-
|
|
404
|
+
onSelectConversation={setSelectedConversation}
|
|
405
|
+
selectedConversationId={selectedConversation?.id}
|
|
77
406
|
/>
|
|
78
|
-
|
|
407
|
+
</div>
|
|
408
|
+
<div style={{ flex: 1 }}>
|
|
409
|
+
{selectedConversation ? (
|
|
410
|
+
<ChatWindow
|
|
411
|
+
conversation={selectedConversation}
|
|
412
|
+
currentUser={currentUser}
|
|
413
|
+
onClose={() => setSelectedConversation(null)}
|
|
414
|
+
onBack={() => setSelectedConversation(null)}
|
|
415
|
+
/>
|
|
416
|
+
) : (
|
|
417
|
+
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', height: '100%' }}>
|
|
418
|
+
Select a conversation
|
|
419
|
+
</div>
|
|
420
|
+
)}
|
|
421
|
+
</div>
|
|
79
422
|
</div>
|
|
80
423
|
);
|
|
81
424
|
}
|
|
82
425
|
```
|
|
83
426
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
## Components
|
|
427
|
+
---
|
|
87
428
|
|
|
88
|
-
|
|
429
|
+
## 6. Sending Messages
|
|
89
430
|
|
|
90
|
-
|
|
431
|
+
### Send a Text Message
|
|
91
432
|
|
|
92
433
|
```tsx
|
|
93
|
-
|
|
94
|
-
conversation={selectedConversation}
|
|
95
|
-
currentUser={currentUser}
|
|
96
|
-
onClose={handleClose}
|
|
97
|
-
onBack={handleBack}
|
|
98
|
-
/>
|
|
99
|
-
```
|
|
434
|
+
const sdk = ChatSDK.getInstance();
|
|
100
435
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
| `onClose` | `() => void` | No | Called when user clicks close |
|
|
106
|
-
| `onBack` | `() => void` | No | Called when user clicks back (mobile) |
|
|
436
|
+
const message = await sdk.messages.sendMessage(
|
|
437
|
+
conversationId,
|
|
438
|
+
'Hello! Is this still available?'
|
|
439
|
+
);
|
|
107
440
|
|
|
108
|
-
|
|
441
|
+
console.log('Message sent:', message.id);
|
|
442
|
+
```
|
|
109
443
|
|
|
110
|
-
|
|
444
|
+
### Send with Metadata
|
|
111
445
|
|
|
112
446
|
```tsx
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
447
|
+
const message = await sdk.messages.sendMessageWithOptions({
|
|
448
|
+
conversationId: conversationId,
|
|
449
|
+
content: 'Here is my offer',
|
|
450
|
+
type: 'text',
|
|
451
|
+
metadata: { offerAmount: 4500, currency: 'QAR' },
|
|
452
|
+
});
|
|
118
453
|
```
|
|
119
454
|
|
|
120
|
-
|
|
121
|
-
|---|---|---|---|
|
|
122
|
-
| `currentUser` | `ChatSDKUser` | Yes | Authenticated user object |
|
|
123
|
-
| `onSelectConversation` | `(conv: ChatSDKConversation) => void` | Yes | Called when user selects a conversation |
|
|
124
|
-
| `selectedConversationId` | `string` | No | ID of currently selected conversation |
|
|
455
|
+
### Load Messages (with Pagination)
|
|
125
456
|
|
|
126
|
-
|
|
457
|
+
```tsx
|
|
458
|
+
const messages = await sdk.messages.getMessages({
|
|
459
|
+
conversationId: conversationId,
|
|
460
|
+
limit: 50,
|
|
461
|
+
offset: 0,
|
|
462
|
+
});
|
|
463
|
+
|
|
464
|
+
messages.forEach(msg => {
|
|
465
|
+
console.log(`${msg.senderName}: ${msg.content}`);
|
|
466
|
+
});
|
|
467
|
+
```
|
|
127
468
|
|
|
128
|
-
|
|
469
|
+
### Send Typing Indicator
|
|
129
470
|
|
|
130
471
|
```tsx
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
472
|
+
// User started typing
|
|
473
|
+
await sdk.messages.sendTypingIndicator(conversationId, true);
|
|
474
|
+
|
|
475
|
+
// User stopped typing
|
|
476
|
+
await sdk.messages.sendTypingIndicator(conversationId, false);
|
|
134
477
|
```
|
|
135
478
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
479
|
+
### Mark Messages as Read
|
|
480
|
+
|
|
481
|
+
```tsx
|
|
482
|
+
// Mark all messages in a conversation as read
|
|
483
|
+
await sdk.chatUsers.markRoomMessagesRead(conversationId);
|
|
484
|
+
|
|
485
|
+
// Mark a specific message as read
|
|
486
|
+
await sdk.chatUsers.markMessageRead(messageId);
|
|
487
|
+
```
|
|
140
488
|
|
|
141
|
-
|
|
489
|
+
---
|
|
142
490
|
|
|
143
|
-
|
|
491
|
+
## 7. File Uploads & Attachments
|
|
144
492
|
|
|
145
|
-
|
|
493
|
+
### Upload and Send a File
|
|
146
494
|
|
|
147
495
|
```tsx
|
|
148
|
-
|
|
496
|
+
const sdk = ChatSDK.getInstance();
|
|
149
497
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
498
|
+
const result = await sdk.media.uploadMedia({
|
|
499
|
+
file: selectedFile, // File object from input
|
|
500
|
+
type: 'image', // 'image' | 'video' | 'audio' | 'document'
|
|
501
|
+
conversationId: conversationId,
|
|
502
|
+
caption: 'Check this out',
|
|
503
|
+
onProgress: (progress) => {
|
|
504
|
+
console.log(`Upload: ${progress}%`);
|
|
505
|
+
},
|
|
506
|
+
});
|
|
507
|
+
|
|
508
|
+
console.log('Uploaded:', result.fileUrl);
|
|
154
509
|
```
|
|
155
510
|
|
|
156
|
-
###
|
|
157
|
-
|
|
158
|
-
Same API as `useTheme`, but reads from the nearest `<ThemeProvider>` so all descendants share state.
|
|
511
|
+
### Convenience Methods
|
|
159
512
|
|
|
160
513
|
```tsx
|
|
161
|
-
|
|
514
|
+
// Send image
|
|
515
|
+
await sdk.media.sendImage(conversationId, imageFile, 'Photo caption');
|
|
516
|
+
|
|
517
|
+
// Send video
|
|
518
|
+
await sdk.media.sendVideo(conversationId, videoFile);
|
|
162
519
|
|
|
163
|
-
|
|
520
|
+
// Send document (PDF, DOC, etc.)
|
|
521
|
+
await sdk.media.sendDocument(conversationId, pdfFile);
|
|
522
|
+
|
|
523
|
+
// Send audio
|
|
524
|
+
await sdk.media.sendAudio(conversationId, audioFile);
|
|
164
525
|
```
|
|
165
526
|
|
|
166
|
-
|
|
527
|
+
**Supported file types:** Images (`.jpg`, `.png`, `.gif`, `.webp`), Videos (`.mp4`, `.mov`), Documents (`.pdf`, `.doc`, `.docx`), Audio (`.mp3`, `.wav`)
|
|
167
528
|
|
|
168
|
-
|
|
529
|
+
**Note:** The built-in `<ChatWindow />` component handles file picking, uploading, and preview automatically.
|
|
169
530
|
|
|
170
|
-
|
|
171
|
-
// Create React App
|
|
172
|
-
await sdk.init({ apiBaseUrl: process.env.REACT_APP_API_URL, ... })
|
|
531
|
+
---
|
|
173
532
|
|
|
174
|
-
|
|
175
|
-
await sdk.init({ apiBaseUrl: import.meta.env.VITE_API_URL, ... })
|
|
533
|
+
## 8. Unread Message Counts
|
|
176
534
|
|
|
177
|
-
|
|
178
|
-
await sdk.init({ apiBaseUrl: process.env.NEXT_PUBLIC_API_URL, ... })
|
|
535
|
+
### Get Unread Conversations Count
|
|
179
536
|
|
|
180
|
-
|
|
181
|
-
await sdk.
|
|
537
|
+
```tsx
|
|
538
|
+
const count = await sdk.chatUsers.getUnreadConversationsCount();
|
|
539
|
+
console.log(`${count} conversations with unread messages`);
|
|
182
540
|
```
|
|
183
541
|
|
|
184
|
-
###
|
|
542
|
+
### Get Total Unread Message Count
|
|
185
543
|
|
|
186
|
-
```
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
wsUrl?: string;
|
|
190
|
-
appId: string;
|
|
191
|
-
apiKey?: string;
|
|
192
|
-
environment: 'development' | 'staging' | 'production';
|
|
193
|
-
enableLogging?: boolean;
|
|
194
|
-
autoConnect?: boolean;
|
|
195
|
-
timeout?: number;
|
|
196
|
-
}
|
|
544
|
+
```tsx
|
|
545
|
+
const total = await sdk.chatUsers.getTotalUnreadCount();
|
|
546
|
+
console.log(`${total} total unread messages`);
|
|
197
547
|
```
|
|
198
548
|
|
|
199
|
-
|
|
549
|
+
### Get Detailed Unread Summary
|
|
200
550
|
|
|
201
551
|
```tsx
|
|
202
|
-
const
|
|
552
|
+
const summary = await sdk.chatUsers.getUnreadSummary();
|
|
553
|
+
console.log('Total unread conversations:', summary.totalConversationsWithUnread);
|
|
554
|
+
console.log('Total unread messages:', summary.totalUnreadMessages);
|
|
203
555
|
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
{
|
|
208
|
-
productId: 'prod-123',
|
|
209
|
-
productName: 'iPhone 15 Pro',
|
|
210
|
-
productImage: 'https://example.com/iphone.jpg',
|
|
211
|
-
price: 4999,
|
|
212
|
-
currency: 'QAR',
|
|
213
|
-
category: 'Electronics',
|
|
214
|
-
}
|
|
215
|
-
);
|
|
556
|
+
summary.unreadSummary.forEach(item => {
|
|
557
|
+
console.log(`${item.room_name}: ${item.unread_count} unread`);
|
|
558
|
+
});
|
|
216
559
|
```
|
|
217
560
|
|
|
218
|
-
|
|
561
|
+
### Auto-Mark as Read
|
|
219
562
|
|
|
220
|
-
|
|
563
|
+
```tsx
|
|
564
|
+
// When user opens a conversation
|
|
565
|
+
sdk.chatUsers.startViewingConversation(conversationId);
|
|
566
|
+
|
|
567
|
+
// When user leaves the conversation
|
|
568
|
+
sdk.chatUsers.stopViewingConversation(conversationId);
|
|
569
|
+
```
|
|
570
|
+
|
|
571
|
+
---
|
|
572
|
+
|
|
573
|
+
## 9. Block & Unblock Users
|
|
221
574
|
|
|
222
575
|
```tsx
|
|
223
|
-
|
|
576
|
+
await sdk.users.blockUser(userIdToBlock);
|
|
577
|
+
await sdk.users.unblockUser(userIdToUnblock);
|
|
578
|
+
const blockedUsers = await sdk.users.getBlockedUsers();
|
|
579
|
+
```
|
|
224
580
|
|
|
225
|
-
|
|
226
|
-
|
|
581
|
+
**Notes:**
|
|
582
|
+
- Blocking is per-conversation, not global
|
|
583
|
+
- Blocked users cannot send messages in that conversation
|
|
584
|
+
- The built-in `<ChatWindow />` has a block/unblock UI built in
|
|
227
585
|
|
|
228
|
-
|
|
229
|
-
const convos = await sdk.conversations.getConversations({ limit: 20 });
|
|
586
|
+
---
|
|
230
587
|
|
|
231
|
-
|
|
232
|
-
const msgs = await sdk.messages.getMessages({ conversationId: 'abc', limit: 50 });
|
|
588
|
+
## 10. Real-Time Events
|
|
233
589
|
|
|
234
|
-
|
|
235
|
-
const result = await sdk.media.uploadMedia({ file, type: 'image', conversationId: 'abc' });
|
|
590
|
+
### Listen for Events
|
|
236
591
|
|
|
237
|
-
|
|
238
|
-
sdk
|
|
592
|
+
```tsx
|
|
593
|
+
const sdk = ChatSDK.getInstance();
|
|
594
|
+
|
|
595
|
+
sdk.socket.on('message_received', (data) => {
|
|
596
|
+
console.log('New message:', data.message.content);
|
|
597
|
+
});
|
|
598
|
+
|
|
599
|
+
sdk.socket.on('typing_indicator', (data) => {
|
|
600
|
+
console.log(data.isTyping ? 'User is typing...' : 'User stopped typing');
|
|
601
|
+
});
|
|
602
|
+
|
|
603
|
+
sdk.socket.on('user_status_changed', (data) => {
|
|
604
|
+
console.log('User status:', data.status);
|
|
605
|
+
});
|
|
606
|
+
|
|
607
|
+
sdk.socket.on('connection_status', (status) => {
|
|
608
|
+
console.log('WebSocket:', status);
|
|
609
|
+
});
|
|
239
610
|
```
|
|
240
611
|
|
|
241
|
-
|
|
612
|
+
### Available Event Types
|
|
613
|
+
|
|
614
|
+
| Event | Description |
|
|
615
|
+
|-------|-------------|
|
|
616
|
+
| `message_received` | New message arrived |
|
|
617
|
+
| `typing_indicator` | User started/stopped typing |
|
|
618
|
+
| `user_status_changed` | User online/offline status changed |
|
|
619
|
+
| `conversation_updated` | Conversation metadata changed |
|
|
620
|
+
| `connection_status` | WebSocket connected/disconnected/error |
|
|
621
|
+
|
|
622
|
+
### Socket Direct Operations
|
|
242
623
|
|
|
243
624
|
```tsx
|
|
244
|
-
|
|
625
|
+
sdk.socket.joinConversation(conversationId);
|
|
626
|
+
sdk.socket.leaveConversation(conversationId);
|
|
627
|
+
sdk.socket.sendTypingIndicator(conversationId, true);
|
|
628
|
+
const isConnected = sdk.socket.isConnected();
|
|
629
|
+
```
|
|
630
|
+
|
|
631
|
+
---
|
|
245
632
|
|
|
246
|
-
|
|
247
|
-
const message = t('welcome_message');
|
|
633
|
+
## 11. Theme Support
|
|
248
634
|
|
|
249
|
-
|
|
250
|
-
|
|
635
|
+
```tsx
|
|
636
|
+
import { ThemeProvider, ThemeToggle, useTheme } from '@traiyani/chatsdk-react';
|
|
251
637
|
|
|
252
|
-
//
|
|
253
|
-
|
|
254
|
-
|
|
638
|
+
// Option 1: ThemeProvider + ThemeToggle
|
|
639
|
+
function App() {
|
|
640
|
+
return (
|
|
641
|
+
<ThemeProvider>
|
|
642
|
+
<ThemeToggle showLabel />
|
|
643
|
+
</ThemeProvider>
|
|
644
|
+
);
|
|
255
645
|
}
|
|
646
|
+
|
|
647
|
+
// Option 2: useTheme hook
|
|
648
|
+
function MyComponent() {
|
|
649
|
+
const { theme, actualTheme, setTheme, toggleTheme } = useTheme();
|
|
650
|
+
return <button onClick={toggleTheme}>Current: {actualTheme}</button>;
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
// Option 3: Imperative
|
|
654
|
+
import { initializeTheme } from '@traiyani/chatsdk-react';
|
|
655
|
+
initializeTheme();
|
|
256
656
|
```
|
|
257
657
|
|
|
258
|
-
|
|
658
|
+
---
|
|
259
659
|
|
|
260
|
-
##
|
|
660
|
+
## 12. Internationalization (i18n)
|
|
261
661
|
|
|
262
|
-
|
|
662
|
+
Supported languages: **English** (`en`), **Arabic** (`ar` with RTL)
|
|
263
663
|
|
|
264
664
|
```tsx
|
|
265
|
-
import
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
ChatWindowProps,
|
|
272
|
-
ConversationListProps,
|
|
273
|
-
} from '@traiyani/chatsdk-react';
|
|
665
|
+
import { changeLanguage, getCurrentLanguage, isRTL, t } from '@traiyani/chatsdk-react';
|
|
666
|
+
|
|
667
|
+
const title = t('conversations_title');
|
|
668
|
+
changeLanguage('ar'); // Switches to Arabic + sets RTL
|
|
669
|
+
const lang = getCurrentLanguage(); // 'en' or 'ar'
|
|
670
|
+
const rtl = isRTL(); // true if Arabic
|
|
274
671
|
```
|
|
275
672
|
|
|
276
|
-
|
|
673
|
+
---
|
|
277
674
|
|
|
278
|
-
|
|
675
|
+
## 13. Logout & Cleanup
|
|
279
676
|
|
|
280
677
|
```tsx
|
|
281
|
-
|
|
678
|
+
const sdk = ChatSDK.getInstance();
|
|
679
|
+
|
|
680
|
+
// Disconnect WebSocket
|
|
681
|
+
sdk.socket.disconnect();
|
|
682
|
+
|
|
683
|
+
// Logout (clears token)
|
|
684
|
+
await sdk.chatUsers.logout();
|
|
282
685
|
```
|
|
283
686
|
|
|
284
|
-
|
|
687
|
+
---
|
|
285
688
|
|
|
286
|
-
##
|
|
689
|
+
## 14. TypeScript Support
|
|
287
690
|
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
691
|
+
Full type declarations are included. No extra `@types/*` packages needed.
|
|
692
|
+
|
|
693
|
+
```tsx
|
|
694
|
+
import type {
|
|
695
|
+
ChatSDKConfig, ChatSDKUser, ChatSDKMessage, ChatSDKConversation,
|
|
696
|
+
ProductContext, Product, ChatState, ChatWindowProps, ConversationListProps,
|
|
697
|
+
SendMessageOptions, MediaUploadOptions, MediaUploadResult,
|
|
698
|
+
CreateConversationOptions, EventCallback,
|
|
699
|
+
} from '@traiyani/chatsdk-react';
|
|
700
|
+
```
|
|
701
|
+
|
|
702
|
+
---
|
|
703
|
+
|
|
704
|
+
## 15. Troubleshooting
|
|
705
|
+
|
|
706
|
+
| Problem | Solution |
|
|
707
|
+
|---------|----------|
|
|
708
|
+
| WebSocket not connecting | Ensure `autoConnect: true` in config, verify API URL is reachable, check CORS |
|
|
709
|
+
| Messages not loading | Ensure user is authenticated first, verify `conversationId` is valid |
|
|
710
|
+
| CSS not applying | Ensure `import '@traiyani/chatsdk-react/dist/chatsdk.css'` is in your entry file |
|
|
711
|
+
| Authentication fails | Verify `apiBaseUrl` and `appId`, check browser console, ensure CORS is configured |
|
|
712
|
+
| "SDK not initialized" | Ensure `await sdk.init()` is called before any other SDK method |
|
|
301
713
|
|
|
302
714
|
## License
|
|
303
715
|
|
package/package.json
CHANGED