react-native-gifted-chat 2.6.4 → 2.7.0

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 (230) hide show
  1. package/README.md +9 -16
  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} +58 -59
  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} +29 -37
  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 +175 -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 +30 -34
  127. package/src/Actions.tsx +3 -15
  128. package/src/Avatar.tsx +43 -52
  129. package/src/{Bubble.tsx → Bubble/index.tsx} +53 -270
  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 +503 -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 +316 -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 +2 -25
  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/example_bare/vendor/bundle/ruby/2.7.0/gems/ffi-1.17.0/ext/ffi_c/libffi/.ci/compile +0 -351
  188. package/example_bare/vendor/bundle/ruby/2.7.0/gems/ffi-1.17.0/ext/ffi_c/libffi/testsuite/libffi.bhaible/Makefile +0 -28
  189. package/lib/Actions.js.flow +0 -21
  190. package/lib/Avatar.js.flow +0 -27
  191. package/lib/Bubble.d.ts +0 -148
  192. package/lib/Bubble.js.flow +0 -69
  193. package/lib/Bubble.js.map +0 -1
  194. package/lib/Composer.js.flow +0 -24
  195. package/lib/Day.d.ts +0 -15
  196. package/lib/Day.js +0 -36
  197. package/lib/Day.js.flow +0 -23
  198. package/lib/Day.js.map +0 -1
  199. package/lib/GiftedAvatar.js.flow +0 -17
  200. package/lib/GiftedChat.js.flow +0 -163
  201. package/lib/GiftedChat.js.map +0 -1
  202. package/lib/InputToolbar.js.flow +0 -31
  203. package/lib/LoadEarlier.js.flow +0 -20
  204. package/lib/Message.d.ts +0 -67
  205. package/lib/Message.js +0 -157
  206. package/lib/Message.js.flow +0 -32
  207. package/lib/Message.js.map +0 -1
  208. package/lib/MessageAudio.js.flow +0 -15
  209. package/lib/MessageContainer.d.ts +0 -103
  210. package/lib/MessageContainer.js +0 -224
  211. package/lib/MessageContainer.js.flow +0 -39
  212. package/lib/MessageContainer.js.map +0 -1
  213. package/lib/MessageImage.js.flow +0 -20
  214. package/lib/MessageText.js.flow +0 -23
  215. package/lib/MessageVideo.js.flow +0 -16
  216. package/lib/Models.js +0 -2
  217. package/lib/Models.js.map +0 -1
  218. package/lib/QuickReplies.js.flow +0 -25
  219. package/lib/Send.js.flow +0 -19
  220. package/lib/SystemMessage.js.flow +0 -18
  221. package/lib/Time.js.flow +0 -19
  222. package/lib/TypingIndicator.d.ts +0 -6
  223. package/lib/TypingIndicator.js.map +0 -1
  224. package/lib/index.js.flow +0 -2
  225. package/lib/types.js.flow +0 -43
  226. package/lib/utils.js.flow +0 -16
  227. package/src/Day.tsx +0 -71
  228. package/src/GiftedChat.tsx +0 -678
  229. package/src/Message.tsx +0 -229
  230. package/src/MessageContainer.tsx +0 -359
@@ -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,316 @@
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, useSharedValue } 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
+ scrollToBottom = 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 [isScrollToBottomVisible, setIsScrollToBottomVisible] = useState(false)
56
+ const [hasScrolled, setHasScrolled] = useState(false)
57
+
58
+ const daysPositions = useSharedValue<DaysPositions>({})
59
+ const listHeight = useSharedValue(0)
60
+ const scrolledY = useSharedValue(0)
61
+
62
+ const renderTypingIndicator = useCallback(() => {
63
+ if (renderTypingIndicatorProp)
64
+ return renderTypingIndicatorProp()
65
+
66
+ return <TypingIndicator isTyping={isTyping} />
67
+ }, [isTyping, renderTypingIndicatorProp])
68
+
69
+ const ListFooterComponent = useMemo(() => {
70
+ if (renderFooterProp)
71
+ return <>{renderFooterProp(props)}</>
72
+
73
+ return <>{renderTypingIndicator()}</>
74
+ }, [renderFooterProp, renderTypingIndicator, props])
75
+
76
+ const renderLoadEarlier = useCallback(() => {
77
+ if (loadEarlier === true) {
78
+ if (renderLoadEarlierProp)
79
+ return renderLoadEarlierProp(props)
80
+
81
+ return <LoadEarlier {...props} />
82
+ }
83
+
84
+ return null
85
+ }, [loadEarlier, renderLoadEarlierProp, props])
86
+
87
+ const scrollTo = useCallback((options: { animated?: boolean, offset: number }) => {
88
+ if (forwardRef?.current && options)
89
+ forwardRef.current.scrollToOffset(options)
90
+ }, [forwardRef])
91
+
92
+ const doScrollToBottom = useCallback((animated: boolean = true) => {
93
+ if (inverted)
94
+ scrollTo({ offset: 0, animated })
95
+ else if (forwardRef?.current)
96
+ forwardRef.current.scrollToEnd({ animated })
97
+ }, [forwardRef, inverted, scrollTo])
98
+
99
+ const handleOnScroll = useCallback((event: ReanimatedScrollEvent) => {
100
+ handleOnScrollProp?.(event)
101
+
102
+ const {
103
+ contentOffset: { y: contentOffsetY },
104
+ contentSize: { height: contentSizeHeight },
105
+ layoutMeasurement: { height: layoutMeasurementHeight },
106
+ } = event
107
+
108
+ if (inverted)
109
+ if (contentOffsetY > scrollToBottomOffset!)
110
+ setIsScrollToBottomVisible(true)
111
+ else
112
+ setIsScrollToBottomVisible(false)
113
+ else if (
114
+ contentOffsetY < scrollToBottomOffset! &&
115
+ contentSizeHeight - layoutMeasurementHeight > scrollToBottomOffset!
116
+ )
117
+ setIsScrollToBottomVisible(true)
118
+ else
119
+ setIsScrollToBottomVisible(false)
120
+
121
+ setHasScrolled(true)
122
+ }, [handleOnScrollProp, inverted, scrollToBottomOffset])
123
+
124
+ const handleLayoutDayWrapper = useCallback((ref: unknown, id: string | number, createdAt: number) => {
125
+ setTimeout(() => { // do not delete "setTimeout". It's necessary for get correct layout.
126
+ const itemLayout = forwardRef?.current?.recyclerlistview_unsafe?.getLayout(messages.findIndex(m => m._id === id))
127
+
128
+ if (ref && itemLayout) {
129
+ daysPositions.value = {
130
+ ...daysPositions.value,
131
+ [id]: {
132
+ ...itemLayout,
133
+ createdAt,
134
+ },
135
+ }
136
+ } else if (daysPositions.value[id] != null) {
137
+ const nextDaysPositions = { ...daysPositions.value }
138
+ delete nextDaysPositions[id]
139
+ daysPositions.value = nextDaysPositions
140
+ }
141
+ }, 100)
142
+ }, [messages, daysPositions, forwardRef])
143
+
144
+ const renderItem = useCallback(({ item, index }: ListRenderItemInfo<unknown>): React.ReactElement | null => {
145
+ const messageItem = item as TMessage
146
+
147
+ if (!messageItem._id && messageItem._id !== 0)
148
+ warning('GiftedChat: `_id` is missing for message', JSON.stringify(item))
149
+
150
+ if (!messageItem.user) {
151
+ if (!messageItem.system)
152
+ warning(
153
+ 'GiftedChat: `user` is missing for message',
154
+ JSON.stringify(messageItem)
155
+ )
156
+
157
+ messageItem.user = { _id: 0 }
158
+ }
159
+
160
+ const { messages, ...restProps } = props
161
+
162
+ if (messages && user) {
163
+ const previousMessage =
164
+ (inverted ? messages[index + 1] : messages[index - 1]) || {}
165
+ const nextMessage =
166
+ (inverted ? messages[index - 1] : messages[index + 1]) || {}
167
+
168
+ const messageProps: ItemProps = {
169
+ ...restProps,
170
+ currentMessage: messageItem,
171
+ previousMessage,
172
+ nextMessage,
173
+ position: messageItem.user._id === user._id ? 'right' : 'left',
174
+ onRefDayWrapper: handleLayoutDayWrapper,
175
+ scrolledY,
176
+ daysPositions,
177
+ listHeight,
178
+ }
179
+
180
+ return (
181
+ <Item {...messageProps} />
182
+ )
183
+ }
184
+
185
+ return null
186
+ }, [props, inverted, handleLayoutDayWrapper, scrolledY, daysPositions, listHeight, user])
187
+
188
+ const renderChatEmpty = useCallback(() => {
189
+ if (renderChatEmptyProp)
190
+ return inverted
191
+ ? (
192
+ renderChatEmptyProp()
193
+ )
194
+ : (
195
+ <View style={[stylesCommon.fill, styles.emptyChatContainer]}>
196
+ {renderChatEmptyProp()}
197
+ </View>
198
+ )
199
+
200
+ return <View style={stylesCommon.fill} />
201
+ }, [inverted, renderChatEmptyProp])
202
+
203
+ const ListHeaderComponent = useMemo(() => {
204
+ const content = renderLoadEarlier()
205
+
206
+ if (!content)
207
+ return null
208
+
209
+ return (
210
+ <View style={stylesCommon.fill}>{content}</View>
211
+ )
212
+ }, [renderLoadEarlier])
213
+
214
+ const renderScrollBottomComponent = useCallback(() => {
215
+ if (scrollToBottomComponentProp)
216
+ return scrollToBottomComponentProp()
217
+
218
+ return <Text>{'V'}</Text>
219
+ }, [scrollToBottomComponentProp])
220
+
221
+ const renderScrollToBottomWrapper = useCallback(() => {
222
+ return (
223
+ <View style={[stylesCommon.centerItems, styles.scrollToBottomStyle, scrollToBottomStyle]}>
224
+ <TouchableOpacity
225
+ onPress={() => doScrollToBottom()}
226
+ hitSlop={{ top: 5, left: 5, right: 5, bottom: 5 }}
227
+ >
228
+ {renderScrollBottomComponent()}
229
+ </TouchableOpacity>
230
+ </View>
231
+ )
232
+ }, [scrollToBottomStyle, renderScrollBottomComponent, doScrollToBottom])
233
+
234
+ const onLayoutList = useCallback((event: LayoutChangeEvent) => {
235
+ listHeight.value = event.nativeEvent.layout.height
236
+
237
+ if (
238
+ !inverted &&
239
+ messages?.length
240
+ )
241
+ setTimeout(() => {
242
+ doScrollToBottom(false)
243
+ }, 500)
244
+
245
+ listViewProps?.onLayout?.(event)
246
+ }, [inverted, messages, doScrollToBottom, listHeight, listViewProps])
247
+
248
+ const onEndReached = useCallback(() => {
249
+ if (
250
+ infiniteScroll &&
251
+ hasScrolled &&
252
+ loadEarlier &&
253
+ onLoadEarlier &&
254
+ !isLoadingEarlier &&
255
+ Platform.OS !== 'web'
256
+ )
257
+ onLoadEarlier()
258
+ }, [hasScrolled, infiniteScroll, loadEarlier, onLoadEarlier, isLoadingEarlier])
259
+
260
+ const keyExtractor = useCallback((item: unknown) => (item as TMessage)._id.toString(), [])
261
+
262
+ const scrollHandler = useAnimatedScrollHandler({
263
+ onScroll: event => {
264
+ scrolledY.value = event.contentOffset.y
265
+
266
+ runOnJS(handleOnScroll)(event)
267
+ },
268
+ }, [handleOnScroll])
269
+
270
+ return (
271
+ <View
272
+ style={[
273
+ styles.contentContainerStyle,
274
+ alignTop ? styles.containerAlignTop : stylesCommon.fill,
275
+ ]}
276
+ >
277
+ <AnimatedFlashList
278
+ ref={forwardRef}
279
+ extraData={[extraData, isTyping]}
280
+ keyExtractor={keyExtractor}
281
+ automaticallyAdjustContentInsets={false}
282
+ inverted={inverted}
283
+ data={messages}
284
+ style={stylesCommon.fill}
285
+ renderItem={renderItem}
286
+ {...invertibleScrollViewProps}
287
+ ListEmptyComponent={renderChatEmpty}
288
+ ListFooterComponent={
289
+ inverted ? ListHeaderComponent : ListFooterComponent
290
+ }
291
+ ListHeaderComponent={
292
+ inverted ? ListFooterComponent : ListHeaderComponent
293
+ }
294
+ onScroll={scrollHandler}
295
+ scrollEventThrottle={16}
296
+ onEndReached={onEndReached}
297
+ onEndReachedThreshold={0.1}
298
+ estimatedItemSize={100}
299
+ {...listViewProps}
300
+ onLayout={onLayoutList}
301
+ />
302
+ {isScrollToBottomVisible && scrollToBottom
303
+ ? renderScrollToBottomWrapper()
304
+ : null}
305
+ <DayAnimated
306
+ scrolledY={scrolledY}
307
+ daysPositions={daysPositions}
308
+ listHeight={listHeight}
309
+ messages={messages}
310
+ isLoadingEarlier={isLoadingEarlier}
311
+ />
312
+ </View>
313
+ )
314
+ }
315
+
316
+ 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
+ scrollToBottom?: 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
 
@@ -152,7 +150,7 @@ export function MessageText<TMessage extends IMessage = IMessage> ({
152
150
  customTextStyle,
153
151
  ]}
154
152
  parse={[
155
- ...(parsePatterns ? parsePatterns(linkStyle as TextStyle) : []),
153
+ ...(parsePatterns ? parsePatterns(linkStyle as unknown as TextStyle) : []),
156
154
  { type: 'url', style: linkStyle, onPress: onUrlPress },
157
155
  { type: 'phone', style: linkStyle, onPress: onPhonePress },
158
156
  { type: 'email', style: linkStyle, onPress: onEmailPress },
@@ -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
  )