react-native-gifted-chat 2.6.5 → 2.7.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 (228) hide show
  1. package/README.md +8 -17
  2. package/lib/Actions.d.ts +0 -11
  3. package/lib/Actions.js +2 -14
  4. package/lib/Actions.js.map +1 -1
  5. package/lib/Avatar.d.ts +1 -23
  6. package/lib/Avatar.js +32 -40
  7. package/lib/Avatar.js.map +1 -1
  8. package/lib/Bubble/index.d.ts +30 -0
  9. package/lib/{Bubble.js → Bubble/index.js} +31 -181
  10. package/lib/Bubble/index.js.map +1 -0
  11. package/lib/Bubble/styles.d.ts +69 -0
  12. package/lib/Bubble/styles.js +72 -0
  13. package/lib/Bubble/styles.js.map +1 -0
  14. package/lib/Bubble/types.d.ts +47 -0
  15. package/lib/Bubble/types.js +2 -0
  16. package/lib/Bubble/types.js.map +1 -0
  17. package/lib/Composer.d.ts +0 -17
  18. package/lib/Composer.js +3 -18
  19. package/lib/Composer.js.map +1 -1
  20. package/lib/Constant.d.ts +1 -1
  21. package/lib/Constant.js +1 -1
  22. package/lib/Constant.js.map +1 -1
  23. package/lib/Day/index.d.ts +4 -0
  24. package/lib/Day/index.js +39 -0
  25. package/lib/Day/index.js.map +1 -0
  26. package/lib/Day/styles.d.ts +20 -0
  27. package/lib/Day/styles.js +22 -0
  28. package/lib/Day/styles.js.map +1 -0
  29. package/lib/Day/types.d.ts +9 -0
  30. package/lib/Day/types.js +2 -0
  31. package/lib/Day/types.js.map +1 -0
  32. package/lib/GiftedAvatar.d.ts +1 -11
  33. package/lib/GiftedAvatar.js +26 -33
  34. package/lib/GiftedAvatar.js.map +1 -1
  35. package/lib/GiftedChat/index.d.ts +26 -0
  36. package/lib/{GiftedChat.js → GiftedChat/index.js} +46 -52
  37. package/lib/GiftedChat/index.js.map +1 -0
  38. package/lib/GiftedChat/styles.d.ts +6 -0
  39. package/lib/GiftedChat/styles.js +7 -0
  40. package/lib/GiftedChat/styles.js.map +1 -0
  41. package/lib/{GiftedChat.d.ts → GiftedChat/types.d.ts} +28 -38
  42. package/lib/GiftedChat/types.js +2 -0
  43. package/lib/GiftedChat/types.js.map +1 -0
  44. package/lib/InputToolbar.d.ts +1 -14
  45. package/lib/InputToolbar.js +0 -12
  46. package/lib/InputToolbar.js.map +1 -1
  47. package/lib/LoadEarlier.d.ts +0 -14
  48. package/lib/LoadEarlier.js +2 -16
  49. package/lib/LoadEarlier.js.map +1 -1
  50. package/lib/Message/index.d.ts +6 -0
  51. package/lib/Message/index.js +85 -0
  52. package/lib/Message/index.js.map +1 -0
  53. package/lib/Message/styles.d.ts +21 -0
  54. package/lib/Message/styles.js +22 -0
  55. package/lib/Message/styles.js.map +1 -0
  56. package/lib/Message/types.d.ts +22 -0
  57. package/lib/Message/types.js +2 -0
  58. package/lib/Message/types.js.map +1 -0
  59. package/lib/MessageAudio.js +1 -2
  60. package/lib/MessageAudio.js.map +1 -1
  61. package/lib/MessageContainer/components/DayAnimated/index.d.ts +5 -0
  62. package/lib/MessageContainer/components/DayAnimated/index.js +85 -0
  63. package/lib/MessageContainer/components/DayAnimated/index.js.map +1 -0
  64. package/lib/MessageContainer/components/DayAnimated/styles.d.ts +11 -0
  65. package/lib/MessageContainer/components/DayAnimated/styles.js +12 -0
  66. package/lib/MessageContainer/components/DayAnimated/styles.js.map +1 -0
  67. package/lib/MessageContainer/components/DayAnimated/types.d.ts +17 -0
  68. package/lib/MessageContainer/components/DayAnimated/types.js +2 -0
  69. package/lib/MessageContainer/components/DayAnimated/types.js.map +1 -0
  70. package/lib/MessageContainer/components/Item/index.d.ts +22 -0
  71. package/lib/MessageContainer/components/Item/index.js +69 -0
  72. package/lib/MessageContainer/components/Item/index.js.map +1 -0
  73. package/lib/MessageContainer/components/Item/types.d.ts +18 -0
  74. package/lib/MessageContainer/components/Item/types.js +2 -0
  75. package/lib/MessageContainer/components/Item/types.js.map +1 -0
  76. package/lib/MessageContainer/index.d.ts +6 -0
  77. package/lib/MessageContainer/index.js +200 -0
  78. package/lib/MessageContainer/index.js.map +1 -0
  79. package/lib/MessageContainer/styles.d.ts +34 -0
  80. package/lib/MessageContainer/styles.js +31 -0
  81. package/lib/MessageContainer/styles.js.map +1 -0
  82. package/lib/MessageContainer/types.d.ts +54 -0
  83. package/lib/MessageContainer/types.js +2 -0
  84. package/lib/MessageContainer/types.js.map +1 -0
  85. package/lib/MessageImage.d.ts +1 -12
  86. package/lib/MessageImage.js +2 -12
  87. package/lib/MessageImage.js.map +1 -1
  88. package/lib/MessageText.d.ts +1 -24
  89. package/lib/MessageText.js +0 -22
  90. package/lib/MessageText.js.map +1 -1
  91. package/lib/MessageVideo.js +1 -2
  92. package/lib/MessageVideo.js.map +1 -1
  93. package/lib/QuickReplies.d.ts +1 -12
  94. package/lib/QuickReplies.js +10 -20
  95. package/lib/QuickReplies.js.map +1 -1
  96. package/lib/Send.d.ts +2 -16
  97. package/lib/Send.js +0 -13
  98. package/lib/Send.js.map +1 -1
  99. package/lib/SystemMessage.d.ts +1 -10
  100. package/lib/SystemMessage.js +2 -12
  101. package/lib/SystemMessage.js.map +1 -1
  102. package/lib/Time.d.ts +1 -17
  103. package/lib/Time.js +0 -15
  104. package/lib/Time.js.map +1 -1
  105. package/lib/TypingIndicator/index.d.ts +5 -0
  106. package/lib/{TypingIndicator.js → TypingIndicator/index.js} +9 -30
  107. package/lib/TypingIndicator/index.js.map +1 -0
  108. package/lib/TypingIndicator/styles.d.ts +20 -0
  109. package/lib/TypingIndicator/styles.js +22 -0
  110. package/lib/TypingIndicator/styles.js.map +1 -0
  111. package/lib/TypingIndicator/types.d.ts +3 -0
  112. package/lib/TypingIndicator/types.js +2 -0
  113. package/lib/TypingIndicator/types.js.map +1 -0
  114. package/lib/index.d.ts +0 -1
  115. package/lib/index.js +0 -1
  116. package/lib/index.js.map +1 -1
  117. package/lib/styles.d.ts +10 -0
  118. package/lib/styles.js +11 -0
  119. package/lib/styles.js.map +1 -0
  120. package/lib/{Models.d.ts → types.d.ts} +1 -1
  121. package/lib/types.js +2 -0
  122. package/lib/types.js.map +1 -0
  123. package/lib/utils.d.ts +1 -3
  124. package/lib/utils.js +0 -7
  125. package/lib/utils.js.map +1 -1
  126. package/package.json +21 -29
  127. package/src/Actions.tsx +3 -15
  128. package/src/Avatar.tsx +43 -52
  129. package/src/{Bubble.tsx → Bubble/index.tsx} +52 -269
  130. package/src/Bubble/styles.ts +73 -0
  131. package/src/Bubble/types.ts +90 -0
  132. package/src/Composer.tsx +3 -19
  133. package/src/Constant.ts +1 -1
  134. package/src/Day/index.tsx +63 -0
  135. package/src/Day/styles.ts +22 -0
  136. package/src/Day/types.ts +14 -0
  137. package/src/GiftedAvatar.tsx +31 -38
  138. package/src/GiftedChat/index.tsx +501 -0
  139. package/src/GiftedChat/styles.ts +7 -0
  140. package/src/GiftedChat/types.ts +206 -0
  141. package/src/InputToolbar.tsx +1 -14
  142. package/src/LoadEarlier.tsx +2 -17
  143. package/src/Message/index.tsx +135 -0
  144. package/src/Message/styles.ts +22 -0
  145. package/src/Message/types.ts +26 -0
  146. package/src/MessageAudio.tsx +1 -4
  147. package/src/MessageContainer/components/DayAnimated/index.tsx +143 -0
  148. package/src/MessageContainer/components/DayAnimated/styles.ts +12 -0
  149. package/src/MessageContainer/components/DayAnimated/types.ts +12 -0
  150. package/src/MessageContainer/components/Item/index.tsx +136 -0
  151. package/src/MessageContainer/components/Item/types.ts +13 -0
  152. package/src/MessageContainer/index.tsx +343 -0
  153. package/src/MessageContainer/styles.ts +31 -0
  154. package/src/MessageContainer/types.ts +60 -0
  155. package/src/MessageImage.tsx +3 -14
  156. package/src/MessageText.tsx +1 -24
  157. package/src/MessageVideo.tsx +1 -4
  158. package/src/QuickReplies.tsx +14 -25
  159. package/src/Send.tsx +1 -15
  160. package/src/SystemMessage.tsx +3 -14
  161. package/src/Time.tsx +1 -17
  162. package/src/{TypingIndicator.tsx → TypingIndicator/index.tsx} +12 -35
  163. package/src/TypingIndicator/styles.ts +22 -0
  164. package/src/TypingIndicator/types.ts +3 -0
  165. package/src/__tests__/Bubble.test.tsx +7 -4
  166. package/src/__tests__/GiftedAvatar.test.tsx +6 -2
  167. package/src/__tests__/GiftedChat.test.tsx +20 -14
  168. package/src/__tests__/Message.test.tsx +4 -1
  169. package/src/__tests__/__snapshots__/Actions.test.tsx.snap +6 -2
  170. package/src/__tests__/__snapshots__/Bubble.test.tsx.snap +3 -1
  171. package/src/__tests__/__snapshots__/Composer.test.tsx.snap +2 -0
  172. package/src/__tests__/__snapshots__/Constant.test.tsx.snap +1 -1
  173. package/src/__tests__/__snapshots__/Day.test.tsx.snap +1 -33
  174. package/src/__tests__/__snapshots__/GiftedAvatar.test.tsx.snap +4 -2
  175. package/src/__tests__/__snapshots__/GiftedChat.test.tsx.snap +22 -9
  176. package/src/__tests__/__snapshots__/InputToolbar.test.tsx.snap +2 -0
  177. package/src/__tests__/__snapshots__/LoadEarlier.test.tsx.snap +3 -1
  178. package/src/__tests__/__snapshots__/Message.test.tsx.snap +21 -100
  179. package/src/__tests__/__snapshots__/MessageContainer.test.tsx.snap +282 -82
  180. package/src/__tests__/__snapshots__/MessageImage.test.tsx.snap +8 -4
  181. package/src/__tests__/__snapshots__/SystemMessage.test.tsx.snap +5 -1
  182. package/src/__tests__/data.ts +1 -1
  183. package/src/index.ts +0 -2
  184. package/src/styles.ts +11 -0
  185. package/src/{Models.ts → types.ts} +1 -1
  186. package/src/utils.ts +1 -9
  187. package/lib/Actions.js.flow +0 -21
  188. package/lib/Avatar.js.flow +0 -27
  189. package/lib/Bubble.d.ts +0 -148
  190. package/lib/Bubble.js.flow +0 -69
  191. package/lib/Bubble.js.map +0 -1
  192. package/lib/Composer.js.flow +0 -24
  193. package/lib/Day.d.ts +0 -15
  194. package/lib/Day.js +0 -36
  195. package/lib/Day.js.flow +0 -23
  196. package/lib/Day.js.map +0 -1
  197. package/lib/GiftedAvatar.js.flow +0 -17
  198. package/lib/GiftedChat.js.flow +0 -163
  199. package/lib/GiftedChat.js.map +0 -1
  200. package/lib/InputToolbar.js.flow +0 -31
  201. package/lib/LoadEarlier.js.flow +0 -20
  202. package/lib/Message.d.ts +0 -67
  203. package/lib/Message.js +0 -157
  204. package/lib/Message.js.flow +0 -32
  205. package/lib/Message.js.map +0 -1
  206. package/lib/MessageAudio.js.flow +0 -15
  207. package/lib/MessageContainer.d.ts +0 -105
  208. package/lib/MessageContainer.js +0 -224
  209. package/lib/MessageContainer.js.flow +0 -39
  210. package/lib/MessageContainer.js.map +0 -1
  211. package/lib/MessageImage.js.flow +0 -20
  212. package/lib/MessageText.js.flow +0 -23
  213. package/lib/MessageVideo.js.flow +0 -16
  214. package/lib/Models.js +0 -2
  215. package/lib/Models.js.map +0 -1
  216. package/lib/QuickReplies.js.flow +0 -25
  217. package/lib/Send.js.flow +0 -19
  218. package/lib/SystemMessage.js.flow +0 -18
  219. package/lib/Time.js.flow +0 -19
  220. package/lib/TypingIndicator.d.ts +0 -6
  221. package/lib/TypingIndicator.js.map +0 -1
  222. package/lib/index.js.flow +0 -2
  223. package/lib/types.js.flow +0 -43
  224. package/lib/utils.js.flow +0 -16
  225. package/src/Day.tsx +0 -71
  226. package/src/GiftedChat.tsx +0 -689
  227. package/src/Message.tsx +0 -229
  228. package/src/MessageContainer.tsx +0 -361
@@ -0,0 +1,22 @@
1
+ import { StyleSheet } from 'react-native'
2
+ import Color from '../Color'
3
+
4
+ export default StyleSheet.create({
5
+ container: {
6
+ marginTop: 5,
7
+ marginBottom: 10,
8
+ },
9
+ wrapper: {
10
+ backgroundColor: 'rgba(0, 0, 0, 0.75)',
11
+ paddingTop: 6,
12
+ paddingBottom: 6,
13
+ paddingLeft: 10,
14
+ paddingRight: 10,
15
+ borderRadius: 15,
16
+ },
17
+ text: {
18
+ color: Color.white,
19
+ fontSize: 12,
20
+ fontWeight: '600',
21
+ },
22
+ })
@@ -0,0 +1,14 @@
1
+ import {
2
+ StyleProp,
3
+ ViewStyle,
4
+ TextStyle,
5
+ } from 'react-native'
6
+
7
+ export interface DayProps {
8
+ createdAt: Date | number
9
+ dateFormat?: string
10
+ dateFormatCalendar?: object
11
+ containerStyle?: StyleProp<ViewStyle>
12
+ wrapperStyle?: StyleProp<ViewStyle>
13
+ textStyle?: StyleProp<TextStyle>
14
+ }
@@ -1,5 +1,4 @@
1
- import PropTypes from 'prop-types'
2
- import React, { useCallback, useRef } from 'react'
1
+ import React, { useCallback, useEffect, useState } from 'react'
3
2
  import {
4
3
  Image,
5
4
  Text,
@@ -11,8 +10,8 @@ import {
11
10
  TextStyle,
12
11
  } from 'react-native'
13
12
  import Color from './Color'
14
- import { User } from './Models'
15
- import { StylePropType } from './utils'
13
+ import { User } from './types'
14
+ import stylesCommon from './styles'
16
15
 
17
16
  const {
18
17
  carrot,
@@ -26,8 +25,6 @@ const {
26
25
 
27
26
  const styles = StyleSheet.create({
28
27
  avatarStyle: {
29
- justifyContent: 'center',
30
- alignItems: 'center',
31
28
  width: 40,
32
29
  height: 40,
33
30
  borderRadius: 20,
@@ -54,29 +51,29 @@ export interface GiftedAvatarProps {
54
51
  export function GiftedAvatar (
55
52
  props: GiftedAvatarProps
56
53
  ) {
57
- const avatarNameRef = useRef<string | undefined>(undefined)
58
- const avatarColorRef = useRef<string | undefined>(undefined)
54
+ const [avatarName, setAvatarName] = useState<string | undefined>(undefined)
55
+ const [backgroundColor, setBackgroundColor] = useState<string | undefined>(undefined)
59
56
 
60
57
  const {
61
- user = {
62
- name: null,
63
- avatar: null,
64
- },
65
- avatarStyle = {},
66
- textStyle = {},
58
+ user,
59
+ avatarStyle,
60
+ textStyle,
67
61
  onPress,
68
62
  } = props
69
63
 
70
64
  const setAvatarColor = useCallback(() => {
71
- const userName = user.name || ''
65
+ if (backgroundColor)
66
+ return
67
+
68
+ const userName = user?.name || ''
72
69
  const name = userName.toUpperCase().split(' ')
73
70
 
74
71
  if (name.length === 1)
75
- avatarNameRef.current = `${name[0].charAt(0)}`
72
+ setAvatarName(`${name[0].charAt(0)}`)
76
73
  else if (name.length > 1)
77
- avatarNameRef.current = `${name[0].charAt(0)}${name[1].charAt(0)}`
74
+ setAvatarName(`${name[0].charAt(0)}${name[1].charAt(0)}`)
78
75
  else
79
- avatarNameRef.current = ''
76
+ setAvatarName('')
80
77
 
81
78
  let sumChars = 0
82
79
  for (let i = 0; i < userName.length; i += 1)
@@ -94,39 +91,39 @@ export function GiftedAvatar (
94
91
  midnightBlue,
95
92
  ]
96
93
 
97
- avatarColorRef.current = colors[sumChars % colors.length]
98
- }, [user.name])
94
+ setBackgroundColor(colors[sumChars % colors.length])
95
+ }, [user?.name, backgroundColor])
99
96
 
100
97
  const renderAvatar = useCallback(() => {
101
- switch (typeof user.avatar) {
98
+ switch (typeof user?.avatar) {
102
99
  case 'function':
103
- return user.avatar([styles.avatarStyle, avatarStyle])
100
+ return user.avatar([stylesCommon.centerItems, styles.avatarStyle, avatarStyle])
104
101
  case 'string':
105
102
  return (
106
103
  <Image
107
104
  source={{ uri: user.avatar }}
108
- style={[styles.avatarStyle, avatarStyle]}
105
+ style={[stylesCommon.centerItems, styles.avatarStyle, avatarStyle]}
109
106
  />
110
107
  )
111
108
  case 'number':
112
109
  return (
113
110
  <Image
114
111
  source={user.avatar}
115
- style={[styles.avatarStyle, avatarStyle]}
112
+ style={[stylesCommon.centerItems, styles.avatarStyle, avatarStyle]}
116
113
  />
117
114
  )
118
115
  default:
119
116
  return null
120
117
  }
121
- }, [user.name, user.avatar, avatarStyle])
118
+ }, [user, avatarStyle])
122
119
 
123
120
  const renderInitials = useCallback(() => {
124
121
  return (
125
122
  <Text style={[styles.textStyle, textStyle]}>
126
- {avatarNameRef.current}
123
+ {avatarName}
127
124
  </Text>
128
125
  )
129
- }, [textStyle])
126
+ }, [textStyle, avatarName])
130
127
 
131
128
  const handleOnPress = () => {
132
129
  const {
@@ -148,11 +145,16 @@ export function GiftedAvatar (
148
145
  onLongPress(rest)
149
146
  }
150
147
 
148
+ useEffect(() => {
149
+ setAvatarColor()
150
+ }, [setAvatarColor])
151
+
151
152
  if (!user || (!user.name && !user.avatar))
152
153
  // render placeholder
153
154
  return (
154
155
  <View
155
156
  style={[
157
+ stylesCommon.centerItems,
156
158
  styles.avatarStyle,
157
159
  styles.avatarTransparent,
158
160
  avatarStyle,
@@ -173,16 +175,15 @@ export function GiftedAvatar (
173
175
  </TouchableOpacity>
174
176
  )
175
177
 
176
- setAvatarColor()
177
-
178
178
  return (
179
179
  <TouchableOpacity
180
180
  disabled={!onPress}
181
181
  onPress={handleOnPress}
182
182
  onLongPress={handleOnLongPress}
183
183
  style={[
184
+ stylesCommon.centerItems,
184
185
  styles.avatarStyle,
185
- { backgroundColor: avatarColorRef.current },
186
+ { backgroundColor },
186
187
  avatarStyle,
187
188
  ]}
188
189
  accessibilityRole='image'
@@ -191,11 +192,3 @@ export function GiftedAvatar (
191
192
  </TouchableOpacity>
192
193
  )
193
194
  }
194
-
195
- GiftedAvatar.propTypes = {
196
- user: PropTypes.object,
197
- onPress: PropTypes.func,
198
- onLongPress: PropTypes.func,
199
- avatarStyle: StylePropType,
200
- textStyle: StylePropType,
201
- }
@@ -0,0 +1,501 @@
1
+ import React, {
2
+ createRef,
3
+ useEffect,
4
+ useMemo,
5
+ useRef,
6
+ useState,
7
+ useCallback,
8
+ RefObject,
9
+ } from 'react'
10
+ import {
11
+ ActionSheetProvider,
12
+ ActionSheetProviderRef,
13
+ } from '@expo/react-native-action-sheet'
14
+ import dayjs from 'dayjs'
15
+ import localizedFormat from 'dayjs/plugin/localizedFormat'
16
+ import {
17
+ Platform,
18
+ TextInput,
19
+ View,
20
+ LayoutChangeEvent,
21
+ } from 'react-native'
22
+ import { Actions } from '../Actions'
23
+ import { Avatar } from '../Avatar'
24
+ import Bubble from '../Bubble'
25
+ import { Composer } from '../Composer'
26
+ import { MAX_COMPOSER_HEIGHT, MIN_COMPOSER_HEIGHT, TEST_ID } from '../Constant'
27
+ import { Day } from '../Day'
28
+ import { GiftedAvatar } from '../GiftedAvatar'
29
+ import { GiftedChatContext } from '../GiftedChatContext'
30
+ import { InputToolbar } from '../InputToolbar'
31
+ import { LoadEarlier } from '../LoadEarlier'
32
+ import Message from '../Message'
33
+ import MessageContainer, { AnimatedList } from '../MessageContainer'
34
+ import { MessageImage } from '../MessageImage'
35
+ import { MessageText } from '../MessageText'
36
+ import {
37
+ IMessage,
38
+ } from '../types'
39
+ import { Send } from '../Send'
40
+ import { SystemMessage } from '../SystemMessage'
41
+ import { Time } from '../Time'
42
+ import * as utils from '../utils'
43
+ import Animated, {
44
+ useAnimatedStyle,
45
+ useAnimatedReaction,
46
+ useSharedValue,
47
+ withTiming,
48
+ runOnJS,
49
+ } from 'react-native-reanimated'
50
+ import { KeyboardProvider, useReanimatedKeyboardAnimation } from 'react-native-keyboard-controller'
51
+ import { GiftedChatProps } from './types'
52
+
53
+ import stylesCommon from '../styles'
54
+ import styles from './styles'
55
+
56
+ dayjs.extend(localizedFormat)
57
+
58
+ function GiftedChat<TMessage extends IMessage = IMessage> (
59
+ props: GiftedChatProps
60
+ ) {
61
+ const {
62
+ messages = [],
63
+ initialText = '',
64
+ isTyping,
65
+ messageIdGenerator = () => crypto.randomUUID(),
66
+ user = {},
67
+ onSend,
68
+ locale = 'en',
69
+ renderLoading,
70
+ actionSheet = null,
71
+ textInputProps,
72
+ renderChatFooter = null,
73
+ renderInputToolbar = null,
74
+ bottomOffset = 0,
75
+ focusOnInputWhenOpeningKeyboard = true,
76
+ keyboardShouldPersistTaps = Platform.select({
77
+ ios: 'never',
78
+ android: 'always',
79
+ default: 'never',
80
+ }),
81
+ onInputTextChanged = null,
82
+ maxInputLength = null,
83
+ inverted = true,
84
+ minComposerHeight = MIN_COMPOSER_HEIGHT,
85
+ maxComposerHeight = MAX_COMPOSER_HEIGHT,
86
+ isKeyboardInternallyHandled = true,
87
+ } = props
88
+
89
+ const actionSheetRef = useRef<ActionSheetProviderRef>(null)
90
+
91
+ const messageContainerRef = useMemo(
92
+ () => props.messageContainerRef || createRef<AnimatedList>(),
93
+ [props.messageContainerRef]
94
+ )
95
+
96
+ const textInputRef = useMemo(
97
+ () => props.textInputRef || createRef<TextInput>(),
98
+ [props.textInputRef]
99
+ )
100
+
101
+ const isTextInputWasFocused: RefObject<boolean> = useRef(false)
102
+
103
+ const [isInitialized, setIsInitialized] = useState<boolean>(false)
104
+ const [composerHeight, setComposerHeight] = useState<number>(
105
+ minComposerHeight!
106
+ )
107
+ const [text, setText] = useState<string | undefined>(() => props.text || '')
108
+ const [isTypingDisabled, setIsTypingDisabled] = useState<boolean>(false)
109
+
110
+ const keyboard = useReanimatedKeyboardAnimation()
111
+ const trackingKeyboardMovement = useSharedValue(false)
112
+ const debounceEnableTypingTimeoutId = useRef<ReturnType<typeof setTimeout>>(undefined)
113
+ const keyboardOffsetBottom = useSharedValue(0)
114
+
115
+ const contentStyleAnim = useAnimatedStyle(
116
+ () => ({
117
+ transform: [
118
+ { translateY: keyboard.height.value - keyboardOffsetBottom.value },
119
+ ],
120
+ }),
121
+ [keyboard, keyboardOffsetBottom]
122
+ )
123
+
124
+ const getTextFromProp = useCallback(
125
+ (fallback: string) => {
126
+ if (props.text === undefined)
127
+ return fallback
128
+
129
+ return props.text
130
+ },
131
+ [props.text]
132
+ )
133
+
134
+ /**
135
+ * Store text input focus status when keyboard hide to retrieve
136
+ * it afterwards if needed.
137
+ * `onKeyboardWillHide` may be called twice in sequence so we
138
+ * make a guard condition (eg. showing image picker)
139
+ */
140
+ const handleTextInputFocusWhenKeyboardHide = useCallback(() => {
141
+ if (!isTextInputWasFocused.current)
142
+ isTextInputWasFocused.current =
143
+ textInputRef.current?.isFocused() || false
144
+ }, [textInputRef])
145
+
146
+ /**
147
+ * Refocus the text input only if it was focused before showing keyboard.
148
+ * This is needed in some cases (eg. showing image picker).
149
+ */
150
+ const handleTextInputFocusWhenKeyboardShow = useCallback(() => {
151
+ if (
152
+ textInputRef.current &&
153
+ isTextInputWasFocused.current &&
154
+ !textInputRef.current.isFocused()
155
+ )
156
+ textInputRef.current.focus()
157
+
158
+ // Reset the indicator since the keyboard is shown
159
+ isTextInputWasFocused.current = false
160
+ }, [textInputRef])
161
+
162
+ const disableTyping = useCallback(() => {
163
+ clearTimeout(debounceEnableTypingTimeoutId.current)
164
+ setIsTypingDisabled(true)
165
+ }, [])
166
+
167
+ const enableTyping = useCallback(() => {
168
+ clearTimeout(debounceEnableTypingTimeoutId.current)
169
+ setIsTypingDisabled(false)
170
+ }, [])
171
+
172
+ const debounceEnableTyping = useCallback(() => {
173
+ clearTimeout(debounceEnableTypingTimeoutId.current)
174
+ debounceEnableTypingTimeoutId.current = setTimeout(() => {
175
+ enableTyping()
176
+ }, 50)
177
+ }, [enableTyping])
178
+
179
+ const scrollToBottom = useCallback(
180
+ (isAnimated = true) => {
181
+ if (!messageContainerRef?.current)
182
+ return
183
+
184
+ if (inverted) {
185
+ messageContainerRef.current.scrollToOffset({
186
+ offset: 0,
187
+ animated: isAnimated,
188
+ })
189
+ return
190
+ }
191
+
192
+ messageContainerRef.current.scrollToEnd({ animated: isAnimated })
193
+ },
194
+ [inverted, messageContainerRef]
195
+ )
196
+
197
+ const renderMessages = useMemo(() => {
198
+ if (!isInitialized)
199
+ return null
200
+
201
+ const { messagesContainerStyle, ...messagesContainerProps } = props
202
+
203
+ const fragment = (
204
+ <View style={[stylesCommon.fill, messagesContainerStyle]}>
205
+ <MessageContainer
206
+ {...messagesContainerProps}
207
+ invertibleScrollViewProps={{
208
+ inverted,
209
+ keyboardShouldPersistTaps,
210
+ }}
211
+ messages={messages}
212
+ forwardRef={messageContainerRef}
213
+ isTyping={isTyping}
214
+ />
215
+ {renderChatFooter?.()}
216
+ </View>
217
+ )
218
+
219
+ return fragment
220
+ }, [
221
+ isInitialized,
222
+ isTyping,
223
+ messages,
224
+ props,
225
+ inverted,
226
+ keyboardShouldPersistTaps,
227
+ messageContainerRef,
228
+ renderChatFooter,
229
+ ])
230
+
231
+ const notifyInputTextReset = useCallback(() => {
232
+ onInputTextChanged?.('')
233
+ }, [onInputTextChanged])
234
+
235
+ const resetInputToolbar = useCallback(() => {
236
+ textInputRef.current?.clear()
237
+
238
+ notifyInputTextReset()
239
+
240
+ setComposerHeight(minComposerHeight!)
241
+ setText(getTextFromProp(''))
242
+ enableTyping()
243
+ }, [
244
+ minComposerHeight,
245
+ getTextFromProp,
246
+ textInputRef,
247
+ notifyInputTextReset,
248
+ enableTyping,
249
+ ])
250
+
251
+ const _onSend = useCallback(
252
+ (messages: TMessage[] = [], shouldResetInputToolbar = false) => {
253
+ if (!Array.isArray(messages))
254
+ messages = [messages]
255
+
256
+ const newMessages: TMessage[] = messages.map(message => {
257
+ return {
258
+ ...message,
259
+ user: user!,
260
+ createdAt: new Date(),
261
+ _id: messageIdGenerator?.(),
262
+ }
263
+ })
264
+
265
+ if (shouldResetInputToolbar === true) {
266
+ disableTyping()
267
+
268
+ resetInputToolbar()
269
+ }
270
+
271
+ onSend?.(newMessages)
272
+
273
+ setTimeout(() => scrollToBottom(), 10)
274
+ },
275
+ [messageIdGenerator, onSend, user, resetInputToolbar, disableTyping, scrollToBottom]
276
+ )
277
+
278
+ const onInputSizeChanged = useCallback(
279
+ (size: { height: number }) => {
280
+ const newComposerHeight = Math.max(
281
+ minComposerHeight!,
282
+ Math.min(maxComposerHeight!, size.height)
283
+ )
284
+
285
+ setComposerHeight(newComposerHeight)
286
+ },
287
+ [maxComposerHeight, minComposerHeight]
288
+ )
289
+
290
+ const _onInputTextChanged = useCallback(
291
+ (_text: string) => {
292
+ if (isTypingDisabled)
293
+ return
294
+
295
+ onInputTextChanged?.(_text)
296
+
297
+ // Only set state if it's not being overridden by a prop.
298
+ if (props.text === undefined)
299
+ setText(_text)
300
+ },
301
+ [onInputTextChanged, isTypingDisabled, props.text]
302
+ )
303
+
304
+ const onInitialLayoutViewLayout = useCallback(
305
+ (e: LayoutChangeEvent) => {
306
+ if (isInitialized)
307
+ return
308
+
309
+ const { layout } = e.nativeEvent
310
+
311
+ if (layout.height <= 0)
312
+ return
313
+
314
+ notifyInputTextReset()
315
+
316
+ setIsInitialized(true)
317
+ setComposerHeight(minComposerHeight!)
318
+ setText(getTextFromProp(initialText))
319
+ },
320
+ [isInitialized, initialText, minComposerHeight, notifyInputTextReset, getTextFromProp]
321
+ )
322
+
323
+ const inputToolbarFragment = useMemo(() => {
324
+ if (!isInitialized)
325
+ return null
326
+
327
+ const inputToolbarProps = {
328
+ ...props,
329
+ text: getTextFromProp(text!),
330
+ composerHeight: Math.max(minComposerHeight!, composerHeight),
331
+ onSend: _onSend,
332
+ onInputSizeChanged,
333
+ onTextChanged: _onInputTextChanged,
334
+ textInputProps: {
335
+ ...textInputProps,
336
+ ref: textInputRef,
337
+ maxLength: isTypingDisabled ? 0 : maxInputLength,
338
+ },
339
+ }
340
+
341
+ if (renderInputToolbar)
342
+ return renderInputToolbar(inputToolbarProps)
343
+
344
+ return <InputToolbar {...inputToolbarProps} />
345
+ }, [
346
+ isInitialized,
347
+ _onSend,
348
+ getTextFromProp,
349
+ maxInputLength,
350
+ minComposerHeight,
351
+ onInputSizeChanged,
352
+ props,
353
+ text,
354
+ renderInputToolbar,
355
+ composerHeight,
356
+ isTypingDisabled,
357
+ textInputRef,
358
+ textInputProps,
359
+ _onInputTextChanged,
360
+ ])
361
+
362
+ const contextValues = useMemo(
363
+ () => ({
364
+ actionSheet:
365
+ actionSheet ||
366
+ (() => ({
367
+ showActionSheetWithOptions:
368
+ actionSheetRef.current!.showActionSheetWithOptions,
369
+ })),
370
+ getLocale: () => locale,
371
+ }),
372
+ [actionSheet, locale]
373
+ )
374
+
375
+ useEffect(() => {
376
+ if (props.text != null)
377
+ setText(props.text)
378
+ }, [props.text])
379
+
380
+ useAnimatedReaction(
381
+ () => -keyboard.height.value,
382
+ (value, prevValue) => {
383
+ if (prevValue !== null && value !== prevValue) {
384
+ const isKeyboardMovingUp = value > prevValue
385
+ if (isKeyboardMovingUp !== trackingKeyboardMovement.value) {
386
+ trackingKeyboardMovement.value = isKeyboardMovingUp
387
+ keyboardOffsetBottom.value = withTiming(
388
+ isKeyboardMovingUp ? bottomOffset : 0,
389
+ {
390
+ // If `bottomOffset` exists, we change the duration to a smaller value to fix the delay in the keyboard animation speed
391
+ duration: bottomOffset ? 150 : 400,
392
+ }
393
+ )
394
+
395
+ if (focusOnInputWhenOpeningKeyboard)
396
+ if (isKeyboardMovingUp)
397
+ runOnJS(handleTextInputFocusWhenKeyboardShow)()
398
+ else
399
+ runOnJS(handleTextInputFocusWhenKeyboardHide)()
400
+
401
+ if (value === 0) {
402
+ runOnJS(enableTyping)()
403
+ } else {
404
+ runOnJS(disableTyping)()
405
+ runOnJS(debounceEnableTyping)()
406
+ }
407
+ }
408
+ }
409
+ },
410
+ [
411
+ keyboard,
412
+ trackingKeyboardMovement,
413
+ focusOnInputWhenOpeningKeyboard,
414
+ handleTextInputFocusWhenKeyboardHide,
415
+ handleTextInputFocusWhenKeyboardShow,
416
+ enableTyping,
417
+ disableTyping,
418
+ debounceEnableTyping,
419
+ bottomOffset,
420
+ ]
421
+ )
422
+
423
+ return (
424
+ <GiftedChatContext.Provider value={contextValues}>
425
+ <ActionSheetProvider ref={actionSheetRef}>
426
+ <View
427
+ testID={TEST_ID.WRAPPER}
428
+ style={[stylesCommon.fill, styles.contentContainer]}
429
+ onLayout={onInitialLayoutViewLayout}
430
+ >
431
+ {isInitialized
432
+ ? (
433
+ <Animated.View style={[stylesCommon.fill, isKeyboardInternallyHandled && contentStyleAnim]}>
434
+ {renderMessages}
435
+ {inputToolbarFragment}
436
+ </Animated.View>
437
+ )
438
+ : (
439
+ renderLoading?.()
440
+ )}
441
+ </View>
442
+ </ActionSheetProvider>
443
+ </GiftedChatContext.Provider>
444
+ )
445
+ }
446
+
447
+ function GiftedChatWrapper (props: GiftedChatProps) {
448
+ return (
449
+ <KeyboardProvider>
450
+ <GiftedChat {...props} />
451
+ </KeyboardProvider>
452
+ )
453
+ }
454
+
455
+ GiftedChatWrapper.append = <TMessage extends IMessage>(
456
+ currentMessages: TMessage[] = [],
457
+ messages: TMessage[],
458
+ inverted = true
459
+ ) => {
460
+ if (!Array.isArray(messages))
461
+ messages = [messages]
462
+
463
+ return inverted
464
+ ? messages.concat(currentMessages)
465
+ : currentMessages.concat(messages)
466
+ }
467
+
468
+ GiftedChatWrapper.prepend = <TMessage extends IMessage>(
469
+ currentMessages: TMessage[] = [],
470
+ messages: TMessage[],
471
+ inverted = true
472
+ ) => {
473
+ if (!Array.isArray(messages))
474
+ messages = [messages]
475
+
476
+ return inverted
477
+ ? currentMessages.concat(messages)
478
+ : messages.concat(currentMessages)
479
+ }
480
+
481
+ export * from '../types'
482
+
483
+ export {
484
+ GiftedChatWrapper as GiftedChat,
485
+ Actions,
486
+ Avatar,
487
+ Bubble,
488
+ SystemMessage,
489
+ MessageImage,
490
+ MessageText,
491
+ Composer,
492
+ Day,
493
+ InputToolbar,
494
+ LoadEarlier,
495
+ Message,
496
+ MessageContainer,
497
+ Send,
498
+ Time,
499
+ GiftedAvatar,
500
+ utils
501
+ }
@@ -0,0 +1,7 @@
1
+ import { StyleSheet } from 'react-native'
2
+
3
+ export default StyleSheet.create({
4
+ contentContainer: {
5
+ overflow: 'hidden',
6
+ },
7
+ })