@traiyani/chatsdk-react 1.0.2 → 1.0.4
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 +686 -40
- package/dist/chatsdk-react.cjs +2 -2
- package/dist/chatsdk-react.cjs.map +1 -1
- package/dist/chatsdk-react.mjs +93 -93
- package/dist/chatsdk-react.mjs.map +1 -1
- package/dist/chatsdk.css +1 -1
- package/package.json +8 -7
package/README.md
CHANGED
|
@@ -16,67 +16,713 @@ React SDK for ChatSDK - Real-time chat solution with product-based conversations
|
|
|
16
16
|
- Ready-made UI components (Conversation List + Chat Window)
|
|
17
17
|
- Pre-built — works with any bundler, zero config
|
|
18
18
|
|
|
19
|
-
##
|
|
19
|
+
## Table of Contents
|
|
20
20
|
|
|
21
|
-
-
|
|
22
|
-
|
|
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)
|
|
23
36
|
|
|
24
|
-
|
|
37
|
+
---
|
|
38
|
+
|
|
39
|
+
## 1. Installation
|
|
25
40
|
|
|
26
41
|
```bash
|
|
27
42
|
npm install @traiyani/chatsdk-react axios socket.io-client
|
|
28
43
|
```
|
|
29
44
|
|
|
30
|
-
|
|
45
|
+
### Requirements
|
|
46
|
+
|
|
47
|
+
| Dependency | Minimum Version |
|
|
48
|
+
|---|---|
|
|
49
|
+
| `react` | >= 16.8.0 |
|
|
50
|
+
| `react-dom` | >= 16.8.0 |
|
|
51
|
+
| `axios` | >= 0.21.0 |
|
|
52
|
+
| `socket.io-client` | >= 4.0.0 |
|
|
53
|
+
|
|
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:
|
|
31
59
|
|
|
32
60
|
```tsx
|
|
33
61
|
import '@traiyani/chatsdk-react/dist/chatsdk.css';
|
|
34
62
|
```
|
|
35
63
|
|
|
36
|
-
|
|
64
|
+
---
|
|
37
65
|
|
|
38
|
-
|
|
66
|
+
## 2. SDK Initialization
|
|
39
67
|
|
|
40
|
-
|
|
41
|
-
2. User Authentication (login with `isLoggedinUser = true`)
|
|
42
|
-
3. Verify Other User (with `isLoggedinUser = false`)
|
|
43
|
-
4. Generate `externalGroupId` (GUID for fast room lookup)
|
|
44
|
-
5. Start Conversations (direct + product-based with metadata)
|
|
45
|
-
6. Using Ready-Made UI Components (ConversationList + ChatWindow)
|
|
46
|
-
7. Sending Messages
|
|
47
|
-
8. File Uploads
|
|
48
|
-
9. Unread Message Counts
|
|
49
|
-
10. Block & Unblock Users
|
|
50
|
-
11. Real-Time Events
|
|
51
|
-
12. Theme Support
|
|
52
|
-
13. Internationalization (i18n)
|
|
53
|
-
14. Logout & Cleanup
|
|
54
|
-
15. Troubleshooting
|
|
68
|
+
Initialize the SDK once in your app, before any authentication or chat operations.
|
|
55
69
|
|
|
56
|
-
|
|
70
|
+
```tsx
|
|
71
|
+
import { ChatSDK } from '@traiyani/chatsdk-react';
|
|
57
72
|
|
|
58
|
-
|
|
59
|
-
|---------|---------|
|
|
60
|
-
| `react` | >= 16.8.0 |
|
|
61
|
-
| `react-dom` | >= 16.8.0 |
|
|
62
|
-
| `axios` | >= 0.21.0 |
|
|
63
|
-
| `socket.io-client` | >= 4.0.0 |
|
|
73
|
+
const sdk = ChatSDK.getInstance();
|
|
64
74
|
|
|
65
|
-
|
|
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
|
+
```
|
|
66
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
|
+
}
|
|
67
97
|
```
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
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, ... });
|
|
74
112
|
```
|
|
75
113
|
|
|
76
|
-
|
|
114
|
+
---
|
|
77
115
|
|
|
78
|
-
|
|
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
|
+
return await hashString(base);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
async function hashString(input) {
|
|
196
|
+
if (typeof crypto !== 'undefined' && crypto.subtle) {
|
|
197
|
+
const data = new TextEncoder().encode(input);
|
|
198
|
+
const hashBuffer = await crypto.subtle.digest('SHA-256', data);
|
|
199
|
+
return Array.from(new Uint8Array(hashBuffer))
|
|
200
|
+
.map(b => b.toString(16).padStart(2, '0')).join('');
|
|
201
|
+
}
|
|
202
|
+
// Fallback for non-HTTPS environments (e.g. http://localhost)
|
|
203
|
+
let hash = 0x811c9dc5;
|
|
204
|
+
for (let i = 0; i < input.length; i++) {
|
|
205
|
+
hash ^= input.charCodeAt(i);
|
|
206
|
+
hash = (hash * 0x01000193) >>> 0;
|
|
207
|
+
}
|
|
208
|
+
return hash.toString(16).padStart(8, '0');
|
|
209
|
+
}
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
**Usage:**
|
|
213
|
+
|
|
214
|
+
```tsx
|
|
215
|
+
const currentUserId = sdk.getCurrentUser().id;
|
|
216
|
+
const externalGroupId = await generateExternalGroupId(currentUserId, otherUser.id, 'PRODUCT_123');
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
### 4.2 Simple Direct Chat
|
|
220
|
+
|
|
221
|
+
```tsx
|
|
222
|
+
const conversation = await sdk.chatUsers.startChat(
|
|
223
|
+
externalGroupId, // Generated GUID
|
|
224
|
+
otherUser.id // The other participant's chat user ID
|
|
225
|
+
);
|
|
226
|
+
|
|
227
|
+
console.log('Chat started:', conversation.id);
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
### 4.3 Chat with Product Context & Metadata
|
|
231
|
+
|
|
232
|
+
```tsx
|
|
233
|
+
// Create product context
|
|
234
|
+
const productContext = {
|
|
235
|
+
productId: 'CAR_456',
|
|
236
|
+
productName: 'Toyota Camry 2023',
|
|
237
|
+
productImage: 'https://example.com/car.jpg',
|
|
238
|
+
price: 25000,
|
|
239
|
+
currency: 'QAR',
|
|
240
|
+
category: 'Vehicles',
|
|
241
|
+
productMetadata: {
|
|
242
|
+
mileage: '50000',
|
|
243
|
+
transmission: 'Automatic',
|
|
244
|
+
year: '2023',
|
|
245
|
+
},
|
|
246
|
+
};
|
|
247
|
+
|
|
248
|
+
// Optional: custom chat metadata
|
|
249
|
+
const chatMetadata = {
|
|
250
|
+
dealType: 'sale',
|
|
251
|
+
negotiable: true,
|
|
252
|
+
priority: 'high',
|
|
253
|
+
source: 'featured_listing',
|
|
254
|
+
};
|
|
255
|
+
|
|
256
|
+
// Start product chat
|
|
257
|
+
const conversation = await sdk.chatUsers.startChatWithProduct(
|
|
258
|
+
externalGroupId, // Generated GUID (include productId in generation)
|
|
259
|
+
otherUser.id, // Seller's chat user ID
|
|
260
|
+
productContext, // Product information
|
|
261
|
+
chatMetadata // Optional chat metadata
|
|
262
|
+
);
|
|
263
|
+
|
|
264
|
+
console.log('Product chat started:', conversation.id);
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
### 4.4 Complete Flow: Login → Verify Seller → Start Product Chat
|
|
268
|
+
|
|
269
|
+
```tsx
|
|
270
|
+
import { ChatSDK } from '@traiyani/chatsdk-react';
|
|
271
|
+
|
|
272
|
+
const sdk = ChatSDK.getInstance();
|
|
273
|
+
|
|
274
|
+
// Step 1: Initialize SDK
|
|
275
|
+
await sdk.init({
|
|
276
|
+
apiBaseUrl: 'https://your-chat-api.com/api',
|
|
277
|
+
appId: 'your-app-id',
|
|
278
|
+
environment: 'production',
|
|
279
|
+
});
|
|
280
|
+
|
|
281
|
+
// Step 2: Login the current user (buyer)
|
|
282
|
+
const currentUser = await sdk.chatUsers.authenticate(
|
|
283
|
+
'buyer-external-id',
|
|
284
|
+
'your-app-id',
|
|
285
|
+
'Ahmed',
|
|
286
|
+
'ahmed@example.com',
|
|
287
|
+
true // isLoggedinUser = true → this is the logged-in user
|
|
288
|
+
);
|
|
289
|
+
await sdk.connect(currentUser.id);
|
|
290
|
+
|
|
291
|
+
// Step 3: Verify/register the other user (seller) — does NOT switch session
|
|
292
|
+
const seller = await sdk.chatUsers.authenticate(
|
|
293
|
+
'seller-external-id',
|
|
294
|
+
'your-app-id',
|
|
295
|
+
'Mohammed',
|
|
296
|
+
'mohammed@example.com',
|
|
297
|
+
false // isLoggedinUser = false → just verify existence
|
|
298
|
+
);
|
|
299
|
+
|
|
300
|
+
// Step 4: Generate externalGroupId
|
|
301
|
+
const externalGroupId = await generateExternalGroupId(
|
|
302
|
+
currentUser.id,
|
|
303
|
+
seller.id,
|
|
304
|
+
'PRODUCT_789'
|
|
305
|
+
);
|
|
306
|
+
|
|
307
|
+
// Step 5: Start chat with product
|
|
308
|
+
const conversation = await sdk.chatUsers.startChatWithProduct(
|
|
309
|
+
externalGroupId,
|
|
310
|
+
seller.id,
|
|
311
|
+
{
|
|
312
|
+
productId: 'PRODUCT_789',
|
|
313
|
+
productName: 'iPhone 15 Pro',
|
|
314
|
+
productImage: 'https://example.com/iphone.jpg',
|
|
315
|
+
price: 4999,
|
|
316
|
+
currency: 'QAR',
|
|
317
|
+
category: 'Electronics',
|
|
318
|
+
}
|
|
319
|
+
);
|
|
320
|
+
|
|
321
|
+
// Step 6: Now show ChatWindow component with this conversation
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
---
|
|
325
|
+
|
|
326
|
+
## 5. Using Ready-Made UI Components
|
|
327
|
+
|
|
328
|
+
The SDK includes two ready-made React components with full functionality.
|
|
329
|
+
|
|
330
|
+
### 5.1 Conversation List
|
|
331
|
+
|
|
332
|
+
Displays all conversations with last message preview, unread badges, product context, and real-time updates.
|
|
333
|
+
|
|
334
|
+
```tsx
|
|
335
|
+
import { ConversationList } from '@traiyani/chatsdk-react';
|
|
336
|
+
|
|
337
|
+
<ConversationList
|
|
338
|
+
currentUser={currentUser}
|
|
339
|
+
onSelectConversation={(conversation) => setSelectedConversation(conversation)}
|
|
340
|
+
selectedConversationId={selectedConversation?.id}
|
|
341
|
+
/>
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
**Props:**
|
|
345
|
+
|
|
346
|
+
```typescript
|
|
347
|
+
interface ConversationListProps {
|
|
348
|
+
currentUser: ChatSDKUser; // Required - authenticated user
|
|
349
|
+
onSelectConversation: (conv: Conversation) => void; // Required - selection callback
|
|
350
|
+
selectedConversationId?: string; // Highlights active conversation
|
|
351
|
+
}
|
|
352
|
+
```
|
|
353
|
+
|
|
354
|
+
### 5.2 Chat Window
|
|
355
|
+
|
|
356
|
+
Full chat interface with messaging, file uploads, typing indicators, message status, block/unblock, and product context bar.
|
|
357
|
+
|
|
358
|
+
```tsx
|
|
359
|
+
import { ChatWindow } from '@traiyani/chatsdk-react';
|
|
360
|
+
|
|
361
|
+
<ChatWindow
|
|
362
|
+
conversation={selectedConversation}
|
|
363
|
+
currentUser={currentUser}
|
|
364
|
+
onClose={() => setSelectedConversation(null)}
|
|
365
|
+
onBack={() => setSelectedConversation(null)}
|
|
366
|
+
/>
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
**Props:**
|
|
370
|
+
|
|
371
|
+
```typescript
|
|
372
|
+
interface ChatWindowProps {
|
|
373
|
+
conversation: Conversation | null; // Required - active conversation
|
|
374
|
+
currentUser: ChatSDKUser; // Required - authenticated user
|
|
375
|
+
onClose?: () => void; // Called when close button pressed
|
|
376
|
+
onBack?: () => void; // Called when back button pressed (mobile)
|
|
377
|
+
}
|
|
378
|
+
```
|
|
379
|
+
|
|
380
|
+
### 5.3 Complete App Example
|
|
381
|
+
|
|
382
|
+
```tsx
|
|
383
|
+
import React, { useState, useEffect } from 'react';
|
|
384
|
+
import { ChatSDK, ConversationList, ChatWindow } from '@traiyani/chatsdk-react';
|
|
385
|
+
import '@traiyani/chatsdk-react/dist/chatsdk.css';
|
|
386
|
+
|
|
387
|
+
function ChatApp() {
|
|
388
|
+
const [currentUser, setCurrentUser] = useState(null);
|
|
389
|
+
const [selectedConversation, setSelectedConversation] = useState(null);
|
|
390
|
+
|
|
391
|
+
useEffect(() => {
|
|
392
|
+
const init = async () => {
|
|
393
|
+
const sdk = ChatSDK.getInstance();
|
|
394
|
+
await sdk.init({
|
|
395
|
+
apiBaseUrl: process.env.REACT_APP_API_URL,
|
|
396
|
+
appId: process.env.REACT_APP_APP_ID,
|
|
397
|
+
environment: 'production',
|
|
398
|
+
});
|
|
399
|
+
|
|
400
|
+
const user = await sdk.chatUsers.authenticate(
|
|
401
|
+
'user-123', 'your-app-id', 'John Doe', 'john@example.com', true
|
|
402
|
+
);
|
|
403
|
+
await sdk.connect(user.id);
|
|
404
|
+
setCurrentUser(user);
|
|
405
|
+
};
|
|
406
|
+
init();
|
|
407
|
+
}, []);
|
|
408
|
+
|
|
409
|
+
if (!currentUser) return <div>Loading...</div>;
|
|
410
|
+
|
|
411
|
+
return (
|
|
412
|
+
<div style={{ display: 'flex', height: '100vh' }}>
|
|
413
|
+
<div style={{ width: '350px', borderRight: '1px solid #e0e0e0' }}>
|
|
414
|
+
<ConversationList
|
|
415
|
+
currentUser={currentUser}
|
|
416
|
+
onSelectConversation={setSelectedConversation}
|
|
417
|
+
selectedConversationId={selectedConversation?.id}
|
|
418
|
+
/>
|
|
419
|
+
</div>
|
|
420
|
+
<div style={{ flex: 1 }}>
|
|
421
|
+
{selectedConversation ? (
|
|
422
|
+
<ChatWindow
|
|
423
|
+
conversation={selectedConversation}
|
|
424
|
+
currentUser={currentUser}
|
|
425
|
+
onClose={() => setSelectedConversation(null)}
|
|
426
|
+
onBack={() => setSelectedConversation(null)}
|
|
427
|
+
/>
|
|
428
|
+
) : (
|
|
429
|
+
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', height: '100%' }}>
|
|
430
|
+
Select a conversation
|
|
431
|
+
</div>
|
|
432
|
+
)}
|
|
433
|
+
</div>
|
|
434
|
+
</div>
|
|
435
|
+
);
|
|
436
|
+
}
|
|
437
|
+
```
|
|
438
|
+
|
|
439
|
+
---
|
|
440
|
+
|
|
441
|
+
## 6. Sending Messages
|
|
442
|
+
|
|
443
|
+
### Send a Text Message
|
|
79
444
|
|
|
80
|
-
|
|
445
|
+
```tsx
|
|
446
|
+
const sdk = ChatSDK.getInstance();
|
|
447
|
+
|
|
448
|
+
const message = await sdk.messages.sendMessage(
|
|
449
|
+
conversationId,
|
|
450
|
+
'Hello! Is this still available?'
|
|
451
|
+
);
|
|
452
|
+
|
|
453
|
+
console.log('Message sent:', message.id);
|
|
454
|
+
```
|
|
455
|
+
|
|
456
|
+
### Send with Metadata
|
|
457
|
+
|
|
458
|
+
```tsx
|
|
459
|
+
const message = await sdk.messages.sendMessageWithOptions({
|
|
460
|
+
conversationId: conversationId,
|
|
461
|
+
content: 'Here is my offer',
|
|
462
|
+
type: 'text',
|
|
463
|
+
metadata: { offerAmount: 4500, currency: 'QAR' },
|
|
464
|
+
});
|
|
465
|
+
```
|
|
466
|
+
|
|
467
|
+
### Load Messages (with Pagination)
|
|
468
|
+
|
|
469
|
+
```tsx
|
|
470
|
+
const messages = await sdk.messages.getMessages({
|
|
471
|
+
conversationId: conversationId,
|
|
472
|
+
limit: 50,
|
|
473
|
+
offset: 0,
|
|
474
|
+
});
|
|
475
|
+
|
|
476
|
+
messages.forEach(msg => {
|
|
477
|
+
console.log(`${msg.senderName}: ${msg.content}`);
|
|
478
|
+
});
|
|
479
|
+
```
|
|
480
|
+
|
|
481
|
+
### Send Typing Indicator
|
|
482
|
+
|
|
483
|
+
```tsx
|
|
484
|
+
// User started typing
|
|
485
|
+
await sdk.messages.sendTypingIndicator(conversationId, true);
|
|
486
|
+
|
|
487
|
+
// User stopped typing
|
|
488
|
+
await sdk.messages.sendTypingIndicator(conversationId, false);
|
|
489
|
+
```
|
|
490
|
+
|
|
491
|
+
### Mark Messages as Read
|
|
492
|
+
|
|
493
|
+
```tsx
|
|
494
|
+
// Mark all messages in a conversation as read
|
|
495
|
+
await sdk.chatUsers.markRoomMessagesRead(conversationId);
|
|
496
|
+
|
|
497
|
+
// Mark a specific message as read
|
|
498
|
+
await sdk.chatUsers.markMessageRead(messageId);
|
|
499
|
+
```
|
|
500
|
+
|
|
501
|
+
---
|
|
502
|
+
|
|
503
|
+
## 7. File Uploads & Attachments
|
|
504
|
+
|
|
505
|
+
### Upload and Send a File
|
|
506
|
+
|
|
507
|
+
```tsx
|
|
508
|
+
const sdk = ChatSDK.getInstance();
|
|
509
|
+
|
|
510
|
+
const result = await sdk.media.uploadMedia({
|
|
511
|
+
file: selectedFile, // File object from input
|
|
512
|
+
type: 'image', // 'image' | 'video' | 'audio' | 'document'
|
|
513
|
+
conversationId: conversationId,
|
|
514
|
+
caption: 'Check this out',
|
|
515
|
+
onProgress: (progress) => {
|
|
516
|
+
console.log(`Upload: ${progress}%`);
|
|
517
|
+
},
|
|
518
|
+
});
|
|
519
|
+
|
|
520
|
+
console.log('Uploaded:', result.fileUrl);
|
|
521
|
+
```
|
|
522
|
+
|
|
523
|
+
### Convenience Methods
|
|
524
|
+
|
|
525
|
+
```tsx
|
|
526
|
+
// Send image
|
|
527
|
+
await sdk.media.sendImage(conversationId, imageFile, 'Photo caption');
|
|
528
|
+
|
|
529
|
+
// Send video
|
|
530
|
+
await sdk.media.sendVideo(conversationId, videoFile);
|
|
531
|
+
|
|
532
|
+
// Send document (PDF, DOC, etc.)
|
|
533
|
+
await sdk.media.sendDocument(conversationId, pdfFile);
|
|
534
|
+
|
|
535
|
+
// Send audio
|
|
536
|
+
await sdk.media.sendAudio(conversationId, audioFile);
|
|
537
|
+
```
|
|
538
|
+
|
|
539
|
+
**Supported file types:** Images (`.jpg`, `.png`, `.gif`, `.webp`), Videos (`.mp4`, `.mov`), Documents (`.pdf`, `.doc`, `.docx`), Audio (`.mp3`, `.wav`)
|
|
540
|
+
|
|
541
|
+
**Note:** The built-in `<ChatWindow />` component handles file picking, uploading, and preview automatically.
|
|
542
|
+
|
|
543
|
+
---
|
|
544
|
+
|
|
545
|
+
## 8. Unread Message Counts
|
|
546
|
+
|
|
547
|
+
### Get Unread Conversations Count
|
|
548
|
+
|
|
549
|
+
```tsx
|
|
550
|
+
const count = await sdk.chatUsers.getUnreadConversationsCount();
|
|
551
|
+
console.log(`${count} conversations with unread messages`);
|
|
552
|
+
```
|
|
553
|
+
|
|
554
|
+
### Get Total Unread Message Count
|
|
555
|
+
|
|
556
|
+
```tsx
|
|
557
|
+
const total = await sdk.chatUsers.getTotalUnreadCount();
|
|
558
|
+
console.log(`${total} total unread messages`);
|
|
559
|
+
```
|
|
560
|
+
|
|
561
|
+
### Get Detailed Unread Summary
|
|
562
|
+
|
|
563
|
+
```tsx
|
|
564
|
+
const summary = await sdk.chatUsers.getUnreadSummary();
|
|
565
|
+
console.log('Total unread conversations:', summary.totalConversationsWithUnread);
|
|
566
|
+
console.log('Total unread messages:', summary.totalUnreadMessages);
|
|
567
|
+
|
|
568
|
+
summary.unreadSummary.forEach(item => {
|
|
569
|
+
console.log(`${item.room_name}: ${item.unread_count} unread`);
|
|
570
|
+
});
|
|
571
|
+
```
|
|
572
|
+
|
|
573
|
+
### Auto-Mark as Read
|
|
574
|
+
|
|
575
|
+
```tsx
|
|
576
|
+
// When user opens a conversation
|
|
577
|
+
sdk.chatUsers.startViewingConversation(conversationId);
|
|
81
578
|
|
|
82
|
-
|
|
579
|
+
// When user leaves the conversation
|
|
580
|
+
sdk.chatUsers.stopViewingConversation(conversationId);
|
|
581
|
+
```
|
|
582
|
+
|
|
583
|
+
---
|
|
584
|
+
|
|
585
|
+
## 9. Block & Unblock Users
|
|
586
|
+
|
|
587
|
+
```tsx
|
|
588
|
+
await sdk.users.blockUser(userIdToBlock);
|
|
589
|
+
await sdk.users.unblockUser(userIdToUnblock);
|
|
590
|
+
const blockedUsers = await sdk.users.getBlockedUsers();
|
|
591
|
+
```
|
|
592
|
+
|
|
593
|
+
**Notes:**
|
|
594
|
+
- Blocking is per-conversation, not global
|
|
595
|
+
- Blocked users cannot send messages in that conversation
|
|
596
|
+
- The built-in `<ChatWindow />` has a block/unblock UI built in
|
|
597
|
+
|
|
598
|
+
---
|
|
599
|
+
|
|
600
|
+
## 10. Real-Time Events
|
|
601
|
+
|
|
602
|
+
### Listen for Events
|
|
603
|
+
|
|
604
|
+
```tsx
|
|
605
|
+
const sdk = ChatSDK.getInstance();
|
|
606
|
+
|
|
607
|
+
sdk.socket.on('message_received', (data) => {
|
|
608
|
+
console.log('New message:', data.message.content);
|
|
609
|
+
});
|
|
610
|
+
|
|
611
|
+
sdk.socket.on('typing_indicator', (data) => {
|
|
612
|
+
console.log(data.isTyping ? 'User is typing...' : 'User stopped typing');
|
|
613
|
+
});
|
|
614
|
+
|
|
615
|
+
sdk.socket.on('user_status_changed', (data) => {
|
|
616
|
+
console.log('User status:', data.status);
|
|
617
|
+
});
|
|
618
|
+
|
|
619
|
+
sdk.socket.on('connection_status', (status) => {
|
|
620
|
+
console.log('WebSocket:', status);
|
|
621
|
+
});
|
|
622
|
+
```
|
|
623
|
+
|
|
624
|
+
### Available Event Types
|
|
625
|
+
|
|
626
|
+
| Event | Description |
|
|
627
|
+
|-------|-------------|
|
|
628
|
+
| `message_received` | New message arrived |
|
|
629
|
+
| `typing_indicator` | User started/stopped typing |
|
|
630
|
+
| `user_status_changed` | User online/offline status changed |
|
|
631
|
+
| `conversation_updated` | Conversation metadata changed |
|
|
632
|
+
| `connection_status` | WebSocket connected/disconnected/error |
|
|
633
|
+
|
|
634
|
+
### Socket Direct Operations
|
|
635
|
+
|
|
636
|
+
```tsx
|
|
637
|
+
sdk.socket.joinConversation(conversationId);
|
|
638
|
+
sdk.socket.leaveConversation(conversationId);
|
|
639
|
+
sdk.socket.sendTypingIndicator(conversationId, true);
|
|
640
|
+
const isConnected = sdk.socket.isConnected();
|
|
641
|
+
```
|
|
642
|
+
|
|
643
|
+
---
|
|
644
|
+
|
|
645
|
+
## 11. Theme Support
|
|
646
|
+
|
|
647
|
+
```tsx
|
|
648
|
+
import { ThemeProvider, ThemeToggle, useTheme } from '@traiyani/chatsdk-react';
|
|
649
|
+
|
|
650
|
+
// Option 1: ThemeProvider + ThemeToggle
|
|
651
|
+
function App() {
|
|
652
|
+
return (
|
|
653
|
+
<ThemeProvider>
|
|
654
|
+
<ThemeToggle showLabel />
|
|
655
|
+
</ThemeProvider>
|
|
656
|
+
);
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
// Option 2: useTheme hook
|
|
660
|
+
function MyComponent() {
|
|
661
|
+
const { theme, actualTheme, setTheme, toggleTheme } = useTheme();
|
|
662
|
+
return <button onClick={toggleTheme}>Current: {actualTheme}</button>;
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
// Option 3: Imperative
|
|
666
|
+
import { initializeTheme } from '@traiyani/chatsdk-react';
|
|
667
|
+
initializeTheme();
|
|
668
|
+
```
|
|
669
|
+
|
|
670
|
+
---
|
|
671
|
+
|
|
672
|
+
## 12. Internationalization (i18n)
|
|
673
|
+
|
|
674
|
+
Supported languages: **English** (`en`), **Arabic** (`ar` with RTL)
|
|
675
|
+
|
|
676
|
+
```tsx
|
|
677
|
+
import { changeLanguage, getCurrentLanguage, isRTL, t } from '@traiyani/chatsdk-react';
|
|
678
|
+
|
|
679
|
+
const title = t('conversations_title');
|
|
680
|
+
changeLanguage('ar'); // Switches to Arabic + sets RTL
|
|
681
|
+
const lang = getCurrentLanguage(); // 'en' or 'ar'
|
|
682
|
+
const rtl = isRTL(); // true if Arabic
|
|
683
|
+
```
|
|
684
|
+
|
|
685
|
+
---
|
|
686
|
+
|
|
687
|
+
## 13. Logout & Cleanup
|
|
688
|
+
|
|
689
|
+
```tsx
|
|
690
|
+
const sdk = ChatSDK.getInstance();
|
|
691
|
+
|
|
692
|
+
// Disconnect WebSocket
|
|
693
|
+
sdk.socket.disconnect();
|
|
694
|
+
|
|
695
|
+
// Logout (clears token)
|
|
696
|
+
await sdk.chatUsers.logout();
|
|
697
|
+
```
|
|
698
|
+
|
|
699
|
+
---
|
|
700
|
+
|
|
701
|
+
## 14. TypeScript Support
|
|
702
|
+
|
|
703
|
+
Full type declarations are included. No extra `@types/*` packages needed.
|
|
704
|
+
|
|
705
|
+
```tsx
|
|
706
|
+
import type {
|
|
707
|
+
ChatSDKConfig, ChatSDKUser, ChatSDKMessage, ChatSDKConversation,
|
|
708
|
+
ProductContext, Product, ChatState, ChatWindowProps, ConversationListProps,
|
|
709
|
+
SendMessageOptions, MediaUploadOptions, MediaUploadResult,
|
|
710
|
+
CreateConversationOptions, EventCallback,
|
|
711
|
+
} from '@traiyani/chatsdk-react';
|
|
712
|
+
```
|
|
713
|
+
|
|
714
|
+
---
|
|
715
|
+
|
|
716
|
+
## 15. Troubleshooting
|
|
717
|
+
|
|
718
|
+
| Problem | Solution |
|
|
719
|
+
|---------|----------|
|
|
720
|
+
| WebSocket not connecting | Ensure `autoConnect: true` in config, verify API URL is reachable, check CORS |
|
|
721
|
+
| Messages not loading | Ensure user is authenticated first, verify `conversationId` is valid |
|
|
722
|
+
| CSS not applying | Ensure `import '@traiyani/chatsdk-react/dist/chatsdk.css'` is in your entry file |
|
|
723
|
+
| Authentication fails | Verify `apiBaseUrl` and `appId`, check browser console, ensure CORS is configured |
|
|
724
|
+
| "SDK not initialized" | Ensure `await sdk.init()` is called before any other SDK method |
|
|
725
|
+
|
|
726
|
+
## License
|
|
727
|
+
|
|
728
|
+
MIT
|