@vserifsaglam/chat-react-client 1.0.2

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 ADDED
@@ -0,0 +1,674 @@
1
+ # Chat React Client
2
+
3
+ > A React library for integratin with the Chat Backend API
4
+
5
+ [![NPM](https://img.shields.io/npm/v/chat-react-client.svg)](https://www.npmjs.com/package/chat-react-client) [![JavaScript Style Guide](https://img.shields.io/badge/code_style-standard-brightgreen.svg)](https://standardjs.com)
6
+
7
+ ## Features
8
+
9
+ - 🔒 Secure authentication with JWT tokens
10
+ - 🔄 Real-time chat functionality with WebSocket
11
+ - 📁 File uploads and attachments
12
+ - 📱 Responsive design
13
+ - 🧩 Modular architecture with React hooks
14
+ - 📝 TypeScript support
15
+ - 🔁 Automatic reconnection and fallback mechanisms
16
+ - 📋 Message queue for handling failed sends
17
+ - 📄 Pagination support for message history
18
+ - 🔔 Unread message indicators
19
+ - 🔄 Conversation and message management hooks for simplified integration
20
+
21
+ ## Installation
22
+
23
+ ```bash
24
+ npm install --save chat-react-client
25
+ # or
26
+ yarn add chat-react-client
27
+ ```
28
+
29
+ ## Quick Start
30
+
31
+ Wrap your application with the `ChatClient` component to provide authentication and API access:
32
+
33
+ ```tsx
34
+ import React from 'react';
35
+ import { ChatClient, ConnectionType } from 'chat-react-client';
36
+
37
+ const App = () => {
38
+ return (
39
+ <ChatClient
40
+ token="your_jwt_token"
41
+ baseUrl="https://api.example.com"
42
+ connectionOptions={{
43
+ connectionType: ConnectionType.WebSocket,
44
+ autoReconnect: true
45
+ }}
46
+ >
47
+ <YourChatComponent />
48
+ </ChatClient>
49
+ );
50
+ };
51
+
52
+ // Example with long polling only
53
+ const AppWithLongPolling = () => {
54
+ return (
55
+ <ChatClient
56
+ token="your_jwt_token"
57
+ baseUrl="https://api.example.com"
58
+ connectionOptions={{
59
+ connectionType: ConnectionType.LongPolling,
60
+ onlyLongpolling: true,
61
+ autoReconnect: true
62
+ }}
63
+ >
64
+ <YourChatComponent />
65
+ </ChatClient>
66
+ );
67
+ };
68
+
69
+ export default App;
70
+ ```
71
+
72
+ The `connectionOptions` prop allows you to configure the realtime connection:
73
+
74
+ - `connectionType`: Select the connection type (WebSocket or LongPolling)
75
+ - `autoReconnect`: Automatically reconnect if the connection is lost
76
+ - `onlyLongpolling`: Force the connection to use long polling only (no WebSocket)
77
+
78
+ ## Hooks
79
+
80
+ ### useConversationManagement
81
+
82
+ A higher-level hook for managing conversations, including conversation selection and state management:
83
+
84
+ ```tsx
85
+ import React from 'react';
86
+ import { useConversationManagement } from 'chat-react-client';
87
+
88
+ const ConversationsManager = () => {
89
+ const {
90
+ conversations,
91
+ conversationsLoading,
92
+ selectedConversationId,
93
+ selectedConversation,
94
+ handleSelectConversation,
95
+ getConversations
96
+ } = useConversationManagement();
97
+
98
+ if (conversationsLoading) return <div>Loading conversations...</div>;
99
+
100
+ return (
101
+ <div className="chat-container">
102
+ <div className="conversations-list">
103
+ {conversations.map(conversation => (
104
+ <div
105
+ key={conversation.id}
106
+ className={`conversation ${selectedConversationId === conversation.id ? 'selected' : ''}`}
107
+ onClick={() => handleSelectConversation(conversation)}
108
+ >
109
+ <h3>{conversation.user.name}</h3>
110
+ <p>{conversation.lastMessage?.content}</p>
111
+ {conversation.unreadCount > 0 && (
112
+ <span className="unread-badge">{conversation.unreadCount}</span>
113
+ )}
114
+ </div>
115
+ ))}
116
+ </div>
117
+
118
+ {selectedConversation && (
119
+ <div className="conversation-details">
120
+ <h2>Chat with {selectedConversation.user.name}</h2>
121
+ <MessagePanel conversationId={selectedConversationId} />
122
+ </div>
123
+ )}
124
+ </div>
125
+ );
126
+ };
127
+ ```
128
+
129
+ ### useMessageManagement
130
+
131
+ A higher-level hook for managing messages in a conversation, including pagination, file uploads, and real-time updates:
132
+
133
+ ```tsx
134
+ import React, { useState, useRef } from 'react';
135
+ import { useMessageManagement } from 'chat-react-client';
136
+
137
+ const MessagePanel = ({ conversationId, receiverId }) => {
138
+ const [messageText, setMessageText] = useState('');
139
+ const [file, setFile] = useState(null);
140
+ const messagesEndRef = useRef(null);
141
+
142
+ const {
143
+ messages,
144
+ messagesLoading,
145
+ fileLoading,
146
+ uploadProgress,
147
+ uploadFile,
148
+ handleSendMessage,
149
+ handleSendFileMessage,
150
+ loadMoreMessages,
151
+ hasMore
152
+ } = useMessageManagement(conversationId);
153
+
154
+ const handleSend = async () => {
155
+ if (!messageText.trim()) return;
156
+
157
+ const success = await handleSendMessage(messageText, receiverId);
158
+ if (success) {
159
+ setMessageText('');
160
+ }
161
+ };
162
+
163
+ const handleFileUpload = async (e) => {
164
+ const selectedFile = e.target.files[0];
165
+ if (!selectedFile) return;
166
+
167
+ setFile(selectedFile);
168
+ const fileId = await uploadFile(selectedFile);
169
+
170
+ if (fileId) {
171
+ await handleSendFileMessage(`Sent a file: ${selectedFile.name}`, fileId, receiverId);
172
+ setFile(null);
173
+ }
174
+ };
175
+
176
+ return (
177
+ <div className="message-panel">
178
+ {hasMore && (
179
+ <button
180
+ onClick={loadMoreMessages}
181
+ disabled={messagesLoading}
182
+ className="load-more-button"
183
+ >
184
+ {messagesLoading ? 'Loading...' : 'Load Previous Messages'}
185
+ </button>
186
+ )}
187
+
188
+ <div className="messages-container">
189
+ {messages.map(message => (
190
+ <div key={message.id} className={`message ${message.is_mine ? 'mine' : 'theirs'}`}>
191
+ <p>{message.content}</p>
192
+ {message.attachments?.length > 0 && (
193
+ <div className="attachments">
194
+ {message.attachments.map(attachment => (
195
+ <a key={attachment.id} href={attachment.url} target="_blank" rel="noopener noreferrer">
196
+ {attachment.filename}
197
+ </a>
198
+ ))}
199
+ </div>
200
+ )}
201
+ <small>{new Date(message.timestamp).toLocaleString()}</small>
202
+ </div>
203
+ ))}
204
+ <div ref={messagesEndRef} />
205
+ </div>
206
+
207
+ <div className="message-input">
208
+ {fileLoading && (
209
+ <div className="upload-progress">
210
+ <div className="progress-bar" style={{ width: `${uploadProgress}%` }}></div>
211
+ <span>{uploadProgress}%</span>
212
+ </div>
213
+ )}
214
+
215
+ <input
216
+ type="text"
217
+ value={messageText}
218
+ onChange={e => setMessageText(e.target.value)}
219
+ placeholder="Type a message..."
220
+ />
221
+ <input type="file" onChange={handleFileUpload} />
222
+ <button onClick={handleSend} disabled={messagesLoading || fileLoading}>Send</button>
223
+ </div>
224
+ </div>
225
+ );
226
+ };
227
+ ```
228
+
229
+ ### useConversations
230
+
231
+ Access and manage conversations:
232
+
233
+ ```tsx
234
+ import React, { useEffect } from 'react';
235
+ import { useConversations } from 'chat-react-client';
236
+
237
+ const ConversationsList = () => {
238
+ const {
239
+ conversations,
240
+ loading,
241
+ error,
242
+ getConversations,
243
+ archiveConversation,
244
+ unarchiveConversation,
245
+ deleteConversation
246
+ } = useConversations();
247
+
248
+ useEffect(() => {
249
+ getConversations();
250
+ }, [getConversations]);
251
+
252
+ if (loading) return <div>Loading...</div>;
253
+ if (error) return <div>Error: {error.message}</div>;
254
+
255
+ return (
256
+ <div>
257
+ {conversations.map(conversation => (
258
+ <div key={conversation.id}>
259
+ <h3>{conversation.user.name}</h3>
260
+ <p>{conversation.lastMessage?.content}</p>
261
+ <button onClick={() => archiveConversation(conversation.id)}>
262
+ Archive
263
+ </button>
264
+ </div>
265
+ ))}
266
+ </div>
267
+ );
268
+ };
269
+ ```
270
+
271
+ ### useMessages
272
+
273
+ Send and receive messages:
274
+
275
+ ```tsx
276
+ import React, { useState, useEffect } from 'react';
277
+ import { useMessages } from 'chat-react-client';
278
+
279
+ const ChatMessages = ({ conversationId }) => {
280
+ const [messageText, setMessageText] = useState('');
281
+ const {
282
+ messages,
283
+ loading,
284
+ sendMessage,
285
+ getMessages,
286
+ markConversationAsRead
287
+ } = useMessages(conversationId);
288
+
289
+ useEffect(() => {
290
+ if (conversationId) {
291
+ getMessages();
292
+ markConversationAsRead();
293
+ }
294
+ }, [conversationId, getMessages, markConversationAsRead]);
295
+
296
+ const handleSend = async () => {
297
+ if (!messageText.trim()) return;
298
+
299
+ await sendMessage({
300
+ receiver_client_id: 'recipient_id',
301
+ content: messageText
302
+ });
303
+
304
+ setMessageText('');
305
+ };
306
+
307
+ return (
308
+ <div>
309
+ <div className="messages-container">
310
+ {messages.map(message => (
311
+ <div key={message.id} className="message">
312
+ <p>{message.content}</p>
313
+ <small>{new Date(message.timestamp).toLocaleString()}</small>
314
+ </div>
315
+ ))}
316
+ </div>
317
+
318
+ <div className="message-input">
319
+ <input
320
+ type="text"
321
+ value={messageText}
322
+ onChange={e => setMessageText(e.target.value)}
323
+ placeholder="Type a message..."
324
+ />
325
+ <button onClick={handleSend} disabled={loading}>Send</button>
326
+ </div>
327
+ </div>
328
+ );
329
+ };
330
+ ```
331
+
332
+ ### useFiles
333
+
334
+ Upload files and attachments:
335
+
336
+ ```tsx
337
+ import React, { useState } from 'react';
338
+ import { useFiles, useMessages } from 'chat-react-client';
339
+
340
+ const FileUploader = ({ conversationId }) => {
341
+ const { uploadFile, uploadProgress, loading } = useFiles();
342
+ const { sendMessage } = useMessages(conversationId);
343
+ const [file, setFile] = useState(null);
344
+
345
+ const handleFileChange = (e) => {
346
+ setFile(e.target.files[0]);
347
+ };
348
+
349
+ const handleUpload = async () => {
350
+ if (!file) return;
351
+
352
+ const response = await uploadFile(file);
353
+
354
+ if (response) {
355
+ // Send a message with the file attachment
356
+ await sendMessage({
357
+ receiver_client_id: 'recipient_id',
358
+ content: 'I sent you a file',
359
+ attachments: [response.file_id]
360
+ });
361
+ }
362
+ };
363
+
364
+ return (
365
+ <div>
366
+ <input type="file" onChange={handleFileChange} />
367
+
368
+ {file && (
369
+ <>
370
+ <div className="progress-bar">
371
+ <div
372
+ className="progress"
373
+ style={{ width: `${uploadProgress}%` }}
374
+ ></div>
375
+ </div>
376
+ <button onClick={handleUpload} disabled={loading}>
377
+ Upload and Send
378
+ </button>
379
+ </>
380
+ )}
381
+ </div>
382
+ );
383
+ };
384
+ ```
385
+
386
+ ## API Reference
387
+
388
+ ### ChatClient
389
+
390
+ The main component that provides authentication and API access.
391
+
392
+ Props:
393
+ - `token` (string): JWT token for authentication
394
+ - `baseUrl` (string): Base URL of the Chat API
395
+ - `connectionOptions` (ConnectionOptions): Options for the realtime connection
396
+ - `children` (ReactNode): Child components
397
+
398
+ ### ConnectionOptions
399
+
400
+ Options for configuring the realtime connection:
401
+
402
+ - `connectionType` (ConnectionType): The type of connection to use (WebSocket or LongPolling)
403
+ - `autoReconnect` (boolean): Whether to automatically reconnect if the connection is lost
404
+ - `reconnectAttempts` (number): Maximum number of reconnection attempts
405
+ - `reconnectDelay` (number): Delay between reconnection attempts in milliseconds
406
+ - `onlyLongpolling` (boolean): Force the connection to use long polling only (no WebSocket)
407
+
408
+ ### User
409
+
410
+ Interface representing a user in the chat system:
411
+
412
+ - `id` (string): Unique identifier for the user
413
+ - `name` (string): Display name of the user
414
+ - `avatar` (string): URL to the user's avatar image
415
+ - `is_admin` (boolean): Whether the user is an admin
416
+ - `attributes` (Record<string, any>): Additional attributes for the user
417
+ - `file_storage_provider` (string): The storage provider for the user's files
418
+ - `owner_client_id` (string): The client ID of the user's owner
419
+
420
+ ### Conversation
421
+
422
+ Interface representing a conversation:
423
+
424
+ - `id` (string): Unique identifier for the conversation
425
+ - `status` (string): Status of the conversation (e.g., "answered")
426
+ - `user` (User): The user participating in the conversation
427
+ - `lastMessage` (LastMessage): The last message in the conversation
428
+ - `unreadCount` (number): Number of unread messages in the conversation
429
+
430
+ ### ConversationDetails
431
+
432
+ Interface representing detailed information about a conversation:
433
+
434
+ - `conversation_uid` (string): Unique identifier for the conversation
435
+ - `user_message_count` (number): Number of messages sent by the user
436
+ - `user_attachment_count` (number): Number of attachments sent by the user
437
+ - `user_attachments` (Attachment[]): List of attachments sent by the user
438
+ - `participants` (User[]): List of participants in the conversation
439
+
440
+ ### useConversations
441
+
442
+ Hook for managing conversations.
443
+
444
+ Returns:
445
+ - `conversations` (Conversation[]): List of conversations
446
+ - `loading` (boolean): Loading state
447
+ - `error` (Error | null): Error state
448
+ - `getConversations(archived?: boolean, clientId?: string)`: Get conversations
449
+ - `deleteConversation(conversationUid: string)`: Delete a conversation
450
+ - `archiveConversation(conversationUid: string)`: Archive a conversation
451
+ - `unarchiveConversation(conversationUid: string)`: Unarchive a conversation
452
+ - `getConversationDetails(conversationUid: string)`: Get conversation details
453
+ - `searchConversation(conversationUid: string, term: string)`: Search messages in a conversation
454
+
455
+ ### useMessages
456
+
457
+ Hook for managing messages.
458
+
459
+ Parameters:
460
+ - `conversationUid` (string, optional): ID of the conversation
461
+
462
+ Returns:
463
+ - `messages` (Message[]): List of messages
464
+ - `pagination` (MessagesPagination): Pagination information
465
+ - `loading` (boolean): Loading state
466
+ - `error` (Error | null): Error state
467
+ - `getMessages(conversationId?: string, page?: number, pageSize?: number)`: Get messages
468
+ - `markMessageAsRead(messageId: number)`: Mark a message as read
469
+ - `sendMessage(message: SendMessageRequest)`: Send a message
470
+ - `markConversationAsRead(conversationId?: string)`: Mark all messages in a conversation as read
471
+ - `updateMessage(messageId: number, update: UpdateMessageRequest)`: Update a message
472
+ - `deleteMessage(messageId: number)`: Delete a message
473
+
474
+ ### useFiles
475
+
476
+ Hook for managing file uploads.
477
+
478
+ Returns:
479
+ - `loading` (boolean): Loading state
480
+ - `error` (Error | null): Error state
481
+ - `uploadProgress` (number): Upload progress (0-100)
482
+ - `uploadFile(file: File)`: Upload a file
483
+
484
+ ### useConnection
485
+
486
+ Hook for managing the realtime connection.
487
+
488
+ Returns:
489
+ - `status` (ConnectionStatus): Current connection status
490
+ - `isConnected` (boolean): Whether the connection is active
491
+ - `isConnecting` (boolean): Whether the connection is in progress
492
+ - `hasError` (boolean): Whether the connection has an error
493
+ - `connect()`: Connect to the server
494
+ - `disconnect()`: Disconnect from the server
495
+ - `sendMessage(message: SendMessageRequest, maxAttempts?: number)`: Send a message with retry support
496
+
497
+ ### useRealtime
498
+
499
+ Hook for subscribing to realtime events.
500
+
501
+ Parameters:
502
+ - `eventType` (EventType): The event type to subscribe to
503
+
504
+ Returns:
505
+ - `event` (RealtimeEvent | null): The latest event
506
+ - `clearEvent()`: Clear the current event
507
+
508
+ ### useMessageSentEvents
509
+
510
+ Hook for subscribing to message sent events.
511
+
512
+ Returns:
513
+ - `event` (RealtimeEvent<MessageSentEvent> | null): The latest message sent event
514
+ - `clearEvent()`: Clear the current event
515
+
516
+ ### useMessageEditedEvents
517
+
518
+ Hook for subscribing to message edited events.
519
+
520
+ Returns:
521
+ - `event` (RealtimeEvent<MessageEditedEvent> | null): The latest message edited event
522
+ - `clearEvent()`: Clear the current event
523
+
524
+ ### useMessageDeletedEvents
525
+
526
+ Hook for subscribing to message deleted events.
527
+
528
+ Returns:
529
+ - `event` (RealtimeEvent<MessageDeletedEvent> | null): The latest message deleted event
530
+ - `clearEvent()`: Clear the current event
531
+
532
+ ### useConversationManagement
533
+
534
+ Hook for managing conversations with selection state.
535
+
536
+ Returns:
537
+ - `conversations` (Conversation[]): List of conversations
538
+ - `conversationsLoading` (boolean): Loading state for conversations
539
+ - `selectedConversationId` (string): ID of the currently selected conversation
540
+ - `selectedConversation` (Conversation | undefined): The currently selected conversation object
541
+ - `handleSelectConversation(conversation: Conversation)`: Function to select a conversation
542
+ - `getConversations()`: Function to load conversations
543
+
544
+ ### useMessageManagement
545
+
546
+ Hook for managing messages in a conversation with pagination and file handling.
547
+
548
+ Parameters:
549
+ - `selectedConversationId` (string): ID of the conversation to manage messages for
550
+
551
+ Returns:
552
+ - `messages` (Message[]): List of messages in the conversation
553
+ - `messagesLoading` (boolean): Loading state for messages
554
+ - `fileLoading` (boolean): Loading state for file uploads
555
+ - `uploadProgress` (number): Upload progress percentage (0-100)
556
+ - `uploadFile(file: File)`: Function to upload a file
557
+ - `handleSendMessage(content: string, receiverId: string)`: Function to send a text message
558
+ - `handleSendFileMessage(content: string, fileId: number, receiverId: string)`: Function to send a message with a file attachment
559
+ - `getMessages(conversationId?: string, page?: number)`: Function to get messages for a conversation
560
+ - `loadMoreMessages()`: Function to load more messages (pagination)
561
+ - `hasMore` (boolean): Whether there are more messages to load
562
+
563
+ ### useAllMessageEvents
564
+
565
+ Hook for subscribing to all message events.
566
+
567
+ Returns:
568
+ - `messageSent` (RealtimeEvent<MessageSentEvent> | null): The latest message sent event
569
+ - `messageEdited` (RealtimeEvent<MessageEditedEvent> | null): The latest message edited event
570
+ - `messageDeleted` (RealtimeEvent<MessageDeletedEvent> | null): The latest message deleted event
571
+ - `clearEvents()`: Clear all events
572
+
573
+ ## Realtime Examples
574
+
575
+ ### Connection Management
576
+
577
+ ```tsx
578
+ import React, { useEffect } from 'react';
579
+ import { useConnection, ConnectionStatus } from 'chat-react-client';
580
+
581
+ const ConnectionStatusComponent = () => {
582
+ const { status, isConnected, connect, disconnect } = useConnection();
583
+
584
+ useEffect(() => {
585
+ // Connect when the component mounts
586
+ connect();
587
+
588
+ // Disconnect when the component unmounts
589
+ return () => {
590
+ disconnect();
591
+ };
592
+ }, [connect, disconnect]);
593
+
594
+ return (
595
+ <div>
596
+ <div>Connection Status: {status}</div>
597
+ <button onClick={connect} disabled={isConnected}>
598
+ Connect
599
+ </button>
600
+ <button onClick={disconnect} disabled={!isConnected}>
601
+ Disconnect
602
+ </button>
603
+ </div>
604
+ );
605
+ };
606
+
607
+ ### Event Listening
608
+
609
+ ```tsx
610
+ import React, { useEffect, useState } from 'react';
611
+ import { useAllMessageEvents } from 'chat-react-client';
612
+
613
+ const MessageEventListener = () => {
614
+ const [messages, setMessages] = useState([]);
615
+ const {
616
+ messageSent,
617
+ messageEdited,
618
+ messageDeleted,
619
+ clearEvents
620
+ } = useAllMessageEvents();
621
+
622
+ // Handle new message
623
+ useEffect(() => {
624
+ if (messageSent) {
625
+ setMessages(prevMessages => [...prevMessages, messageSent.data]);
626
+ clearEvents();
627
+ }
628
+ }, [messageSent, clearEvents]);
629
+
630
+ // Handle edited message
631
+ useEffect(() => {
632
+ if (messageEdited) {
633
+ setMessages(prevMessages =>
634
+ prevMessages.map(msg =>
635
+ msg.id === messageEdited.data.msg_id
636
+ ? { ...msg, content: messageEdited.data.new_content }
637
+ : msg
638
+ )
639
+ );
640
+ clearEvents();
641
+ }
642
+ }, [messageEdited, clearEvents]);
643
+
644
+ // Handle deleted message
645
+ useEffect(() => {
646
+ if (messageDeleted) {
647
+ setMessages(prevMessages =>
648
+ prevMessages.filter(msg => msg.id !== messageDeleted.data.msg_id)
649
+ );
650
+ clearEvents();
651
+ }
652
+ }, [messageDeleted, clearEvents]);
653
+
654
+ return (
655
+ <div>
656
+ <h3>Messages</h3>
657
+ <ul>
658
+ {messages.map(msg => (
659
+ <li key={msg.id}>{msg.content}</li>
660
+ ))}
661
+ </ul>
662
+ </div>
663
+ );
664
+ };
665
+ ```
666
+
667
+
668
+ ## Complete Example
669
+
670
+ For a complete example of how to use the library, see the [example](https://github.com/vahit/chat-react-client/tree/master/example) directory.
671
+
672
+ ## License
673
+
674
+ MIT © [vahit](https://github.com/vahit)