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.
- package/README.md +18 -17
- package/package.json +41 -35
- package/src/Bubble/index.tsx +15 -39
- package/src/Bubble/types.ts +5 -5
- package/src/Composer.tsx +19 -26
- package/src/Constant.ts +0 -1
- package/src/GiftedAvatar.tsx +29 -36
- package/src/GiftedChat/index.tsx +34 -65
- package/src/GiftedChat/types.ts +8 -53
- package/src/InputToolbar.tsx +25 -8
- package/src/LoadEarlier.tsx +19 -17
- package/src/MessageAudio.tsx +19 -7
- package/src/MessageContainer/components/DayAnimated/index.tsx +14 -9
- package/src/MessageContainer/components/Item/index.tsx +7 -1
- package/src/MessageContainer/index.tsx +104 -64
- package/src/MessageContainer/styles.ts +3 -2
- package/src/MessageContainer/types.ts +36 -14
- package/src/MessageImage.tsx +18 -6
- package/src/MessageText.tsx +88 -128
- package/src/MessageVideo.tsx +19 -7
- package/src/QuickReplies.tsx +17 -10
- package/src/Send.tsx +7 -1
- package/src/SystemMessage.tsx +12 -2
- package/src/Time.tsx +9 -2
- package/src/TypingIndicator/index.tsx +2 -1
- package/src/TypingIndicator/types.ts +3 -0
- package/src/__tests__/Actions.test.tsx +3 -4
- package/src/__tests__/Avatar.test.tsx +5 -6
- package/src/__tests__/Bubble.test.tsx +14 -19
- package/src/__tests__/Composer.test.tsx +3 -4
- package/src/__tests__/Day.test.tsx +5 -8
- package/src/__tests__/DayAnimated.test.tsx +52 -0
- package/src/__tests__/GiftedAvatar.test.tsx +3 -8
- package/src/__tests__/GiftedChat.test.tsx +37 -21
- package/src/__tests__/InputToolbar.test.tsx +3 -4
- package/src/__tests__/LoadEarlier.test.tsx +3 -4
- package/src/__tests__/Message.test.tsx +51 -58
- package/src/__tests__/MessageContainer.test.tsx +39 -5
- package/src/__tests__/MessageImage.test.tsx +12 -15
- package/src/__tests__/MessageText.test.tsx +7 -4
- package/src/__tests__/Send.test.tsx +7 -8
- package/src/__tests__/SystemMessage.test.tsx +12 -15
- package/src/__tests__/Time.test.tsx +5 -8
- package/src/__tests__/__snapshots__/Bubble.test.tsx.snap +48 -50
- package/src/__tests__/__snapshots__/Composer.test.tsx.snap +1 -2
- package/src/__tests__/__snapshots__/Constant.test.tsx.snap +0 -1
- package/src/__tests__/__snapshots__/DayAnimated.test.tsx.snap +5 -0
- package/src/__tests__/__snapshots__/GiftedChat.test.tsx.snap +25 -0
- package/src/__tests__/__snapshots__/InputToolbar.test.tsx.snap +2 -2
- package/src/__tests__/__snapshots__/Message.test.tsx.snap +146 -150
- package/src/__tests__/__snapshots__/MessageImage.test.tsx.snap +12 -10
- package/src/__tests__/__snapshots__/MessageText.test.tsx.snap +12 -8
- package/src/__tests__/__snapshots__/Send.test.tsx.snap +2 -0
- package/src/reanimatedCompat.ts +27 -0
- package/src/types.ts +4 -0
- package/src/utils.ts +77 -1
- package/lib/Actions.d.ts +0 -14
- package/lib/Actions.js +0 -57
- package/lib/Actions.js.map +0 -1
- package/lib/Avatar.d.ts +0 -18
- package/lib/Avatar.js +0 -93
- package/lib/Avatar.js.map +0 -1
- package/lib/Bubble/index.d.ts +0 -6
- package/lib/Bubble/index.js +0 -257
- package/lib/Bubble/index.js.map +0 -1
- package/lib/Bubble/styles.d.ts +0 -69
- package/lib/Bubble/styles.js +0 -72
- package/lib/Bubble/styles.js.map +0 -1
- package/lib/Bubble/types.d.ts +0 -47
- package/lib/Bubble/types.js +0 -2
- package/lib/Bubble/types.js.map +0 -1
- package/lib/Color.d.ts +0 -18
- package/lib/Color.js +0 -18
- package/lib/Color.js.map +0 -1
- package/lib/Composer.d.ts +0 -20
- package/lib/Composer.js +0 -60
- package/lib/Composer.js.map +0 -1
- package/lib/Constant.d.ts +0 -10
- package/lib/Constant.js +0 -17
- package/lib/Constant.js.map +0 -1
- package/lib/Day/index.d.ts +0 -4
- package/lib/Day/index.js +0 -39
- package/lib/Day/index.js.map +0 -1
- package/lib/Day/styles.d.ts +0 -20
- package/lib/Day/styles.js +0 -22
- package/lib/Day/styles.js.map +0 -1
- package/lib/Day/types.d.ts +0 -9
- package/lib/Day/types.js +0 -2
- package/lib/Day/types.js.map +0 -1
- package/lib/GiftedAvatar.d.ts +0 -11
- package/lib/GiftedAvatar.js +0 -104
- package/lib/GiftedAvatar.js.map +0 -1
- package/lib/GiftedChat/index.d.ts +0 -26
- package/lib/GiftedChat/index.js +0 -302
- package/lib/GiftedChat/index.js.map +0 -1
- package/lib/GiftedChat/styles.d.ts +0 -6
- package/lib/GiftedChat/styles.js +0 -7
- package/lib/GiftedChat/styles.js.map +0 -1
- package/lib/GiftedChat/types.d.ts +0 -117
- package/lib/GiftedChat/types.js +0 -2
- package/lib/GiftedChat/types.js.map +0 -1
- package/lib/GiftedChatContext.d.ts +0 -9
- package/lib/GiftedChatContext.js +0 -9
- package/lib/GiftedChatContext.js.map +0 -1
- package/lib/InputToolbar.d.ts +0 -23
- package/lib/InputToolbar.js +0 -56
- package/lib/InputToolbar.js.map +0 -1
- package/lib/LoadEarlier.d.ts +0 -14
- package/lib/LoadEarlier.js +0 -45
- package/lib/LoadEarlier.js.map +0 -1
- package/lib/Message/index.d.ts +0 -6
- package/lib/Message/index.js +0 -80
- package/lib/Message/index.js.map +0 -1
- package/lib/Message/styles.d.ts +0 -21
- package/lib/Message/styles.js +0 -22
- package/lib/Message/styles.js.map +0 -1
- package/lib/Message/types.d.ts +0 -22
- package/lib/Message/types.js +0 -2
- package/lib/Message/types.js.map +0 -1
- package/lib/MessageAudio.d.ts +0 -2
- package/lib/MessageAudio.js +0 -14
- package/lib/MessageAudio.js.map +0 -1
- package/lib/MessageContainer/components/DayAnimated/index.d.ts +0 -5
- package/lib/MessageContainer/components/DayAnimated/index.js +0 -85
- package/lib/MessageContainer/components/DayAnimated/index.js.map +0 -1
- package/lib/MessageContainer/components/DayAnimated/styles.d.ts +0 -11
- package/lib/MessageContainer/components/DayAnimated/styles.js +0 -12
- package/lib/MessageContainer/components/DayAnimated/styles.js.map +0 -1
- package/lib/MessageContainer/components/DayAnimated/types.d.ts +0 -17
- package/lib/MessageContainer/components/DayAnimated/types.js +0 -2
- package/lib/MessageContainer/components/DayAnimated/types.js.map +0 -1
- package/lib/MessageContainer/components/Item/index.d.ts +0 -23
- package/lib/MessageContainer/components/Item/index.js +0 -88
- package/lib/MessageContainer/components/Item/index.js.map +0 -1
- package/lib/MessageContainer/components/Item/types.d.ts +0 -17
- package/lib/MessageContainer/components/Item/types.js +0 -2
- package/lib/MessageContainer/components/Item/types.js.map +0 -1
- package/lib/MessageContainer/index.d.ts +0 -6
- package/lib/MessageContainer/index.js +0 -224
- package/lib/MessageContainer/index.js.map +0 -1
- package/lib/MessageContainer/styles.d.ts +0 -34
- package/lib/MessageContainer/styles.js +0 -31
- package/lib/MessageContainer/styles.js.map +0 -1
- package/lib/MessageContainer/types.d.ts +0 -54
- package/lib/MessageContainer/types.js +0 -2
- package/lib/MessageContainer/types.js.map +0 -1
- package/lib/MessageImage.d.ts +0 -13
- package/lib/MessageImage.js +0 -30
- package/lib/MessageImage.js.map +0 -1
- package/lib/MessageText.d.ts +0 -15
- package/lib/MessageText.js +0 -108
- package/lib/MessageText.js.map +0 -1
- package/lib/MessageVideo.d.ts +0 -2
- package/lib/MessageVideo.js +0 -14
- package/lib/MessageVideo.js.map +0 -1
- package/lib/QuickReplies.d.ts +0 -15
- package/lib/QuickReplies.js +0 -101
- package/lib/QuickReplies.js.map +0 -1
- package/lib/Send.d.ts +0 -15
- package/lib/Send.js +0 -34
- package/lib/Send.js.map +0 -1
- package/lib/SystemMessage.d.ts +0 -10
- package/lib/SystemMessage.js +0 -26
- package/lib/SystemMessage.js.map +0 -1
- package/lib/Time.d.ts +0 -11
- package/lib/Time.js +0 -56
- package/lib/Time.js.map +0 -1
- package/lib/TypingIndicator/index.d.ts +0 -5
- package/lib/TypingIndicator/index.js +0 -94
- package/lib/TypingIndicator/index.js.map +0 -1
- package/lib/TypingIndicator/styles.d.ts +0 -20
- package/lib/TypingIndicator/styles.js +0 -22
- package/lib/TypingIndicator/styles.js.map +0 -1
- package/lib/TypingIndicator/types.d.ts +0 -3
- package/lib/TypingIndicator/types.js +0 -2
- package/lib/TypingIndicator/types.js.map +0 -1
- package/lib/hooks/useUpdateLayoutEffect.d.ts +0 -8
- package/lib/hooks/useUpdateLayoutEffect.js +0 -17
- package/lib/hooks/useUpdateLayoutEffect.js.map +0 -1
- package/lib/index.d.ts +0 -4
- package/lib/index.js +0 -5
- package/lib/index.js.map +0 -1
- package/lib/logging.d.ts +0 -2
- package/lib/logging.js +0 -5
- package/lib/logging.js.map +0 -1
- package/lib/styles.d.ts +0 -10
- package/lib/styles.js +0 -11
- package/lib/styles.js.map +0 -1
- package/lib/types.d.ts +0 -67
- package/lib/types.js +0 -2
- package/lib/types.js.map +0 -1
- package/lib/utils.d.ts +0 -3
- package/lib/utils.js +0 -17
- package/lib/utils.js.map +0 -1
- 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
|
-
|
|
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 '
|
|
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
|
-
|
|
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
|
-
|
|
42
|
-
|
|
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
|
|
80
|
+
return renderFooterProp(props)
|
|
78
81
|
|
|
79
|
-
return
|
|
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 (
|
|
95
|
-
forwardRef
|
|
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
|
-
|
|
133
|
+
isScrollingDown.value =
|
|
134
|
+
(inverted && lastScrolledY.value > contentOffsetY) ||
|
|
135
|
+
(!inverted && lastScrolledY.value < contentOffsetY)
|
|
115
136
|
|
|
116
|
-
|
|
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
|
-
|
|
141
|
+
changeScrollToBottomVisibility(true)
|
|
131
142
|
else
|
|
132
|
-
|
|
143
|
+
changeScrollToBottomVisibility(false)
|
|
133
144
|
else if (
|
|
134
145
|
contentOffsetY < scrollToBottomOffset! &&
|
|
135
146
|
contentSizeHeight - layoutMeasurementHeight > scrollToBottomOffset!
|
|
136
147
|
)
|
|
137
|
-
|
|
148
|
+
changeScrollToBottomVisibility(false)
|
|
138
149
|
else
|
|
139
|
-
|
|
140
|
-
}, [handleOnScrollProp, inverted, scrollToBottomOffset,
|
|
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
|
-
}, [
|
|
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
|
-
|
|
211
|
+
emptyContent
|
|
190
212
|
)
|
|
191
213
|
: (
|
|
192
214
|
<View style={[stylesCommon.fill, styles.emptyChatContainer]}>
|
|
193
|
-
{
|
|
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
|
|
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
|
-
<
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
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
|
-
}, [
|
|
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
|
-
|
|
250
|
-
}, [inverted, messages, doScrollToBottom, listHeight,
|
|
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
|
-
|
|
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((
|
|
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[
|
|
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
|
-
{
|
|
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
|
|
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
|
-
{...
|
|
412
|
+
{...listProps}
|
|
372
413
|
onLayout={onLayoutList}
|
|
373
414
|
CellRendererComponent={renderCell}
|
|
374
415
|
/>
|
|
375
|
-
|
|
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
|
-
|
|
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, {
|
|
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 '
|
|
13
|
-
import {
|
|
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
|
|
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> =
|
|
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
|
-
|
|
25
|
+
/** User sending the messages: { _id, name, avatar } */
|
|
26
26
|
user?: User
|
|
27
|
-
|
|
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
|
-
|
|
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 {
|
package/src/MessageImage.tsx
CHANGED
|
@@ -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={
|
|
62
|
-
source={
|
|
73
|
+
style={computedImageStyle}
|
|
74
|
+
source={imageSource}
|
|
63
75
|
/>
|
|
64
76
|
</Lightbox>
|
|
65
77
|
</View>
|