react-native-gifted-chat 2.8.1 → 2.8.2-alpha.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 (195) hide show
  1. package/README.md +18 -17
  2. package/package.json +41 -35
  3. package/src/Bubble/index.tsx +15 -39
  4. package/src/Bubble/types.ts +5 -5
  5. package/src/Composer.tsx +19 -26
  6. package/src/Constant.ts +0 -1
  7. package/src/GiftedAvatar.tsx +29 -36
  8. package/src/GiftedChat/index.tsx +34 -65
  9. package/src/GiftedChat/types.ts +8 -53
  10. package/src/InputToolbar.tsx +25 -8
  11. package/src/LoadEarlier.tsx +19 -17
  12. package/src/MessageAudio.tsx +19 -7
  13. package/src/MessageContainer/components/DayAnimated/index.tsx +14 -9
  14. package/src/MessageContainer/components/Item/index.tsx +7 -1
  15. package/src/MessageContainer/index.tsx +104 -64
  16. package/src/MessageContainer/styles.ts +3 -2
  17. package/src/MessageContainer/types.ts +36 -14
  18. package/src/MessageImage.tsx +18 -6
  19. package/src/MessageText.tsx +88 -128
  20. package/src/MessageVideo.tsx +19 -7
  21. package/src/QuickReplies.tsx +17 -10
  22. package/src/Send.tsx +7 -1
  23. package/src/SystemMessage.tsx +12 -2
  24. package/src/Time.tsx +9 -2
  25. package/src/TypingIndicator/index.tsx +2 -1
  26. package/src/TypingIndicator/types.ts +3 -0
  27. package/src/__tests__/Actions.test.tsx +3 -4
  28. package/src/__tests__/Avatar.test.tsx +5 -6
  29. package/src/__tests__/Bubble.test.tsx +14 -19
  30. package/src/__tests__/Composer.test.tsx +3 -4
  31. package/src/__tests__/Day.test.tsx +5 -8
  32. package/src/__tests__/DayAnimated.test.tsx +52 -0
  33. package/src/__tests__/GiftedAvatar.test.tsx +3 -8
  34. package/src/__tests__/GiftedChat.test.tsx +37 -21
  35. package/src/__tests__/InputToolbar.test.tsx +3 -4
  36. package/src/__tests__/LoadEarlier.test.tsx +3 -4
  37. package/src/__tests__/Message.test.tsx +51 -58
  38. package/src/__tests__/MessageContainer.test.tsx +39 -5
  39. package/src/__tests__/MessageImage.test.tsx +12 -15
  40. package/src/__tests__/MessageText.test.tsx +7 -4
  41. package/src/__tests__/Send.test.tsx +7 -8
  42. package/src/__tests__/SystemMessage.test.tsx +12 -15
  43. package/src/__tests__/Time.test.tsx +5 -8
  44. package/src/__tests__/__snapshots__/Bubble.test.tsx.snap +48 -50
  45. package/src/__tests__/__snapshots__/Composer.test.tsx.snap +1 -2
  46. package/src/__tests__/__snapshots__/Constant.test.tsx.snap +0 -1
  47. package/src/__tests__/__snapshots__/DayAnimated.test.tsx.snap +5 -0
  48. package/src/__tests__/__snapshots__/GiftedChat.test.tsx.snap +25 -0
  49. package/src/__tests__/__snapshots__/InputToolbar.test.tsx.snap +2 -2
  50. package/src/__tests__/__snapshots__/Message.test.tsx.snap +146 -150
  51. package/src/__tests__/__snapshots__/MessageImage.test.tsx.snap +12 -10
  52. package/src/__tests__/__snapshots__/MessageText.test.tsx.snap +12 -8
  53. package/src/__tests__/__snapshots__/Send.test.tsx.snap +2 -0
  54. package/src/reanimatedCompat.ts +27 -0
  55. package/src/types.ts +4 -0
  56. package/src/utils.ts +77 -1
  57. package/lib/Actions.d.ts +0 -14
  58. package/lib/Actions.js +0 -57
  59. package/lib/Actions.js.map +0 -1
  60. package/lib/Avatar.d.ts +0 -18
  61. package/lib/Avatar.js +0 -93
  62. package/lib/Avatar.js.map +0 -1
  63. package/lib/Bubble/index.d.ts +0 -6
  64. package/lib/Bubble/index.js +0 -257
  65. package/lib/Bubble/index.js.map +0 -1
  66. package/lib/Bubble/styles.d.ts +0 -69
  67. package/lib/Bubble/styles.js +0 -72
  68. package/lib/Bubble/styles.js.map +0 -1
  69. package/lib/Bubble/types.d.ts +0 -47
  70. package/lib/Bubble/types.js +0 -2
  71. package/lib/Bubble/types.js.map +0 -1
  72. package/lib/Color.d.ts +0 -18
  73. package/lib/Color.js +0 -18
  74. package/lib/Color.js.map +0 -1
  75. package/lib/Composer.d.ts +0 -20
  76. package/lib/Composer.js +0 -60
  77. package/lib/Composer.js.map +0 -1
  78. package/lib/Constant.d.ts +0 -10
  79. package/lib/Constant.js +0 -17
  80. package/lib/Constant.js.map +0 -1
  81. package/lib/Day/index.d.ts +0 -4
  82. package/lib/Day/index.js +0 -39
  83. package/lib/Day/index.js.map +0 -1
  84. package/lib/Day/styles.d.ts +0 -20
  85. package/lib/Day/styles.js +0 -22
  86. package/lib/Day/styles.js.map +0 -1
  87. package/lib/Day/types.d.ts +0 -9
  88. package/lib/Day/types.js +0 -2
  89. package/lib/Day/types.js.map +0 -1
  90. package/lib/GiftedAvatar.d.ts +0 -11
  91. package/lib/GiftedAvatar.js +0 -104
  92. package/lib/GiftedAvatar.js.map +0 -1
  93. package/lib/GiftedChat/index.d.ts +0 -26
  94. package/lib/GiftedChat/index.js +0 -302
  95. package/lib/GiftedChat/index.js.map +0 -1
  96. package/lib/GiftedChat/styles.d.ts +0 -6
  97. package/lib/GiftedChat/styles.js +0 -7
  98. package/lib/GiftedChat/styles.js.map +0 -1
  99. package/lib/GiftedChat/types.d.ts +0 -117
  100. package/lib/GiftedChat/types.js +0 -2
  101. package/lib/GiftedChat/types.js.map +0 -1
  102. package/lib/GiftedChatContext.d.ts +0 -9
  103. package/lib/GiftedChatContext.js +0 -9
  104. package/lib/GiftedChatContext.js.map +0 -1
  105. package/lib/InputToolbar.d.ts +0 -23
  106. package/lib/InputToolbar.js +0 -56
  107. package/lib/InputToolbar.js.map +0 -1
  108. package/lib/LoadEarlier.d.ts +0 -14
  109. package/lib/LoadEarlier.js +0 -45
  110. package/lib/LoadEarlier.js.map +0 -1
  111. package/lib/Message/index.d.ts +0 -6
  112. package/lib/Message/index.js +0 -80
  113. package/lib/Message/index.js.map +0 -1
  114. package/lib/Message/styles.d.ts +0 -21
  115. package/lib/Message/styles.js +0 -22
  116. package/lib/Message/styles.js.map +0 -1
  117. package/lib/Message/types.d.ts +0 -22
  118. package/lib/Message/types.js +0 -2
  119. package/lib/Message/types.js.map +0 -1
  120. package/lib/MessageAudio.d.ts +0 -2
  121. package/lib/MessageAudio.js +0 -14
  122. package/lib/MessageAudio.js.map +0 -1
  123. package/lib/MessageContainer/components/DayAnimated/index.d.ts +0 -5
  124. package/lib/MessageContainer/components/DayAnimated/index.js +0 -85
  125. package/lib/MessageContainer/components/DayAnimated/index.js.map +0 -1
  126. package/lib/MessageContainer/components/DayAnimated/styles.d.ts +0 -11
  127. package/lib/MessageContainer/components/DayAnimated/styles.js +0 -12
  128. package/lib/MessageContainer/components/DayAnimated/styles.js.map +0 -1
  129. package/lib/MessageContainer/components/DayAnimated/types.d.ts +0 -17
  130. package/lib/MessageContainer/components/DayAnimated/types.js +0 -2
  131. package/lib/MessageContainer/components/DayAnimated/types.js.map +0 -1
  132. package/lib/MessageContainer/components/Item/index.d.ts +0 -23
  133. package/lib/MessageContainer/components/Item/index.js +0 -88
  134. package/lib/MessageContainer/components/Item/index.js.map +0 -1
  135. package/lib/MessageContainer/components/Item/types.d.ts +0 -17
  136. package/lib/MessageContainer/components/Item/types.js +0 -2
  137. package/lib/MessageContainer/components/Item/types.js.map +0 -1
  138. package/lib/MessageContainer/index.d.ts +0 -6
  139. package/lib/MessageContainer/index.js +0 -224
  140. package/lib/MessageContainer/index.js.map +0 -1
  141. package/lib/MessageContainer/styles.d.ts +0 -34
  142. package/lib/MessageContainer/styles.js +0 -31
  143. package/lib/MessageContainer/styles.js.map +0 -1
  144. package/lib/MessageContainer/types.d.ts +0 -54
  145. package/lib/MessageContainer/types.js +0 -2
  146. package/lib/MessageContainer/types.js.map +0 -1
  147. package/lib/MessageImage.d.ts +0 -13
  148. package/lib/MessageImage.js +0 -30
  149. package/lib/MessageImage.js.map +0 -1
  150. package/lib/MessageText.d.ts +0 -15
  151. package/lib/MessageText.js +0 -108
  152. package/lib/MessageText.js.map +0 -1
  153. package/lib/MessageVideo.d.ts +0 -2
  154. package/lib/MessageVideo.js +0 -14
  155. package/lib/MessageVideo.js.map +0 -1
  156. package/lib/QuickReplies.d.ts +0 -15
  157. package/lib/QuickReplies.js +0 -101
  158. package/lib/QuickReplies.js.map +0 -1
  159. package/lib/Send.d.ts +0 -15
  160. package/lib/Send.js +0 -34
  161. package/lib/Send.js.map +0 -1
  162. package/lib/SystemMessage.d.ts +0 -10
  163. package/lib/SystemMessage.js +0 -26
  164. package/lib/SystemMessage.js.map +0 -1
  165. package/lib/Time.d.ts +0 -11
  166. package/lib/Time.js +0 -56
  167. package/lib/Time.js.map +0 -1
  168. package/lib/TypingIndicator/index.d.ts +0 -5
  169. package/lib/TypingIndicator/index.js +0 -94
  170. package/lib/TypingIndicator/index.js.map +0 -1
  171. package/lib/TypingIndicator/styles.d.ts +0 -20
  172. package/lib/TypingIndicator/styles.js +0 -22
  173. package/lib/TypingIndicator/styles.js.map +0 -1
  174. package/lib/TypingIndicator/types.d.ts +0 -3
  175. package/lib/TypingIndicator/types.js +0 -2
  176. package/lib/TypingIndicator/types.js.map +0 -1
  177. package/lib/hooks/useUpdateLayoutEffect.d.ts +0 -8
  178. package/lib/hooks/useUpdateLayoutEffect.js +0 -17
  179. package/lib/hooks/useUpdateLayoutEffect.js.map +0 -1
  180. package/lib/index.d.ts +0 -4
  181. package/lib/index.js +0 -5
  182. package/lib/index.js.map +0 -1
  183. package/lib/logging.d.ts +0 -2
  184. package/lib/logging.js +0 -5
  185. package/lib/logging.js.map +0 -1
  186. package/lib/styles.d.ts +0 -10
  187. package/lib/styles.js +0 -11
  188. package/lib/styles.js.map +0 -1
  189. package/lib/types.d.ts +0 -67
  190. package/lib/types.js +0 -2
  191. package/lib/types.js.map +0 -1
  192. package/lib/utils.d.ts +0 -3
  193. package/lib/utils.js +0 -17
  194. package/lib/utils.js.map +0 -1
  195. package/src/__tests__/__snapshots__/MessageContainer.test.tsx.snap +0 -101
package/README.md CHANGED
@@ -78,6 +78,10 @@
78
78
  <a href="https://bit.ly/ethorachat" target="_blank">Check out our GitHub</a>
79
79
  </p>
80
80
  </p>
81
+ <br>
82
+ <p align="center">
83
+ <a href="https://amzn.to/3ZmTyb2" target="_blank">React Key Concepts: Consolidate your knowledge of React’s core features (2nd ed. Edition)</a>
84
+ </p>
81
85
 
82
86
  ## Features
83
87
 
@@ -351,8 +355,9 @@ interface QuickReplies {
351
355
  - **`messages`** _(Array)_ - Messages to display
352
356
  - **`isTyping`** _(Bool)_ - Typing Indicator state; default `false`. If you use`renderFooter` it will override this.
353
357
  - **`isKeyboardInternallyHandled`** _(Bool)_ - Determine whether to handle keyboard awareness inside the plugin. If you have your own keyboard handling outside the plugin set this to false; default is `true`
358
+ - **`disableKeyboardController`** _(Bool)_ - Completely disable react-native-keyboard-controller. Useful when using react-native-navigation or other conflicting keyboard libraries; default is `false`
354
359
  - **`text`** _(String)_ - Input text; default is `undefined`, but if specified, it will override GiftedChat's internal state (e.g. for redux; [see notes below](#notes-for-redux))
355
- - **`placeholder`** _(String)_ - Placeholder when `text` is empty; default is `'Type a message...'`
360
+ - **`onInputTextChanged`** _(Function)_ - Callback when the input text changes
356
361
  - **`messageIdGenerator`** _(Function)_ - Generate an id for new messages. Defaults to UUID v4, generated by [uuid](https://github.com/kelektiv/node-uuid)
357
362
  - **`user`** _(Object)_ - User sending the messages: `{ _id, name, avatar }`
358
363
  - **`onSend`** _(Function)_ - Callback when sending a message
@@ -360,7 +365,7 @@ interface QuickReplies {
360
365
  - **`locale`** _(String)_ - Locale to localize the dates. You need first to import the locale you need (ie. `require('dayjs/locale/de')` or `import 'dayjs/locale/fr'`)
361
366
  - **`timeFormat`** _(String)_ - Format to use for rendering times; default is `'LT'` (see [Day.js Format](https://day.js.org/docs/en/display/format))
362
367
  - **`dateFormat`** _(String)_ - Format to use for rendering dates; default is `'D MMMM'` (see [Day.js Format](https://day.js.org/docs/en/display/format))
363
- - **`dateFormatCalendar`** _(String)_ - Format to use for rendering relative times; Today - for now (see [Day.js Calendar](https://day.js.org/docs/en/plugin/calendar))
368
+ - **`dateFormatCalendar`** _(Object)_ - Format to use for rendering relative times; default is `{ sameDay: '[Today]' }` (see [Day.js Calendar](https://day.js.org/docs/en/plugin/calendar))
364
369
  - **`loadEarlier`** _(Bool)_ - Enables the "load earlier messages" button, required for `infiniteScroll`
365
370
  - **`onLoadEarlier`** _(Function)_ - Callback when loading earlier messages
366
371
  - **`isLoadingEarlier`** _(Bool)_ - Display an `ActivityIndicator` when loading earlier messages
@@ -375,8 +380,8 @@ interface QuickReplies {
375
380
  - **`renderBubble`** _(Function)_ - Custom message bubble
376
381
  - **`renderTicks`** _(Function(`message`))_ - Custom ticks indicator to display message status
377
382
  - **`renderSystemMessage`** _(Function)_ - Custom system message
378
- - **`onPress`** _(Function(`context`, `message`))_ - Callback when a message bubble is pressed
379
- - **`onLongPress`** _(Function(`context`, `message`))_ - Callback when a message bubble is long-pressed (see [example using `showActionSheetWithOptions()`](https://github.com/FaridSafi/react-native-gifted-chat/blob/master@%7B2017-09-25%7D/src/Bubble.js#L96-L119))
383
+ - **`onPressMessage`** _(Function(`context`, `message`))_ - Callback when a message bubble is pressed
384
+ - **`onLongPressMessage`** _(Function(`context`, `message`))_ - Callback when a message bubble is long-pressed (see [example using `showActionSheetWithOptions()`](https://github.com/FaridSafi/react-native-gifted-chat/blob/master@%7B2017-09-25%7D/src/Bubble.js#L96-L119))
380
385
  - **`inverted`** _(Bool)_ - Reverses display order of `messages`; default is `true`
381
386
  - **`renderUsernameOnMessage`** _(Bool)_ - Indicate whether to show the user's username inside the message bubble; default is `false`
382
387
  - **`renderUsername`** _(Function)_ - Custom Username container
@@ -384,7 +389,7 @@ interface QuickReplies {
384
389
  - **`renderMessageText`** _(Function)_ - Custom message text
385
390
  - **`renderMessageImage`** _(Function)_ - Custom message image
386
391
  - **`renderMessageVideo`** _(Function)_ - Custom message video
387
- - **`imageProps`** _(Object)_ - Extra props to be passed to the [`<Image>`](https://facebook.github.io/react-native/docs/image.html) component created by the default `renderMessageImage`
392
+ - **`imageProps`** _(Object)_ - Extra props to be passed to the [`<Image>`](https://reactnative.dev/docs/image.html) component created by the default `renderMessageImage`
388
393
  - **`videoProps`** _(Object)_ - Extra props to be passed to the video component created by the required `renderMessageVideo`
389
394
  - **`lightboxProps`** _(Object)_ - Extra props to be passed to the `MessageImage`'s [Lightbox](https://github.com/oblador/react-native-lightbox)
390
395
  - **`isCustomViewBottom`** _(Bool)_ - Determine whether renderCustomView is displayed before or after the text, image and video views; default is `false`
@@ -403,20 +408,15 @@ interface QuickReplies {
403
408
  - **`bottomOffset`** _(Integer)_ - Distance of the chat from the bottom of the screen (e.g. useful if you display a tab bar)
404
409
  - **`focusOnInputWhenOpeningKeyboard`** _(Bool)_ - Focus on <TextInput> automatically when opening the keyboard; default `true`
405
410
  - **`minInputToolbarHeight`** _(Integer)_ - Minimum height of the input toolbar; default is `44`
406
- - **`listViewProps`** _(Object)_ - Extra props to be passed to the messages [`<ListView>`](https://facebook.github.io/react-native/docs/listview.html); some props can't be overridden, see the code in `MessageContainer.render()` for details
407
- - **`textInputProps`** _(Object)_ - Extra props to be passed to the [`<TextInput>`](https://facebook.github.io/react-native/docs/textinput.html)
408
- - **`textInputStyle`** _(Object)_ - Custom style to be passed to the [`<TextInput>`](https://facebook.github.io/react-native/docs/textinput.html)
409
- - **`multiline`** _(Bool)_ - Indicates whether to allow the [`<TextInput>`](https://facebook.github.io/react-native/docs/textinput.html) to be multiple lines or not; default `true`.
410
- - **`keyboardShouldPersistTaps`** _(Enum)_ - Determines whether the keyboard should stay visible after a tap; see [`<ScrollView>`](https://facebook.github.io/react-native/docs/scrollview.html) docs
411
- - **`onInputTextChanged`** _(Function)_ - Callback when the input text changes
412
- - **`maxInputLength`** _(Integer)_ - Max message composer TextInput length
413
- - **`parsePatterns`** _(Function)_ - Custom parse patterns for [react-native-parsed-text](https://github.com/taskrabbit/react-native-parsed-text) used to linking message content (like URLs and phone numbers), e.g.:
411
+ - **`listProps`** _(Object)_ - Extra props to be passed to the messages [`<FlatList>`](https://reactnative.dev/docs/flatlist.html); some props can't be overridden, see the code in `MessageContainer.render()` for details
412
+ - **`textInputProps`** _(Object)_ - props to be passed to the [`<TextInput>`](https://reactnative.dev/docs/textinput.html).
413
+ - **`matchers`** _(Array)_ - Custom matchers for [react-native-autolink](https://github.com/joshswan/react-native-autolink) used to linking message content (like URLs and phone numbers), e.g.:
414
414
 
415
415
  ```js
416
416
  <GiftedChat
417
- parsePatterns={(linkStyle) => [
418
- { type: 'phone', style: linkStyle, onPress: this.onPressPhoneNumber },
419
- { pattern: /#(\w+)/, style: { ...linkStyle, styles.hashtag }, onPress: this.onPressHashtag },
417
+ matchers={[
418
+ { type: 'phone', style: linkStyle, onPress: onPressPhoneNumber },
419
+ { pattern: /#(\w+)/, style: [linkStyle, styles.hashtag], onPress: onPressHashtag },
420
420
  ]}
421
421
  />
422
422
  ```
@@ -436,6 +436,7 @@ interface QuickReplies {
436
436
  * **`renderQuickReplySend`** _(Function)_ - Custom quick reply **send** view
437
437
  * **`shouldUpdateMessage`** _(Function)_ - Lets the message component know when to update outside of normal cases.
438
438
  * **`infiniteScroll`** _(Bool)_ - infinite scroll up when reach the top of messages container, automatically call onLoadEarlier function if exist (not yet supported for the web). You need to add `loadEarlier` prop too.
439
+ * **`typingIndicatorStyle`** _(StyleProp<ViewStyle>)_ - Custom style for the TypingIndicator component.
439
440
 
440
441
  ## Notes for [Redux](https://github.com/reactjs/redux)
441
442
 
@@ -469,7 +470,7 @@ If you are using Create React Native App / Expo, no Android specific installatio
469
470
 
470
471
  - For **Expo**, there are at least 2 solutions to fix it:
471
472
 
472
- - Append [`KeyboardAvoidingView`](https://facebook.github.io/react-native/docs/keyboardavoidingview) after GiftedChat. This should only be done for Android, as `KeyboardAvoidingView` may conflict with the iOS keyboard avoidance already built into GiftedChat, e.g.:
473
+ - Append [`KeyboardAvoidingView`](https://reactnative.dev/docs/keyboardavoidingview) after GiftedChat. This should only be done for Android, as `KeyboardAvoidingView` may conflict with the iOS keyboard avoidance already built into GiftedChat, e.g.:
473
474
 
474
475
  ```
475
476
  <View style={{ flex: 1 }}>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-gifted-chat",
3
- "version": "2.8.1",
3
+ "version": "2.8.2-alpha.1",
4
4
  "description": "The most complete chat UI for React Native",
5
5
  "keywords": [
6
6
  "android",
@@ -23,17 +23,15 @@
23
23
  "license": "MIT",
24
24
  "author": "Farid Safi",
25
25
  "type": "module",
26
- "main": "lib/index.js",
27
- "types": "lib/index.d.ts",
26
+ "main": "src/index.ts",
27
+ "types": "src/index.ts",
28
28
  "files": [
29
- "src",
30
- "lib"
29
+ "src"
31
30
  ],
32
31
  "scripts": {
33
- "build": "rm -rf lib/ && yarn tsc",
34
32
  "lint": "yarn eslint src",
35
33
  "lint:fix": "yarn eslint --cache --fix",
36
- "prepublishOnly": "yarn lint && yarn build && yarn test",
34
+ "prepublishOnly": "yarn lint && yarn test",
37
35
  "start": "cd example && expo start",
38
36
  "start:web": "cd example && expo start -w --dev",
39
37
  "test": "TZ=Europe/Paris jest --no-watchman",
@@ -46,64 +44,72 @@
46
44
  },
47
45
  "lint-staged": {
48
46
  "src/*.{json,js,jsx,ts,tsx}": [
49
- "yarn lint:fix",
50
- "bash -c 'yarn tsc:write'"
47
+ "yarn lint:fix"
51
48
  ]
52
49
  },
53
50
  "dependencies": {
54
51
  "@expo/react-native-action-sheet": "^4.1.1",
55
52
  "@types/lodash.isequal": "^4.5.8",
56
- "dayjs": "^1.11.13",
53
+ "dayjs": "^1.11.19",
57
54
  "lodash.isequal": "^4.5.0",
55
+ "react-native-autolink": "^4.2.0",
58
56
  "react-native-communications": "^2.2.1",
59
- "react-native-iphone-x-helper": "^1.3.1",
60
- "react-native-lightbox-v2": "^0.9.2",
61
- "react-native-parsed-text": "^0.0.22"
57
+ "react-native-lightbox-v2": "^0.9.2"
62
58
  },
63
59
  "devDependencies": {
64
- "@babel/core": "^7.26.10",
65
- "@babel/plugin-transform-react-jsx": "^7.25.9",
66
- "@babel/plugin-transform-unicode-property-regex": "^7.25.9",
67
- "@babel/preset-env": "^7.26.9",
68
- "@react-native/eslint-config": "^0.76.6",
60
+ "@babel/core": "^7.28.5",
61
+ "@babel/plugin-transform-react-jsx": "^7.27.1",
62
+ "@babel/plugin-transform-unicode-property-regex": "^7.27.1",
63
+ "@babel/preset-env": "^7.28.5",
64
+ "@react-native-community/cli": "20.0.0",
65
+ "@react-native-community/cli-platform-android": "20.0.0",
66
+ "@react-native-community/cli-platform-ios": "20.0.0",
67
+ "@react-native/babel-preset": "0.81.5",
68
+ "@react-native/eslint-config": "0.81.5",
69
+ "@react-native/metro-config": "0.81.5",
70
+ "@react-native/typescript-config": "0.81.5",
69
71
  "@stylistic/eslint-plugin": "^3.1.0",
70
- "@types/jest": "^29.5.14",
71
- "@types/react": "^19.0.10",
72
- "@types/react-dom": "^19.0.4",
72
+ "@testing-library/dom": "^10.4.1",
73
+ "@testing-library/react": "^16.3.0",
74
+ "@testing-library/react-native": "^13.3.3",
75
+ "@types/jest": "^29.5.13",
76
+ "@types/react": "^19.2.5",
77
+ "@types/react-dom": "^19.2.3",
73
78
  "@types/react-native": "^0.72.8",
74
- "@typescript-eslint/eslint-plugin": "^8.28.0",
75
- "@typescript-eslint/parser": "^8.28.0",
79
+ "@types/react-test-renderer": "^19.1.0",
80
+ "@typescript-eslint/eslint-plugin": "^8.46.4",
81
+ "@typescript-eslint/parser": "^8.46.4",
76
82
  "babel-jest": "^29.7.0",
77
83
  "eslint": "^8.57.0",
78
84
  "eslint-config-standard": "^17.1.0",
79
85
  "eslint-config-standard-jsx": "^11.0.0",
80
- "eslint-plugin-import": "^2.31.0",
86
+ "eslint-plugin-import": "^2.32.0",
81
87
  "eslint-plugin-jest": "^28.11.0",
82
88
  "eslint-plugin-json": "^4.0.1",
83
- "eslint-plugin-n": "^17.17.0",
89
+ "eslint-plugin-n": "^17.23.1",
84
90
  "eslint-plugin-node": "^11.1.0",
85
91
  "eslint-plugin-promise": "^7.2.1",
86
- "eslint-plugin-react": "^7.37.4",
92
+ "eslint-plugin-react": "^7.37.5",
87
93
  "husky": "^9.1.7",
88
94
  "jest": "^29.7.0",
89
95
  "json": "^11.0.0",
90
96
  "lint-staged": "^15.5.0",
91
- "react": "^18.3.1",
92
- "react-dom": "^18.3.1",
93
- "react-native": "^0.76.6",
94
- "react-native-keyboard-controller": "^1.16.8",
95
- "react-native-reanimated": "^3.17.1",
96
- "react-test-renderer": "^18.3.1",
97
- "typescript": "^5.8.2"
97
+ "react": "19.1.0",
98
+ "react-dom": "19.1.0",
99
+ "react-native": "0.81.5",
100
+ "react-native-keyboard-controller": "^1.19.5",
101
+ "react-native-reanimated": "^3.19.4",
102
+ "react-test-renderer": "19.1.0",
103
+ "typescript": "^5.9.3"
98
104
  },
99
105
  "peerDependencies": {
100
106
  "react": ">=18.0.0",
101
107
  "react-native": "*",
102
108
  "react-native-keyboard-controller": ">=1.0.0",
103
- "react-native-reanimated": ">=3.0.0"
109
+ "react-native-reanimated": ">=3.0.0 || ^4.0.0"
104
110
  },
105
111
  "packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e",
106
112
  "engines": {
107
- "node": ">=18"
113
+ "node": ">=20"
108
114
  }
109
115
  }
@@ -1,7 +1,7 @@
1
- import React, { useCallback } from 'react'
1
+ import React, { JSX, useCallback } from 'react'
2
2
  import {
3
3
  Text,
4
- TouchableWithoutFeedback,
4
+ Pressable,
5
5
  View,
6
6
  } from 'react-native'
7
7
 
@@ -22,7 +22,7 @@ import styles from './styles'
22
22
 
23
23
  export * from './types'
24
24
 
25
- const Bubble: React.FC<BubbleProps<IMessage>> = (props: BubbleProps<IMessage>) => {
25
+ const Bubble = <TMessage extends IMessage = IMessage>(props: BubbleProps<TMessage>): JSX.Element => {
26
26
  const {
27
27
  currentMessage,
28
28
  nextMessage,
@@ -38,46 +38,22 @@ const Bubble: React.FC<BubbleProps<IMessage>> = (props: BubbleProps<IMessage>) =
38
38
  containerStyle,
39
39
  wrapperStyle,
40
40
  bottomContainerStyle,
41
+ onPressMessage: onPressMessageProp,
42
+ onLongPressMessage: onLongPressMessageProp,
41
43
  } = props
42
44
 
43
45
  const context = useChatContext()
44
46
 
45
47
  const onPress = useCallback(() => {
46
- if (props.onPress)
47
- props.onPress(context, currentMessage)
48
- }, [context, props, currentMessage])
48
+ onPressMessageProp?.(context, currentMessage)
49
+ }, [onPressMessageProp, context, currentMessage])
49
50
 
50
51
  const onLongPress = useCallback(() => {
51
- const {
52
- onLongPress,
53
- optionTitles,
54
- } = props
55
-
56
- if (onLongPress) {
57
- onLongPress(context, currentMessage)
58
- return
59
- }
60
-
61
- if (!optionTitles?.length)
62
- return
63
-
64
- const options = optionTitles
65
- const cancelButtonIndex = options.length - 1
66
-
67
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
68
- ;(context as any).actionSheet().showActionSheetWithOptions(
69
- {
70
- options,
71
- cancelButtonIndex,
72
- },
73
- (buttonIndex: number) => {
74
- console.log('onLongPress', { buttonIndex })
75
- }
76
- )
52
+ onLongPressMessageProp?.(context, currentMessage)
77
53
  }, [
78
54
  currentMessage,
79
55
  context,
80
- props,
56
+ onLongPressMessageProp,
81
57
  ])
82
58
 
83
59
  const styledBubbleToNext = useCallback(() => {
@@ -111,7 +87,7 @@ const Bubble: React.FC<BubbleProps<IMessage>> = (props: BubbleProps<IMessage>) =
111
87
  )
112
88
  return [
113
89
  styles[position].containerToPrevious,
114
- containerToPreviousStyle && containerToPreviousStyle[position],
90
+ containerToPreviousStyle?.[position],
115
91
  ]
116
92
 
117
93
  return null
@@ -166,7 +142,6 @@ const Bubble: React.FC<BubbleProps<IMessage>> = (props: BubbleProps<IMessage>) =
166
142
  /* eslint-disable @typescript-eslint/no-unused-vars */
167
143
  containerStyle,
168
144
  wrapperStyle,
169
- optionTitles,
170
145
  /* eslint-enable @typescript-eslint/no-unused-vars */
171
146
  ...messageTextProps
172
147
  } = props
@@ -176,6 +151,7 @@ const Bubble: React.FC<BubbleProps<IMessage>> = (props: BubbleProps<IMessage>) =
176
151
 
177
152
  return <MessageText {...messageTextProps} />
178
153
  }
154
+
179
155
  return null
180
156
  }, [props, currentMessage])
181
157
 
@@ -194,6 +170,7 @@ const Bubble: React.FC<BubbleProps<IMessage>> = (props: BubbleProps<IMessage>) =
194
170
 
195
171
  return <MessageImage {...messageImageProps} />
196
172
  }
173
+
197
174
  return null
198
175
  }, [props, currentMessage])
199
176
 
@@ -374,10 +351,9 @@ const Bubble: React.FC<BubbleProps<IMessage>> = (props: BubbleProps<IMessage>) =
374
351
  wrapperStyle && wrapperStyle[position],
375
352
  ]}
376
353
  >
377
- <TouchableWithoutFeedback
354
+ <Pressable
378
355
  onPress={onPress}
379
356
  onLongPress={onLongPress}
380
- accessibilityRole='text'
381
357
  {...props.touchableProps}
382
358
  >
383
359
  <View>
@@ -385,7 +361,7 @@ const Bubble: React.FC<BubbleProps<IMessage>> = (props: BubbleProps<IMessage>) =
385
361
  <View
386
362
  style={[
387
363
  styles[position].bottom,
388
- bottomContainerStyle && bottomContainerStyle[position],
364
+ bottomContainerStyle?.[position],
389
365
  ]}
390
366
  >
391
367
  {renderUsername()}
@@ -393,7 +369,7 @@ const Bubble: React.FC<BubbleProps<IMessage>> = (props: BubbleProps<IMessage>) =
393
369
  {renderTicks()}
394
370
  </View>
395
371
  </View>
396
- </TouchableWithoutFeedback>
372
+ </Pressable>
397
373
  </View>
398
374
  {renderQuickReplies()}
399
375
  </View>
@@ -5,7 +5,7 @@ import {
5
5
  TextStyle,
6
6
  } from 'react-native'
7
7
  import { QuickRepliesProps } from '../QuickReplies'
8
- import { MessageTextProps } from '../MessageText'
8
+ import { MessageTextProps, MessageOption } from '../MessageText'
9
9
  import { MessageImageProps } from '../MessageImage'
10
10
  import { TimeProps } from '../Time'
11
11
  import {
@@ -39,7 +39,7 @@ export type RenderMessageAudioProps<TMessage extends IMessage> = Omit<
39
39
 
40
40
  export type RenderMessageTextProps<TMessage extends IMessage> = Omit<
41
41
  BubbleProps<TMessage>,
42
- 'containerStyle' | 'wrapperStyle'
42
+ 'containerStyle' | 'wrapperStyle' | 'options'
43
43
  > &
44
44
  MessageTextProps<TMessage>
45
45
  /* eslint-enable no-use-before-define */
@@ -54,7 +54,7 @@ export interface BubbleProps<TMessage extends IMessage> {
54
54
  currentMessage: TMessage
55
55
  nextMessage?: TMessage
56
56
  previousMessage?: TMessage
57
- optionTitles?: string[]
57
+ options?: MessageOption[]
58
58
  containerStyle?: LeftRightStyle<ViewStyle>
59
59
  wrapperStyle?: LeftRightStyle<ViewStyle>
60
60
  textStyle?: LeftRightStyle<TextStyle>
@@ -66,8 +66,8 @@ export interface BubbleProps<TMessage extends IMessage> {
66
66
  quickReplyStyle?: StyleProp<ViewStyle>
67
67
  quickReplyTextStyle?: StyleProp<TextStyle>
68
68
  quickReplyContainerStyle?: StyleProp<ViewStyle>
69
- onPress?(context?: unknown, message?: unknown): void
70
- onLongPress?(context?: unknown, message?: unknown): void
69
+ onPressMessage?(context?: unknown, message?: unknown): void
70
+ onLongPressMessage?(context?: unknown, message?: unknown): void
71
71
  onQuickReply?(replies: Reply[]): void
72
72
  renderMessageImage?(
73
73
  props: RenderMessageImageProps<TMessage>,
package/src/Composer.tsx CHANGED
@@ -6,41 +6,30 @@ import {
6
6
  TextInputProps,
7
7
  NativeSyntheticEvent,
8
8
  TextInputContentSizeChangeEventData,
9
+ useColorScheme,
9
10
  } from 'react-native'
10
- import { MIN_COMPOSER_HEIGHT, DEFAULT_PLACEHOLDER } from './Constant'
11
+ import { MIN_COMPOSER_HEIGHT } from './Constant'
11
12
  import Color from './Color'
12
13
  import stylesCommon from './styles'
13
14
 
14
15
  export interface ComposerProps {
15
16
  composerHeight?: number
16
17
  text?: string
17
- placeholder?: string
18
- placeholderTextColor?: string
19
18
  textInputProps?: Partial<TextInputProps>
20
- textInputStyle?: TextInputProps['style']
21
- textInputAutoFocus?: boolean
22
- keyboardAppearance?: TextInputProps['keyboardAppearance']
23
- multiline?: boolean
24
- disableComposer?: boolean
25
19
  onTextChanged?(text: string): void
26
20
  onInputSizeChanged?(layout: { width: number, height: number }): void
27
21
  }
28
22
 
29
23
  export function Composer ({
30
24
  composerHeight = MIN_COMPOSER_HEIGHT,
31
- disableComposer = false,
32
- keyboardAppearance = 'default',
33
- multiline = true,
34
25
  onInputSizeChanged,
35
26
  onTextChanged,
36
- placeholder = DEFAULT_PLACEHOLDER,
37
- placeholderTextColor = Color.defaultColor,
38
27
  text = '',
39
- textInputAutoFocus = false,
40
28
  textInputProps,
41
- textInputStyle,
42
29
  }: ComposerProps): React.ReactElement {
43
30
  const dimensionsRef = useRef<{ width: number, height: number }>(null)
31
+ const colorScheme = useColorScheme()
32
+ const isDark = colorScheme === 'dark'
44
33
 
45
34
  const determineInputSizeChange = useCallback(
46
35
  (dimensions: { width: number, height: number }) => {
@@ -69,21 +58,28 @@ export function Composer ({
69
58
  [determineInputSizeChange]
70
59
  )
71
60
 
61
+ const placeholder = textInputProps?.placeholder ?? 'Type a message...'
62
+
72
63
  return (
73
64
  <TextInput
74
65
  testID={placeholder}
75
66
  accessible
76
67
  accessibilityLabel={placeholder}
77
- placeholder={placeholder}
78
- placeholderTextColor={placeholderTextColor}
79
- multiline={multiline}
80
- editable={!disableComposer}
68
+ placeholderTextColor={textInputProps?.placeholderTextColor ?? (isDark ? '#888' : Color.defaultColor)}
81
69
  onContentSizeChange={handleContentSizeChange}
82
70
  onChangeText={onTextChanged}
71
+ value={text}
72
+ enablesReturnKeyAutomatically
73
+ underlineColorAndroid='transparent'
74
+ keyboardAppearance={isDark ? 'dark' : 'default'}
75
+ multiline
76
+ placeholder={placeholder}
77
+ {...textInputProps}
83
78
  style={[
84
79
  stylesCommon.fill,
85
80
  styles.textInput,
86
- textInputStyle,
81
+ styles[`textInput_${colorScheme}`],
82
+ textInputProps?.style,
87
83
  {
88
84
  height: composerHeight,
89
85
  ...Platform.select({
@@ -95,12 +91,6 @@ export function Composer ({
95
91
  }),
96
92
  },
97
93
  ]}
98
- autoFocus={textInputAutoFocus}
99
- value={text}
100
- enablesReturnKeyAutomatically
101
- underlineColorAndroid='transparent'
102
- keyboardAppearance={keyboardAppearance}
103
- {...textInputProps}
104
94
  />
105
95
  )
106
96
  }
@@ -127,4 +117,7 @@ const styles = StyleSheet.create({
127
117
  web: 4,
128
118
  }),
129
119
  },
120
+ textInput_dark: {
121
+ color: '#fff',
122
+ },
130
123
  })
package/src/Constant.ts CHANGED
@@ -7,7 +7,6 @@ export const MIN_COMPOSER_HEIGHT = Platform.select({
7
7
  windows: 34,
8
8
  })
9
9
  export const MAX_COMPOSER_HEIGHT = 200
10
- export const DEFAULT_PLACEHOLDER = 'Type a message...'
11
10
  export const DATE_FORMAT = 'D MMMM'
12
11
  export const TIME_FORMAT = 'LT'
13
12
 
@@ -1,4 +1,4 @@
1
- import React, { useCallback, useEffect, useState } from 'react'
1
+ import React, { useCallback, useMemo } from 'react'
2
2
  import {
3
3
  Image,
4
4
  Text,
@@ -51,9 +51,6 @@ export interface GiftedAvatarProps {
51
51
  export function GiftedAvatar (
52
52
  props: GiftedAvatarProps
53
53
  ) {
54
- const [avatarName, setAvatarName] = useState<string | undefined>(undefined)
55
- const [backgroundColor, setBackgroundColor] = useState<string | undefined>(undefined)
56
-
57
54
  const {
58
55
  user,
59
56
  avatarStyle,
@@ -61,23 +58,23 @@ export function GiftedAvatar (
61
58
  onPress,
62
59
  } = props
63
60
 
64
- const setAvatarColor = useCallback(() => {
65
- if (backgroundColor)
66
- return
67
-
61
+ const avatarName = useMemo(() => {
68
62
  const userName = user?.name || ''
69
63
  const name = userName.toUpperCase().split(' ')
70
64
 
71
65
  if (name.length === 1)
72
- setAvatarName(`${name[0].charAt(0)}`)
66
+ return `${name[0].charAt(0)}`
73
67
  else if (name.length > 1)
74
- setAvatarName(`${name[0].charAt(0)}${name[1].charAt(0)}`)
68
+ return `${name[0].charAt(0)}${name[1].charAt(0)}`
75
69
  else
76
- setAvatarName('')
70
+ return ''
71
+ }, [user?.name])
77
72
 
73
+ const backgroundColor = useMemo(() => {
78
74
  let sumChars = 0
79
- for (let i = 0; i < userName.length; i += 1)
80
- sumChars += userName.charCodeAt(i)
75
+ if (user?.name)
76
+ for (let i = 0; i < user.name.length; i += 1)
77
+ sumChars += user.name.charCodeAt(i)
81
78
 
82
79
  // inspired by https://github.com/wbinnssmith/react-user-avatar
83
80
  // colors from https://flatuicolors.com/
@@ -91,8 +88,8 @@ export function GiftedAvatar (
91
88
  midnightBlue,
92
89
  ]
93
90
 
94
- setBackgroundColor(colors[sumChars % colors.length])
95
- }, [user?.name, backgroundColor])
91
+ return colors[sumChars % colors.length]
92
+ }, [user?.name])
96
93
 
97
94
  const renderAvatar = useCallback(() => {
98
95
  switch (typeof user?.avatar) {
@@ -125,17 +122,16 @@ export function GiftedAvatar (
125
122
  )
126
123
  }, [textStyle, avatarName])
127
124
 
128
- const handleOnPress = () => {
125
+ const handleOnPress = useCallback(() => {
129
126
  const {
130
127
  onPress,
131
128
  ...rest
132
129
  } = props
133
130
 
134
- if (onPress)
135
- onPress(rest)
136
- }
131
+ onPress?.(rest)
132
+ }, [props])
137
133
 
138
- const handleOnLongPress = () => {
134
+ const handleOnLongPress = useCallback(() => {
139
135
  const {
140
136
  onLongPress,
141
137
  ...rest
@@ -143,25 +139,22 @@ export function GiftedAvatar (
143
139
 
144
140
  if (onLongPress)
145
141
  onLongPress(rest)
146
- }
142
+ }, [props])
147
143
 
148
- useEffect(() => {
149
- setAvatarColor()
150
- }, [setAvatarColor])
144
+ const placeholderView = useMemo(() => (
145
+ <View
146
+ style={[
147
+ stylesCommon.centerItems,
148
+ styles.avatarStyle,
149
+ styles.avatarTransparent,
150
+ avatarStyle,
151
+ ]}
152
+ accessibilityRole='image'
153
+ />
154
+ ), [avatarStyle])
151
155
 
152
156
  if (!user || (!user.name && !user.avatar))
153
- // render placeholder
154
- return (
155
- <View
156
- style={[
157
- stylesCommon.centerItems,
158
- styles.avatarStyle,
159
- styles.avatarTransparent,
160
- avatarStyle,
161
- ]}
162
- accessibilityRole='image'
163
- />
164
- )
157
+ return placeholderView
165
158
 
166
159
  if (user.avatar)
167
160
  return (