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
@@ -1,7 +1,7 @@
1
1
  import React, { useCallback, useEffect, useMemo, useState } from 'react'
2
2
  import {
3
3
  View,
4
- TouchableOpacity,
4
+ Pressable,
5
5
  Text,
6
6
  Platform,
7
7
  LayoutChangeEvent,
@@ -10,7 +10,7 @@ import {
10
10
  CellRendererProps,
11
11
  } from 'react-native'
12
12
  import Animated, { runOnJS, useAnimatedScrollHandler, useAnimatedStyle, useSharedValue, withTiming } from 'react-native-reanimated'
13
- import { ReanimatedScrollEvent } from 'react-native-reanimated/lib/typescript/hook/commonTypes'
13
+ import { ReanimatedScrollEvent } from '../reanimatedCompat'
14
14
  import DayAnimated from './components/DayAnimated'
15
15
  import Item from './components/Item'
16
16
 
@@ -23,11 +23,12 @@ import { ItemProps } from './components/Item/types'
23
23
  import { warning } from '../logging'
24
24
  import stylesCommon from '../styles'
25
25
  import styles from './styles'
26
- import { isSameDay } from '../utils'
26
+ import { isSameDay, useCallbackThrottled } from '../utils'
27
27
 
28
28
  export * from './types'
29
29
 
30
- const AnimatedFlatList = Animated.createAnimatedComponent(FlatList)
30
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
31
+ const AnimatedFlatList = Animated.createAnimatedComponent(FlatList) as React.ComponentType<any>
31
32
 
32
33
  function MessageContainer<TMessage extends IMessage = IMessage> (props: MessageContainerProps<TMessage>) {
33
34
  const {
@@ -38,9 +39,8 @@ function MessageContainer<TMessage extends IMessage = IMessage> (props: MessageC
38
39
  onLoadEarlier,
39
40
  inverted = true,
40
41
  loadEarlier = false,
41
- listViewProps,
42
- invertibleScrollViewProps,
43
- extraData = null,
42
+ listProps,
43
+ extraData,
44
44
  isScrollToBottomEnabled = false,
45
45
  scrollToBottomOffset = 200,
46
46
  alignTop = false,
@@ -53,9 +53,12 @@ function MessageContainer<TMessage extends IMessage = IMessage> (props: MessageC
53
53
  forwardRef,
54
54
  handleOnScroll: handleOnScrollProp,
55
55
  scrollToBottomComponent: scrollToBottomComponentProp,
56
+ renderDay: renderDayProp,
56
57
  } = props
57
58
 
58
59
  const scrollToBottomOpacity = useSharedValue(0)
60
+ const isScrollingDown = useSharedValue(false)
61
+ const lastScrolledY = useSharedValue(0)
59
62
  const [isScrollToBottomVisible, setIsScrollToBottomVisible] = useState(false)
60
63
  const scrollToBottomStyleAnim = useAnimatedStyle(() => ({
61
64
  opacity: scrollToBottomOpacity.value,
@@ -69,14 +72,14 @@ function MessageContainer<TMessage extends IMessage = IMessage> (props: MessageC
69
72
  if (renderTypingIndicatorProp)
70
73
  return renderTypingIndicatorProp()
71
74
 
72
- return <TypingIndicator isTyping={isTyping} />
73
- }, [isTyping, renderTypingIndicatorProp])
75
+ return <TypingIndicator isTyping={isTyping} style={props.typingIndicatorStyle} />
76
+ }, [isTyping, renderTypingIndicatorProp, props.typingIndicatorStyle])
74
77
 
75
78
  const ListFooterComponent = useMemo(() => {
76
79
  if (renderFooterProp)
77
- return <>{renderFooterProp(props)}</>
80
+ return renderFooterProp(props)
78
81
 
79
- return <>{renderTypingIndicator()}</>
82
+ return renderTypingIndicator()
80
83
  }, [renderFooterProp, renderTypingIndicator, props])
81
84
 
82
85
  const renderLoadEarlier = useCallback(() => {
@@ -90,17 +93,33 @@ function MessageContainer<TMessage extends IMessage = IMessage> (props: MessageC
90
93
  return null
91
94
  }, [loadEarlier, renderLoadEarlierProp, props])
92
95
 
96
+ const changeScrollToBottomVisibility: (isVisible: boolean) => void = useCallbackThrottled((isVisible: boolean) => {
97
+ if (isScrollingDown.value && isVisible)
98
+ return
99
+
100
+ if (isVisible)
101
+ setIsScrollToBottomVisible(true)
102
+
103
+ scrollToBottomOpacity.value = withTiming(isVisible ? 1 : 0, { duration: 250 }, isFinished => {
104
+ if (isFinished && !isVisible)
105
+ runOnJS(setIsScrollToBottomVisible)(false)
106
+ })
107
+ }, [scrollToBottomOpacity, isScrollingDown], 50)
108
+
93
109
  const scrollTo = useCallback((options: { animated?: boolean, offset: number }) => {
94
- if (forwardRef?.current && options)
95
- forwardRef.current.scrollToOffset(options)
110
+ if (options)
111
+ forwardRef?.current?.scrollToOffset(options)
96
112
  }, [forwardRef])
97
113
 
98
114
  const doScrollToBottom = useCallback((animated: boolean = true) => {
115
+ isScrollingDown.value = true
116
+ changeScrollToBottomVisibility(false)
117
+
99
118
  if (inverted)
100
119
  scrollTo({ offset: 0, animated })
101
120
  else if (forwardRef?.current)
102
121
  forwardRef.current.scrollToEnd({ animated })
103
- }, [forwardRef, inverted, scrollTo])
122
+ }, [forwardRef, inverted, scrollTo, isScrollingDown, changeScrollToBottomVisibility])
104
123
 
105
124
  const handleOnScroll = useCallback((event: ReanimatedScrollEvent) => {
106
125
  handleOnScrollProp?.(event)
@@ -111,33 +130,31 @@ function MessageContainer<TMessage extends IMessage = IMessage> (props: MessageC
111
130
  layoutMeasurement: { height: layoutMeasurementHeight },
112
131
  } = event
113
132
 
114
- const duration = 250
133
+ isScrollingDown.value =
134
+ (inverted && lastScrolledY.value > contentOffsetY) ||
135
+ (!inverted && lastScrolledY.value < contentOffsetY)
115
136
 
116
- const makeScrollToBottomVisible = () => {
117
- setIsScrollToBottomVisible(true)
118
- scrollToBottomOpacity.value = withTiming(1, { duration })
119
- }
120
-
121
- const makeScrollToBottomHidden = () => {
122
- scrollToBottomOpacity.value = withTiming(0, { duration }, isFinished => {
123
- if (isFinished)
124
- runOnJS(setIsScrollToBottomVisible)(false)
125
- })
126
- }
137
+ lastScrolledY.value = contentOffsetY
127
138
 
128
139
  if (inverted)
129
140
  if (contentOffsetY > scrollToBottomOffset!)
130
- makeScrollToBottomVisible()
141
+ changeScrollToBottomVisibility(true)
131
142
  else
132
- makeScrollToBottomHidden()
143
+ changeScrollToBottomVisibility(false)
133
144
  else if (
134
145
  contentOffsetY < scrollToBottomOffset! &&
135
146
  contentSizeHeight - layoutMeasurementHeight > scrollToBottomOffset!
136
147
  )
137
- makeScrollToBottomVisible()
148
+ changeScrollToBottomVisibility(false)
138
149
  else
139
- makeScrollToBottomHidden()
140
- }, [handleOnScrollProp, inverted, scrollToBottomOffset, scrollToBottomOpacity])
150
+ changeScrollToBottomVisibility(false)
151
+ }, [handleOnScrollProp, inverted, scrollToBottomOffset, changeScrollToBottomVisibility, isScrollingDown, lastScrolledY])
152
+
153
+ const restProps = useMemo(() => {
154
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
155
+ const { messages: _, ...rest } = props
156
+ return rest
157
+ }, [props])
141
158
 
142
159
  const renderItem = useCallback(({ item, index }: ListRenderItemInfo<unknown>): React.ReactElement | null => {
143
160
  const messageItem = item as TMessage
@@ -155,8 +172,6 @@ function MessageContainer<TMessage extends IMessage = IMessage> (props: MessageC
155
172
  messageItem.user = { _id: 0 }
156
173
  }
157
174
 
158
- const { messages, ...restProps } = props
159
-
160
175
  if (messages && user) {
161
176
  const previousMessage =
162
177
  (inverted ? messages[index + 1] : messages[index - 1]) || {}
@@ -180,22 +195,29 @@ function MessageContainer<TMessage extends IMessage = IMessage> (props: MessageC
180
195
  }
181
196
 
182
197
  return null
183
- }, [props, inverted, scrolledY, daysPositions, listHeight, user])
198
+ }, [messages, restProps, inverted, scrolledY, daysPositions, listHeight, user])
199
+
200
+ const emptyContent = useMemo(() => {
201
+ if (!renderChatEmptyProp)
202
+ return null
203
+
204
+ return renderChatEmptyProp()
205
+ }, [renderChatEmptyProp])
184
206
 
185
207
  const renderChatEmpty = useCallback(() => {
186
208
  if (renderChatEmptyProp)
187
209
  return inverted
188
210
  ? (
189
- renderChatEmptyProp()
211
+ emptyContent
190
212
  )
191
213
  : (
192
214
  <View style={[stylesCommon.fill, styles.emptyChatContainer]}>
193
- {renderChatEmptyProp()}
215
+ {emptyContent}
194
216
  </View>
195
217
  )
196
218
 
197
219
  return <View style={stylesCommon.fill} />
198
- }, [inverted, renderChatEmptyProp])
220
+ }, [inverted, renderChatEmptyProp, emptyContent])
199
221
 
200
222
  const ListHeaderComponent = useMemo(() => {
201
223
  const content = renderLoadEarlier()
@@ -215,39 +237,56 @@ function MessageContainer<TMessage extends IMessage = IMessage> (props: MessageC
215
237
  return <Text>{'V'}</Text>
216
238
  }, [scrollToBottomComponentProp])
217
239
 
218
- const renderScrollToBottomWrapper = useCallback(() => {
240
+ const handleScrollToBottomPress = useCallback(() => {
241
+ doScrollToBottom()
242
+ }, [doScrollToBottom])
243
+
244
+ const scrollToBottomContent = useMemo(() => {
245
+ return (
246
+ <Animated.View
247
+ style={[
248
+ stylesCommon.centerItems,
249
+ styles.scrollToBottomContent,
250
+ scrollToBottomStyle,
251
+ scrollToBottomStyleAnim,
252
+ ]}
253
+ >
254
+ {renderScrollBottomComponent()}
255
+ </Animated.View>
256
+ )
257
+ }, [scrollToBottomStyle, scrollToBottomStyleAnim, renderScrollBottomComponent])
258
+
259
+ const ScrollToBottomWrapper = useCallback(() => {
260
+ if (!isScrollToBottomEnabled)
261
+ return null
262
+
219
263
  if (!isScrollToBottomVisible)
220
264
  return null
221
265
 
222
266
  return (
223
- <TouchableOpacity onPress={() => doScrollToBottom()}>
224
- <Animated.View
225
- style={[
226
- stylesCommon.centerItems,
227
- styles.scrollToBottomStyle,
228
- scrollToBottomStyle,
229
- scrollToBottomStyleAnim,
230
- ]}
231
- >
232
- {renderScrollBottomComponent()}
233
- </Animated.View>
234
- </TouchableOpacity>
267
+ <Pressable
268
+ style={styles.scrollToBottom}
269
+ onPress={handleScrollToBottomPress}
270
+ >
271
+ {scrollToBottomContent}
272
+ </Pressable>
235
273
  )
236
- }, [scrollToBottomStyle, renderScrollBottomComponent, doScrollToBottom, scrollToBottomStyleAnim, isScrollToBottomVisible])
274
+ }, [isScrollToBottomEnabled, isScrollToBottomVisible, handleScrollToBottomPress, scrollToBottomContent])
237
275
 
238
276
  const onLayoutList = useCallback((event: LayoutChangeEvent) => {
239
277
  listHeight.value = event.nativeEvent.layout.height
240
278
 
241
279
  if (
242
280
  !inverted &&
243
- messages?.length
281
+ messages?.length &&
282
+ isScrollToBottomEnabled
244
283
  )
245
284
  setTimeout(() => {
246
285
  doScrollToBottom(false)
247
286
  }, 500)
248
287
 
249
- listViewProps?.onLayout?.(event)
250
- }, [inverted, messages, doScrollToBottom, listHeight, listViewProps])
288
+ listProps?.onLayout?.(event)
289
+ }, [inverted, messages, doScrollToBottom, listHeight, listProps, isScrollToBottomEnabled])
251
290
 
252
291
  const onEndReached = useCallback(() => {
253
292
  if (
@@ -263,15 +302,18 @@ function MessageContainer<TMessage extends IMessage = IMessage> (props: MessageC
263
302
  const keyExtractor = useCallback((item: unknown) => (item as TMessage)._id.toString(), [])
264
303
 
265
304
  const renderCell = useCallback((props: CellRendererProps<unknown>) => {
305
+ const { item, onLayout: onLayoutProp, children } = props
306
+ const id = (item as IMessage)._id.toString()
307
+
266
308
  const handleOnLayout = (event: LayoutChangeEvent) => {
267
- props.onLayout?.(event)
309
+ onLayoutProp?.(event)
268
310
 
269
311
  const { y, height } = event.nativeEvent.layout
270
312
 
271
313
  const newValue = {
272
314
  y,
273
315
  height,
274
- createdAt: new Date((props.item as IMessage).createdAt).getTime(),
316
+ createdAt: new Date((item as IMessage).createdAt).getTime(),
275
317
  }
276
318
 
277
319
  daysPositions.modify(value => {
@@ -295,7 +337,7 @@ function MessageContainer<TMessage extends IMessage = IMessage> (props: MessageC
295
337
  }
296
338
 
297
339
  // @ts-expect-error: https://docs.swmansion.com/react-native-reanimated/docs/core/useSharedValue#remarks
298
- value[props.item._id] = newValue
340
+ value[id] = newValue
299
341
  return value
300
342
  })
301
343
  }
@@ -305,7 +347,7 @@ function MessageContainer<TMessage extends IMessage = IMessage> (props: MessageC
305
347
  {...props}
306
348
  onLayout={handleOnLayout}
307
349
  >
308
- {props.children}
350
+ {children}
309
351
  </View>
310
352
  )
311
353
  }, [daysPositions, inverted])
@@ -349,14 +391,13 @@ function MessageContainer<TMessage extends IMessage = IMessage> (props: MessageC
349
391
  >
350
392
  <AnimatedFlatList
351
393
  extraData={extraData}
352
- ref={forwardRef as React.Ref<FlatList<unknown>>}
394
+ ref={forwardRef}
353
395
  keyExtractor={keyExtractor}
354
396
  data={messages}
355
397
  renderItem={renderItem}
356
398
  inverted={inverted}
357
399
  automaticallyAdjustContentInsets={false}
358
400
  style={stylesCommon.fill}
359
- {...invertibleScrollViewProps}
360
401
  ListEmptyComponent={renderChatEmpty}
361
402
  ListFooterComponent={
362
403
  inverted ? ListHeaderComponent : ListFooterComponent
@@ -368,17 +409,16 @@ function MessageContainer<TMessage extends IMessage = IMessage> (props: MessageC
368
409
  scrollEventThrottle={1}
369
410
  onEndReached={onEndReached}
370
411
  onEndReachedThreshold={0.1}
371
- {...listViewProps}
412
+ {...listProps}
372
413
  onLayout={onLayoutList}
373
414
  CellRendererComponent={renderCell}
374
415
  />
375
- {isScrollToBottomEnabled
376
- ? renderScrollToBottomWrapper()
377
- : null}
416
+ <ScrollToBottomWrapper />
378
417
  <DayAnimated
379
418
  scrolledY={scrolledY}
380
419
  daysPositions={daysPositions}
381
420
  listHeight={listHeight}
421
+ renderDay={renderDayProp}
382
422
  messages={messages}
383
423
  isLoadingEarlier={isLoadingEarlier}
384
424
  />
@@ -13,12 +13,13 @@ export default StyleSheet.create({
13
13
  emptyChatContainer: {
14
14
  transform: [{ scaleY: -1 }],
15
15
  },
16
- scrollToBottomStyle: {
17
- opacity: 0.8,
16
+ scrollToBottom: {
18
17
  position: 'absolute',
19
18
  right: 10,
20
19
  bottom: 30,
21
20
  zIndex: 999,
21
+ },
22
+ scrollToBottomContent: {
22
23
  height: 40,
23
24
  width: 40,
24
25
  borderRadius: 20,
@@ -1,49 +1,71 @@
1
- import React, { Component, RefObject } from 'react'
1
+ import React, { RefObject } from 'react'
2
2
  import {
3
3
  FlatListProps,
4
- LayoutChangeEvent,
5
4
  StyleProp,
6
5
  ViewStyle,
6
+ FlatList,
7
7
  } from 'react-native'
8
8
 
9
9
  import { LoadEarlierProps } from '../LoadEarlier'
10
10
  import { MessageProps } from '../Message'
11
- import { User, IMessage, Reply } from '../types'
12
- import { ReanimatedScrollEvent } from 'react-native-reanimated/lib/typescript/hook/commonTypes'
13
- import { FlatList } from 'react-native-reanimated/lib/typescript/Animated'
14
- import { AnimateProps } from 'react-native-reanimated'
11
+ import { User, IMessage, Reply, DayProps } from '../types'
12
+ import { ReanimatedScrollEvent } from '../reanimatedCompat'
13
+ import { TypingIndicatorProps } from '../TypingIndicator/types'
15
14
 
16
- export type ListViewProps = {
17
- onLayout?: (event: LayoutChangeEvent) => void
18
- } & object
15
+ export type ListProps<TMessage extends IMessage = IMessage> = Partial<FlatListProps<TMessage>>
19
16
 
20
- export type AnimatedList<TMessage> = Component<AnimateProps<FlatListProps<TMessage>>, unknown, unknown> & FlatList<FlatListProps<TMessage>>
17
+ export type AnimatedList<TMessage> = FlatList<TMessage>
21
18
 
22
- export interface MessageContainerProps<TMessage extends IMessage = IMessage> {
19
+ export interface MessageContainerProps<TMessage extends IMessage = IMessage>
20
+ extends Omit<TypingIndicatorProps, 'style'> {
21
+ /** Ref for the FlatList message container */
23
22
  forwardRef?: RefObject<AnimatedList<TMessage>>
23
+ /** Messages to display */
24
24
  messages?: TMessage[]
25
- isTyping?: boolean
25
+ /** User sending the messages: { _id, name, avatar } */
26
26
  user?: User
27
- listViewProps?: ListViewProps
27
+ /** Additional props for FlatList */
28
+ listProps?: ListProps<TMessage>
29
+ /** Reverses display order of messages; default is true */
28
30
  inverted?: boolean
31
+ /** Enables the "Load earlier messages" button */
29
32
  loadEarlier?: boolean
33
+ /** Controls whether or not the message bubbles appear at the top of the chat */
30
34
  alignTop?: boolean
35
+ /** Enables the isScrollToBottomEnabled Component */
31
36
  isScrollToBottomEnabled?: boolean
37
+ /** Scroll to bottom wrapper style */
32
38
  scrollToBottomStyle?: StyleProp<ViewStyle>
33
- invertibleScrollViewProps?: object
39
+ /** This can be used to pass unknown data which needs to be re-rendered */
34
40
  extraData?: object
41
+ /** Distance from bottom before showing scroll to bottom button */
35
42
  scrollToBottomOffset?: number
43
+ /** Custom component to render when messages are empty */
36
44
  renderChatEmpty?(): React.ReactNode
45
+ /** Custom footer component on the ListView, e.g. 'User is typing...' */
37
46
  renderFooter?(props: MessageContainerProps<TMessage>): React.ReactNode
47
+ /** Custom message container */
38
48
  renderMessage?(props: MessageProps<TMessage>): React.ReactElement
49
+ /** Custom day above a message */
50
+ renderDay?(props: DayProps): React.ReactNode
51
+ /** Custom "Load earlier messages" button */
39
52
  renderLoadEarlier?(props: LoadEarlierProps): React.ReactNode
53
+ /** Custom typing indicator */
40
54
  renderTypingIndicator?(): React.ReactNode
55
+ /** Scroll to bottom custom component */
41
56
  scrollToBottomComponent?(): React.ReactNode
57
+ /** Callback when loading earlier messages */
42
58
  onLoadEarlier?(): void
59
+ /** Callback when quick reply is sent */
43
60
  onQuickReply?(replies: Reply[]): void
61
+ /** Infinite scroll up when reach the top of messages container, automatically call onLoadEarlier function if exist */
44
62
  infiniteScroll?: boolean
63
+ /** Display an ActivityIndicator when loading earlier messages */
45
64
  isLoadingEarlier?: boolean
65
+ /** Custom scroll event handler */
46
66
  handleOnScroll?(event: ReanimatedScrollEvent): void
67
+ /** Style for TypingIndicator component */
68
+ typingIndicatorStyle?: StyleProp<ViewStyle>
47
69
  }
48
70
 
49
71
  export interface State {
@@ -1,4 +1,4 @@
1
- import React from 'react'
1
+ import React, { useMemo } from 'react'
2
2
  import {
3
3
  Image,
4
4
  StyleSheet,
@@ -44,6 +44,20 @@ export function MessageImage<TMessage extends IMessage = IMessage> ({
44
44
  imageStyle,
45
45
  currentMessage,
46
46
  }: MessageImageProps<TMessage>) {
47
+ const imageSource = useMemo(() => ({
48
+ ...imageSourceProps,
49
+ uri: currentMessage?.image,
50
+ }), [imageSourceProps, currentMessage?.image])
51
+
52
+ const computedImageStyle = useMemo(() => [
53
+ styles.image,
54
+ imageStyle,
55
+ ], [imageStyle])
56
+
57
+ const activePropsStyle = useMemo(() => [{
58
+ style: [stylesCommon.fill, styles.imageActive],
59
+ }], [])
60
+
47
61
  if (currentMessage == null)
48
62
  return null
49
63
 
@@ -51,15 +65,13 @@ export function MessageImage<TMessage extends IMessage = IMessage> ({
51
65
  <View style={containerStyle}>
52
66
  {/* @ts-expect-error: Lightbox types are not fully compatible */}
53
67
  <Lightbox
54
- activeProps={{
55
- style: [stylesCommon.fill, styles.imageActive],
56
- }}
68
+ activeProps={activePropsStyle}
57
69
  {...lightboxProps}
58
70
  >
59
71
  <Image
60
72
  {...imageProps}
61
- style={[styles.image, imageStyle]}
62
- source={{ ...imageSourceProps, uri: currentMessage.image }}
73
+ style={computedImageStyle}
74
+ source={imageSource}
63
75
  />
64
76
  </Lightbox>
65
77
  </View>