@realtimexsco/live-chat 1.0.0

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 RealtimeX
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,398 @@
1
+ # @realtimexsco/live-chat
2
+
3
+ > Drop-in real-time chat UI for React — direct messages, group chats, reactions, replies, forwarding, pinned/starred messages, and more. Built on Socket.IO, Zustand, Tailwind CSS v4, and Radix UI.
4
+
5
+ [![npm version](https://img.shields.io/npm/v/@realtimexsco/live-chat.svg)](https://www.npmjs.com/package/@realtimexsco/live-chat)
6
+ [![npm downloads](https://img.shields.io/npm/dm/@realtimexsco/live-chat.svg)](https://www.npmjs.com/package/@realtimexsco/live-chat)
7
+ [![license](https://img.shields.io/npm/l/@realtimexsco/live-chat.svg)](https://www.npmjs.com/package/@realtimexsco/live-chat)
8
+
9
+ Use the all-in-one `<ChatMain />` for a complete chat app in minutes, or compose
10
+ the individual pieces (`<Chat>`, `<ChannelList>`, `<MessageList>`, …) for full
11
+ control over layout and styling.
12
+
13
+ ---
14
+
15
+ ## Table of contents
16
+
17
+ - [Features](#features)
18
+ - [Requirements](#requirements)
19
+ - [Installation](#installation)
20
+ - [Tailwind CSS v4 setup (required)](#tailwind-css-v4-setup-required)
21
+ - [Quick start — full UI](#quick-start--full-ui)
22
+ - [Composable usage (recommended)](#composable-usage-recommended)
23
+ - [How it works](#how-it-works)
24
+ - [`Chat` provider props](#chat-provider-props)
25
+ - [Exported components & hooks](#exported-components--hooks)
26
+ - [Theming](#theming)
27
+ - [Troubleshooting](#troubleshooting)
28
+ - [License](#license)
29
+
30
+ ---
31
+
32
+ ## Features
33
+
34
+ - 💬 **1-to-1 and group chats** with real-time delivery over Socket.IO
35
+ - ⚡ **Optimistic UI** — messages appear instantly, reconcile on server ack
36
+ - 😀 Reactions, replies, forwarding, pinned, starred & saved messages
37
+ - 🔎 Conversation + message search, attachments, member/admin management
38
+ - 🎨 **Themeable** via a single `themeColor` prop and CSS variables (light/dark)
39
+ - 🧩 **Two integration modes** — all-in-one component or composable building blocks
40
+ - 🦾 Ships TypeScript types and ESM + CJS builds
41
+
42
+ ---
43
+
44
+ ## Requirements
45
+
46
+ This package relies on the following **peer dependencies** in your app:
47
+
48
+ | Peer dependency | Version |
49
+ | --------------- | ------- |
50
+ | `react` | ≥ 17 (19 recommended) |
51
+ | `react-dom` | ≥ 17 |
52
+ | `zustand` | ≥ 4 |
53
+ | `tailwindcss` | **v4** (required) |
54
+
55
+ You also need, at runtime:
56
+
57
+ - **Client ID** — your RealtimeX client/app id
58
+ - **Access token** — JWT for the logged-in user
59
+ - **Logged user details** — `{ _id, name, email }`
60
+ - **API URL** — e.g. `https://your-server.com/api/v1`
61
+ - **Socket URL** — e.g. `https://your-server.com`
62
+
63
+ ---
64
+
65
+ ## Installation
66
+
67
+ ```bash
68
+ npm install @realtimexsco/live-chat
69
+ # install peer dependencies if you don't have them yet
70
+ npm install react react-dom zustand tailwindcss @tailwindcss/vite
71
+ ```
72
+
73
+ > Published on the public npm registry — no `.npmrc` or auth token needed.
74
+
75
+ ---
76
+
77
+ ## Tailwind CSS v4 setup (required)
78
+
79
+ The package ships Tailwind utility classes, so your app must (1) import the
80
+ package stylesheet and (2) scan the built package output so Tailwind generates
81
+ the classes it uses.
82
+
83
+ In your global stylesheet (e.g. `src/index.css`):
84
+
85
+ ```css
86
+ @import "tailwindcss";
87
+ @import "@realtimexsco/live-chat/styles.css";
88
+
89
+ /* Scan the package dist so Tailwind emits the classes it uses */
90
+ @source "../node_modules/@realtimexsco/live-chat/dist/index.mjs";
91
+ @source "../node_modules/@realtimexsco/live-chat/dist/index.cjs";
92
+
93
+ :root {
94
+ --radius: 0.625rem;
95
+
96
+ /* Chat theme — overridden at runtime by the Chat `themeColor` prop */
97
+ --chat-theme: #3b82f6;
98
+ --chat-theme-5: #3b82f60d;
99
+ --chat-theme-10: #3b82f61a;
100
+ --chat-theme-20: #3b82f633;
101
+ --chat-theme-40: #3b82f666;
102
+ --chat-theme-60: #3b82f699;
103
+ }
104
+ ```
105
+
106
+ If you use **Vite**, dedupe React/Zustand and pre-bundle the package:
107
+
108
+ ```ts
109
+ // vite.config.ts
110
+ import { defineConfig } from "vite";
111
+ import react from "@vitejs/plugin-react";
112
+ import tailwindcss from "@tailwindcss/vite";
113
+
114
+ export default defineConfig({
115
+ plugins: [react(), tailwindcss()],
116
+ resolve: { dedupe: ["react", "react-dom", "zustand"] },
117
+ optimizeDeps: { include: ["@realtimexsco/live-chat", "zustand"] },
118
+ });
119
+ ```
120
+
121
+ ---
122
+
123
+ ## Quick start — full UI
124
+
125
+ The fastest path. `ChatMain` renders the sidebar, chat area, and modals in one
126
+ component.
127
+
128
+ ```tsx
129
+ import ChatMain from "@realtimexsco/live-chat";
130
+
131
+ export default function App() {
132
+ return (
133
+ <ChatMain
134
+ clientId="YOUR_CLIENT_ID"
135
+ accessToken="YOUR_JWT_TOKEN"
136
+ loggedUserDetails={{
137
+ _id: "USER_ID",
138
+ name: "John Doe",
139
+ email: "john@example.com",
140
+ }}
141
+ apiUrl="https://your-server.com/api/v1"
142
+ socketUrl="https://your-server.com"
143
+ themeColor="#3b82f6"
144
+ />
145
+ );
146
+ }
147
+ ```
148
+
149
+ ---
150
+
151
+ ## Composable usage (recommended)
152
+
153
+ Build your own layout from the exported pieces for full control over the
154
+ sidebar, routing, and styling.
155
+
156
+ ```tsx
157
+ import { useState, useCallback } from "react";
158
+ import {
159
+ Chat,
160
+ Channel,
161
+ ChannelList,
162
+ ChannelHeader,
163
+ MessageList,
164
+ MessageInput,
165
+ LoggedUserDetails,
166
+ ForwardModal,
167
+ useChatContext,
168
+ useChatStore,
169
+ } from "@realtimexsco/live-chat";
170
+
171
+ function MainChatArea() {
172
+ const { activeChannel } = useChatContext();
173
+ const { setActiveChannel, error, status } = useChatStore();
174
+
175
+ const [search, setSearch] = useState("");
176
+ const [isMenuOpen, setIsMenuOpen] = useState(false);
177
+ const [replyToMessage, setReplyToMessage] = useState<any>(null);
178
+ const [forwardMessage, setForwardMessage] = useState<any>(null);
179
+
180
+ // ⚠️ Declare all hooks BEFORE any conditional return (see Troubleshooting)
181
+ const handleUserSelect = useCallback(
182
+ (channel: any) => {
183
+ setActiveChannel(channel);
184
+ setIsMenuOpen(false);
185
+ setReplyToMessage(null);
186
+ setForwardMessage(null);
187
+ },
188
+ [setActiveChannel]
189
+ );
190
+
191
+ if (error) return <div>Connection error: {error}</div>;
192
+ if (status === "connecting" || status === "idle") return <div>Loading chat…</div>;
193
+
194
+ return (
195
+ <div className="flex h-screen w-screen overflow-hidden">
196
+ <aside className="w-[400px] border-r flex flex-col">
197
+ <LoggedUserDetails
198
+ searchQuery={search}
199
+ setSearchQuery={setSearch}
200
+ isCreateModalOpen={isMenuOpen}
201
+ setIsCreateModalOpen={setIsMenuOpen}
202
+ onUserSelect={handleUserSelect}
203
+ />
204
+ <ChannelList debouncedSearch={search} onNewChatClick={() => setIsMenuOpen(true)} />
205
+ </aside>
206
+
207
+ <main className="flex flex-col flex-1 min-w-0">
208
+ {/* Channel resolves the conversation, fetches messages, and joins the socket room */}
209
+ <Channel>
210
+ {activeChannel ? (
211
+ <>
212
+ <ChannelHeader />
213
+ <div className="flex-1 min-h-0">
214
+ <MessageList
215
+ onReply={(msg) => setReplyToMessage(msg)}
216
+ onForward={(msg) => setForwardMessage(msg)}
217
+ />
218
+ </div>
219
+ <MessageInput
220
+ replyTo={replyToMessage}
221
+ onCancelReply={() => setReplyToMessage(null)}
222
+ onMessageSent={() => setReplyToMessage(null)}
223
+ />
224
+ </>
225
+ ) : (
226
+ <div className="flex h-full items-center justify-center text-gray-400">
227
+ Select a conversation
228
+ </div>
229
+ )}
230
+ </Channel>
231
+ </main>
232
+
233
+ {forwardMessage && (
234
+ <ForwardModal
235
+ isOpen
236
+ onClose={() => setForwardMessage(null)}
237
+ message={forwardMessage}
238
+ onForwardComplete={(channel) => setActiveChannel(channel)}
239
+ />
240
+ )}
241
+ </div>
242
+ );
243
+ }
244
+
245
+ export default function App() {
246
+ return (
247
+ <Chat
248
+ client={{ id: "YOUR_CLIENT_ID" }}
249
+ accessToken="YOUR_JWT_TOKEN"
250
+ loggedUserDetails={{ _id: "USER_ID", name: "John Doe", email: "john@example.com" }}
251
+ apiUrl="https://your-server.com/api/v1"
252
+ socketUrl="https://your-server.com"
253
+ themeColor="#3b82f6"
254
+ onMessageReceived={(msg) => console.log("Background message:", msg)}
255
+ onSocketConnected={(id) => console.log("Socket connected:", id)}
256
+ onSocketError={(err) => console.error("Socket error:", err)}
257
+ >
258
+ <MainChatArea />
259
+ </Chat>
260
+ );
261
+ }
262
+ ```
263
+
264
+ > **Important:** Wrap the active chat panel in `<Channel>`. It resolves the
265
+ > conversation id, fetches messages, and emits the correct group/DM socket
266
+ > events. Don't manually emit `group_join` / `group_message_read` yourself.
267
+
268
+ ---
269
+
270
+ ## How it works
271
+
272
+ ```
273
+ ┌─────────────────────────────────────────────────────────────────────┐
274
+ │ <Chat> — provider │
275
+ │ initializes auth (clientId + accessToken), REST API client, │
276
+ │ Socket.IO connection, and the Zustand store │
277
+ └───────────────┬───────────────────────────────────────────────────────┘
278
+ │ provides context + store
279
+ ┌───────┴────────┐
280
+ ▼ ▼
281
+ <ChannelList> <Channel> (wraps the active conversation)
282
+ lists convos 1. resolves conversationId (DM vs group)
283
+ click → sets 2. fetches message history via API
284
+ activeChannel 3. joins the socket room, marks messages read
285
+ │ │
286
+ ▼ ▼
287
+ activeChannel <MessageList> renders the thread
288
+ in store <MessageInput> sends (optimistic UI)
289
+
290
+
291
+ Socket events (new / edited / deleted message,
292
+ reactions, read receipts, forwards) update the
293
+ Zustand store → React re-renders automatically
294
+ ```
295
+
296
+ **Lifecycle in short:**
297
+
298
+ 1. `<Chat>` boots auth, the API client, the socket, and the store.
299
+ 2. `<ChannelList>` shows conversations; selecting one calls `setActiveChannel`.
300
+ 3. `<Channel>` reacts to the active channel — fetches messages, joins the socket
301
+ room, and marks them read.
302
+ 4. `<MessageInput>` sends a message **optimistically** (it shows immediately),
303
+ then reconciles with the server acknowledgement.
304
+ 5. Incoming socket events mutate the Zustand store, and the UI updates reactively.
305
+
306
+ ---
307
+
308
+ ## `Chat` provider props
309
+
310
+ | Prop | Type | Required | Description |
311
+ | ---- | ---- | -------- | ----------- |
312
+ | `client` | `{ id: string }` | Yes | RealtimeX client / app id |
313
+ | `accessToken` | `string` | Yes | JWT access token |
314
+ | `loggedUserDetails` | `{ _id, name, email, ... }` | Yes | Current user profile |
315
+ | `apiUrl` | `string` | No | REST API base (default: `http://localhost:5000/api/v1`) |
316
+ | `socketUrl` | `string` | No | Socket server URL (default: `http://localhost:5000`) |
317
+ | `themeColor` | `string` | No | Primary chat accent color (default: `#7494ec`) |
318
+ | `theme` | `"light" \| "dark"` | No | Color scheme |
319
+ | `uiConfig` | `UIConfig` | No | Sidebar / header UI toggles |
320
+ | `onMessageReceived` | `(msg) => void` | No | Fired when a message arrives in a non-active chat |
321
+ | `onSocketConnected` | `(socketId) => void` | No | Socket connected callback |
322
+ | `onSocketError` | `(error) => void` | No | Socket error callback |
323
+ | `children` | `ReactNode` | Yes | Your chat layout |
324
+
325
+ ### `UIConfig`
326
+
327
+ ```ts
328
+ {
329
+ showSidebarAvatar?: boolean; // default: true
330
+ sidebarAvatarColor?: string;
331
+ showChannelDetails?: boolean; // default: true
332
+ messageDateFormat?: (date: string) => string;
333
+ }
334
+ ```
335
+
336
+ ---
337
+
338
+ ## Exported components & hooks
339
+
340
+ | Export | Description |
341
+ | ------ | ----------- |
342
+ | `ChatMain` | All-in-one chat UI (default export) |
343
+ | `Chat` | Provider — initializes auth, API, socket, store |
344
+ | `Channel` | Active conversation lifecycle (fetch, join, mark-read) |
345
+ | `ChannelList` | Conversation sidebar list |
346
+ | `ChannelHeader` | Chat header with search, details, actions |
347
+ | `MessageList` | Message thread |
348
+ | `MessageInput` | Compose box |
349
+ | `LoggedUserDetails` | User profile + new chat / create group |
350
+ | `ForwardModal` | Forward message modal |
351
+ | `useChatContext` | `{ activeChannel, setActiveChannel, themeColor, uiConfig }` |
352
+ | `useChatStore` | Full Zustand store (messages, socket actions, status, error) |
353
+ | `socketService` | Low-level socket service |
354
+ | `setChatClientId`, `setChatAccessToken`, `setChatBaseURL` | Manual API config |
355
+
356
+ ---
357
+
358
+ ## Theming
359
+
360
+ Set a single accent color via the `themeColor` prop; it drives the
361
+ `--chat-theme*` CSS variables. Override any of the variables shown in the
362
+ [Tailwind setup](#tailwind-css-v4-setup-required) to customize further, and pass
363
+ `theme="dark"` for dark mode.
364
+
365
+ ```tsx
366
+ <Chat themeColor="#7c3aed" theme="dark" /* … */>{/* … */}</Chat>
367
+ ```
368
+
369
+ ---
370
+
371
+ ## Troubleshooting
372
+
373
+ **`Rendered more hooks than during the previous render`**
374
+ Declare all hooks (`useState`, `useCallback`, …) at the **top** of the component,
375
+ before any conditional `return`. Handle loading/error states after the hooks.
376
+
377
+ **Styles missing / unstyled components**
378
+ 1. Confirm the `@source` paths in your CSS point to the package `dist/` files.
379
+ 2. Import `@realtimexsco/live-chat/styles.css`.
380
+ 3. Restart your dev server after installing or updating the package.
381
+
382
+ **Chat loads but messages don't appear**
383
+ Ensure `<Channel>` wraps `MessageList` and `MessageInput`. The package keys
384
+ messages by `conversationId` internally — don't key DM messages by user id.
385
+
386
+ **Group events firing in DM (or vice versa)**
387
+ Always use the composable pattern with `<Channel>`, and don't manually emit
388
+ `group_join` / `group_message_read` outside the package.
389
+
390
+ **Duplicate React / invalid hook call**
391
+ Add `resolve: { dedupe: ["react", "react-dom", "zustand"] }` to your
392
+ `vite.config.ts`.
393
+
394
+ ---
395
+
396
+ ## License
397
+
398
+ [MIT](https://www.npmjs.com/package/@realtimexsco/live-chat) © RealtimeX