movius-chats 1.2.0 → 1.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (122) hide show
  1. package/README.md +530 -176
  2. package/lib/commonjs/index.js +14 -1
  3. package/lib/commonjs/index.js.map +1 -1
  4. package/lib/module/index.js +14 -1
  5. package/lib/module/index.js.map +1 -1
  6. package/lib/typescript/assets/Icons/CheckAllIcon.d.ts +5 -2
  7. package/lib/typescript/assets/Icons/CheckIcon.d.ts +5 -2
  8. package/lib/typescript/assets/Icons/FileIcon.d.ts +5 -0
  9. package/lib/typescript/assets/Icons/LoadingIcon.d.ts +6 -3
  10. package/lib/typescript/assets/Icons/XIcon.d.ts +1 -1
  11. package/lib/typescript/components/ChatInput/ChatInput.d.ts +2 -2
  12. package/lib/typescript/components/ChatInput/FilePreview.d.ts +4 -0
  13. package/lib/typescript/components/ChatInput/TruncateFileName.d.ts +7 -0
  14. package/lib/typescript/components/ChatInput/types.d.ts +18 -2
  15. package/lib/typescript/context/ChatContext.d.ts +2 -2
  16. package/lib/typescript/types/index.d.ts +32 -2
  17. package/package.json +8 -7
  18. package/src/assets/Icons/CheckAllIcon.tsx +6 -1
  19. package/src/assets/Icons/CheckIcon.tsx +6 -2
  20. package/src/assets/Icons/FileIcon.tsx +22 -0
  21. package/src/assets/Icons/LoadingIcon.tsx +38 -10
  22. package/src/assets/Icons/XIcon.tsx +7 -7
  23. package/src/components/ChatBubble/ChatBubble.tsx +2 -2
  24. package/src/components/ChatBubble/MessageContent.tsx +18 -7
  25. package/src/components/ChatBubble/MessageStatus.tsx +6 -3
  26. package/src/components/ChatInput/ChatInput.tsx +160 -120
  27. package/src/components/ChatInput/FilePreview.tsx +161 -0
  28. package/src/components/ChatInput/TruncateFileName.tsx +33 -0
  29. package/src/components/ChatInput/types.ts +8 -2
  30. package/src/components/MediaViewer/MediaViewer.tsx +4 -6
  31. package/src/components/TypingComponent/TypingIndicator.tsx +3 -6
  32. package/src/context/ChatContext.tsx +4 -4
  33. package/src/index.tsx +62 -50
  34. package/src/types/index.ts +22 -2
  35. package/lib/commonjs/assets/Icons/ArrowBack2RoundedIcon.js +0 -2
  36. package/lib/commonjs/assets/Icons/ArrowBack2RoundedIcon.js.map +0 -1
  37. package/lib/commonjs/assets/Icons/CameraIcon.js +0 -2
  38. package/lib/commonjs/assets/Icons/CameraIcon.js.map +0 -1
  39. package/lib/commonjs/assets/Icons/CheckAllIcon.js +0 -2
  40. package/lib/commonjs/assets/Icons/CheckAllIcon.js.map +0 -1
  41. package/lib/commonjs/assets/Icons/CheckIcon.js +0 -2
  42. package/lib/commonjs/assets/Icons/CheckIcon.js.map +0 -1
  43. package/lib/commonjs/assets/Icons/EmojiFunnySquareIcon.js +0 -2
  44. package/lib/commonjs/assets/Icons/EmojiFunnySquareIcon.js.map +0 -1
  45. package/lib/commonjs/assets/Icons/LoadingIcon.js +0 -2
  46. package/lib/commonjs/assets/Icons/LoadingIcon.js.map +0 -1
  47. package/lib/commonjs/assets/Icons/MicrophoneIcon.js +0 -2
  48. package/lib/commonjs/assets/Icons/MicrophoneIcon.js.map +0 -1
  49. package/lib/commonjs/assets/Icons/PaperClipIcon.js +0 -2
  50. package/lib/commonjs/assets/Icons/PaperClipIcon.js.map +0 -1
  51. package/lib/commonjs/assets/Icons/PaperPlaneIcon.js +0 -2
  52. package/lib/commonjs/assets/Icons/PaperPlaneIcon.js.map +0 -1
  53. package/lib/commonjs/assets/Icons/PauseIcon.js +0 -2
  54. package/lib/commonjs/assets/Icons/PauseIcon.js.map +0 -1
  55. package/lib/commonjs/assets/Icons/PlayIcon.js +0 -2
  56. package/lib/commonjs/assets/Icons/PlayIcon.js.map +0 -1
  57. package/lib/commonjs/assets/Icons/XIcon.js +0 -2
  58. package/lib/commonjs/assets/Icons/XIcon.js.map +0 -1
  59. package/lib/commonjs/components/AudioPlayer/AudioPlayer.js +0 -2
  60. package/lib/commonjs/components/AudioPlayer/AudioPlayer.js.map +0 -1
  61. package/lib/commonjs/components/ChatBubble/ChatBubble.js +0 -2
  62. package/lib/commonjs/components/ChatBubble/ChatBubble.js.map +0 -1
  63. package/lib/commonjs/components/ChatBubble/MessageContent.js +0 -2
  64. package/lib/commonjs/components/ChatBubble/MessageContent.js.map +0 -1
  65. package/lib/commonjs/components/ChatBubble/MessageStatus.js +0 -2
  66. package/lib/commonjs/components/ChatBubble/MessageStatus.js.map +0 -1
  67. package/lib/commonjs/components/ChatInput/ChatInput.js +0 -2
  68. package/lib/commonjs/components/ChatInput/ChatInput.js.map +0 -1
  69. package/lib/commonjs/components/MediaViewer/MediaViewer.js +0 -2
  70. package/lib/commonjs/components/MediaViewer/MediaViewer.js.map +0 -1
  71. package/lib/commonjs/components/TypingComponent/TypingIndicator.js +0 -2
  72. package/lib/commonjs/components/TypingComponent/TypingIndicator.js.map +0 -1
  73. package/lib/commonjs/context/AudioContext.js +0 -2
  74. package/lib/commonjs/context/AudioContext.js.map +0 -1
  75. package/lib/commonjs/context/ChatContext.js +0 -2
  76. package/lib/commonjs/context/ChatContext.js.map +0 -1
  77. package/lib/commonjs/utils/datefunc.js +0 -2
  78. package/lib/commonjs/utils/datefunc.js.map +0 -1
  79. package/lib/module/assets/Icons/ArrowBack2RoundedIcon.js +0 -2
  80. package/lib/module/assets/Icons/ArrowBack2RoundedIcon.js.map +0 -1
  81. package/lib/module/assets/Icons/CameraIcon.js +0 -2
  82. package/lib/module/assets/Icons/CameraIcon.js.map +0 -1
  83. package/lib/module/assets/Icons/CheckAllIcon.js +0 -2
  84. package/lib/module/assets/Icons/CheckAllIcon.js.map +0 -1
  85. package/lib/module/assets/Icons/CheckIcon.js +0 -2
  86. package/lib/module/assets/Icons/CheckIcon.js.map +0 -1
  87. package/lib/module/assets/Icons/EmojiFunnySquareIcon.js +0 -2
  88. package/lib/module/assets/Icons/EmojiFunnySquareIcon.js.map +0 -1
  89. package/lib/module/assets/Icons/LoadingIcon.js +0 -2
  90. package/lib/module/assets/Icons/LoadingIcon.js.map +0 -1
  91. package/lib/module/assets/Icons/MicrophoneIcon.js +0 -2
  92. package/lib/module/assets/Icons/MicrophoneIcon.js.map +0 -1
  93. package/lib/module/assets/Icons/PaperClipIcon.js +0 -2
  94. package/lib/module/assets/Icons/PaperClipIcon.js.map +0 -1
  95. package/lib/module/assets/Icons/PaperPlaneIcon.js +0 -2
  96. package/lib/module/assets/Icons/PaperPlaneIcon.js.map +0 -1
  97. package/lib/module/assets/Icons/PauseIcon.js +0 -2
  98. package/lib/module/assets/Icons/PauseIcon.js.map +0 -1
  99. package/lib/module/assets/Icons/PlayIcon.js +0 -2
  100. package/lib/module/assets/Icons/PlayIcon.js.map +0 -1
  101. package/lib/module/assets/Icons/XIcon.js +0 -2
  102. package/lib/module/assets/Icons/XIcon.js.map +0 -1
  103. package/lib/module/components/AudioPlayer/AudioPlayer.js +0 -2
  104. package/lib/module/components/AudioPlayer/AudioPlayer.js.map +0 -1
  105. package/lib/module/components/ChatBubble/ChatBubble.js +0 -2
  106. package/lib/module/components/ChatBubble/ChatBubble.js.map +0 -1
  107. package/lib/module/components/ChatBubble/MessageContent.js +0 -2
  108. package/lib/module/components/ChatBubble/MessageContent.js.map +0 -1
  109. package/lib/module/components/ChatBubble/MessageStatus.js +0 -2
  110. package/lib/module/components/ChatBubble/MessageStatus.js.map +0 -1
  111. package/lib/module/components/ChatInput/ChatInput.js +0 -2
  112. package/lib/module/components/ChatInput/ChatInput.js.map +0 -1
  113. package/lib/module/components/MediaViewer/MediaViewer.js +0 -2
  114. package/lib/module/components/MediaViewer/MediaViewer.js.map +0 -1
  115. package/lib/module/components/TypingComponent/TypingIndicator.js +0 -2
  116. package/lib/module/components/TypingComponent/TypingIndicator.js.map +0 -1
  117. package/lib/module/context/AudioContext.js +0 -2
  118. package/lib/module/context/AudioContext.js.map +0 -1
  119. package/lib/module/context/ChatContext.js +0 -2
  120. package/lib/module/context/ChatContext.js.map +0 -1
  121. package/lib/module/utils/datefunc.js +0 -2
  122. package/lib/module/utils/datefunc.js.map +0 -1
package/README.md CHANGED
@@ -1,262 +1,616 @@
1
- # React Native Modern Chats UI
2
-
3
- A highly customizable, feature-rich chats interface component for React Native applications. Built with performance and flexibility in mind, this component provides a complete solution for implementing chats functionality in your mobile applications.
4
-
5
- ## ⚠️ Important Implementation Notes
6
-
7
- - **Native Rebuild Required**: This package uses native modules that require rebuilding your application after installation.
8
- - **Expo Go Compatibility**: This package is **not compatible** with Expo Go due to its native dependencies. You must use a development build or eject from Expo Go to use this library.
9
- - **Development Build**: For Expo users, you'll need to create a [Development Build](https://docs.expo.dev/develop/development-builds/introduction/) to use this package.
10
-
11
- ## Features
12
-
13
- - 🚀 Full TypeScript support
14
- - 📱 Native performance optimizations
15
- - 🎨 Extensive theme customization
16
- - 🖼️ Multi-media message support (text, images, video, audio)
17
- - 👤 Avatar and username display options
18
- - ⌨️ Typing indicators
19
- - 📎 File attachments
20
- - 🎥 Camera integration
21
- - 🎤 Voice messages
22
- - 💬 Message status indicators (sent, delivered, read)
23
- - 🎯 Custom component injection
24
- - 🔧 Comprehensive styling API
25
- - 🔄 Lazy loading for media messages
26
- - 📡 Debounced typing indicators
27
- - 🖼️ Avatar image caching
1
+ # movius-chats
2
+
3
+ A highly customizable, feature-rich chat UI for **React Native**. Drop in a single `ChatScreen` component to get message bubbles, media (images, video, audio), typing indicators, attachment previews, and a full input bar—with deep theming and custom icon/component hooks.
4
+
5
+ **npm:** [`movius-chats`](https://www.npmjs.com/package/movius-chats)
6
+ **Repository:** [github.com/David-Atueyi/Movius-Chats](https://github.com/David-Atueyi/Movius-Chats)
7
+
8
+ ---
9
+
10
+ ## Table of contents
11
+
12
+ - [Requirements](#requirements)
13
+ - [Important: native modules & Expo](#important-native-modules--expo)
14
+ - [Installation](#installation)
15
+ - [Quick start](#quick-start)
16
+ - [Message data model](#message-data-model)
17
+ - [Message list ordering](#message-list-ordering)
18
+ - [ChatScreen API](#chatscreen-api)
19
+ - [Core props](#core-props)
20
+ - [Feature flags](#feature-flags)
21
+ - [Input & typing](#input--typing)
22
+ - [Attachment preview](#attachment-preview)
23
+ - [Theming](#theming)
24
+ - [Custom components & icons](#custom-components--icons)
25
+ - [Usage examples](#usage-examples)
26
+ - [Text messages](#text-messages)
27
+ - [Media messages](#media-messages)
28
+ - [Typing indicators](#typing-indicators)
29
+ - [Attachments & camera (parent-controlled)](#attachments--camera-parent-controlled)
30
+ - [Attachment preview before send](#attachment-preview-before-send)
31
+ - [Custom theme](#custom-theme)
32
+ - [Custom input bar](#custom-input-bar)
33
+ - [Long-press actions](#long-press-actions)
34
+ - [TypeScript](#typescript)
35
+ - [Architecture overview](#architecture-overview)
36
+ - [Troubleshooting](#troubleshooting)
37
+ - [Contributing](#contributing)
38
+ - [License](#license)
39
+
40
+ ---
41
+
42
+ ## Requirements
43
+
44
+ | Dependency | Role |
45
+ |------------|------|
46
+ | `react` ≥ 16.8 | Peer dependency |
47
+ | `react-native` | Peer dependency |
48
+ | `react-native-reanimated` | Audio scrubber animations (peer) |
49
+ | `react-native-image-zoom-viewer` | Full-screen image viewer |
50
+ | `react-native-parsed-text` | Clickable URLs in messages |
51
+ | `react-native-sound` | Voice message playback |
52
+ | `react-native-svg` | Built-in icons |
53
+ | `react-native-video` | Video bubbles & preview |
54
+ | `twrnc` | Tailwind-style utility classes |
55
+
56
+ ---
57
+
58
+ ## Important: native modules & Expo
59
+
60
+ - **Rebuild required** after install. This library uses native modules (`react-native-sound`, `react-native-video`, etc.).
61
+ - **Not compatible with Expo Go.** Use a [development build](https://docs.expo.dev/develop/development-builds/introduction/) or a bare React Native app.
62
+ - **iOS:** run `pod install` in the `ios` folder after adding dependencies.
63
+
64
+ ---
28
65
 
29
66
  ## Installation
30
67
 
68
+ ### 1. Install the package
69
+
31
70
  ```bash
32
71
  npm install movius-chats
33
72
  # or
34
73
  yarn add movius-chats
74
+ # or
75
+ bun install movius-chats
35
76
  ```
36
77
 
37
- ### Required Dependencies
78
+ ### 2. Install peer & native dependencies
38
79
 
39
- The following packages are required for movius-chats to function properly. Install them using npm or yarn:
80
+ These are required in **your app** (some are bundled as dependencies of `movius-chats`, but you must still link/native-build them in the host app):
40
81
 
41
82
  ```bash
42
- # Using npm
43
- npm install react-native-image-zoom-viewer react-native-reanimated react-native-sound react-native-svg react-native-video twrnc
44
-
45
- # Using yarn
46
- yarn add react-native-image-zoom-viewer react-native-reanimated react-native-sound react-native-svg react-native-video twrnc
83
+ npm install react-native-reanimated react-native-image-zoom-viewer react-native-sound react-native-svg react-native-video twrnc
84
+ # or
85
+ yarn add react-native-reanimated react-native-image-zoom-viewer react-native-sound react-native-svg react-native-video twrnc
86
+ #or
87
+ bun install react-native-reanimated react-native-image-zoom-viewer react-native-sound react-native-svg react-native-video twrnc
47
88
  ```
48
89
 
49
- ### Additional Setup
90
+ > `react-native-parsed-text` is pulled in transitively; no extra install step unless your bundler requires it.
91
+
92
+ ### 3. Configure Reanimated
50
93
 
51
- For react-native-reanimated, add this line to your `babel.config.js`:
94
+ Add the Reanimated Babel plugin **last** in `babel.config.js`:
52
95
 
53
96
  ```javascript
54
97
  module.exports = {
55
- plugins: ['react-native-reanimated/plugin'],
98
+ presets: ['module:metro-react-native-babel-preset'],
99
+ plugins: [
100
+ // ...other plugins
101
+ 'react-native-reanimated/plugin',
102
+ ],
56
103
  };
57
104
  ```
58
105
 
59
- ### Post-Installation Steps
106
+ ### 4. Configure react-native-sound (recommended)
60
107
 
61
- After installing this package and its dependencies:
108
+ **iOS** enable playback in silent mode (optional, in `AppDelegate`):
62
109
 
63
- 1. **For React Native CLI Projects**:
64
- ```bash
65
- npx pod-install # For iOS
66
- npx react-native run-android # Rebuild for Android
67
- npx react-native run-ios # Rebuild for iOS
68
- ```
110
+ ```objc
111
+ #import <AVFoundation/AVFoundation.h>
69
112
 
70
- 2. **For Expo Projects**:
71
- ```bash
72
- npx expo prebuild # Generate native code
73
- npx expo run:android # Build and run on Android
74
- npx expo run:ios # Build and run on iOS
75
- ```
113
+ // inside didFinishLaunchingWithOptions:
114
+ [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:nil];
115
+ ```
76
116
 
77
- ## Basic Usage
117
+ **Android** ensure internet permission if loading remote audio URLs in `AndroidManifest.xml`:
78
118
 
79
- ```typescript
119
+ ```xml
120
+ <uses-permission android:name="android.permission.INTERNET" />
121
+ ```
122
+
123
+ ### 5. Rebuild the native app
124
+
125
+ **React Native CLI:**
126
+
127
+ ```bash
128
+ cd ios && pod install && cd ..
129
+ npx react-native run-ios
130
+ npx react-native run-android
131
+ ```
132
+
133
+ **Expo (development build):**
134
+
135
+ ```bash
136
+ npx expo prebuild
137
+ npx expo run:ios
138
+ # or
139
+ npx expo run:android
140
+ ```
141
+
142
+ After native dependency updates:
143
+
144
+ ```bash
145
+ npx expo prebuild --clean
146
+ ```
147
+
148
+ ---
149
+
150
+ ## Quick start
151
+
152
+ ```tsx
153
+ import React, { useState } from 'react';
154
+ import { SafeAreaView } from 'react-native';
80
155
  import ChatScreen from 'movius-chats';
81
- import { Message } from 'movius-chats/lib/typescript/types';
82
- import { useState } from 'react';
156
+ import type { Message } from 'movius-chats/lib/typescript/types';
83
157
 
84
- const App = () => {
158
+ export default function App() {
85
159
  const [messages, setMessages] = useState<Message[]>([]);
160
+ const currentUserId = 'user-1';
86
161
 
87
- const handleSendMessage = (message: Omit<Message, "id" | "time" | "status">) => {
88
- // Handle sending message
162
+ const handleSendMessage = (
163
+ payload: Omit<Message, 'id' | 'time' | 'status'>
164
+ ) => {
89
165
  const newMessage: Message = {
90
- ...message,
91
- id: Date.now().toString(),
92
- time: new Date().toLocaleTimeString(),
93
- status: 'sent'
166
+ ...payload,
167
+ id: String(Date.now()),
168
+ time: new Date().toLocaleTimeString([], {
169
+ hour: '2-digit',
170
+ minute: '2-digit',
171
+ }),
172
+ status: 'sent',
94
173
  };
95
- setMessages(prev => [newMessage, ...prev]);
174
+ // Newest first — see "Message list ordering"
175
+ setMessages((prev) => [newMessage, ...prev]);
96
176
  };
97
177
 
98
178
  return (
99
- <ChatScreen
100
- messages={messages}
101
- currentUserId="user123"
102
- onSendMessage={handleSendMessage}
103
- showAvatars
104
- />
179
+ <SafeAreaView style={{ flex: 1 }}>
180
+ <ChatScreen
181
+ messages={messages}
182
+ currentUserId={currentUserId}
183
+ onSendMessage={handleSendMessage}
184
+ placeholder="Type a message..."
185
+ showAvatars
186
+ showBubbleTail
187
+ showMessageStatus
188
+ showEmojiButton
189
+ showAttachmentsButton
190
+ showCameraButton
191
+ showVoiceRecordButton
192
+ />
193
+ </SafeAreaView>
105
194
  );
195
+ }
196
+ ```
197
+
198
+ ---
199
+
200
+ ## Message data model
201
+
202
+ Each item in `messages` must match the `Message` interface:
203
+
204
+ | Field | Type | Required | Description |
205
+ |-------|------|----------|-------------|
206
+ | `id` | `string` | Yes | Unique message id |
207
+ | `senderId` | `string` | Yes | User id of the sender |
208
+ | `time` | `string` | Yes | Display time (e.g. `"2:30 PM"`) — you format this |
209
+ | `status` | `'sent' \| 'delivered' \| 'read'` | Yes | Delivery state (shown only for current user) |
210
+ | `text` | `string` | No | Plain text; URLs are auto-linked |
211
+ | `image` | `string` | No | Image URI |
212
+ | `video` | `string` | No | Video URI |
213
+ | `audio` | `string` | No | Audio file URI |
214
+ | `senderName` | `string` | No | Shown when `showUserNames` is true |
215
+ | `senderAvatar` | `string` | No | Avatar image URI; falls back to first letter of `senderName` |
216
+
217
+ A message can combine fields (e.g. text + image), but typically you use one primary content type per bubble.
218
+
219
+ ```typescript
220
+ import type { Message } from 'movius-chats/lib/typescript/types';
221
+
222
+ const example: Message = {
223
+ id: '1',
224
+ senderId: 'user-2',
225
+ senderName: 'Alex',
226
+ senderAvatar: 'https://example.com/avatar.jpg',
227
+ text: 'Check this out https://example.com',
228
+ time: '10:42 AM',
229
+ status: 'read',
106
230
  };
107
231
  ```
108
232
 
109
- ## Props
233
+ ---
234
+
235
+ ## Message list ordering
110
236
 
111
- ### Core Props
237
+ `ChatScreen` uses an **inverted** `FlatList`. Put the **newest message at index `0`** of the `messages` array:
238
+
239
+ ```typescript
240
+ setMessages((prev) => [newMessage, ...prev]); // ✅ correct
241
+ ```
242
+
243
+ Older messages sit at higher indices and appear higher on screen.
244
+
245
+ **Grouping:** consecutive messages from the same sender share bubble styling; avatars and bubble tails show on the first message of a sequence (`isFirstInSequence`).
246
+
247
+ ---
248
+
249
+ ## ChatScreen API
250
+
251
+ `ChatScreen` is the **default export** from `movius-chats`. It wraps your chat in `AudioProvider` + `ChatProvider` and renders the message list, typing indicator, input (or custom input), and full-screen media viewer.
252
+
253
+ ### Core props
112
254
 
113
255
  | Prop | Type | Required | Description |
114
256
  |------|------|----------|-------------|
115
- | messages | Message[] | Yes | Array of message objects to display |
116
- | currentUserId | string | Yes | ID of the current user |
117
- | onSendMessage | (message: Omit<Message, "id" \| "time" \| "status">) => void | Yes | Callback when a message is sent |
118
- | onMessageLongPress | (message: Message) => void | No | Callback for long-pressing a message |
119
- | onAttachmentPress | () => void | No | Callback for attachment button press |
120
- | onAudioRecordStart | () => void | No | Callback when audio recording starts |
121
- | onAudioRecordEnd | () => void | No | Callback when audio recording ends |
122
- | onCameraPress | () => void | No | Callback for camera button press |
123
- | onTypingStart | () => void | No | Callback when user starts typing |
124
- | onTypingEnd | () => void | No | Callback when user stops typing |
125
- | placeholder | string | No | Input placeholder text |
126
- | typingUsers | Array<{ id: string; avatar: string; name: string }> | No | List of users who are typing |
257
+ | `messages` | `Message[]` | Yes | Messages to render (newest first) |
258
+ | `currentUserId` | `string` | Yes | Logged-in user id; used for bubble alignment & status |
259
+ | `onSendMessage` | `(msg: Omit<Message, 'id' \| 'time' \| 'status'>) => void` | Yes | Fired when user taps send with text and/or `previewData` |
260
+ | `onMessageLongPress` | `(message: Message) => void` | No | Long-press on a bubble (reply, delete, etc.) |
261
+ | `placeholder` | `string` | No | Input placeholder (default: `"Message"`) |
262
+
263
+ ### Feature flags
264
+
265
+ All flags below default to **falsy** (hidden) unless you pass `true`:
266
+
267
+ | Prop | Description |
268
+ |------|-------------|
269
+ | `showAvatars` | Avatar (or initial) on received messages & typing row |
270
+ | `showUserNames` | Sender name above received bubbles |
271
+ | `showBubbleTail` | WhatsApp-style tail on first bubble in a sequence |
272
+ | `showMessageStatus` | Timestamp + checkmarks for sent messages |
273
+ | `showEmojiButton` | Emoji button in input (UI only; wire your own picker) |
274
+ | `showAttachmentsButton` | Paperclip → calls `onAttachmentPress` |
275
+ | `showCameraButton` | Camera icon when input is empty → `onCameraPress` |
276
+ | `showVoiceRecordButton` | Mic when input empty; send when text present |
277
+
278
+ ### Input & typing
279
+
280
+ | Prop | Type | Description |
281
+ |------|------|-------------|
282
+ | `onTypingStart` | `() => void` | Called when input has non-empty text |
283
+ | `onTypingEnd` | `() => void` | Called when input is cleared |
284
+ | `onAttachmentPress` | `() => void` | User tapped attachment — open document picker, etc. |
285
+ | `onCameraPress` | `() => void` | User tapped camera — open camera / image picker |
286
+ | `onAudioRecordStart` | `() => void` | Mic press / long-press start |
287
+ | `onAudioRecordEnd` | `() => void` | Mic release — upload recorded audio and append to messages |
288
+ | `typingUsers` | `Array<{ id: string; avatar: string; name: string }>` | Users currently typing (excludes `currentUserId` in UI) |
289
+
290
+ **Note:** Built-in `onSendMessage` from the default input only includes `{ text, senderId }`. Recording, camera, and file picking are **intentionally delegated** to your app via the callbacks above.
291
+
292
+ ### Attachment preview
293
+
294
+ Show a file/image/video preview above the input before sending:
295
+
296
+ | Prop | Type | Description |
297
+ |------|------|-------------|
298
+ | `previewData` | `{ uri: string; type: string; name: string }` | MIME type in `type` (e.g. `image/jpeg`, `video/mp4`, `application/pdf`) |
299
+ | `closePreview` | `() => void` | Clear preview when user taps X |
300
+
301
+ When `previewData` is set, send is enabled even if text is empty. Your `onSendMessage` handler should read `previewData` from closure/state and attach `image`, `video`, or file metadata to the outgoing message.
127
302
 
128
303
  ### Theming
129
304
 
130
- The component supports extensive theming through the `theme` prop:
305
+ Pass a `theme` object to customize colors, typography, and styles. All keys are optional.
131
306
 
132
307
  ```typescript
133
- theme?: {
134
- colors?: {
135
- sentMessageTailColor?: string;
136
- receivedMessageTailColor?: string;
137
- timestamp?: string;
138
- inputsIconsColor?: string;
139
- sendIconsColor?: string;
140
- placeholderTextColor?: string;
141
- audioPlayIconColor?: string;
142
- audioPauseIconColor?: string;
143
- videoPlayIconColor?: string;
144
- };
145
- bubbleStyle?: {
146
- sent?: ViewStyle;
147
- received?: ViewStyle;
148
- avatarTextStyle?: TextStyle;
149
- userNameStyle?: TextStyle;
150
- avatarImageStyle?: ImageStyle;
151
- typingContainerStyle?: ViewStyle;
152
- additionalTypingUsersContainerStyle?: ViewStyle;
153
- additionalTypingUsersTextStyle?: TextStyle;
154
- };
155
- messageStyle?: {
156
- sentTextStyle?: TextStyle;
157
- receivedTextStyle?: TextStyle;
158
- audioPlayButtonStyle?: ViewStyle;
159
- audioKnobStyle?: ViewStyle;
160
- progressBarStyle?: ViewStyle;
161
- activeProgressBarStyle?: ViewStyle;
162
- audioDurationStyle?: TextStyle;
163
- };
164
- inputStyles?: {
165
- inputSectionContainerStyle?: ViewStyle;
166
- inputContainerStyle?: ViewStyle;
167
- sendButtonStyle?: ViewStyle;
168
- };
308
+ theme?: {
309
+ fontFamily?: string;
310
+
311
+ colors?: {
312
+ sentMessageTailColor?: string;
313
+ receivedMessageTailColor?: string;
314
+ timestamp?: string;
315
+ inputsIconsColor?: string;
316
+ sendIconsColor?: string;
317
+ placeholderTextColor?: string;
318
+ inputTextColor?: string;
319
+ audioPlayIconColor?: string;
320
+ audioPauseIconColor?: string;
321
+ videoPlayIconColor?: string;
322
+ sentIconColor?: string;
323
+ deliveredIconColor?: string;
324
+ readIconColor?: string;
325
+ };
326
+
327
+ sizes?: {
328
+ inputIconSize?: string; // twrnc class, e.g. 'h-6 w-6'
329
+ };
330
+
331
+ bubbleStyle?: {
332
+ sent?: ViewStyle;
333
+ received?: ViewStyle;
334
+ avatarTextStyle?: TextStyle;
335
+ userNameStyle?: TextStyle;
336
+ avatarImageStyle?: ImageStyle;
337
+ typingContainerStyle?: ViewStyle;
338
+ additionalTypingUsersContainerStyle?: ViewStyle;
339
+ additionalTypingUsersTextStyle?: TextStyle;
340
+ };
341
+
342
+ messageStyle?: {
343
+ sentTextStyle?: TextStyle;
344
+ receivedTextStyle?: TextStyle;
345
+ audioPlayButtonStyle?: ViewStyle;
346
+ audioKnobStyle?: ViewStyle;
347
+ progressBarStyle?: ViewStyle;
348
+ activeProgressBarStyle?: ViewStyle;
349
+ audioDurationStyle?: TextStyle;
169
350
  };
351
+
352
+ inputStyles?: {
353
+ inputSectionContainerStyle?: ViewStyle;
354
+ inputContainerStyle?: ViewStyle;
355
+ sendButtonStyle?: ViewStyle;
356
+ };
357
+
358
+ filePreviewStyle?: {
359
+ root?: ViewStyle;
360
+ container?: ViewStyle;
361
+ iconContainer?: ViewStyle;
362
+ nameContainer?: ViewStyle;
363
+ text?: TextStyle;
364
+ };
365
+ }
170
366
  ```
171
367
 
172
- ### Custom Components
368
+ Default bubble colors (before `theme` overrides): sent ≈ green (`bg-green-500`), received ≈ white.
369
+
370
+ ### Custom components & icons
173
371
 
174
372
  | Prop | Type | Description |
175
373
  |------|------|-------------|
176
- | renderCustomInput | () => React.ReactNode | Custom input component |
177
- | renderCustomVideoBubbleError | () => React.ReactNode | Custom video error display |
178
- | renderCustomTyping | () => React.ReactNode | Custom typing indicator |
179
- | CustomEmojiIcon | () => React.ReactNode | Custom emoji picker icon |
180
- | CustomAttachmentIcon | () => React.ReactNode | Custom attachment icon |
181
- | CustomCameraIcon | () => React.ReactNode | Custom camera icon |
182
- | CustomSendIcon | () => React.ReactNode | Custom send button icon |
183
- | CustomMicrophoneIcon | () => React.ReactNode | Custom microphone icon |
184
- | CustomPlayIcon | () => React.ReactNode | Custom play icon |
185
- | CustomPauseIcon | () => React.ReactNode | Custom pause icon |
374
+ | `renderCustomInput` | `() => React.ReactNode` | Replace entire input bar |
375
+ | `renderCustomTyping` | `() => React.ReactNode` | Replace typing bubble content |
376
+ | `renderCustomVideoBubbleError` | `() => React.ReactNode` | Replace inline video error UI |
377
+ | `CustomEmojiIcon` | `() => React.ReactNode` | Emoji button |
378
+ | `CustomAttachmentIcon` | `() => React.ReactNode` | Attachment button |
379
+ | `CustomCameraIcon` | `() => React.ReactNode` | Camera button |
380
+ | `CustomSendIcon` | `() => React.ReactNode` | Send button |
381
+ | `CustomMicrophoneIcon` | `() => React.ReactNode` | Microphone button |
382
+ | `CustomPlayIcon` | `() => React.ReactNode` | Play icon in video/audio bubbles |
383
+ | `CustomPauseIcon` | `() => React.ReactNode` | Pause icon in audio player |
384
+ | `CustomFileIcon` | `React.ComponentType<{ style?: any }>` | Generic file preview icon |
385
+ | `CustomImagePreview` | `React.ComponentType<{ uri: string }>` | Image attachment preview |
386
+ | `CustomVideoPreview` | `React.ComponentType<{ uri: string }>` | Video attachment preview |
387
+
388
+ ---
389
+
390
+ ## Usage examples
391
+
392
+ ### Text messages
393
+
394
+ ```tsx
395
+ onSendMessage={({ text, senderId }) => {
396
+ addMessage({ text, senderId, status: 'sent' });
397
+ }}
398
+ ```
186
399
 
187
- ## Advanced Usage
400
+ ### Media messages
188
401
 
189
- ### Custom Theme Example
402
+ Add URIs when building the `Message` object (usually after upload):
190
403
 
191
- ```typescript
404
+ ```tsx
405
+ const imageMessage: Message = {
406
+ id: '2',
407
+ senderId: currentUserId,
408
+ image: 'https://cdn.example.com/photo.jpg',
409
+ time: '11:00 AM',
410
+ status: 'delivered',
411
+ };
412
+
413
+ const audioMessage: Message = {
414
+ id: '3',
415
+ senderId: 'user-2',
416
+ audio: 'file:///path/to/recording.m4a',
417
+ time: '11:05 AM',
418
+ status: 'read',
419
+ };
420
+ ```
421
+
422
+ Tap image/video bubbles to open the built-in **MediaViewer** (pinch-zoom for images, native controls for video).
423
+
424
+ ### Typing indicators
425
+
426
+ ```tsx
427
+ const [typingUsers, setTypingUsers] = useState<
428
+ { id: string; avatar: string; name: string }[]
429
+ >([]);
430
+
431
+ <ChatScreen
432
+ typingUsers={typingUsers}
433
+ onTypingStart={() => notifyServer('typing-start')}
434
+ onTypingEnd={() => notifyServer('typing-end')}
435
+ // ...
436
+ />
437
+ ```
438
+
439
+ When your socket receives “user X is typing”, push into `typingUsers`. The component shows up to two avatars plus a “+N” badge.
440
+
441
+ ### Attachments & camera (parent-controlled)
442
+
443
+ ```tsx
444
+ import { launchImageLibrary } from 'react-native-image-picker';
445
+
446
+ <ChatScreen
447
+ showAttachmentsButton
448
+ showCameraButton
449
+ onAttachmentPress={async () => {
450
+ const result = await launchImageLibrary({ mediaType: 'mixed' });
451
+ }}
452
+ onCameraPress={async () => {
453
+ // open camera, then add message with image/video URI
454
+ }}
455
+ onAudioRecordStart={() => {
456
+ // start native recorder
457
+ }}
458
+ onAudioRecordEnd={async () => {
459
+ // stop recorder, upload, then:
460
+ // addMessage({ audio: uploadedUrl, senderId: currentUserId, ... });
461
+ }}
462
+ />
463
+ ```
464
+
465
+ ### Attachment preview before send
466
+
467
+ ```tsx
468
+ const [previewData, setPreviewData] = useState<{
469
+ uri: string;
470
+ type: string;
471
+ name: string;
472
+ } | null>(null);
473
+
474
+ <ChatScreen
475
+ previewData={previewData ?? undefined}
476
+ closePreview={() => setPreviewData(null)}
477
+ onAttachmentPress={async () => {
478
+ const asset = await pickDocument();
479
+ setPreviewData({
480
+ uri: asset.uri,
481
+ type: asset.type ?? 'application/octet-stream',
482
+ name: asset.name ?? 'file',
483
+ });
484
+ }}
485
+ onSendMessage={({ text, senderId }) => {
486
+ const msg: Message = {
487
+ id: String(Date.now()),
488
+ senderId,
489
+ text: text || undefined,
490
+ image: previewData?.type.startsWith('image/')
491
+ ? previewData.uri
492
+ : undefined,
493
+ video: previewData?.type.startsWith('video/')
494
+ ? previewData.uri
495
+ : undefined,
496
+ time: formatTime(new Date()),
497
+ status: 'sent',
498
+ };
499
+ setMessages((prev) => [msg, ...prev]);
500
+ setPreviewData(null);
501
+ }}
502
+ />
503
+ ```
504
+
505
+ ### Custom theme
506
+
507
+ ```tsx
192
508
  <ChatScreen
193
- messages={messages}
194
- currentUserId="user123"
195
- onSendMessage={handleSendMessage}
196
509
  theme={{
510
+ fontFamily: 'Inter-Regular',
197
511
  colors: {
198
512
  sentMessageTailColor: '#007AFF',
199
513
  receivedMessageTailColor: '#E9E9EB',
200
514
  timestamp: '#8E8E93',
515
+ readIconColor: '#34C759',
516
+ inputTextColor: '#000000',
201
517
  },
202
518
  bubbleStyle: {
203
- sent: {
204
- backgroundColor: '#007AFF',
205
- borderRadius: 20,
206
- },
207
- received: {
208
- backgroundColor: '#E9E9EB',
209
- borderRadius: 20,
210
- },
519
+ sent: { backgroundColor: '#007AFF' },
520
+ received: { backgroundColor: '#E9E9EB' },
521
+ },
522
+ messageStyle: {
523
+ sentTextStyle: { color: '#FFFFFF' },
524
+ receivedTextStyle: { color: '#000000' },
525
+ },
526
+ inputStyles: {
527
+ sendButtonStyle: { backgroundColor: '#007AFF' },
211
528
  },
212
529
  }}
530
+ // ...
213
531
  />
214
532
  ```
215
533
 
216
- ### Expo Usage
534
+ ### Custom input bar
217
535
 
218
- If you're using Expo, follow these steps:
536
+ ```tsx
537
+ <ChatScreen
538
+ renderCustomInput={() => <MyComposer />}
539
+ // Still use messages / currentUserId / onSendMessage via your own state
540
+ />
541
+ ```
542
+
543
+ When using `renderCustomInput`, you are responsible for calling your send logic; the default `ChatInput` is not mounted.
219
544
 
220
- 1. Create a development build of your app:
221
- ```bash
222
- npx expo prebuild
223
- ```
545
+ ### Long-press actions
224
546
 
225
- 2. Run on your desired platform:
226
- ```bash
227
- npx expo run:android
228
- # or
229
- npx expo run:ios
230
- ```
547
+ ```tsx
548
+ <ChatScreen
549
+ onMessageLongPress={(message) => {
550
+ }}
551
+ />
552
+ ```
231
553
 
232
- 3. For subsequent updates to the native modules, you'll need to rebuild:
233
- ```bash
234
- npx expo prebuild --clean
235
- ```
554
+ ---
236
555
 
237
- ### Performance Considerations
556
+ ## TypeScript
238
557
 
239
- - Messages are rendered using `FlatList` for optimal performance
240
- - Avatar images are cached automatically
241
- - Media messages use lazy loading
242
- - Typing indicators are debounced
558
+ The main export is the default `ChatScreen` component. Types live in the build output:
559
+
560
+ ```typescript
561
+ import ChatScreen from 'movius-chats';
562
+ import type {
563
+ Message,
564
+ ChatScreenProps,
565
+ } from 'movius-chats/lib/typescript/types';
566
+ ```
567
+
568
+ `ChatScreenProps` is the full props interface for `ChatScreen`.
569
+
570
+ ---
571
+
572
+ ## Architecture overview
573
+
574
+ ```
575
+ ChatScreen
576
+ ├── AudioProvider # one audio message plays at a time
577
+ ├── ChatProvider # props, theme, media viewer state
578
+ ├── FlatList (inverted) # ChatBubble per message
579
+ │ └── ListHeaderComponent → TypingIndicator
580
+ ├── ChatInput (optional) # text, icons, file preview
581
+ └── MediaViewer (Modal) # full-screen image / video
582
+ ```
583
+
584
+ Internal pieces (not exported from the package entry, but useful when reading source):
585
+
586
+ - **ChatBubble** — layout, tail, avatar, `MessageContent`, `MessageStatus`
587
+ - **MessageContent** — image, video thumbnail, `AudioPlayer`, parsed text
588
+ - **AudioPlayer** — `react-native-sound` + Reanimated scrubber
589
+ - **FilePreview** — pre-send attachment chip above input
590
+
591
+ ---
243
592
 
244
593
  ## Troubleshooting
245
594
 
246
- ### Common Issues
595
+ | Issue | What to try |
596
+ |-------|-------------|
597
+ | `Native module not found` | Rebuild iOS/Android after install; run `pod install` on iOS |
598
+ | Crashes in Expo Go | Use a development build; native modules are not in Expo Go |
599
+ | Audio silent on iOS | Set `AVAudioSession` category to playback (see installation) |
600
+ | Video/audio won’t load | Check URI scheme (`https://`, `file://`) and Android `INTERNET` permission |
601
+ | Reanimated worklet errors | Ensure `react-native-reanimated/plugin` is **last** in Babel config |
602
+ | Types not found | Import from `movius-chats/lib/typescript/types` |
603
+ | Messages appear in wrong order | Newest item must be `messages[0]` (inverted list) |
604
+ | Icons/buttons missing | Pass feature flags (`showEmojiButton`, etc.) — they default to off |
247
605
 
248
- - **"Native module not found" error**: Ensure you've rebuilt your app after installing the package.
249
- - **Crashes in Expo Go**: This package uses native modules that are not compatible with Expo Go. Use a development build instead.
250
- - **Audio/Video not working**: Check that you've installed all required dependencies and rebuilt the app.
606
+ ---
251
607
 
252
608
  ## Contributing
253
609
 
254
- We welcome contributions! Please see our contributing guide for details.
255
-
256
- ## License
610
+ Issues and pull requests are welcome on [GitHub](https://github.com/David-Atueyi/Movius-Chats/issues).
257
611
 
258
- MIT
612
+ ---
259
613
 
260
- ## Support
614
+ ## License
261
615
 
262
- For issues and feature requests, please file an issue on the GitHub repository.
616
+ ISC see [package.json](./package.json).