react-native-gifted-chat 3.0.0-alpha.1 → 3.0.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 (31) hide show
  1. package/CHANGELOG.md +295 -0
  2. package/README.md +12 -30
  3. package/package.json +1 -1
  4. package/src/Actions.tsx +27 -18
  5. package/src/Composer.tsx +21 -84
  6. package/src/Constant.ts +0 -9
  7. package/src/GiftedChat/index.tsx +13 -38
  8. package/src/GiftedChat/types.ts +8 -6
  9. package/src/InputToolbar.tsx +6 -11
  10. package/src/MessageImage.tsx +94 -57
  11. package/src/{MessageContainer → MessagesContainer}/components/Item/index.tsx +21 -17
  12. package/src/{MessageContainer → MessagesContainer}/components/Item/types.ts +3 -2
  13. package/src/{MessageContainer → MessagesContainer}/index.tsx +16 -14
  14. package/src/{MessageContainer → MessagesContainer}/types.ts +4 -2
  15. package/src/Send.tsx +40 -22
  16. package/src/__tests__/DayAnimated.test.tsx +1 -1
  17. package/src/__tests__/{MessageContainer.test.tsx → MessagesContainer.test.tsx} +7 -7
  18. package/src/__tests__/__snapshots__/Actions.test.tsx.snap +31 -23
  19. package/src/__tests__/__snapshots__/Composer.test.tsx.snap +29 -30
  20. package/src/__tests__/__snapshots__/Constant.test.tsx.snap +0 -2
  21. package/src/__tests__/__snapshots__/InputToolbar.test.tsx.snap +102 -31
  22. package/src/__tests__/__snapshots__/MessageImage.test.tsx.snap +251 -0
  23. package/src/__tests__/__snapshots__/Send.test.tsx.snap +189 -49
  24. package/src/index.ts +1 -1
  25. package/src/styles.ts +5 -0
  26. package/src/types.ts +1 -1
  27. package/CHANGELOG_2.8.1_to_2.8.2-alpha.5.md +0 -374
  28. /package/src/{MessageContainer → MessagesContainer}/components/DayAnimated/index.tsx +0 -0
  29. /package/src/{MessageContainer → MessagesContainer}/components/DayAnimated/styles.ts +0 -0
  30. /package/src/{MessageContainer → MessagesContainer}/components/DayAnimated/types.ts +0 -0
  31. /package/src/{MessageContainer → MessagesContainer}/styles.ts +0 -0
@@ -21,10 +21,10 @@ import localizedFormat from 'dayjs/plugin/localizedFormat'
21
21
  import { GestureHandlerRootView } from 'react-native-gesture-handler'
22
22
  import { KeyboardAvoidingView, KeyboardProvider } from 'react-native-keyboard-controller'
23
23
  import { SafeAreaProvider } from 'react-native-safe-area-context'
24
- import { MAX_COMPOSER_HEIGHT, MIN_COMPOSER_HEIGHT, TEST_ID } from '../Constant'
24
+ import { TEST_ID } from '../Constant'
25
25
  import { GiftedChatContext } from '../GiftedChatContext'
26
26
  import { InputToolbar } from '../InputToolbar'
27
- import { MessageContainer, AnimatedList } from '../MessageContainer'
27
+ import { MessagesContainer, AnimatedList } from '../MessagesContainer'
28
28
  import { IMessage } from '../Models'
29
29
  import stylesCommon from '../styles'
30
30
  import { renderComponentOrElement } from '../utils'
@@ -55,15 +55,13 @@ function GiftedChat<TMessage extends IMessage = IMessage> (
55
55
  renderChatFooter,
56
56
  renderInputToolbar,
57
57
  isInverted = true,
58
- minComposerHeight = MIN_COMPOSER_HEIGHT,
59
- maxComposerHeight = MAX_COMPOSER_HEIGHT,
60
58
  } = props
61
59
 
62
60
  const actionSheetRef = useRef<ActionSheetProviderRef>(null)
63
61
 
64
- const messageContainerRef = useMemo(
65
- () => props.messageContainerRef || createRef<AnimatedList<TMessage>>(),
66
- [props.messageContainerRef]
62
+ const messagesContainerRef = useMemo(
63
+ () => props.messagesContainerRef || createRef<AnimatedList<TMessage>>(),
64
+ [props.messagesContainerRef]
67
65
  ) as RefObject<AnimatedList<TMessage>>
68
66
 
69
67
  const textInputRef = useMemo(
@@ -72,9 +70,6 @@ function GiftedChat<TMessage extends IMessage = IMessage> (
72
70
  )
73
71
 
74
72
  const [isInitialized, setIsInitialized] = useState<boolean>(false)
75
- const [composerHeight, setComposerHeight] = useState<number>(
76
- minComposerHeight!
77
- )
78
73
  const [text, setText] = useState<string | undefined>(() => props.text || '')
79
74
 
80
75
  const getTextFromProp = useCallback(
@@ -89,20 +84,20 @@ function GiftedChat<TMessage extends IMessage = IMessage> (
89
84
 
90
85
  const scrollToBottom = useCallback(
91
86
  (isAnimated = true) => {
92
- if (!messageContainerRef?.current)
87
+ if (!messagesContainerRef?.current)
93
88
  return
94
89
 
95
90
  if (isInverted) {
96
- messageContainerRef.current.scrollToOffset({
91
+ messagesContainerRef.current.scrollToOffset({
97
92
  offset: 0,
98
93
  animated: isAnimated,
99
94
  })
100
95
  return
101
96
  }
102
97
 
103
- messageContainerRef.current.scrollToEnd({ animated: isAnimated })
98
+ messagesContainerRef.current.scrollToEnd({ animated: isAnimated })
104
99
  },
105
- [isInverted, messageContainerRef]
100
+ [isInverted, messagesContainerRef]
106
101
  )
107
102
 
108
103
  const renderMessages = useMemo(() => {
@@ -113,11 +108,11 @@ function GiftedChat<TMessage extends IMessage = IMessage> (
113
108
 
114
109
  return (
115
110
  <View style={[stylesCommon.fill, messagesContainerStyle]}>
116
- <MessageContainer<TMessage>
111
+ <MessagesContainer<TMessage>
117
112
  {...messagesContainerProps}
118
113
  isInverted={isInverted}
119
114
  messages={messages}
120
- forwardRef={messageContainerRef}
115
+ forwardRef={messagesContainerRef}
121
116
  isTyping={isTyping}
122
117
  />
123
118
  {renderComponentOrElement(renderChatFooter, {})}
@@ -129,7 +124,7 @@ function GiftedChat<TMessage extends IMessage = IMessage> (
129
124
  messages,
130
125
  props,
131
126
  isInverted,
132
- messageContainerRef,
127
+ messagesContainerRef,
133
128
  renderChatFooter,
134
129
  ])
135
130
 
@@ -142,10 +137,8 @@ function GiftedChat<TMessage extends IMessage = IMessage> (
142
137
 
143
138
  notifyInputTextReset()
144
139
 
145
- setComposerHeight(minComposerHeight!)
146
140
  setText(getTextFromProp(''))
147
141
  }, [
148
- minComposerHeight,
149
142
  getTextFromProp,
150
143
  textInputRef,
151
144
  notifyInputTextReset,
@@ -175,18 +168,6 @@ function GiftedChat<TMessage extends IMessage = IMessage> (
175
168
  [messageIdGenerator, onSend, user, resetInputToolbar, scrollToBottom]
176
169
  )
177
170
 
178
- const onInputSizeChanged = useCallback(
179
- (size: { height: number }) => {
180
- const newComposerHeight = Math.max(
181
- minComposerHeight!,
182
- Math.min(maxComposerHeight!, size.height)
183
- )
184
-
185
- setComposerHeight(newComposerHeight)
186
- },
187
- [maxComposerHeight, minComposerHeight]
188
- )
189
-
190
171
  const _onChangeText = useCallback(
191
172
  (text: string) => {
192
173
  props.textInputProps?.onChangeText?.(text)
@@ -211,10 +192,9 @@ function GiftedChat<TMessage extends IMessage = IMessage> (
211
192
  notifyInputTextReset()
212
193
 
213
194
  setIsInitialized(true)
214
- setComposerHeight(minComposerHeight!)
215
195
  setText(getTextFromProp(initialText))
216
196
  },
217
- [isInitialized, initialText, minComposerHeight, notifyInputTextReset, getTextFromProp]
197
+ [isInitialized, initialText, notifyInputTextReset, getTextFromProp]
218
198
  )
219
199
 
220
200
  const inputToolbarFragment = useMemo(() => {
@@ -224,9 +204,7 @@ function GiftedChat<TMessage extends IMessage = IMessage> (
224
204
  const inputToolbarProps = {
225
205
  ...props,
226
206
  text: getTextFromProp(text!),
227
- composerHeight: Math.max(minComposerHeight!, composerHeight),
228
207
  onSend: _onSend,
229
- onInputSizeChanged,
230
208
  textInputProps: {
231
209
  ...textInputProps,
232
210
  onChangeText: _onChangeText,
@@ -242,12 +220,9 @@ function GiftedChat<TMessage extends IMessage = IMessage> (
242
220
  isInitialized,
243
221
  _onSend,
244
222
  getTextFromProp,
245
- minComposerHeight,
246
- onInputSizeChanged,
247
223
  props,
248
224
  text,
249
225
  renderInputToolbar,
250
- composerHeight,
251
226
  textInputRef,
252
227
  textInputProps,
253
228
  _onChangeText,
@@ -14,8 +14,8 @@ import { AvatarProps } from '../Avatar'
14
14
  import { BubbleProps } from '../Bubble'
15
15
  import { ComposerProps } from '../Composer'
16
16
  import { InputToolbarProps } from '../InputToolbar'
17
- import { AnimatedList, MessageContainerProps } from '../MessageContainer'
18
17
  import { MessageImageProps } from '../MessageImage'
18
+ import { AnimatedList, MessagesContainerProps } from '../MessagesContainer'
19
19
  import { MessageTextProps } from '../MessageText'
20
20
  import {
21
21
  IMessage,
@@ -29,9 +29,9 @@ import { SendProps } from '../Send'
29
29
  import { SystemMessageProps } from '../SystemMessage'
30
30
  import { TimeProps } from '../Time'
31
31
 
32
- export interface GiftedChatProps<TMessage extends IMessage> extends Partial<MessageContainerProps<TMessage>> {
33
- /* Message container ref */
34
- messageContainerRef?: RefObject<AnimatedList<TMessage>>
32
+ export interface GiftedChatProps<TMessage extends IMessage> extends Partial<MessagesContainerProps<TMessage>> {
33
+ /* Messages container ref */
34
+ messagesContainerRef?: RefObject<AnimatedList<TMessage>>
35
35
  /* text input ref */
36
36
  textInputRef?: RefObject<TextInput>
37
37
  /* Controls whether or not to show user.name property in the message bubble */
@@ -115,13 +115,13 @@ export interface GiftedChatProps<TMessage extends IMessage> extends Partial<Mess
115
115
  renderMessageImage?: (props: MessageImageProps<TMessage>) => React.ReactNode
116
116
  /* Custom message video */
117
117
  renderMessageVideo?: (props: MessageVideoProps<TMessage>) => React.ReactNode
118
- /* Custom message video */
118
+ /* Custom message audio */
119
119
  renderMessageAudio?: (props: MessageAudioProps<TMessage>) => React.ReactNode
120
120
  /* Custom view inside the bubble */
121
121
  renderCustomView?: (props: BubbleProps<TMessage>) => React.ReactNode
122
122
  /* Custom time inside a message */
123
123
  renderTime?: (props: TimeProps<TMessage>) => React.ReactNode
124
- /* Custom component to render below the MessageContainer (separate from the ListView) */
124
+ /* Custom component to render below the MessagesContainer */
125
125
  renderChatFooter?: () => React.ReactNode
126
126
  /* Custom message composer container. Can be a component, element, render function, or null */
127
127
  renderInputToolbar?: React.ComponentType<InputToolbarProps<TMessage>> | React.ReactElement | ((props: InputToolbarProps<TMessage>) => React.ReactNode) | null
@@ -143,4 +143,6 @@ export interface GiftedChatProps<TMessage extends IMessage> extends Partial<Mess
143
143
  renderQuickReplySend?: () => React.ReactNode
144
144
  keyboardProviderProps?: React.ComponentProps<typeof KeyboardProvider>
145
145
  keyboardAvoidingViewProps?: KeyboardAvoidingViewProps
146
+ /** Enable animated day label that appears on scroll; default is true */
147
+ isDayAnimationEnabled?: boolean
146
148
  }
@@ -6,6 +6,7 @@ import { Color } from './Color'
6
6
  import { Composer, ComposerProps } from './Composer'
7
7
  import { IMessage } from './Models'
8
8
  import { Send, SendProps } from './Send'
9
+ import { getColorSchemeStyle } from './styles'
9
10
  import { renderComponentOrElement } from './utils'
10
11
 
11
12
  export interface InputToolbarProps<TMessage extends IMessage> {
@@ -13,7 +14,6 @@ export interface InputToolbarProps<TMessage extends IMessage> {
13
14
  actionSheetOptionTintColor?: string
14
15
  containerStyle?: StyleProp<ViewStyle>
15
16
  primaryStyle?: StyleProp<ViewStyle>
16
- accessoryStyle?: StyleProp<ViewStyle>
17
17
  renderAccessory?: (props: InputToolbarProps<TMessage>) => React.ReactNode
18
18
  renderActions?: (props: ActionsProps) => React.ReactNode
19
19
  renderSend?: (props: SendProps<TMessage>) => React.ReactNode
@@ -88,16 +88,14 @@ export function InputToolbar<TMessage extends IMessage = IMessage> (
88
88
  if (!renderAccessory)
89
89
  return null
90
90
 
91
- return (
92
- <View style={[styles.accessory, props.accessoryStyle]}>
93
- {renderComponentOrElement(renderAccessory, props)}
94
- </View>
95
- )
91
+ return renderComponentOrElement(renderAccessory, props)
96
92
  }, [renderAccessory, props])
97
93
 
98
94
  return (
99
- <View style={[styles.container, colorScheme === 'dark' && styles.container_dark, containerStyle]}>
100
- <View style={[styles.primary, props.primaryStyle]}>
95
+ <View
96
+ style={[getColorSchemeStyle(styles, 'container', colorScheme), containerStyle]}
97
+ >
98
+ <View style={[getColorSchemeStyle(styles, 'primary', colorScheme), props.primaryStyle]}>
101
99
  {actionsFragment}
102
100
  {composerFragment}
103
101
  {sendFragment}
@@ -121,7 +119,4 @@ const styles = StyleSheet.create({
121
119
  flexDirection: 'row',
122
120
  alignItems: 'flex-end',
123
121
  },
124
- accessory: {
125
- height: 44,
126
- },
127
122
  })
@@ -19,14 +19,91 @@ import Animated, {
19
19
  useAnimatedStyle,
20
20
  useSharedValue,
21
21
  withTiming,
22
- runOnJS,
23
22
  Easing,
23
+ runOnJS,
24
24
  } from 'react-native-reanimated'
25
- import { useSafeAreaInsets } from 'react-native-safe-area-context'
25
+ import { SafeAreaProvider, useSafeAreaInsets } from 'react-native-safe-area-context'
26
26
  import Zoom from 'react-native-zoom-reanimated'
27
27
  import { IMessage } from './Models'
28
28
  import commonStyles from './styles'
29
29
 
30
+ interface ModalContentProps {
31
+ isVisible: boolean
32
+ imageSource: ImageURISource
33
+ modalImageDimensions: { width: number, height: number } | undefined
34
+ imageProps?: Partial<ImageProps>
35
+ onClose: () => void
36
+ }
37
+
38
+ function ModalContent({ isVisible, imageSource, modalImageDimensions, imageProps, onClose }: ModalContentProps) {
39
+ const insets = useSafeAreaInsets()
40
+
41
+ // Animation values
42
+ const modalOpacity = useSharedValue(0)
43
+ const modalScale = useSharedValue(0.9)
44
+ const modalBorderRadius = useSharedValue(40)
45
+
46
+ const handleModalClose = useCallback(() => {
47
+ modalOpacity.value = withTiming(0, { duration: 200, easing: Easing.in(Easing.ease) })
48
+ modalScale.value = withTiming(0.9, { duration: 200, easing: Easing.in(Easing.ease) }, () => {
49
+ runOnJS(onClose)()
50
+ })
51
+ modalBorderRadius.value = withTiming(40, { duration: 200, easing: Easing.in(Easing.ease) })
52
+ }, [onClose, modalOpacity, modalScale, modalBorderRadius])
53
+
54
+ // Animate on visibility change
55
+ useEffect(() => {
56
+ if (isVisible) {
57
+ modalOpacity.value = withTiming(1, { duration: 300, easing: Easing.out(Easing.ease) })
58
+ modalScale.value = withTiming(1, { duration: 300, easing: Easing.out(Easing.ease) })
59
+ modalBorderRadius.value = withTiming(0, { duration: 300, easing: Easing.out(Easing.ease) })
60
+ }
61
+ }, [isVisible, modalOpacity, modalScale, modalBorderRadius])
62
+
63
+ const modalAnimatedStyle = useAnimatedStyle(() => ({
64
+ opacity: modalOpacity.value,
65
+ transform: [{ scale: modalScale.value }],
66
+ }), [modalOpacity, modalScale])
67
+
68
+ const modalBorderRadiusStyle = useAnimatedStyle(() => ({
69
+ borderRadius: modalBorderRadius.value,
70
+ }), [modalBorderRadius])
71
+
72
+ return (
73
+ <>
74
+ <StatusBar animated barStyle='dark-content' />
75
+ <Animated.View style={[styles.modalOverlay, modalAnimatedStyle, modalBorderRadiusStyle]}>
76
+ <GestureHandlerRootView style={commonStyles.fill}>
77
+ <Animated.View style={[commonStyles.fill, styles.modalContent, modalBorderRadiusStyle, { paddingTop: insets.top, paddingBottom: insets.bottom }]}>
78
+
79
+ {/* close button */}
80
+ <View style={styles.closeButtonContainer}>
81
+ <BaseButton onPress={handleModalClose}>
82
+ <View style={styles.closeButtonContent}>
83
+ <Text style={styles.closeButtonIcon}>
84
+ {'X'}
85
+ </Text>
86
+ </View>
87
+ </BaseButton>
88
+ </View>
89
+
90
+ <View style={[commonStyles.fill, commonStyles.centerItems]}>
91
+ <Zoom>
92
+ <Image
93
+ style={modalImageDimensions}
94
+ source={imageSource}
95
+ resizeMode='contain'
96
+ {...imageProps}
97
+ />
98
+ </Zoom>
99
+ </View>
100
+ </Animated.View>
101
+ </GestureHandlerRootView>
102
+ </Animated.View>
103
+ </>
104
+ )
105
+ }
106
+
30
107
  export interface MessageImageProps<TMessage extends IMessage> {
31
108
  currentMessage: TMessage
32
109
  containerStyle?: StyleProp<ViewStyle>
@@ -46,12 +123,6 @@ export function MessageImage<TMessage extends IMessage = IMessage> ({
46
123
  const [imageDimensions, setImageDimensions] = useState<{ width: number, height: number }>()
47
124
  const windowDimensions = useWindowDimensions()
48
125
 
49
- const insets = useSafeAreaInsets()
50
-
51
- // Animation values
52
- const modalOpacity = useSharedValue(0)
53
- const modalScale = useSharedValue(0.9)
54
-
55
126
  const imageSource = useMemo(() => ({
56
127
  ...imageSourceProps,
57
128
  uri: currentMessage?.image,
@@ -70,23 +141,15 @@ export function MessageImage<TMessage extends IMessage = IMessage> ({
70
141
 
71
142
  setIsModalVisible(true)
72
143
 
73
- // Animate modal entrance
74
- modalOpacity.value = withTiming(1, { duration: 300, easing: Easing.out(Easing.ease) })
75
- modalScale.value = withTiming(1, { duration: 300, easing: Easing.out(Easing.ease) })
76
-
77
144
  if (isImageSourceChanged.current || !imageDimensions)
78
145
  Image.getSize(imageSource.uri, (width, height) => {
79
146
  setImageDimensions({ width, height })
80
147
  })
81
- }, [imageSource.uri, imageDimensions, modalOpacity, modalScale])
148
+ }, [imageSource.uri, imageDimensions])
82
149
 
83
150
  const handleModalClose = useCallback(() => {
84
- // Animate modal exit
85
- modalOpacity.value = withTiming(0, { duration: 200, easing: Easing.in(Easing.ease) })
86
- modalScale.value = withTiming(0.9, { duration: 200, easing: Easing.in(Easing.ease) }, () => {
87
- runOnJS(setIsModalVisible)(false)
88
- })
89
- }, [modalOpacity, modalScale])
151
+ setIsModalVisible(false)
152
+ }, [])
90
153
 
91
154
  const handleImageLayout = useCallback((e: LayoutChangeEvent) => {
92
155
  setImageDimensions({
@@ -115,11 +178,6 @@ export function MessageImage<TMessage extends IMessage = IMessage> ({
115
178
  }
116
179
  }, [imageDimensions, windowDimensions.height, windowDimensions.width])
117
180
 
118
- const modalAnimatedStyle = useAnimatedStyle(() => ({
119
- opacity: modalOpacity.value,
120
- transform: [{ scale: modalScale.value }],
121
- }), [modalOpacity, modalScale])
122
-
123
181
  useEffect(() => {
124
182
  isImageSourceChanged.current = true
125
183
  }, [imageSource.uri])
@@ -139,39 +197,17 @@ export function MessageImage<TMessage extends IMessage = IMessage> ({
139
197
  />
140
198
  </TouchableOpacity>
141
199
 
142
- {isModalVisible && (
143
- <OverKeyboardView visible={isModalVisible}>
144
- <StatusBar animated barStyle='dark-content' />
145
- <Animated.View style={[styles.modalOverlay, modalAnimatedStyle]}>
146
- <GestureHandlerRootView style={commonStyles.fill}>
147
- <View style={[commonStyles.fill, styles.modalContent, { paddingTop: insets.top, paddingBottom: insets.bottom }]}>
148
-
149
- {/* close button */}
150
- <View style={styles.closeButtonContainer}>
151
- <BaseButton onPress={handleModalClose}>
152
- <View style={styles.closeButtonContent}>
153
- <Text style={styles.closeButtonIcon}>
154
- {'X'}
155
- </Text>
156
- </View>
157
- </BaseButton>
158
- </View>
159
-
160
- <View style={[commonStyles.fill, commonStyles.centerItems]}>
161
- <Zoom>
162
- <Image
163
- style={modalImageDimensions}
164
- source={imageSource}
165
- resizeMode='contain'
166
- {...imageProps}
167
- />
168
- </Zoom>
169
- </View>
170
- </View>
171
- </GestureHandlerRootView>
172
- </Animated.View>
173
- </OverKeyboardView>
174
- )}
200
+ <OverKeyboardView visible={isModalVisible}>
201
+ <SafeAreaProvider>
202
+ <ModalContent
203
+ isVisible={isModalVisible}
204
+ imageSource={imageSource}
205
+ modalImageDimensions={modalImageDimensions}
206
+ imageProps={imageProps}
207
+ onClose={handleModalClose}
208
+ />
209
+ </SafeAreaProvider>
210
+ </OverKeyboardView>
175
211
  </View>
176
212
  )
177
213
  }
@@ -193,6 +229,7 @@ const styles = StyleSheet.create({
193
229
  },
194
230
  modalContent: {
195
231
  backgroundColor: '#000',
232
+ overflow: 'hidden',
196
233
  },
197
234
  modalImageContainer: {
198
235
  width: '100%',
@@ -103,6 +103,7 @@ export const Item = <TMessage extends IMessage>(props: ItemProps<TMessage>) => {
103
103
  scrolledY,
104
104
  daysPositions,
105
105
  listHeight,
106
+ isDayAnimationEnabled,
106
107
  ...rest
107
108
  } = props
108
109
 
@@ -121,23 +122,26 @@ export const Item = <TMessage extends IMessage>(props: ItemProps<TMessage>) => {
121
122
  }, [dayContainerHeight])
122
123
 
123
124
  const style = useAnimatedStyle(() => ({
124
- opacity: interpolate(
125
- relativeScrolledPositionToBottomOfDay.value,
126
- [
127
- -dayTopOffset,
128
- -0.0001,
129
- 0,
130
- dayContainerHeight.value + dayTopOffset,
131
- ],
132
- [
133
- 0,
134
- 0,
135
- 1,
136
- 1,
137
- ],
138
- 'clamp'
139
- ),
140
- }), [relativeScrolledPositionToBottomOfDay, dayContainerHeight, dayTopOffset])
125
+ opacity:
126
+ isDayAnimationEnabled
127
+ ? interpolate(
128
+ relativeScrolledPositionToBottomOfDay.value,
129
+ [
130
+ -dayTopOffset,
131
+ -0.0001,
132
+ 0,
133
+ dayContainerHeight.value + dayTopOffset,
134
+ ],
135
+ [
136
+ 0,
137
+ 0,
138
+ 1,
139
+ 1,
140
+ ],
141
+ 'clamp'
142
+ )
143
+ : 1,
144
+ }), [relativeScrolledPositionToBottomOfDay, dayContainerHeight, dayTopOffset, isDayAnimationEnabled])
141
145
 
142
146
  return (
143
147
  // do not remove key. it helps to get correct position of the day container
@@ -1,7 +1,7 @@
1
1
  import { IMessage } from '../../../Models'
2
- import { MessageContainerProps, DaysPositions } from '../../types'
2
+ import { MessagesContainerProps, DaysPositions } from '../../types'
3
3
 
4
- export interface ItemProps<TMessage extends IMessage> extends MessageContainerProps<TMessage> {
4
+ export interface ItemProps<TMessage extends IMessage> extends MessagesContainerProps<TMessage> {
5
5
  currentMessage: TMessage
6
6
  previousMessage?: TMessage
7
7
  nextMessage?: TMessage
@@ -9,4 +9,5 @@ export interface ItemProps<TMessage extends IMessage> extends MessageContainerPr
9
9
  scrolledY: { value: number }
10
10
  daysPositions: { value: DaysPositions }
11
11
  listHeight: { value: number }
12
+ isDayAnimationEnabled?: boolean
12
13
  }
@@ -1,13 +1,12 @@
1
1
  import React, { useCallback, useEffect, useMemo, useState } from 'react'
2
2
  import {
3
3
  View,
4
- Pressable,
5
4
  Text,
6
5
  LayoutChangeEvent,
7
6
  ListRenderItemInfo,
8
7
  CellRendererProps,
9
8
  } from 'react-native'
10
- import { FlatList } from 'react-native-gesture-handler'
9
+ import { FlatList, Pressable } from 'react-native-gesture-handler'
11
10
  import Animated, { runOnJS, useAnimatedScrollHandler, useAnimatedStyle, useSharedValue, withTiming } from 'react-native-reanimated'
12
11
  import { LoadEarlierMessages } from '../LoadEarlierMessages'
13
12
  import { warning } from '../logging'
@@ -21,14 +20,13 @@ import { DayAnimated } from './components/DayAnimated'
21
20
  import { Item } from './components/Item'
22
21
  import { ItemProps } from './components/Item/types'
23
22
  import styles from './styles'
24
- import { MessageContainerProps, DaysPositions } from './types'
23
+ import { MessagesContainerProps, DaysPositions } from './types'
25
24
 
26
25
  export * from './types'
27
26
 
28
-
29
27
  const AnimatedFlatList = Animated.createAnimatedComponent(FlatList) as React.ComponentType<any>
30
28
 
31
- export const MessageContainer = <TMessage extends IMessage>(props: MessageContainerProps<TMessage>) => {
29
+ export const MessagesContainer = <TMessage extends IMessage>(props: MessagesContainerProps<TMessage>) => {
32
30
  const {
33
31
  messages = [],
34
32
  user,
@@ -47,6 +45,7 @@ export const MessageContainer = <TMessage extends IMessage>(props: MessageContai
47
45
  forwardRef,
48
46
  scrollToBottomComponent: scrollToBottomComponentProp,
49
47
  renderDay: renderDayProp,
48
+ isDayAnimationEnabled = true,
50
49
  } = props
51
50
 
52
51
  const listPropsOnScrollProp = listProps?.onScroll
@@ -182,6 +181,7 @@ export const MessageContainer = <TMessage extends IMessage>(props: MessageContai
182
181
  scrolledY,
183
182
  daysPositions,
184
183
  listHeight,
184
+ isDayAnimationEnabled,
185
185
  }
186
186
 
187
187
  return (
@@ -190,7 +190,7 @@ export const MessageContainer = <TMessage extends IMessage>(props: MessageContai
190
190
  }
191
191
 
192
192
  return null
193
- }, [messages, restProps, isInverted, scrolledY, daysPositions, listHeight, user])
193
+ }, [messages, restProps, isInverted, scrolledY, daysPositions, listHeight, isDayAnimationEnabled, user])
194
194
 
195
195
  const emptyContent = useMemo(() => {
196
196
  if (!renderChatEmptyProp)
@@ -409,14 +409,16 @@ export const MessageContainer = <TMessage extends IMessage>(props: MessageContai
409
409
  CellRendererComponent={renderCell}
410
410
  />
411
411
  <ScrollToBottomWrapper />
412
- <DayAnimated
413
- scrolledY={scrolledY}
414
- daysPositions={daysPositions}
415
- listHeight={listHeight}
416
- renderDay={renderDayProp}
417
- messages={messages}
418
- isLoading={loadEarlierMessagesProps?.isLoading ?? false}
419
- />
412
+ {isDayAnimationEnabled && (
413
+ <DayAnimated
414
+ scrolledY={scrolledY}
415
+ daysPositions={daysPositions}
416
+ listHeight={listHeight}
417
+ renderDay={renderDayProp}
418
+ messages={messages}
419
+ isLoading={loadEarlierMessagesProps?.isLoading ?? false}
420
+ />
421
+ )}
420
422
  </View>
421
423
  )
422
424
  }
@@ -16,7 +16,7 @@ export type ListProps<TMessage extends IMessage = IMessage> = Partial<FlatListPr
16
16
 
17
17
  export type AnimatedList<TMessage> = FlatList<TMessage>
18
18
 
19
- export interface MessageContainerProps<TMessage extends IMessage = IMessage>
19
+ export interface MessagesContainerProps<TMessage extends IMessage = IMessage>
20
20
  extends Omit<TypingIndicatorProps, 'style'> {
21
21
  /** Ref for the FlatList message container */
22
22
  forwardRef?: RefObject<AnimatedList<TMessage>>
@@ -39,7 +39,7 @@ export interface MessageContainerProps<TMessage extends IMessage = IMessage>
39
39
  /** Custom component to render when messages are empty */
40
40
  renderChatEmpty?: () => React.ReactNode
41
41
  /** Custom footer component on the ListView, e.g. 'User is typing...' */
42
- renderFooter?: (props: MessageContainerProps<TMessage>) => React.ReactNode
42
+ renderFooter?: (props: MessagesContainerProps<TMessage>) => React.ReactNode
43
43
  /** Custom message container */
44
44
  renderMessage?: (props: MessageProps<TMessage>) => React.ReactElement
45
45
  /** Custom day above a message */
@@ -56,6 +56,8 @@ export interface MessageContainerProps<TMessage extends IMessage = IMessage>
56
56
  loadEarlierMessagesProps?: LoadEarlierMessagesProps
57
57
  /** Style for TypingIndicator component */
58
58
  typingIndicatorStyle?: StyleProp<ViewStyle>
59
+ /** Enable animated day label that appears on scroll; default is true */
60
+ isDayAnimationEnabled?: boolean
59
61
  }
60
62
 
61
63
  export interface State {