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,136 @@
1
+ import React, { forwardRef, useCallback, useMemo } from 'react'
2
+ import { LayoutChangeEvent, View } from 'react-native'
3
+ import { IMessage } from '../../../types'
4
+ import Message, { MessageProps } from '../../../Message'
5
+ import Animated, { interpolate, useAnimatedStyle, useDerivedValue, useSharedValue } from 'react-native-reanimated'
6
+ import { DaysPositions } from '../../types'
7
+ import { Day } from '../../../Day'
8
+ import { isSameDay } from '../../../utils'
9
+ import { ItemProps } from './types'
10
+
11
+ export * from './types'
12
+
13
+ export const useScrolledPosition = (listHeight: { value: number }, scrolledY: { value: number }, containerHeight: { value: number }, dayBottomMargin: number, dayTopOffset: number) => {
14
+ const scrolledPosition = useDerivedValue(() =>
15
+ listHeight.value + scrolledY.value - containerHeight.value - dayBottomMargin - dayTopOffset
16
+ , [listHeight, scrolledY, containerHeight, dayBottomMargin, dayTopOffset])
17
+
18
+ return scrolledPosition
19
+ }
20
+
21
+ export const useTopOffset = (
22
+ listHeight: { value: number },
23
+ scrolledY: { value: number },
24
+ daysPositions: { value: DaysPositions },
25
+ containerHeight: { value: number },
26
+ dayBottomMargin: number,
27
+ dayTopOffset: number
28
+ ) => {
29
+ const scrolledPosition = useScrolledPosition(listHeight, scrolledY, containerHeight, dayBottomMargin, dayTopOffset)
30
+
31
+ const daysPositionsArray = useDerivedValue(() => Object.values(daysPositions.value).sort((a, b) => a.y - b.y))
32
+
33
+ const currentDayPosition = useDerivedValue(() =>
34
+ daysPositionsArray.value.find((day, index) => {
35
+ const dayPosition = day.y + day.height
36
+ return (scrolledPosition.value < dayPosition) || index === daysPositionsArray.value.length - 1
37
+ })
38
+ , [daysPositionsArray, scrolledPosition])
39
+
40
+ const topOffset = useDerivedValue(() => {
41
+ const scrolledBottomY = listHeight.value + scrolledY.value - (currentDayPosition.value?.height ?? 0) - 5 // 5 is marginTop in Day component.
42
+ const dateBottomY = currentDayPosition.value?.y ?? 0
43
+
44
+ const topOffset = scrolledBottomY - dateBottomY
45
+
46
+ return topOffset
47
+ }, [listHeight, scrolledY, currentDayPosition])
48
+
49
+ return topOffset
50
+ }
51
+
52
+ const DayWrapper = forwardRef<View, MessageProps<IMessage>>((props, ref) => {
53
+ const {
54
+ renderDay: renderDayProp,
55
+ currentMessage,
56
+ previousMessage,
57
+ } = props
58
+
59
+ if (!currentMessage?.createdAt || isSameDay(currentMessage, previousMessage))
60
+ return null
61
+
62
+ const {
63
+ /* eslint-disable @typescript-eslint/no-unused-vars */
64
+ containerStyle,
65
+ onMessageLayout,
66
+ /* eslint-enable @typescript-eslint/no-unused-vars */
67
+ ...rest
68
+ } = props
69
+
70
+ return (
71
+ <View ref={ref}>
72
+ {
73
+ renderDayProp
74
+ ? renderDayProp({ ...rest, createdAt: currentMessage.createdAt })
75
+ : <Day {...rest} createdAt={currentMessage.createdAt} />
76
+ }
77
+ </View>
78
+ )
79
+ })
80
+
81
+ const Item = (props: ItemProps) => {
82
+ const {
83
+ onRefDayWrapper,
84
+ renderMessage: renderMessageProp,
85
+ scrolledY,
86
+ daysPositions,
87
+ listHeight,
88
+ ...rest
89
+ } = props
90
+
91
+ const containerHeight = useSharedValue(0)
92
+ const dayTopOffset = useMemo(() => 10, [])
93
+ const dayBottomMargin = useMemo(() => 10, [])
94
+ const topOffset = useTopOffset(listHeight, scrolledY, daysPositions, containerHeight, dayBottomMargin, dayTopOffset)
95
+
96
+ const handleLayout = useCallback(({ nativeEvent }: LayoutChangeEvent) => {
97
+ containerHeight.value = nativeEvent.layout.height
98
+ }, [containerHeight])
99
+
100
+ const style = useAnimatedStyle(() => ({
101
+ opacity: interpolate(
102
+ topOffset.value,
103
+ [-dayTopOffset, -0.0001, 0, containerHeight.value + dayTopOffset],
104
+ [0, 0, 1, 1],
105
+ 'clamp'
106
+ ),
107
+ }), [topOffset, containerHeight, dayTopOffset])
108
+
109
+ const handleRef = useCallback((ref: unknown) => {
110
+ onRefDayWrapper(
111
+ ref,
112
+ props.currentMessage._id,
113
+ props.currentMessage.createdAt instanceof Date
114
+ ? props.currentMessage.createdAt.getTime()
115
+ : props.currentMessage.createdAt
116
+ )
117
+ }, [onRefDayWrapper, props.currentMessage])
118
+
119
+ return (
120
+ <View key={props.currentMessage._id.toString()}>
121
+ <Animated.View style={style} onLayout={handleLayout}>
122
+ <DayWrapper
123
+ {...rest as MessageProps<IMessage>}
124
+ ref={handleRef}
125
+ />
126
+ </Animated.View>
127
+ {
128
+ renderMessageProp
129
+ ? renderMessageProp(rest as MessageProps<IMessage>)
130
+ : <Message {...rest as MessageProps<IMessage>} />
131
+ }
132
+ </View>
133
+ )
134
+ }
135
+
136
+ export default Item
@@ -0,0 +1,13 @@
1
+ import { MessageContainerProps, DaysPositions } from '../../types'
2
+ import { IMessage } from '../../../types'
3
+
4
+ export interface ItemProps extends MessageContainerProps<IMessage> {
5
+ onRefDayWrapper: (ref: unknown, id: string | number, createdAt: number) => void
6
+ currentMessage: IMessage
7
+ previousMessage?: IMessage
8
+ nextMessage?: IMessage
9
+ position: 'left' | 'right'
10
+ scrolledY: { value: number }
11
+ daysPositions: { value: DaysPositions }
12
+ listHeight: { value: number }
13
+ }
@@ -0,0 +1,343 @@
1
+ import React, { useCallback, useMemo, useState } from 'react'
2
+ import {
3
+ View,
4
+ TouchableOpacity,
5
+ Text,
6
+ Platform,
7
+ LayoutChangeEvent,
8
+ } from 'react-native'
9
+ import { FlashList, ListRenderItemInfo } from '@shopify/flash-list'
10
+ import Animated, { runOnJS, useAnimatedScrollHandler, useAnimatedStyle, useSharedValue, withTiming } from 'react-native-reanimated'
11
+ import { ReanimatedScrollEvent } from 'react-native-reanimated/lib/typescript/hook/commonTypes'
12
+ import DayAnimated from './components/DayAnimated'
13
+ import Item from './components/Item'
14
+
15
+ import { LoadEarlier } from '../LoadEarlier'
16
+ import { IMessage } from '../types'
17
+ import TypingIndicator from '../TypingIndicator'
18
+ import { MessageContainerProps, DaysPositions } from './types'
19
+ import { ItemProps } from './components/Item/types'
20
+
21
+ import { warning } from '../logging'
22
+ import stylesCommon from '../styles'
23
+ import styles from './styles'
24
+
25
+ export * from './types'
26
+
27
+ const AnimatedFlashList = Animated.createAnimatedComponent(FlashList)
28
+
29
+ function MessageContainer<TMessage extends IMessage = IMessage> (props: MessageContainerProps<TMessage>) {
30
+ const {
31
+ messages = [],
32
+ user,
33
+ isTyping = false,
34
+ renderChatEmpty: renderChatEmptyProp,
35
+ onLoadEarlier,
36
+ inverted = true,
37
+ loadEarlier = false,
38
+ listViewProps,
39
+ invertibleScrollViewProps,
40
+ extraData = null,
41
+ isScrollToBottomEnabled = false,
42
+ scrollToBottomOffset = 200,
43
+ alignTop = false,
44
+ scrollToBottomStyle,
45
+ infiniteScroll = false,
46
+ isLoadingEarlier = false,
47
+ renderTypingIndicator: renderTypingIndicatorProp,
48
+ renderFooter: renderFooterProp,
49
+ renderLoadEarlier: renderLoadEarlierProp,
50
+ forwardRef,
51
+ handleOnScroll: handleOnScrollProp,
52
+ scrollToBottomComponent: scrollToBottomComponentProp,
53
+ } = props
54
+
55
+ const scrollToBottomOpacity = useSharedValue(0)
56
+ const [isScrollToBottomVisible, setIsScrollToBottomVisible] = useState(false)
57
+ const scrollToBottomStyleAnim = useAnimatedStyle(() => ({
58
+ opacity: scrollToBottomOpacity.value,
59
+ }), [scrollToBottomOpacity])
60
+
61
+ const [hasScrolled, setHasScrolled] = useState(false)
62
+
63
+ const daysPositions = useSharedValue<DaysPositions>({})
64
+ const listHeight = useSharedValue(0)
65
+ const scrolledY = useSharedValue(0)
66
+
67
+ const renderTypingIndicator = useCallback(() => {
68
+ if (renderTypingIndicatorProp)
69
+ return renderTypingIndicatorProp()
70
+
71
+ return <TypingIndicator isTyping={isTyping} />
72
+ }, [isTyping, renderTypingIndicatorProp])
73
+
74
+ const ListFooterComponent = useMemo(() => {
75
+ if (renderFooterProp)
76
+ return <>{renderFooterProp(props)}</>
77
+
78
+ return <>{renderTypingIndicator()}</>
79
+ }, [renderFooterProp, renderTypingIndicator, props])
80
+
81
+ const renderLoadEarlier = useCallback(() => {
82
+ if (loadEarlier === true) {
83
+ if (renderLoadEarlierProp)
84
+ return renderLoadEarlierProp(props)
85
+
86
+ return <LoadEarlier {...props} />
87
+ }
88
+
89
+ return null
90
+ }, [loadEarlier, renderLoadEarlierProp, props])
91
+
92
+ const scrollTo = useCallback((options: { animated?: boolean, offset: number }) => {
93
+ if (forwardRef?.current && options)
94
+ forwardRef.current.scrollToOffset(options)
95
+ }, [forwardRef])
96
+
97
+ const doScrollToBottom = useCallback((animated: boolean = true) => {
98
+ if (inverted)
99
+ scrollTo({ offset: 0, animated })
100
+ else if (forwardRef?.current)
101
+ forwardRef.current.scrollToEnd({ animated })
102
+ }, [forwardRef, inverted, scrollTo])
103
+
104
+ const handleOnScroll = useCallback((event: ReanimatedScrollEvent) => {
105
+ handleOnScrollProp?.(event)
106
+
107
+ const {
108
+ contentOffset: { y: contentOffsetY },
109
+ contentSize: { height: contentSizeHeight },
110
+ layoutMeasurement: { height: layoutMeasurementHeight },
111
+ } = event
112
+
113
+ const duration = 250
114
+
115
+ if (inverted) {
116
+ if (contentOffsetY > scrollToBottomOffset!) {
117
+ setIsScrollToBottomVisible(true)
118
+ scrollToBottomOpacity.value = withTiming(1, { duration })
119
+ } else {
120
+ scrollToBottomOpacity.value = withTiming(0, { duration }, isFinished => {
121
+ if (isFinished)
122
+ runOnJS(setIsScrollToBottomVisible)(false)
123
+ })
124
+ }
125
+ } else if (
126
+ contentOffsetY < scrollToBottomOffset! &&
127
+ contentSizeHeight - layoutMeasurementHeight > scrollToBottomOffset!
128
+ ) {
129
+ setIsScrollToBottomVisible(true)
130
+ scrollToBottomOpacity.value = withTiming(1, { duration })
131
+ } else {
132
+ scrollToBottomOpacity.value = withTiming(0, { duration }, isFinished => {
133
+ if (isFinished)
134
+ runOnJS(setIsScrollToBottomVisible)(false)
135
+ })
136
+ }
137
+
138
+ setHasScrolled(true)
139
+ }, [handleOnScrollProp, inverted, scrollToBottomOffset, scrollToBottomOpacity])
140
+
141
+ const handleLayoutDayWrapper = useCallback((ref: unknown, id: string | number, createdAt: number) => {
142
+ setTimeout(() => { // do not delete "setTimeout". It's necessary for get correct layout.
143
+ const itemLayout = forwardRef?.current?.recyclerlistview_unsafe?.getLayout(messages.findIndex(m => m._id === id))
144
+
145
+ if (ref && itemLayout) {
146
+ daysPositions.value = {
147
+ ...daysPositions.value,
148
+ [id]: {
149
+ ...itemLayout,
150
+ createdAt,
151
+ },
152
+ }
153
+ } else if (daysPositions.value[id] != null) {
154
+ const nextDaysPositions = { ...daysPositions.value }
155
+ delete nextDaysPositions[id]
156
+ daysPositions.value = nextDaysPositions
157
+ }
158
+ }, 100)
159
+ }, [messages, daysPositions, forwardRef])
160
+
161
+ const renderItem = useCallback(({ item, index }: ListRenderItemInfo<unknown>): React.ReactElement | null => {
162
+ const messageItem = item as TMessage
163
+
164
+ if (!messageItem._id && messageItem._id !== 0)
165
+ warning('GiftedChat: `_id` is missing for message', JSON.stringify(item))
166
+
167
+ if (!messageItem.user) {
168
+ if (!messageItem.system)
169
+ warning(
170
+ 'GiftedChat: `user` is missing for message',
171
+ JSON.stringify(messageItem)
172
+ )
173
+
174
+ messageItem.user = { _id: 0 }
175
+ }
176
+
177
+ const { messages, ...restProps } = props
178
+
179
+ if (messages && user) {
180
+ const previousMessage =
181
+ (inverted ? messages[index + 1] : messages[index - 1]) || {}
182
+ const nextMessage =
183
+ (inverted ? messages[index - 1] : messages[index + 1]) || {}
184
+
185
+ const messageProps: ItemProps = {
186
+ ...restProps,
187
+ currentMessage: messageItem,
188
+ previousMessage,
189
+ nextMessage,
190
+ position: messageItem.user._id === user._id ? 'right' : 'left',
191
+ onRefDayWrapper: handleLayoutDayWrapper,
192
+ scrolledY,
193
+ daysPositions,
194
+ listHeight,
195
+ }
196
+
197
+ return (
198
+ <Item {...messageProps} />
199
+ )
200
+ }
201
+
202
+ return null
203
+ }, [props, inverted, handleLayoutDayWrapper, scrolledY, daysPositions, listHeight, user])
204
+
205
+ const renderChatEmpty = useCallback(() => {
206
+ if (renderChatEmptyProp)
207
+ return inverted
208
+ ? (
209
+ renderChatEmptyProp()
210
+ )
211
+ : (
212
+ <View style={[stylesCommon.fill, styles.emptyChatContainer]}>
213
+ {renderChatEmptyProp()}
214
+ </View>
215
+ )
216
+
217
+ return <View style={stylesCommon.fill} />
218
+ }, [inverted, renderChatEmptyProp])
219
+
220
+ const ListHeaderComponent = useMemo(() => {
221
+ const content = renderLoadEarlier()
222
+
223
+ if (!content)
224
+ return null
225
+
226
+ return (
227
+ <View style={stylesCommon.fill}>{content}</View>
228
+ )
229
+ }, [renderLoadEarlier])
230
+
231
+ const renderScrollBottomComponent = useCallback(() => {
232
+ if (scrollToBottomComponentProp)
233
+ return scrollToBottomComponentProp()
234
+
235
+ return <Text>{'V'}</Text>
236
+ }, [scrollToBottomComponentProp])
237
+
238
+ const renderScrollToBottomWrapper = useCallback(() => {
239
+ if (!isScrollToBottomVisible)
240
+ return null
241
+
242
+ return (
243
+ <Animated.View
244
+ style={[
245
+ stylesCommon.centerItems,
246
+ styles.scrollToBottomStyle,
247
+ scrollToBottomStyle,
248
+ scrollToBottomStyleAnim,
249
+ ]}
250
+ >
251
+ <TouchableOpacity
252
+ onPress={() => doScrollToBottom()}
253
+ hitSlop={{ top: 5, left: 5, right: 5, bottom: 5 }}
254
+ >
255
+ {renderScrollBottomComponent()}
256
+ </TouchableOpacity>
257
+ </Animated.View>
258
+ )
259
+ }, [scrollToBottomStyle, renderScrollBottomComponent, doScrollToBottom, scrollToBottomStyleAnim, isScrollToBottomVisible])
260
+
261
+ const onLayoutList = useCallback((event: LayoutChangeEvent) => {
262
+ listHeight.value = event.nativeEvent.layout.height
263
+
264
+ if (
265
+ !inverted &&
266
+ messages?.length
267
+ )
268
+ setTimeout(() => {
269
+ doScrollToBottom(false)
270
+ }, 500)
271
+
272
+ listViewProps?.onLayout?.(event)
273
+ }, [inverted, messages, doScrollToBottom, listHeight, listViewProps])
274
+
275
+ const onEndReached = useCallback(() => {
276
+ if (
277
+ infiniteScroll &&
278
+ hasScrolled &&
279
+ loadEarlier &&
280
+ onLoadEarlier &&
281
+ !isLoadingEarlier &&
282
+ Platform.OS !== 'web'
283
+ )
284
+ onLoadEarlier()
285
+ }, [hasScrolled, infiniteScroll, loadEarlier, onLoadEarlier, isLoadingEarlier])
286
+
287
+ const keyExtractor = useCallback((item: unknown) => (item as TMessage)._id.toString(), [])
288
+
289
+ const scrollHandler = useAnimatedScrollHandler({
290
+ onScroll: event => {
291
+ scrolledY.value = event.contentOffset.y
292
+
293
+ runOnJS(handleOnScroll)(event)
294
+ },
295
+ }, [handleOnScroll])
296
+
297
+ return (
298
+ <View
299
+ style={[
300
+ styles.contentContainerStyle,
301
+ alignTop ? styles.containerAlignTop : stylesCommon.fill,
302
+ ]}
303
+ >
304
+ <AnimatedFlashList
305
+ ref={forwardRef}
306
+ extraData={[extraData, isTyping]}
307
+ keyExtractor={keyExtractor}
308
+ automaticallyAdjustContentInsets={false}
309
+ inverted={inverted}
310
+ data={messages}
311
+ style={stylesCommon.fill}
312
+ renderItem={renderItem}
313
+ {...invertibleScrollViewProps}
314
+ ListEmptyComponent={renderChatEmpty}
315
+ ListFooterComponent={
316
+ inverted ? ListHeaderComponent : ListFooterComponent
317
+ }
318
+ ListHeaderComponent={
319
+ inverted ? ListFooterComponent : ListHeaderComponent
320
+ }
321
+ onScroll={scrollHandler}
322
+ scrollEventThrottle={16}
323
+ onEndReached={onEndReached}
324
+ onEndReachedThreshold={0.1}
325
+ estimatedItemSize={100}
326
+ {...listViewProps}
327
+ onLayout={onLayoutList}
328
+ />
329
+ {isScrollToBottomEnabled
330
+ ? renderScrollToBottomWrapper()
331
+ : null}
332
+ <DayAnimated
333
+ scrolledY={scrolledY}
334
+ daysPositions={daysPositions}
335
+ listHeight={listHeight}
336
+ messages={messages}
337
+ isLoadingEarlier={isLoadingEarlier}
338
+ />
339
+ </View>
340
+ )
341
+ }
342
+
343
+ export default MessageContainer
@@ -0,0 +1,31 @@
1
+ import { StyleSheet } from 'react-native'
2
+ import Color from '../Color'
3
+
4
+ export default StyleSheet.create({
5
+ containerAlignTop: {
6
+ flexDirection: 'row',
7
+ alignItems: 'flex-start',
8
+ },
9
+ contentContainerStyle: {
10
+ flexGrow: 1,
11
+ justifyContent: 'flex-start',
12
+ },
13
+ emptyChatContainer: {
14
+ transform: [{ scaleY: -1 }],
15
+ },
16
+ scrollToBottomStyle: {
17
+ opacity: 0.8,
18
+ position: 'absolute',
19
+ right: 10,
20
+ bottom: 30,
21
+ zIndex: 999,
22
+ height: 40,
23
+ width: 40,
24
+ borderRadius: 20,
25
+ backgroundColor: Color.white,
26
+ shadowColor: Color.black,
27
+ shadowOpacity: 0.5,
28
+ shadowOffset: { width: 0, height: 0 },
29
+ shadowRadius: 1,
30
+ },
31
+ })
@@ -0,0 +1,60 @@
1
+ import React, { Component, RefObject } from 'react'
2
+ import {
3
+ LayoutChangeEvent,
4
+ StyleProp,
5
+ ViewStyle,
6
+ } from 'react-native'
7
+ import { FlashList, FlashListProps } from '@shopify/flash-list'
8
+
9
+ import { LoadEarlierProps } from '../LoadEarlier'
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 { AnimateProps } from 'react-native-reanimated'
14
+
15
+ export type ListViewProps = {
16
+ onLayout?: (event: LayoutChangeEvent) => void
17
+ } & object
18
+
19
+ export type AnimatedList = Component<AnimateProps<FlashListProps<unknown>>, unknown, unknown> & FlashList<FlashListProps<unknown>>
20
+
21
+ export interface MessageContainerProps<TMessage extends IMessage> {
22
+ forwardRef?: RefObject<AnimatedList | null>
23
+ messages?: TMessage[]
24
+ isTyping?: boolean
25
+ user?: User
26
+ listViewProps?: ListViewProps
27
+ inverted?: boolean
28
+ loadEarlier?: boolean
29
+ alignTop?: boolean
30
+ isScrollToBottomEnabled?: boolean
31
+ scrollToBottomStyle?: StyleProp<ViewStyle>
32
+ invertibleScrollViewProps?: object
33
+ extraData?: object
34
+ scrollToBottomOffset?: number
35
+ renderChatEmpty?(): React.ReactNode
36
+ renderFooter?(props: MessageContainerProps<TMessage>): React.ReactNode
37
+ renderMessage?(props: MessageProps<TMessage>): React.ReactElement
38
+ renderLoadEarlier?(props: LoadEarlierProps): React.ReactNode
39
+ renderTypingIndicator?(): React.ReactNode
40
+ scrollToBottomComponent?(): React.ReactNode
41
+ onLoadEarlier?(): void
42
+ onQuickReply?(replies: Reply[]): void
43
+ infiniteScroll?: boolean
44
+ isLoadingEarlier?: boolean
45
+ handleOnScroll?(event: ReanimatedScrollEvent): void
46
+ }
47
+
48
+ export interface State {
49
+ showScrollBottom: boolean
50
+ hasScrolled: boolean
51
+ }
52
+
53
+ interface ViewLayout {
54
+ x: number
55
+ y: number
56
+ width: number
57
+ height: number
58
+ }
59
+
60
+ export type DaysPositions = { [key: string]: ViewLayout & { createdAt: number } }
@@ -1,4 +1,3 @@
1
- import PropTypes from 'prop-types'
2
1
  import React from 'react'
3
2
  import {
4
3
  Image,
@@ -12,8 +11,8 @@ import {
12
11
  } from 'react-native'
13
12
  // TODO: support web
14
13
  import Lightbox, { LightboxProps } from 'react-native-lightbox-v2'
15
- import { IMessage } from './Models'
16
- import { StylePropType } from './utils'
14
+ import { IMessage } from './types'
15
+ import stylesCommon from './styles'
17
16
 
18
17
  const styles = StyleSheet.create({
19
18
  image: {
@@ -24,7 +23,6 @@ const styles = StyleSheet.create({
24
23
  resizeMode: 'cover',
25
24
  },
26
25
  imageActive: {
27
- flex: 1,
28
26
  resizeMode: 'contain',
29
27
  },
30
28
  })
@@ -54,7 +52,7 @@ export function MessageImage<TMessage extends IMessage = IMessage> ({
54
52
  {/* @ts-expect-error: Lightbox types are not fully compatible */}
55
53
  <Lightbox
56
54
  activeProps={{
57
- style: styles.imageActive,
55
+ style: [stylesCommon.fill, styles.imageActive],
58
56
  }}
59
57
  {...lightboxProps}
60
58
  >
@@ -67,12 +65,3 @@ export function MessageImage<TMessage extends IMessage = IMessage> ({
67
65
  </View>
68
66
  )
69
67
  }
70
-
71
- MessageImage.propTypes = {
72
- currentMessage: PropTypes.object,
73
- containerStyle: StylePropType,
74
- imageSourceProps: PropTypes.object,
75
- imageStyle: StylePropType,
76
- imageProps: PropTypes.object,
77
- lightboxProps: PropTypes.object,
78
- }
@@ -1,4 +1,3 @@
1
- import PropTypes from 'prop-types'
2
1
  import React from 'react'
3
2
  import {
4
3
  Linking,
@@ -11,8 +10,7 @@ import {
11
10
  } from 'react-native'
12
11
 
13
12
  import ParsedText from 'react-native-parsed-text'
14
- import { LeftRightStyle, IMessage } from './Models'
15
- import { StylePropType } from './utils'
13
+ import { LeftRightStyle, IMessage } from './types'
16
14
  import { useChatContext } from './GiftedChatContext'
17
15
  import { error } from './logging'
18
16
 
@@ -164,24 +162,3 @@ export function MessageText<TMessage extends IMessage = IMessage> ({
164
162
  </View>
165
163
  )
166
164
  }
167
-
168
- MessageText.propTypes = {
169
- position: PropTypes.oneOf(['left', 'right']),
170
- optionTitles: PropTypes.arrayOf(PropTypes.string),
171
- currentMessage: PropTypes.object,
172
- containerStyle: PropTypes.shape({
173
- left: StylePropType,
174
- right: StylePropType,
175
- }),
176
- textStyle: PropTypes.shape({
177
- left: StylePropType,
178
- right: StylePropType,
179
- }),
180
- linkStyle: PropTypes.shape({
181
- left: StylePropType,
182
- right: StylePropType,
183
- }),
184
- parsePatterns: PropTypes.func,
185
- textProps: PropTypes.object,
186
- customTextStyle: StylePropType,
187
- }
@@ -9,10 +9,7 @@ export function MessageVideo () {
9
9
  {'Video is not implemented by GiftedChat.'}
10
10
  </Text>
11
11
  <Text style={{ color: Color.alizarin, fontWeight: '600' }}>
12
- {
13
- 'You need to provide your own implementation by using renderMessageVideo'
14
- }
15
- {'prop.'}
12
+ {'\nYou need to provide your own implementation by using renderMessageVideo prop.'}
16
13
  </Text>
17
14
  </View>
18
15
  )