@sujeetdotkumar/react-native-gifted-chat-performant 1.0.0 → 1.0.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.
- package/README.md +189 -663
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,102 +1,121 @@
|
|
|
1
1
|
<p align="center">
|
|
2
|
-
<a href="https://www.npmjs.com/package/react-native-gifted-chat"><img alt="npm version" src="https://badge.fury.io/js
|
|
3
|
-
<a href="https://www.npmjs.com/package/react-native-gifted-chat"><img alt="npm downloads" src="https://img.shields.io/npm/dm/react-native-gifted-chat.svg"/></a>
|
|
4
|
-
<a href="https://circleci.com/gh/FaridSafi/react-native-gifted-chat"><img src="https://circleci.com/gh/FaridSafi/react-native-gifted-chat.svg?style=shield" alt="build"></a>
|
|
2
|
+
<a href="https://www.npmjs.com/package/@sujeetdotkumar/react-native-gifted-chat-performant"><img alt="npm version" src="https://badge.fury.io/js/@sujeetdotkumar%2Freact-native-gifted-chat-performant.svg"/></a>
|
|
5
3
|
<img src="https://img.shields.io/badge/platforms-iOS%20%7C%20Android%20%7C%20Web-lightgrey.svg" alt="platforms">
|
|
6
4
|
<img src="https://img.shields.io/badge/TypeScript-supported-blue.svg" alt="TypeScript">
|
|
7
5
|
<img src="https://img.shields.io/badge/Expo-compatible-000020.svg" alt="Expo compatible">
|
|
8
6
|
</p>
|
|
9
7
|
|
|
10
|
-
<h1 align="center">
|
|
8
|
+
<h1 align="center">react-native-gifted-chat-performant</h1>
|
|
11
9
|
|
|
12
10
|
<p align="center">
|
|
13
|
-
|
|
14
|
-
</p>
|
|
15
|
-
|
|
16
|
-
<p align="center">
|
|
17
|
-
<a href="https://snack.expo.dev/@kesha-antonov/gifted-chat-playground" target="_blank">
|
|
18
|
-
<img src="https://img.shields.io/badge/▶️_Try_in_Browser-4630EB?style=for-the-badge&logo=expo&logoColor=white" alt="Try GiftedChat on Expo Snack"/>
|
|
19
|
-
</a>
|
|
11
|
+
A high-performance fork of <a href="https://github.com/FaridSafi/react-native-gifted-chat">react-native-gifted-chat</a> built for large message lists.
|
|
20
12
|
</p>
|
|
21
13
|
|
|
22
14
|
---
|
|
23
15
|
|
|
24
|
-
##
|
|
25
|
-
|
|
26
|
-
- 🎨 **Fully Customizable** - Override any component with your own implementation
|
|
27
|
-
- 📎 **Composer Actions** - Attach photos, files, or trigger custom actions
|
|
28
|
-
- ↩️ **Reply to Messages** - Swipe-to-reply with reply preview and message threading
|
|
29
|
-
- ⏮️ **Load Earlier Messages** - Infinite scroll with pagination support
|
|
30
|
-
- 📋 **Copy to Clipboard** - Long-press messages to copy text
|
|
31
|
-
- 🔗 **Smart Link Parsing** - Auto-detect URLs, emails, phone numbers, hashtags, mentions
|
|
32
|
-
- 👤 **Avatars** - User initials or custom avatar images
|
|
33
|
-
- 🌍 **Localized Dates** - Full i18n support via Day.js
|
|
34
|
-
- ⌨️ **Keyboard Handling** - Smart keyboard avoidance for all platforms
|
|
35
|
-
- 💬 **System Messages** - Display system notifications in chat
|
|
36
|
-
- ⚡ **Quick Replies** - Bot-style quick reply buttons
|
|
37
|
-
- ✍️ **Typing Indicator** - Show when users are typing
|
|
38
|
-
- ✅ **Message Status** - Tick indicators for sent/delivered/read states
|
|
39
|
-
- ⬇️ **Scroll to Bottom** - Quick navigation button
|
|
40
|
-
- 🌐 **Web Support** - Works with react-native-web
|
|
41
|
-
- 📱 **Expo Support** - Easy integration with Expo projects
|
|
42
|
-
- 📝 **TypeScript** - Complete TypeScript definitions included
|
|
16
|
+
## What's different from the original
|
|
43
17
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
18
|
+
This fork replaces the core rendering stack with purpose-built, higher-performance primitives while keeping the full public API intact (except the date/time format props — see [Breaking Changes](#breaking-changes)).
|
|
19
|
+
|
|
20
|
+
### List renderer — LegendList v3
|
|
21
|
+
|
|
22
|
+
The `AnimatedFlatList` (a `react-native-gesture-handler` FlatList wrapped in `Animated.createAnimatedComponent`) has been replaced with [`KeyboardChatLegendList`](https://www.legendapp.com/open-source/list/v3/) from `@legendapp/list`.
|
|
23
|
+
|
|
24
|
+
LegendList is purpose-built for chat and large lists in React Native. Key advantages over FlatList:
|
|
25
|
+
- Separate recycling pools per item type (`'message'` vs `'day'`) — prevents layout thrash between different-height items
|
|
26
|
+
- `maintainScrollAtEnd`, `alignItemsAtEnd`, `initialScrollAtEnd` — purpose-built chat scroll semantics without an `inverted` prop hack
|
|
27
|
+
- `sharedValues` prop syncs scroll offset directly to a Reanimated `SharedValue` — no `useAnimatedScrollHandler` needed
|
|
28
|
+
- Pure JS, no native code, no extra `pod install`
|
|
29
|
+
|
|
30
|
+
### Day separator tracking — eliminated
|
|
31
|
+
|
|
32
|
+
The original implementation tracked day separator positions using a `CellRendererComponent` that fired a layout worklet on **every message item** on every render. Those positions were stored in a `daysPositions` SharedValue, sorted (O(n log n)), and interpolated on every scroll frame. All of that is gone.
|
|
33
|
+
|
|
34
|
+
Day separators are now interleaved directly in the data array (`displayData`) alongside messages. An `onViewableItemsChanged` callback tracks the topmost visible day for the animated overlay — zero per-item overhead.
|
|
35
|
+
|
|
36
|
+
### Keyboard handling — KeyboardStickyView
|
|
37
|
+
|
|
38
|
+
`KeyboardAvoidingView` (which can cause layout jumps on keyboard open) has been replaced with:
|
|
39
|
+
- `KeyboardChatLegendList` handles keyboard-aware scroll internally
|
|
40
|
+
- `KeyboardStickyView` from `react-native-keyboard-controller` keeps the input toolbar pinned above the keyboard
|
|
41
|
+
|
|
42
|
+
### dayjs removed — native Intl APIs
|
|
43
|
+
|
|
44
|
+
`dayjs` (~5 KB gzipped) has been removed entirely. All date and time formatting now uses the built-in `Intl.DateTimeFormat` API, which is zero-cost (already part of the JS engine), supports the same locale strings, and produces identical output.
|
|
45
|
+
|
|
46
|
+
### lodash.isequal removed
|
|
47
|
+
|
|
48
|
+
`lodash.isequal` was listed as a dependency in the original but was not imported anywhere in `src/`. Removed.
|
|
49
|
+
|
|
50
|
+
### React.memo on leaf components
|
|
51
|
+
|
|
52
|
+
`Day`, `Time`, and `GiftedAvatar` are now wrapped with `React.memo` to prevent unnecessary re-renders when parent state changes don't affect their props.
|
|
53
|
+
|
|
54
|
+
### Performance summary
|
|
55
|
+
|
|
56
|
+
| Bottleneck | Original | This fork |
|
|
57
|
+
|---|---|---|
|
|
58
|
+
| List virtualization | RNGH FlatList + Animated wrapper | LegendList v3 with separate recycling pools |
|
|
59
|
+
| Day position tracking | O(n log n) sort + worklet per item layout | Eliminated — data-driven via `displayData` |
|
|
60
|
+
| Per-item CellRendererComponent | Fires worklet on every item layout | Removed |
|
|
61
|
+
| `daysPositions` SharedValue | Modified per-item, read per scroll frame | Removed |
|
|
62
|
+
| dayjs bundle | ~5 KB gzipped | 0 (native Intl) |
|
|
63
|
+
| lodash.isequal | ~4 KB gzipped | 0 (removed) |
|
|
51
64
|
|
|
52
65
|
---
|
|
53
66
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
<table align="center" border="0" cellspacing="20">
|
|
57
|
-
<tr>
|
|
58
|
-
<td align="center" valign="middle">
|
|
59
|
-
<a href="https://www.lereacteur.io" target="_blank"><img src="https://raw.githubusercontent.com/FaridSafi/react-native-gifted-chat/master/media/logo_sponsor.png" height="50"></a>
|
|
60
|
-
</td>
|
|
61
|
-
<td align="center" valign="middle">
|
|
62
|
-
<a href="https://getstream.io/chat/?utm_source=Github&utm_medium=Github_Repo_Content_Ad&utm_content=Developer&utm_campaign=Github_Jan2022_Chat&utm_term=react-native-gifted-chat" target="_blank"><img src="https://raw.githubusercontent.com/FaridSafi/react-native-gifted-chat/master/media/stream-logo.png" height="35"></a>
|
|
63
|
-
</td>
|
|
64
|
-
<td align="center" valign="middle">
|
|
65
|
-
<a href="https://www.ethora.com" target="_blank"><img src="https://www.dappros.com/wp-content/uploads/2023/12/Ethora-Logo.png" height="50"></a>
|
|
66
|
-
</td>
|
|
67
|
-
</tr>
|
|
68
|
-
</table>
|
|
67
|
+
## Breaking Changes
|
|
69
68
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
69
|
+
### Date & time format props
|
|
70
|
+
|
|
71
|
+
The original `dayjs`-based format props have been replaced with `Intl.DateTimeFormatOptions`:
|
|
72
|
+
|
|
73
|
+
| Original prop | Type | Replacement | Type |
|
|
74
|
+
|---|---|---|---|
|
|
75
|
+
| `timeFormat` | `string` (dayjs format, e.g. `'LT'`) | `timeFormatOptions` | `Intl.DateTimeFormatOptions` |
|
|
76
|
+
| `dateFormat` | `string` (dayjs format, e.g. `'D MMMM'`) | `dateFormatOptions` | `Intl.DateTimeFormatOptions` |
|
|
77
|
+
| `dateFormatCalendar` | `object` (dayjs calendar options) | removed — use `dateFormatOptions` | — |
|
|
78
|
+
|
|
79
|
+
**Migration example:**
|
|
80
|
+
|
|
81
|
+
```tsx
|
|
82
|
+
// Before
|
|
83
|
+
<GiftedChat timeFormat='HH:mm' dateFormat='D MMMM' />
|
|
84
|
+
|
|
85
|
+
// After
|
|
86
|
+
<GiftedChat
|
|
87
|
+
timeFormatOptions={{ hour: '2-digit', minute: '2-digit', hour12: false }}
|
|
88
|
+
dateFormatOptions={{ day: 'numeric', month: 'long' }}
|
|
89
|
+
/>
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
The `locale` prop continues to work — it is passed directly to `Intl.DateTimeFormat` as the locale string.
|
|
79
93
|
|
|
80
94
|
---
|
|
81
95
|
|
|
82
|
-
##
|
|
83
|
-
|
|
84
|
-
-
|
|
85
|
-
-
|
|
86
|
-
-
|
|
87
|
-
-
|
|
88
|
-
-
|
|
89
|
-
-
|
|
90
|
-
-
|
|
91
|
-
-
|
|
92
|
-
-
|
|
93
|
-
-
|
|
94
|
-
-
|
|
95
|
-
-
|
|
96
|
+
## Features
|
|
97
|
+
|
|
98
|
+
- Fully customizable — override any component with your own implementation
|
|
99
|
+
- Composer actions — attach photos, files, or trigger custom actions
|
|
100
|
+
- Reply to messages — swipe-to-reply with reply preview and message threading
|
|
101
|
+
- Load earlier messages — infinite scroll with pagination support
|
|
102
|
+
- Copy to clipboard — long-press messages to copy text
|
|
103
|
+
- Smart link parsing — auto-detect URLs, emails, phone numbers, hashtags, mentions
|
|
104
|
+
- Avatars — user initials or custom avatar images
|
|
105
|
+
- Localized dates — full i18n support via native `Intl.DateTimeFormat`
|
|
106
|
+
- Keyboard handling — `KeyboardChatLegendList` + `KeyboardStickyView` for all platforms
|
|
107
|
+
- System messages — display system notifications in chat
|
|
108
|
+
- Quick replies — bot-style quick reply buttons
|
|
109
|
+
- Typing indicator — show when users are typing
|
|
110
|
+
- Message status — tick indicators for sent/delivered/read states
|
|
111
|
+
- Scroll to bottom — quick navigation button
|
|
112
|
+
- Web support — works with react-native-web
|
|
113
|
+
- Expo support — easy integration with Expo projects
|
|
114
|
+
- TypeScript — complete TypeScript definitions included
|
|
96
115
|
|
|
97
116
|
---
|
|
98
117
|
|
|
99
|
-
##
|
|
118
|
+
## Requirements
|
|
100
119
|
|
|
101
120
|
| Requirement | Version |
|
|
102
121
|
|-------------|---------|
|
|
@@ -104,58 +123,44 @@
|
|
|
104
123
|
| iOS | >= 13.4 |
|
|
105
124
|
| Android | API 21+ (Android 5.0) |
|
|
106
125
|
| Expo | SDK 50+ |
|
|
126
|
+
| react-native-keyboard-controller | >= 1.21.0 |
|
|
107
127
|
| TypeScript | >= 5.0 (optional) |
|
|
108
128
|
|
|
109
129
|
---
|
|
110
130
|
|
|
111
|
-
##
|
|
131
|
+
## Installation
|
|
112
132
|
|
|
113
133
|
### Expo Projects
|
|
114
134
|
|
|
115
135
|
```bash
|
|
116
|
-
npx expo install react-native-gifted-chat react-native-reanimated react-native-gesture-handler react-native-safe-area-context react-native-keyboard-controller
|
|
136
|
+
npx expo install @sujeetdotkumar/react-native-gifted-chat-performant @legendapp/list react-native-reanimated react-native-gesture-handler react-native-safe-area-context react-native-keyboard-controller
|
|
117
137
|
```
|
|
118
138
|
|
|
119
139
|
### Bare React Native Projects
|
|
120
140
|
|
|
121
|
-
**Step 1:** Install the packages
|
|
122
|
-
|
|
123
|
-
Using yarn:
|
|
124
141
|
```bash
|
|
125
|
-
yarn add react-native-gifted-chat react-native-reanimated react-native-gesture-handler react-native-safe-area-context react-native-keyboard-controller
|
|
142
|
+
yarn add @sujeetdotkumar/react-native-gifted-chat-performant @legendapp/list react-native-reanimated react-native-gesture-handler react-native-safe-area-context react-native-keyboard-controller
|
|
126
143
|
```
|
|
127
144
|
|
|
128
|
-
|
|
129
|
-
```bash
|
|
130
|
-
npm install --save react-native-gifted-chat react-native-reanimated react-native-gesture-handler react-native-safe-area-context react-native-keyboard-controller
|
|
131
|
-
```
|
|
132
|
-
|
|
133
|
-
**Step 2:** Install iOS pods
|
|
145
|
+
Then install iOS pods:
|
|
134
146
|
|
|
135
147
|
```bash
|
|
136
148
|
npx pod-install
|
|
137
149
|
```
|
|
138
150
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
Follow the [react-native-reanimated installation guide](https://docs.swmansion.com/react-native-reanimated/docs/fundamentals/getting-started/#step-2-add-reanimateds-babel-plugin) to add the Babel plugin.
|
|
151
|
+
And follow the [react-native-reanimated installation guide](https://docs.swmansion.com/react-native-reanimated/docs/fundamentals/getting-started/#step-2-add-reanimateds-babel-plugin) to add the Babel plugin.
|
|
142
152
|
|
|
143
153
|
---
|
|
144
154
|
|
|
145
|
-
##
|
|
146
|
-
|
|
147
|
-
### Basic Example
|
|
155
|
+
## Usage
|
|
148
156
|
|
|
149
157
|
```jsx
|
|
150
158
|
import React, { useState, useCallback, useEffect } from 'react'
|
|
151
|
-
import { GiftedChat } from 'react-native-gifted-chat'
|
|
159
|
+
import { GiftedChat } from '@sujeetdotkumar/react-native-gifted-chat-performant'
|
|
152
160
|
import { useHeaderHeight } from '@react-navigation/elements'
|
|
153
161
|
|
|
154
162
|
export function Example() {
|
|
155
163
|
const [messages, setMessages] = useState([])
|
|
156
|
-
|
|
157
|
-
// keyboardVerticalOffset = distance from screen top to GiftedChat container
|
|
158
|
-
// useHeaderHeight() returns status bar + navigation header height
|
|
159
164
|
const headerHeight = useHeaderHeight()
|
|
160
165
|
|
|
161
166
|
useEffect(() => {
|
|
@@ -183,54 +188,16 @@ export function Example() {
|
|
|
183
188
|
<GiftedChat
|
|
184
189
|
messages={messages}
|
|
185
190
|
onSend={messages => onSend(messages)}
|
|
186
|
-
user={{
|
|
187
|
-
_id: 1,
|
|
188
|
-
}}
|
|
191
|
+
user={{ _id: 1 }}
|
|
189
192
|
keyboardAvoidingViewProps={{ keyboardVerticalOffset: headerHeight }}
|
|
190
193
|
/>
|
|
191
194
|
)
|
|
192
195
|
}
|
|
193
196
|
```
|
|
194
197
|
|
|
195
|
-
> **💡 Tip:** Check out more examples in the [`example`](example) directory including Slack-style messages, quick replies, and custom components.
|
|
196
|
-
|
|
197
|
-
---
|
|
198
|
-
|
|
199
|
-
## 📊 Data Structure
|
|
200
|
-
|
|
201
|
-
Messages, system messages, and quick replies follow the structure defined in [Models.ts](src/Models.ts).
|
|
202
|
-
|
|
203
|
-
<details>
|
|
204
|
-
<summary><strong>Message Object Structure</strong></summary>
|
|
205
|
-
|
|
206
|
-
```typescript
|
|
207
|
-
interface IMessage {
|
|
208
|
-
_id: string | number
|
|
209
|
-
text: string
|
|
210
|
-
createdAt: Date | number
|
|
211
|
-
user: User
|
|
212
|
-
image?: string
|
|
213
|
-
video?: string
|
|
214
|
-
audio?: string
|
|
215
|
-
system?: boolean
|
|
216
|
-
sent?: boolean
|
|
217
|
-
received?: boolean
|
|
218
|
-
pending?: boolean
|
|
219
|
-
quickReplies?: QuickReplies
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
interface User {
|
|
223
|
-
_id: string | number
|
|
224
|
-
name?: string
|
|
225
|
-
avatar?: string | number | (() => React.ReactNode)
|
|
226
|
-
}
|
|
227
|
-
```
|
|
228
|
-
|
|
229
|
-
</details>
|
|
230
|
-
|
|
231
198
|
---
|
|
232
199
|
|
|
233
|
-
##
|
|
200
|
+
## Props Reference
|
|
234
201
|
|
|
235
202
|
### Core Configuration
|
|
236
203
|
|
|
@@ -238,191 +205,78 @@ interface User {
|
|
|
238
205
|
- **`user`** _(Object)_ - User sending the messages: `{ _id, name, avatar }`
|
|
239
206
|
- **`onSend`** _(Function)_ - Callback when sending a message
|
|
240
207
|
- **`messageIdGenerator`** _(Function)_ - Generate an id for new messages. Defaults to a simple random string generator.
|
|
241
|
-
- **`locale`** _(String)_ - Locale
|
|
242
|
-
- **`colorScheme`** _('light' | 'dark')_ - Force color scheme
|
|
208
|
+
- **`locale`** _(String)_ - Locale string passed to `Intl.DateTimeFormat` (e.g. `'fr'`, `'de'`, `'ja'`)
|
|
209
|
+
- **`colorScheme`** _('light' | 'dark')_ - Force color scheme. When `undefined`, uses the system color scheme.
|
|
243
210
|
|
|
244
211
|
### Refs
|
|
245
212
|
|
|
246
|
-
- **`messagesContainerRef`** _(
|
|
213
|
+
- **`messagesContainerRef`** _(LegendList ref)_ - Ref to the list
|
|
247
214
|
- **`textInputRef`** _(TextInput ref)_ - Ref to the text input
|
|
248
215
|
|
|
249
216
|
### Keyboard & Layout
|
|
250
217
|
|
|
251
|
-
- **`keyboardProviderProps`** _(Object)_ - Props
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
- **`
|
|
255
|
-
- **`isAlignedTop`** _(Boolean)_ Controls whether or not the message bubbles appear at the top of the chat (Default is false - bubbles align to bottom)
|
|
256
|
-
- **`isInverted`** _(Bool)_ - Reverses display order of `messages`; default is `true`
|
|
257
|
-
|
|
258
|
-
#### Understanding `keyboardVerticalOffset`
|
|
259
|
-
|
|
260
|
-
The [`keyboardVerticalOffset`](https://kirillzyusko.github.io/react-native-keyboard-controller/docs/api/components/keyboard-avoiding-view#keyboardverticaloffset) tells the KeyboardAvoidingView where its container starts relative to the top of the screen. This is essential when GiftedChat is not positioned at the very top of the screen (e.g., when you have a navigation header).
|
|
261
|
-
|
|
262
|
-
**Default value:** `insets.top` (status bar height from `useSafeAreaInsets()`). This works correctly only when GiftedChat fills the entire screen without a navigation header. If you have a navigation header, you need to pass the correct offset via `keyboardAvoidingViewProps`.
|
|
263
|
-
|
|
264
|
-
**What the value means:** The offset equals the distance (in points) from the top of the screen to the top of your GiftedChat container. This typically includes:
|
|
265
|
-
- Status bar height
|
|
266
|
-
- Navigation header height (on iOS, `useHeaderHeight()` already includes status bar)
|
|
267
|
-
|
|
268
|
-
**How to use:**
|
|
269
|
-
|
|
270
|
-
```jsx
|
|
271
|
-
import { useHeaderHeight } from '@react-navigation/elements'
|
|
272
|
-
|
|
273
|
-
function ChatScreen() {
|
|
274
|
-
// useHeaderHeight() returns status bar + navigation header height on iOS
|
|
275
|
-
const headerHeight = useHeaderHeight()
|
|
276
|
-
|
|
277
|
-
return (
|
|
278
|
-
<GiftedChat
|
|
279
|
-
keyboardAvoidingViewProps={{ keyboardVerticalOffset: headerHeight }}
|
|
280
|
-
// ... other props
|
|
281
|
-
/>
|
|
282
|
-
)
|
|
283
|
-
}
|
|
284
|
-
```
|
|
285
|
-
|
|
286
|
-
> **Note:** `useHeaderHeight()` requires your chat component to be rendered inside a proper navigation screen (not conditional rendering). If it returns `0`, ensure your chat screen is a real navigation screen with a visible header.
|
|
287
|
-
|
|
288
|
-
**Why this matters:** Without the correct offset, the keyboard may overlap the input field or leave extra space. The KeyboardAvoidingView uses this value to calculate how much to shift the content when the keyboard appears.
|
|
218
|
+
- **`keyboardProviderProps`** _(Object)_ - Props for [`KeyboardProvider`](https://kirillzyusko.github.io/react-native-keyboard-controller/docs/api/keyboard-provider)
|
|
219
|
+
- **`keyboardAvoidingViewProps`** _(Object)_ - Props including `keyboardVerticalOffset` (distance from screen top to GiftedChat container — use `useHeaderHeight()` when inside a navigation stack)
|
|
220
|
+
- **`isAlignedTop`** _(Boolean)_ - Align bubbles to top instead of bottom; default `false`
|
|
221
|
+
- **`isInverted`** _(Bool)_ - Reverses display order of `messages`; default `true`
|
|
289
222
|
|
|
290
223
|
### Text Input & Composer
|
|
291
224
|
|
|
292
|
-
- **`text`** _(String)_ -
|
|
293
|
-
- **`initialText`** _(String)_ - Initial text
|
|
294
|
-
- **`isSendButtonAlwaysVisible`** _(Bool)_ - Always show send button
|
|
295
|
-
- **`
|
|
296
|
-
- **`
|
|
297
|
-
- **`
|
|
298
|
-
- **`
|
|
299
|
-
- **`
|
|
300
|
-
- **`
|
|
301
|
-
- **`
|
|
302
|
-
- **`
|
|
303
|
-
- **`renderAccessory`** _(Component | Function)_ - Custom second line of actions below the message composer
|
|
304
|
-
- **`textInputProps`** _(Object)_ - props to be passed to the [`<TextInput>`](https://reactnative.dev/docs/textinput).
|
|
305
|
-
|
|
306
|
-
### Actions & Action Sheet
|
|
307
|
-
|
|
308
|
-
- **`onPressActionButton`** _(Function)_ - Callback when the Action button is pressed (if set, the default `actionSheet` will not be used)
|
|
309
|
-
- **`actionSheet`** _(Function)_ - Custom action sheet interface for showing action options
|
|
310
|
-
- **`actions`** _(Array)_ - Custom action options for the input toolbar action button; array of objects with `title` (string) and `action` (function) properties
|
|
311
|
-
- **`actionSheetOptionTintColor`** _(String)_ - Tint color for action sheet options
|
|
225
|
+
- **`text`** _(String)_ - Controlled input text
|
|
226
|
+
- **`initialText`** _(String)_ - Initial text in the input field
|
|
227
|
+
- **`isSendButtonAlwaysVisible`** _(Bool)_ - Always show send button; default `false`
|
|
228
|
+
- **`minComposerHeight`** / **`maxComposerHeight`** _(Number)_ - Composer height bounds
|
|
229
|
+
- **`minInputToolbarHeight`** _(Integer)_ - Minimum toolbar height; default `44`
|
|
230
|
+
- **`renderInputToolbar`** _(Component | Function)_ - Custom input toolbar
|
|
231
|
+
- **`renderComposer`** _(Component | Function)_ - Custom text input
|
|
232
|
+
- **`renderSend`** _(Component | Function)_ - Custom send button
|
|
233
|
+
- **`renderActions`** _(Component | Function)_ - Custom action button (left of composer)
|
|
234
|
+
- **`renderAccessory`** _(Component | Function)_ - Custom second line below composer
|
|
235
|
+
- **`textInputProps`** _(Object)_ - Props passed to `<TextInput>`
|
|
312
236
|
|
|
313
237
|
### Messages & Message Container
|
|
314
238
|
|
|
315
|
-
- **`messagesContainerStyle`** _(Object)_ - Custom style for
|
|
239
|
+
- **`messagesContainerStyle`** _(Object)_ - Custom style for messages container
|
|
316
240
|
- **`renderMessage`** _(Component | Function)_ - Custom message container
|
|
317
|
-
- **`renderLoading`** _(Component | Function)_ -
|
|
318
|
-
- **`renderChatEmpty`** _(Component | Function)_ -
|
|
319
|
-
- **`renderChatFooter`** _(Component | Function)_ -
|
|
320
|
-
- **`listProps`** _(Object)_ - Extra props
|
|
241
|
+
- **`renderLoading`** _(Component | Function)_ - Loading view while initializing
|
|
242
|
+
- **`renderChatEmpty`** _(Component | Function)_ - Component when messages are empty
|
|
243
|
+
- **`renderChatFooter`** _(Component | Function)_ - Component below the message list
|
|
244
|
+
- **`listProps`** _(Object)_ - Extra props passed to the underlying `LegendList`
|
|
321
245
|
|
|
322
246
|
### Message Bubbles & Content
|
|
323
247
|
|
|
324
|
-
- **`renderBubble`** _(Component | Function
|
|
248
|
+
- **`renderBubble`** _(Component | Function)_ - Custom message bubble
|
|
325
249
|
- **`renderMessageText`** _(Component | Function)_ - Custom message text
|
|
326
250
|
- **`renderMessageImage`** _(Component | Function)_ - Custom message image
|
|
327
251
|
- **`renderMessageVideo`** _(Component | Function)_ - Custom message video
|
|
328
252
|
- **`renderMessageAudio`** _(Component | Function)_ - Custom message audio
|
|
329
253
|
- **`renderCustomView`** _(Component | Function)_ - Custom view inside the bubble
|
|
330
|
-
- **`isCustomViewBottom`** _(Bool)_ -
|
|
331
|
-
- **`onPressMessage`** _(Function
|
|
332
|
-
- **`
|
|
333
|
-
- **`
|
|
334
|
-
- **`imageStyle`** _(Object)_ - Custom style for message images
|
|
335
|
-
- **`videoProps`** _(Object)_ - Extra props to be passed to the video component created by the required `renderMessageVideo`
|
|
336
|
-
- **`messageTextProps`** _(Object)_ - Extra props to be passed to the MessageText component. Useful for customizing link parsing behavior, text styles, and matchers. Supports the following props:
|
|
337
|
-
- `matchers` - Custom matchers for linking message content (like URLs, phone numbers, hashtags, mentions)
|
|
338
|
-
- `linkStyle` - Custom style for links
|
|
339
|
-
- `email` - Enable/disable email parsing (default: true)
|
|
340
|
-
- `phone` - Enable/disable phone number parsing (default: true)
|
|
341
|
-
- `url` - Enable/disable URL parsing (default: true)
|
|
342
|
-
- `hashtag` - Enable/disable hashtag parsing (default: false)
|
|
343
|
-
- `mention` - Enable/disable mention parsing (default: false)
|
|
344
|
-
- `hashtagUrl` - Base URL for hashtags (e.g., 'https://x.com/hashtag')
|
|
345
|
-
- `mentionUrl` - Base URL for mentions (e.g., 'https://x.com')
|
|
346
|
-
- `stripPrefix` - Strip 'http://' or 'https://' from URL display (default: false)
|
|
347
|
-
- `TextComponent` - Custom Text component to use (e.g., from react-native-gesture-handler)
|
|
348
|
-
|
|
349
|
-
Example:
|
|
350
|
-
|
|
351
|
-
```tsx
|
|
352
|
-
<GiftedChat
|
|
353
|
-
messageTextProps={{
|
|
354
|
-
phone: false, // Disable default phone number linking
|
|
355
|
-
matchers: [
|
|
356
|
-
{
|
|
357
|
-
type: 'phone',
|
|
358
|
-
pattern: /\+?[1-9][0-9\-\(\) ]{7,}[0-9]/g,
|
|
359
|
-
getLinkUrl: (replacerArgs: ReplacerArgs): string => {
|
|
360
|
-
return replacerArgs[0].replace(/[\-\(\) ]/g, '')
|
|
361
|
-
},
|
|
362
|
-
getLinkText: (replacerArgs: ReplacerArgs): string => {
|
|
363
|
-
return replacerArgs[0]
|
|
364
|
-
},
|
|
365
|
-
style: styles.linkStyle,
|
|
366
|
-
onPress: (match: CustomMatch) => {
|
|
367
|
-
const url = match.getAnchorHref()
|
|
368
|
-
|
|
369
|
-
const options: {
|
|
370
|
-
title: string
|
|
371
|
-
action?: () => void
|
|
372
|
-
}[] = [
|
|
373
|
-
{ title: 'Copy', action: () => setStringAsync(url) },
|
|
374
|
-
{ title: 'Call', action: () => Linking.openURL(`tel:${url}`) },
|
|
375
|
-
{ title: 'Send SMS', action: () => Linking.openURL(`sms:${url}`) },
|
|
376
|
-
{ title: 'Cancel' },
|
|
377
|
-
]
|
|
378
|
-
|
|
379
|
-
showActionSheetWithOptions({
|
|
380
|
-
options: options.map(o => o.title),
|
|
381
|
-
cancelButtonIndex: options.length - 1,
|
|
382
|
-
}, (buttonIndex?: number) => {
|
|
383
|
-
if (buttonIndex === undefined)
|
|
384
|
-
return
|
|
385
|
-
|
|
386
|
-
const option = options[buttonIndex]
|
|
387
|
-
option.action?.()
|
|
388
|
-
})
|
|
389
|
-
},
|
|
390
|
-
},
|
|
391
|
-
],
|
|
392
|
-
linkStyle: { left: { color: 'blue' }, right: { color: 'lightblue' } },
|
|
393
|
-
}}
|
|
394
|
-
/>
|
|
395
|
-
```
|
|
396
|
-
|
|
397
|
-
See full example in [LinksExample](example/components/chat-examples/LinksExample.tsx)
|
|
254
|
+
- **`isCustomViewBottom`** _(Bool)_ - Render custom view below text/image; default `false`
|
|
255
|
+
- **`onPressMessage`** / **`onLongPressMessage`** _(Function)_ - Message tap/long-press callbacks
|
|
256
|
+
- **`imageProps`** / **`imageStyle`** / **`videoProps`** - Image and video customization
|
|
257
|
+
- **`messageTextProps`** _(Object)_ - Props for `MessageText` (link parsing, matchers, styles)
|
|
398
258
|
|
|
399
259
|
### Avatars
|
|
400
260
|
|
|
401
|
-
- **`renderAvatar`** _(Component | Function)_ - Custom
|
|
402
|
-
- **`isUserAvatarVisible`** _(Bool)_ -
|
|
403
|
-
- **`isAvatarVisibleForEveryMessage`** _(Bool)_ -
|
|
404
|
-
- **`onPressAvatar`** _(Function
|
|
405
|
-
- **`
|
|
406
|
-
- **`isAvatarOnTop`** _(Bool)_ - Render the message avatar at the top of consecutive messages, rather than the bottom; default is `false`
|
|
261
|
+
- **`renderAvatar`** _(Component | Function | null)_ - Custom avatar; `null` to hide
|
|
262
|
+
- **`isUserAvatarVisible`** _(Bool)_ - Show avatar for current user; default `false`
|
|
263
|
+
- **`isAvatarVisibleForEveryMessage`** _(Bool)_ - Show avatar on every message; default `false`
|
|
264
|
+
- **`onPressAvatar`** / **`onLongPressAvatar`** _(Function)_ - Avatar tap callbacks
|
|
265
|
+
- **`isAvatarOnTop`** _(Bool)_ - Show avatar at top of consecutive messages; default `false`
|
|
407
266
|
|
|
408
267
|
### Username
|
|
409
268
|
|
|
410
|
-
- **`isUsernameVisible`** _(Bool)_ -
|
|
411
|
-
- **`renderUsername`** _(Component | Function)_ - Custom
|
|
269
|
+
- **`isUsernameVisible`** _(Bool)_ - Show username in bubble; default `false`
|
|
270
|
+
- **`renderUsername`** _(Component | Function)_ - Custom username component
|
|
412
271
|
|
|
413
272
|
### Date & Time
|
|
414
273
|
|
|
415
|
-
- **`
|
|
416
|
-
- **`
|
|
417
|
-
- **`
|
|
418
|
-
- **`
|
|
419
|
-
- **`
|
|
420
|
-
|
|
421
|
-
- `wrapperStyle` - Custom style for the day wrapper
|
|
422
|
-
- `textProps` - Props to pass to the Text component (e.g., `style`, `allowFontScaling`, `numberOfLines`)
|
|
423
|
-
- **`renderTime`** _(Component | Function)_ - Custom time inside a message
|
|
424
|
-
- **`timeTextStyle`** _(Object)_ - Custom text style for time inside messages (supports left/right styles)
|
|
425
|
-
- **`isDayAnimationEnabled`** _(Bool)_ - Enable animated day label that appears on scroll; default is `true`
|
|
274
|
+
- **`timeFormatOptions`** _(Intl.DateTimeFormatOptions)_ - Format for message times; default `{ hour: '2-digit', minute: '2-digit' }`
|
|
275
|
+
- **`dateFormatOptions`** _(Intl.DateTimeFormatOptions)_ - Format for day separators; default `{ day: 'numeric', month: 'long' }`
|
|
276
|
+
- **`renderDay`** _(Component | Function)_ - Custom day separator
|
|
277
|
+
- **`renderTime`** _(Component | Function)_ - Custom time component
|
|
278
|
+
- **`timeTextStyle`** _(Object)_ - Custom time text style (supports left/right)
|
|
279
|
+
- **`isDayAnimationEnabled`** _(Bool)_ - Animated day label on scroll; default `true`
|
|
426
280
|
|
|
427
281
|
### System Messages
|
|
428
282
|
|
|
@@ -430,439 +284,111 @@ See full example in [LinksExample](example/components/chat-examples/LinksExample
|
|
|
430
284
|
|
|
431
285
|
### Load Earlier Messages
|
|
432
286
|
|
|
433
|
-
- **`loadEarlierMessagesProps`** _(Object)_
|
|
434
|
-
- `isAvailable` -
|
|
435
|
-
- `onPress` -
|
|
436
|
-
- `isLoading` -
|
|
437
|
-
- `isInfiniteScrollEnabled` -
|
|
438
|
-
- `label`
|
|
439
|
-
|
|
440
|
-
- `wrapperStyle` - Custom style for the button wrapper
|
|
441
|
-
- `textStyle` - Custom style for the button text
|
|
442
|
-
- `activityIndicatorStyle` - Custom style for the loading indicator
|
|
443
|
-
- `activityIndicatorColor` - Color of the loading indicator (default: 'white')
|
|
444
|
-
- `activityIndicatorSize` - Size of the loading indicator (default: 'small')
|
|
445
|
-
- **`renderLoadEarlier`** _(Component | Function)_ - Custom "Load earlier messages" button
|
|
287
|
+
- **`loadEarlierMessagesProps`** _(Object)_
|
|
288
|
+
- `isAvailable` - Show/hide button
|
|
289
|
+
- `onPress` - Load callback
|
|
290
|
+
- `isLoading` - Show spinner
|
|
291
|
+
- `isInfiniteScrollEnabled` - Auto-trigger `onPress` at top of list
|
|
292
|
+
- `label`, `containerStyle`, `wrapperStyle`, `textStyle` - Customization
|
|
293
|
+
- **`renderLoadEarlier`** _(Component | Function)_ - Custom load-earlier button
|
|
446
294
|
|
|
447
295
|
### Typing Indicator
|
|
448
296
|
|
|
449
|
-
- **`isTyping`** _(Bool)_ -
|
|
450
|
-
- **`renderTypingIndicator`** _(Component | Function)_ - Custom typing indicator
|
|
451
|
-
- **`
|
|
452
|
-
- **`renderFooter`** _(Component | Function)_ - Custom footer component on the ListView, e.g. `'User is typing...'`; see [CustomizedFeaturesExample.tsx](example/components/chat-examples/CustomizedFeaturesExample.tsx) for an example. Overrides default typing indicator that triggers when `isTyping` is true.
|
|
297
|
+
- **`isTyping`** _(Bool)_ - Show typing indicator; default `false`
|
|
298
|
+
- **`renderTypingIndicator`** _(Component | Function)_ - Custom typing indicator
|
|
299
|
+
- **`renderFooter`** _(Component | Function)_ - Custom footer (overrides typing indicator)
|
|
453
300
|
|
|
454
301
|
### Quick Replies
|
|
455
302
|
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
- **`
|
|
459
|
-
- **`renderQuickReplies`** _(Function)_ - Custom all quick reply view
|
|
460
|
-
- **`quickReplyStyle`** _(StyleProp<ViewStyle>)_ - Custom quick reply view style
|
|
461
|
-
- **`quickReplyTextStyle`** _(StyleProp<TextStyle>)_ - Custom text style for quick reply buttons
|
|
462
|
-
- **`quickReplyContainerStyle`** _(StyleProp<ViewStyle>)_ - Custom container style for quick replies
|
|
463
|
-
- **`renderQuickReplySend`** _(Function)_ - Custom quick reply **send** view
|
|
303
|
+
- **`onQuickReply`** _(Function)_ - Callback when quick reply is sent
|
|
304
|
+
- **`renderQuickReplies`** / **`renderQuickReplySend`** _(Function)_ - Custom renderers
|
|
305
|
+
- **`quickReplyStyle`** / **`quickReplyTextStyle`** / **`quickReplyContainerStyle`** - Styles
|
|
464
306
|
|
|
465
307
|
### Reply to Messages
|
|
466
308
|
|
|
467
|
-
Gifted Chat supports swipe-to-reply functionality out of the box. When enabled, users can swipe on a message to reply to it, displaying a reply preview in the input toolbar and the replied message above the new message bubble.
|
|
468
|
-
|
|
469
|
-
> **Note:** This feature uses `ReanimatedSwipeable` from `react-native-gesture-handler` and `react-native-reanimated` for smooth, performant animations.
|
|
470
|
-
|
|
471
|
-
#### Basic Usage
|
|
472
|
-
|
|
473
309
|
```tsx
|
|
474
310
|
<GiftedChat
|
|
475
|
-
messages={messages}
|
|
476
|
-
onSend={onSend}
|
|
477
|
-
user={{ _id: 1 }}
|
|
478
311
|
reply={{
|
|
479
312
|
swipe: {
|
|
480
313
|
isEnabled: true,
|
|
481
|
-
direction: 'left',
|
|
314
|
+
direction: 'left',
|
|
315
|
+
onSwipe: (message) => setReplyMessage(message),
|
|
482
316
|
},
|
|
317
|
+
message: replyMessage,
|
|
318
|
+
onClear: () => setReplyMessage(null),
|
|
483
319
|
}}
|
|
484
320
|
/>
|
|
485
321
|
```
|
|
486
322
|
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
The `reply` prop accepts an object with the following structure:
|
|
323
|
+
Full `reply` prop shape:
|
|
490
324
|
|
|
491
325
|
```typescript
|
|
492
326
|
interface ReplyProps<TMessage> {
|
|
493
|
-
// Swipe gesture configuration
|
|
494
327
|
swipe?: {
|
|
495
|
-
isEnabled?: boolean
|
|
496
|
-
direction?: 'left' | 'right'
|
|
497
|
-
onSwipe?: (message: TMessage) => void
|
|
498
|
-
renderAction?: (
|
|
499
|
-
progress: SharedValue<number>,
|
|
500
|
-
translation: SharedValue<number>,
|
|
501
|
-
position: 'left' | 'right'
|
|
502
|
-
) => React.ReactNode
|
|
328
|
+
isEnabled?: boolean
|
|
329
|
+
direction?: 'left' | 'right'
|
|
330
|
+
onSwipe?: (message: TMessage) => void
|
|
331
|
+
renderAction?: (progress, translation, position) => React.ReactNode
|
|
503
332
|
actionContainerStyle?: StyleProp<ViewStyle>
|
|
504
333
|
}
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
imageStyle?: StyleProp<ImageStyle>
|
|
511
|
-
}
|
|
512
|
-
|
|
513
|
-
// In-bubble reply styling
|
|
514
|
-
messageStyle?: {
|
|
515
|
-
containerStyle?: StyleProp<ViewStyle>
|
|
516
|
-
containerStyleLeft?: StyleProp<ViewStyle>
|
|
517
|
-
containerStyleRight?: StyleProp<ViewStyle>
|
|
518
|
-
textStyle?: StyleProp<TextStyle>
|
|
519
|
-
textStyleLeft?: StyleProp<TextStyle>
|
|
520
|
-
textStyleRight?: StyleProp<TextStyle>
|
|
521
|
-
imageStyle?: StyleProp<ImageStyle>
|
|
522
|
-
}
|
|
523
|
-
|
|
524
|
-
// Callbacks and state
|
|
525
|
-
message?: ReplyMessage // Controlled reply state
|
|
526
|
-
onClear?: () => void // Called when reply cleared
|
|
527
|
-
onPress?: (message: TMessage) => void // Called when reply preview tapped
|
|
528
|
-
|
|
529
|
-
// Custom renderers
|
|
334
|
+
previewStyle?: { containerStyle?, textStyle?, imageStyle? }
|
|
335
|
+
messageStyle?: { containerStyle?, textStyle?, imageStyle?, ...left/right variants }
|
|
336
|
+
message?: ReplyMessage
|
|
337
|
+
onClear?: () => void
|
|
338
|
+
onPress?: (message: TMessage) => void
|
|
530
339
|
renderPreview?: (props: ReplyPreviewProps) => React.ReactNode
|
|
531
340
|
renderMessageReply?: (props: MessageReplyProps) => React.ReactNode
|
|
532
341
|
}
|
|
533
342
|
```
|
|
534
343
|
|
|
535
|
-
#### ReplyMessage Structure
|
|
536
|
-
|
|
537
|
-
When a message has a reply, it includes a `replyMessage` property:
|
|
538
|
-
|
|
539
|
-
```typescript
|
|
540
|
-
interface ReplyMessage {
|
|
541
|
-
_id: string | number
|
|
542
|
-
text: string
|
|
543
|
-
user: User
|
|
544
|
-
image?: string
|
|
545
|
-
audio?: string
|
|
546
|
-
}
|
|
547
|
-
```
|
|
548
|
-
|
|
549
|
-
#### Advanced Example with External State
|
|
550
|
-
|
|
551
|
-
```tsx
|
|
552
|
-
const [replyMessage, setReplyMessage] = useState<ReplyMessage | null>(null)
|
|
553
|
-
|
|
554
|
-
<GiftedChat
|
|
555
|
-
messages={messages}
|
|
556
|
-
onSend={messages => {
|
|
557
|
-
const newMessages = messages.map(msg => ({
|
|
558
|
-
...msg,
|
|
559
|
-
replyMessage: replyMessage || undefined,
|
|
560
|
-
}))
|
|
561
|
-
setMessages(prev => GiftedChat.append(prev, newMessages))
|
|
562
|
-
setReplyMessage(null)
|
|
563
|
-
}}
|
|
564
|
-
user={{ _id: 1 }}
|
|
565
|
-
reply={{
|
|
566
|
-
swipe: {
|
|
567
|
-
isEnabled: true,
|
|
568
|
-
direction: 'right',
|
|
569
|
-
onSwipe: setReplyMessage,
|
|
570
|
-
},
|
|
571
|
-
message: replyMessage,
|
|
572
|
-
onClear: () => setReplyMessage(null),
|
|
573
|
-
onPress: (msg) => scrollToMessage(msg._id),
|
|
574
|
-
}}
|
|
575
|
-
/>
|
|
576
|
-
```
|
|
577
|
-
|
|
578
|
-
#### Smooth Animations
|
|
579
|
-
|
|
580
|
-
The reply preview automatically animates when:
|
|
581
|
-
- **Appearing**: Smoothly expands from zero height with fade-in effect
|
|
582
|
-
- **Disappearing**: Smoothly collapses with fade-out effect
|
|
583
|
-
- **Content changes**: Smoothly transitions when replying to a different message
|
|
584
|
-
|
|
585
|
-
These animations use `react-native-reanimated` for 60fps performance.
|
|
586
|
-
|
|
587
344
|
### Scroll to Bottom
|
|
588
345
|
|
|
589
|
-
- **`isScrollToBottomEnabled`** _(Bool)_ -
|
|
590
|
-
- **`scrollToBottomComponent`** _(Function)_ - Custom
|
|
591
|
-
- **`
|
|
592
|
-
- **`scrollToBottomStyle`** _(Object)_ - Custom style for Scroll To Bottom wrapper (position, bottom, right, etc.)
|
|
593
|
-
- **`scrollToBottomContentStyle`** _(Object)_ - Custom style for Scroll To Bottom content (size, background, shadow, etc.)
|
|
594
|
-
|
|
595
|
-
### Maintaining Scroll Position (AI Chatbots)
|
|
596
|
-
|
|
597
|
-
For AI chat interfaces where long responses arrive and you don't want to disrupt the user's reading position, use [`maintainVisibleContentPosition`](https://reactnative.dev/docs/scrollview#maintainvisiblecontentposition) via `listProps`:
|
|
598
|
-
|
|
599
|
-
```tsx
|
|
600
|
-
// Basic usage - always maintain scroll position
|
|
601
|
-
<GiftedChat
|
|
602
|
-
listProps={{
|
|
603
|
-
maintainVisibleContentPosition: {
|
|
604
|
-
minIndexForVisible: 0,
|
|
605
|
-
},
|
|
606
|
-
}}
|
|
607
|
-
/>
|
|
608
|
-
|
|
609
|
-
// With auto-scroll threshold - auto-scroll if within 10 pixels of newest content
|
|
610
|
-
<GiftedChat
|
|
611
|
-
listProps={{
|
|
612
|
-
maintainVisibleContentPosition: {
|
|
613
|
-
minIndexForVisible: 0,
|
|
614
|
-
autoscrollToTopThreshold: 10,
|
|
615
|
-
},
|
|
616
|
-
}}
|
|
617
|
-
/>
|
|
618
|
-
|
|
619
|
-
// Conditionally enable based on scroll state (recommended for chatbots)
|
|
620
|
-
const [isScrolledUp, setIsScrolledUp] = useState(false)
|
|
621
|
-
|
|
622
|
-
<GiftedChat
|
|
623
|
-
listProps={{
|
|
624
|
-
onScroll: (event) => {
|
|
625
|
-
setIsScrolledUp(event.contentOffset.y > 50)
|
|
626
|
-
},
|
|
627
|
-
maintainVisibleContentPosition: isScrolledUp
|
|
628
|
-
? { minIndexForVisible: 0, autoscrollToTopThreshold: 10 }
|
|
629
|
-
: undefined,
|
|
630
|
-
}}
|
|
631
|
-
/>
|
|
632
|
-
```
|
|
346
|
+
- **`isScrollToBottomEnabled`** _(Bool)_ - Show scroll-to-bottom button; default `false`
|
|
347
|
+
- **`scrollToBottomComponent`** _(Function)_ - Custom button content
|
|
348
|
+
- **`scrollToBottomStyle`** / **`scrollToBottomContentStyle`** - Styles
|
|
633
349
|
|
|
634
350
|
---
|
|
635
351
|
|
|
636
|
-
##
|
|
352
|
+
## Platform Notes
|
|
637
353
|
|
|
638
354
|
### Android
|
|
639
355
|
|
|
640
|
-
|
|
641
|
-
<summary><strong>Keyboard configuration</strong></summary>
|
|
642
|
-
|
|
643
|
-
If you are using Create React Native App / Expo, no Android specific installation steps are required. Otherwise, we recommend modifying your project configuration:
|
|
644
|
-
|
|
645
|
-
Make sure you have `android:windowSoftInputMode="adjustResize"` in your `AndroidManifest.xml`:
|
|
356
|
+
Add `android:windowSoftInputMode="adjustResize"` to your `AndroidManifest.xml`:
|
|
646
357
|
|
|
647
358
|
```xml
|
|
648
359
|
<activity
|
|
649
360
|
android:name=".MainActivity"
|
|
650
|
-
android:label="@string/app_name"
|
|
651
361
|
android:windowSoftInputMode="adjustResize"
|
|
652
362
|
android:configChanges="keyboard|keyboardHidden|orientation|screenSize">
|
|
653
363
|
```
|
|
654
364
|
|
|
655
|
-
For **Expo**, you can append `KeyboardAvoidingView` after GiftedChat (Android only):
|
|
656
|
-
|
|
657
|
-
```jsx
|
|
658
|
-
<View style={{ flex: 1 }}>
|
|
659
|
-
<GiftedChat />
|
|
660
|
-
{Platform.OS === 'android' && <KeyboardAvoidingView behavior="padding" />}
|
|
661
|
-
</View>
|
|
662
|
-
```
|
|
663
|
-
|
|
664
|
-
</details>
|
|
665
|
-
|
|
666
365
|
### Web (react-native-web)
|
|
667
366
|
|
|
668
|
-
|
|
669
|
-
<summary><strong>With create-react-app</strong></summary>
|
|
670
|
-
|
|
671
|
-
1. Install react-app-rewired: `yarn add -D react-app-rewired`
|
|
672
|
-
2. Create `config-overrides.js`:
|
|
673
|
-
|
|
674
|
-
```js
|
|
675
|
-
module.exports = function override(config, env) {
|
|
676
|
-
config.module.rules.push({
|
|
677
|
-
test: /\.js$/,
|
|
678
|
-
exclude: /node_modules[/\\](?!react-native-gifted-chat)/,
|
|
679
|
-
use: {
|
|
680
|
-
loader: 'babel-loader',
|
|
681
|
-
options: {
|
|
682
|
-
babelrc: false,
|
|
683
|
-
configFile: false,
|
|
684
|
-
presets: [
|
|
685
|
-
['@babel/preset-env', { useBuiltIns: 'usage' }],
|
|
686
|
-
'@babel/preset-react',
|
|
687
|
-
],
|
|
688
|
-
plugins: ['@babel/plugin-proposal-class-properties'],
|
|
689
|
-
},
|
|
690
|
-
},
|
|
691
|
-
})
|
|
692
|
-
return config
|
|
693
|
-
}
|
|
694
|
-
```
|
|
695
|
-
|
|
696
|
-
> **Examples:**
|
|
697
|
-
> - [xcarpentier/gifted-chat-web-demo](https://github.com/xcarpentier/gifted-chat-web-demo)
|
|
698
|
-
> - [Gatsby example](https://github.com/xcarpentier/clean-archi-boilerplate/tree/develop/apps/web)
|
|
699
|
-
|
|
700
|
-
</details>
|
|
367
|
+
`@legendapp/list` is pure JS and works with react-native-web. Follow the standard `react-native-web` webpack config to alias `react-native` imports.
|
|
701
368
|
|
|
702
369
|
---
|
|
703
370
|
|
|
704
|
-
##
|
|
705
|
-
|
|
706
|
-
<details>
|
|
707
|
-
<summary><strong>Triggering layout events in tests</strong></summary>
|
|
708
|
-
|
|
709
|
-
`TEST_ID` is exported as constants that can be used in your testing library of choice.
|
|
710
|
-
|
|
711
|
-
Gifted Chat uses `onLayout` to determine the height of the chat container. To trigger `onLayout` during your tests:
|
|
712
|
-
|
|
713
|
-
```typescript
|
|
714
|
-
const WIDTH = 200
|
|
715
|
-
const HEIGHT = 2000
|
|
716
|
-
|
|
717
|
-
const loadingWrapper = getByTestId(TEST_ID.LOADING_WRAPPER)
|
|
718
|
-
fireEvent(loadingWrapper, 'layout', {
|
|
719
|
-
nativeEvent: {
|
|
720
|
-
layout: {
|
|
721
|
-
width: WIDTH,
|
|
722
|
-
height: HEIGHT,
|
|
723
|
-
},
|
|
724
|
-
},
|
|
725
|
-
})
|
|
726
|
-
```
|
|
727
|
-
|
|
728
|
-
</details>
|
|
729
|
-
|
|
730
|
-
---
|
|
731
|
-
|
|
732
|
-
## 📦 Example App
|
|
733
|
-
|
|
734
|
-
The repository includes a comprehensive example app demonstrating all features:
|
|
371
|
+
## Contributing
|
|
735
372
|
|
|
736
373
|
```bash
|
|
737
|
-
# Clone and install
|
|
738
|
-
git clone https://github.com/FaridSafi/react-native-gifted-chat.git
|
|
739
|
-
cd react-native-gifted-chat/example
|
|
740
374
|
yarn install
|
|
741
|
-
|
|
742
|
-
#
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
#
|
|
746
|
-
npx expo run:android
|
|
747
|
-
|
|
748
|
-
# Run on Web
|
|
749
|
-
npx expo start --web
|
|
375
|
+
yarn build # outputs to lib/
|
|
376
|
+
yarn test # runs all tests
|
|
377
|
+
yarn lint # check for lint errors
|
|
378
|
+
yarn lint:fix # auto-fix lint errors
|
|
379
|
+
yarn prepublishOnly # full validation: lint + test + build
|
|
750
380
|
```
|
|
751
381
|
|
|
752
|
-
The example app showcases:
|
|
753
|
-
- 💬 Basic chat functionality
|
|
754
|
-
- 🎨 Custom message bubbles and avatars
|
|
755
|
-
- ↩️ Reply to messages with swipe gesture
|
|
756
|
-
- ⚡ Quick replies (bot-style)
|
|
757
|
-
- ✍️ Typing indicators
|
|
758
|
-
- 📎 Attachment actions
|
|
759
|
-
- 🔗 Link parsing and custom matchers
|
|
760
|
-
- 🌐 Web compatibility
|
|
761
|
-
|
|
762
382
|
---
|
|
763
383
|
|
|
764
|
-
##
|
|
765
|
-
|
|
766
|
-
<details>
|
|
767
|
-
<summary><strong>TextInput is hidden on Android</strong></summary>
|
|
768
|
-
|
|
769
|
-
Make sure you have `android:windowSoftInputMode="adjustResize"` in your `AndroidManifest.xml`. See [Android configuration](#android) above.
|
|
770
|
-
|
|
771
|
-
</details>
|
|
772
|
-
|
|
773
|
-
<details>
|
|
774
|
-
<summary><strong>How to set Bubble color for each user?</strong></summary>
|
|
775
|
-
|
|
776
|
-
See [this issue](https://github.com/FaridSafi/react-native-gifted-chat/issues/672) for examples.
|
|
777
|
-
|
|
778
|
-
</details>
|
|
779
|
-
|
|
780
|
-
<details>
|
|
781
|
-
<summary><strong>How to customize InputToolbar styles?</strong></summary>
|
|
782
|
-
|
|
783
|
-
See [this issue](https://github.com/FaridSafi/react-native-gifted-chat/issues/662) for examples.
|
|
784
|
-
|
|
785
|
-
</details>
|
|
786
|
-
|
|
787
|
-
<details>
|
|
788
|
-
<summary><strong>How to manually dismiss the keyboard?</strong></summary>
|
|
789
|
-
|
|
790
|
-
See [this issue](https://github.com/FaridSafi/react-native-gifted-chat/issues/647) for examples.
|
|
791
|
-
|
|
792
|
-
</details>
|
|
793
|
-
|
|
794
|
-
<details>
|
|
795
|
-
<summary><strong>How to use renderLoading?</strong></summary>
|
|
796
|
-
|
|
797
|
-
See [this issue](https://github.com/FaridSafi/react-native-gifted-chat/issues/298) for examples.
|
|
798
|
-
|
|
799
|
-
</details>
|
|
800
|
-
|
|
801
|
-
---
|
|
384
|
+
## Credits
|
|
802
385
|
|
|
803
|
-
|
|
386
|
+
Based on [react-native-gifted-chat](https://github.com/FaridSafi/react-native-gifted-chat) by [Farid Safi](https://www.x.com/FaridSafi) and [contributors](https://github.com/FaridSafi/react-native-gifted-chat/graphs/contributors).
|
|
804
387
|
|
|
805
|
-
|
|
806
|
-
2. Search [existing issues](https://github.com/FaridSafi/react-native-gifted-chat/issues)
|
|
807
|
-
3. Ask on [StackOverflow](https://stackoverflow.com/questions/tagged/react-native-gifted-chat)
|
|
808
|
-
4. Open a new issue if needed
|
|
388
|
+
Performance improvements by [Sujeet Kumar](https://github.com/sujeetdotkumar).
|
|
809
389
|
|
|
810
390
|
---
|
|
811
391
|
|
|
812
|
-
##
|
|
813
|
-
|
|
814
|
-
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
815
|
-
|
|
816
|
-
1. Fork the repository
|
|
817
|
-
2. Create your feature branch (`git checkout -b feature/amazing-feature`)
|
|
818
|
-
3. Install dependencies (`yarn install`)
|
|
819
|
-
4. Make your changes
|
|
820
|
-
5. Run tests (`yarn test`)
|
|
821
|
-
6. Run linting (`yarn lint`)
|
|
822
|
-
7. Build the library (`yarn build`)
|
|
823
|
-
8. Commit your changes (`git commit -m 'Add amazing feature'`)
|
|
824
|
-
9. Push to the branch (`git push origin feature/amazing-feature`)
|
|
825
|
-
10. Open a Pull Request
|
|
826
|
-
|
|
827
|
-
### Development Setup
|
|
828
|
-
|
|
829
|
-
```bash
|
|
830
|
-
# Install dependencies
|
|
831
|
-
yarn install
|
|
832
|
-
|
|
833
|
-
# Build the library
|
|
834
|
-
yarn build
|
|
835
|
-
|
|
836
|
-
# Run tests
|
|
837
|
-
yarn test
|
|
838
|
-
|
|
839
|
-
# Run linting
|
|
840
|
-
yarn lint
|
|
841
|
-
|
|
842
|
-
# Full validation
|
|
843
|
-
yarn prepublishOnly
|
|
844
|
-
```
|
|
845
|
-
|
|
846
|
-
---
|
|
847
|
-
|
|
848
|
-
## 👥 Authors
|
|
849
|
-
|
|
850
|
-
**Original Author:** [Farid Safi](https://www.x.com/FaridSafi)
|
|
851
|
-
|
|
852
|
-
**Co-author:** [Xavier Carpentier](https://www.x.com/xcapetir) - [Hire Xavier](https://xaviercarpentier.com)
|
|
853
|
-
|
|
854
|
-
**Maintainer:** [Kesha Antonov](https://github.com/kesha-antonov)
|
|
855
|
-
|
|
856
|
-
> I've been maintaining this project for 2 years, completely in my free time and without any compensation. If you find it helpful, please consider [becoming a sponsor](https://github.com/sponsors/kesha-antonov) to support continued development. 💖
|
|
857
|
-
|
|
858
|
-
---
|
|
859
|
-
|
|
860
|
-
## 📄 License
|
|
392
|
+
## License
|
|
861
393
|
|
|
862
394
|
[MIT](LICENSE)
|
|
863
|
-
|
|
864
|
-
---
|
|
865
|
-
|
|
866
|
-
<p align="center">
|
|
867
|
-
<sub>Built with ❤️ by the React Native community</sub>
|
|
868
|
-
</p>
|
package/package.json
CHANGED