react-native-gifted-chat 2.8.2-alpha.2 → 2.8.2-alpha.4

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 CHANGED
@@ -26,10 +26,7 @@
26
26
  </p>
27
27
 
28
28
  <p align="center">
29
- <a href="https://reverent-bardeen-47c862.netlify.com/" target="_blank">Demo Web 🖥</a>
30
- </p>
31
- <p align="center">
32
- <a href="https://snack.expo.io/@xcarpentier/giftedchat-playground" target="_blank">Snack GiftedChat playground</a>
29
+ <a href="https://snack.expo.dev/@kesha-antonov/gifted-chat-playground" target="_blank">Snack GiftedChat playground</a>
33
30
  <img height="18" src="https://snack.expo.io/favicon.ico" />
34
31
  </p>
35
32
 
@@ -358,10 +355,12 @@ interface QuickReplies {
358
355
  - **`messageContainerRef`** _(FlatList ref)_ - Ref to the flatlist
359
356
  - **`textInputRef`** _(TextInput ref)_ - Ref to the text input
360
357
  - **`messages`** _(Array)_ - Messages to display
358
+ - **`messagesContainerStyle`** _(Object)_ - Custom style for the messages container
361
359
  - **`isTyping`** _(Bool)_ - Typing Indicator state; default `false`. If you use`renderFooter` it will override this.
362
360
  - **`keyboardBottomOffset`** _(Integer)_ - Distance between the bottom of the screen and bottom of the `GiftedChat` component. Useful when you have a tab bar or navigation bar; default is `0`. Needed for correct keyboard avoiding behavior. Without it you might see gap between the keyboard and the input toolbar if you have a tab bar, navigation bar, or safe area.
363
361
  - **`isKeyboardInternallyHandled`** _(Bool)_ - Determine whether to handle keyboard awareness inside the plugin. If you have your own keyboard handling outside the plugin set this to false; default is `true`
364
362
  - **`text`** _(String)_ - Input text; default is `undefined`, but if specified, it will override GiftedChat's internal state (e.g. for redux; [see notes below](#notes-for-redux))
363
+ - **`initialText`** _(String)_ - Initial text to display in the input field
365
364
  - **`onInputTextChanged`** _(Function)_ - Callback when the input text changes
366
365
  - **`messageIdGenerator`** _(Function)_ - Generate an id for new messages. Defaults to UUID v4, generated by [uuid](https://github.com/kelektiv/node-uuid)
367
366
  - **`user`** _(Object)_ - User sending the messages: `{ _id, name, avatar }`
@@ -371,9 +370,18 @@ interface QuickReplies {
371
370
  - **`timeFormat`** _(String)_ - Format to use for rendering times; default is `'LT'` (see [Day.js Format](https://day.js.org/docs/en/display/format))
372
371
  - **`dateFormat`** _(String)_ - Format to use for rendering dates; default is `'D MMMM'` (see [Day.js Format](https://day.js.org/docs/en/display/format))
373
372
  - **`dateFormatCalendar`** _(Object)_ - Format to use for rendering relative times; default is `{ sameDay: '[Today]' }` (see [Day.js Calendar](https://day.js.org/docs/en/plugin/calendar))
374
- - **`loadEarlier`** _(Bool)_ - Enables the "load earlier messages" button, required for `infiniteScroll`
375
- - **`onLoadEarlier`** _(Function)_ - Callback when loading earlier messages
376
- - **`isLoadingEarlier`** _(Bool)_ - Display an `ActivityIndicator` when loading earlier messages
373
+ - **`loadEarlierMessagesProps`** _(Object)_ - Props to pass to the LoadEarlierMessages component. The button is only visible when `isAvailable` is `true`. Supports the following props:
374
+ - `isAvailable` - Controls button visibility (default: false)
375
+ - `onPress` - Callback when button is pressed
376
+ - `isLoading` - Display loading indicator (default: false)
377
+ - `isInfiniteScrollEnabled` - Enable infinite scroll up when reaching the top of messages container, automatically calls `onPress` (not yet supported for web)
378
+ - `label` - Override the default "Load earlier messages" text
379
+ - `containerStyle` - Custom style for the button container
380
+ - `wrapperStyle` - Custom style for the button wrapper
381
+ - `textStyle` - Custom style for the button text
382
+ - `activityIndicatorStyle` - Custom style for the loading indicator
383
+ - `activityIndicatorColor` - Color of the loading indicator (default: 'white')
384
+ - `activityIndicatorSize` - Size of the loading indicator (default: 'small')
377
385
  - **`renderLoading`** _(Function)_ - Render a loading view when initializing
378
386
  - **`renderLoadEarlier`** _(Function)_ - Custom "Load earlier messages" button
379
387
  - **`renderAvatar`** _(Function)_ - Custom message avatar; set to `null` to not render any avatar for the message
@@ -394,13 +402,17 @@ interface QuickReplies {
394
402
  - **`renderMessageText`** _(Function)_ - Custom message text
395
403
  - **`renderMessageImage`** _(Function)_ - Custom message image
396
404
  - **`renderMessageVideo`** _(Function)_ - Custom message video
405
+ - **`renderMessageAudio`** _(Function)_ - Custom message audio
397
406
  - **`imageProps`** _(Object)_ - Extra props to be passed to the [`<Image>`](https://reactnative.dev/docs/image.html) component created by the default `renderMessageImage`
407
+ - **`imageStyle`** _(Object)_ - Custom style for message images
398
408
  - **`videoProps`** _(Object)_ - Extra props to be passed to the video component created by the required `renderMessageVideo`
399
409
  - **`isCustomViewBottom`** _(Bool)_ - Determine whether renderCustomView is displayed before or after the text, image and video views; default is `false`
400
410
  - **`renderCustomView`** _(Function)_ - Custom view inside the bubble
401
411
  - **`renderDay`** _(Function)_ - Custom day above a message
402
412
  - **`renderTime`** _(Function)_ - Custom time inside a message
413
+ - **`timeTextStyle`** _(Object)_ - Custom text style for time inside messages (supports left/right styles)
403
414
  - **`renderFooter`** _(Function)_ - Custom footer component on the ListView, e.g. `'User is typing...'`; see [App.tsx](/example/App.tsx) for an example. Overrides default typing indicator that triggers when `isTyping` is true.
415
+ - **`renderTypingIndicator`** _(Function)_ - Custom typing indicator component
404
416
  - **`renderChatEmpty`** _(Function)_ - Custom component to render in the ListView when messages are empty
405
417
  - **`renderChatFooter`** _(Function)_ - Custom component to render below the MessageContainer (separate from the ListView)
406
418
  - **`renderInputToolbar`** _(Function)_ - Custom message composer container
@@ -409,6 +421,9 @@ interface QuickReplies {
409
421
  - **`renderSend`** _(Function)_ - Custom send button; you can pass children to the original `Send` component quite easily, for example, to use a custom icon ([example](https://github.com/FaridSafi/react-native-gifted-chat/pull/487))
410
422
  - **`renderAccessory`** _(Function)_ - Custom second line of actions below the message composer
411
423
  - **`onPressActionButton`** _(Function)_ - Callback when the Action button is pressed (if set, the default `actionSheet` will not be used)
424
+ - **`actionSheet`** _(Function)_ - Custom action sheet interface for showing action options
425
+ - **`actions`** _(Array)_ - Custom action options for the input toolbar action button; array of objects with `title` (string) and `action` (function) properties
426
+ - **`actionSheetOptionTintColor`** _(String)_ - Tint color for action sheet options
412
427
  - **`focusOnInputWhenOpeningKeyboard`** _(Bool)_ - Focus on <TextInput> automatically when opening the keyboard; default `true`
413
428
  - **`minInputToolbarHeight`** _(Integer)_ - Minimum height of the input toolbar; default is `44`
414
429
  - **`listProps`** _(Object)_ - Extra props to be passed to the messages [`<FlatList>`](https://reactnative.dev/docs/flatlist.html); some props can't be overridden, see the code in `MessageContainer.render()` for details
@@ -457,10 +472,12 @@ Example:
457
472
  * **`onQuickReply`** _(Function)_ - Callback when sending a quick reply (to backend server)
458
473
  * **`renderQuickReplies`** _(Function)_ - Custom all quick reply view
459
474
  * **`quickReplyStyle`** _(StyleProp<ViewStyle>)_ - Custom quick reply view style
475
+ * **`quickReplyTextStyle`** _(StyleProp<TextStyle>)_ - Custom text style for quick reply buttons
476
+ * **`quickReplyContainerStyle`** _(StyleProp<ViewStyle>)_ - Custom container style for quick replies
460
477
  * **`renderQuickReplySend`** _(Function)_ - Custom quick reply **send** view
461
478
  * **`shouldUpdateMessage`** _(Function)_ - Lets the message component know when to update outside of normal cases.
462
- * **`infiniteScroll`** _(Bool)_ - infinite scroll up when reach the top of messages container, automatically call onLoadEarlier function if exist (not yet supported for the web). You need to add `loadEarlier` prop too.
463
479
  * **`typingIndicatorStyle`** _(StyleProp<ViewStyle>)_ - Custom style for the TypingIndicator component.
480
+ * **`handleOnScroll`** _(Function)_ - Custom scroll event handler for the message list
464
481
 
465
482
  ## Notes for [Redux](https://github.com/reactjs/redux)
466
483
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-gifted-chat",
3
- "version": "2.8.2-alpha.2",
3
+ "version": "2.8.2-alpha.4",
4
4
  "description": "The most complete chat UI for React Native",
5
5
  "keywords": [
6
6
  "android",
package/src/Actions.tsx CHANGED
@@ -14,8 +14,8 @@ import { useChatContext } from './GiftedChatContext'
14
14
  import stylesCommon from './styles'
15
15
 
16
16
  export interface ActionsProps {
17
- options?: { [key: string]: () => void }
18
- optionTintColor?: string
17
+ actions?: Array<{ title: string, action: () => void }>
18
+ actionSheetOptionTintColor?: string
19
19
  icon?: () => ReactNode
20
20
  wrapperStyle?: StyleProp<ViewStyle>
21
21
  iconTextStyle?: StyleProp<TextStyle>
@@ -24,8 +24,8 @@ export interface ActionsProps {
24
24
  }
25
25
 
26
26
  export function Actions ({
27
- options,
28
- optionTintColor = Color.optionTintColor,
27
+ actions,
28
+ actionSheetOptionTintColor = Color.optionTintColor,
29
29
  icon,
30
30
  wrapperStyle,
31
31
  iconTextStyle,
@@ -35,28 +35,26 @@ export function Actions ({
35
35
  const { actionSheet } = useChatContext()
36
36
 
37
37
  const onActionsPress = useCallback(() => {
38
- if (!options)
38
+ if (!actions?.length)
39
39
  return
40
40
 
41
- const optionKeys = Object.keys(options)
42
- const cancelButtonIndex = optionKeys.indexOf('Cancel')
41
+ const titles = actions.map(item => item.title)
43
42
 
44
43
  actionSheet().showActionSheetWithOptions(
45
44
  {
46
- options: optionKeys,
47
- cancelButtonIndex,
48
- tintColor: optionTintColor,
45
+ options: titles,
46
+ cancelButtonIndex: titles.length - 1,
47
+ tintColor: actionSheetOptionTintColor,
49
48
  },
50
- (buttonIndex: number | undefined) => {
49
+ (buttonIndex?: number) => {
51
50
  if (buttonIndex === undefined)
52
51
  return
53
52
 
54
- const key = optionKeys[buttonIndex]
55
- if (key)
56
- options[key]()
53
+ const item = actions[buttonIndex]
54
+ item.action?.()
57
55
  }
58
56
  )
59
- }, [actionSheet, options, optionTintColor])
57
+ }, [actionSheet, actions, actionSheetOptionTintColor])
60
58
 
61
59
  const renderIcon = useCallback(() => {
62
60
  if (icon)
@@ -27,6 +27,7 @@ import Animated, {
27
27
  withTiming,
28
28
  runOnJS,
29
29
  } from 'react-native-reanimated'
30
+ import { SafeAreaProvider } from 'react-native-safe-area-context'
30
31
  import { Actions } from '../Actions'
31
32
  import { Avatar } from '../Avatar'
32
33
  import Bubble from '../Bubble'
@@ -36,7 +37,7 @@ import { Day } from '../Day'
36
37
  import { GiftedAvatar } from '../GiftedAvatar'
37
38
  import { GiftedChatContext } from '../GiftedChatContext'
38
39
  import { InputToolbar } from '../InputToolbar'
39
- import { LoadEarlier } from '../LoadEarlier'
40
+ import { LoadEarlierMessages } from '../LoadEarlierMessages'
40
41
  import Message from '../Message'
41
42
  import MessageContainer, { AnimatedList } from '../MessageContainer'
42
43
  import { MessageImage } from '../MessageImage'
@@ -45,10 +46,10 @@ import { Send } from '../Send'
45
46
  import stylesCommon from '../styles'
46
47
  import { SystemMessage } from '../SystemMessage'
47
48
  import { Time } from '../Time'
49
+
48
50
  import {
49
51
  IMessage,
50
52
  } from '../types'
51
-
52
53
  import * as utils from '../utils'
53
54
  import styles from './styles'
54
55
  import { GiftedChatProps } from './types'
@@ -421,9 +422,11 @@ function GiftedChatWrapper<TMessage extends IMessage = IMessage> (props: GiftedC
421
422
 
422
423
  return (
423
424
  <GestureHandlerRootView style={styles.fill}>
424
- <KeyboardProvider>
425
- <GiftedChat<TMessage> {...props} />
426
- </KeyboardProvider>
425
+ <SafeAreaProvider>
426
+ <KeyboardProvider>
427
+ <GiftedChat<TMessage> {...props} />
428
+ </KeyboardProvider>
429
+ </SafeAreaProvider>
427
430
  </GestureHandlerRootView>
428
431
  )
429
432
  }
@@ -467,7 +470,7 @@ export {
467
470
  Composer,
468
471
  Day,
469
472
  InputToolbar,
470
- LoadEarlier,
473
+ LoadEarlierMessages,
471
474
  Message,
472
475
  MessageContainer,
473
476
  Send,
@@ -77,8 +77,8 @@ export interface GiftedChatProps<TMessage extends IMessage> extends Partial<Mess
77
77
  minComposerHeight?: number
78
78
  /* composer min Height */
79
79
  maxComposerHeight?: number
80
- options?: { [key: string]: () => void }
81
- optionTintColor?: string
80
+ actions?: Array<{ title: string, action: () => void }>
81
+ actionSheetOptionTintColor?: string
82
82
  quickReplyStyle?: StyleProp<ViewStyle>
83
83
  quickReplyTextStyle?: StyleProp<TextStyle>
84
84
  quickReplyContainerStyle?: StyleProp<ViewStyle>
@@ -8,8 +8,8 @@ import { Send, SendProps } from './Send'
8
8
  import { IMessage } from './types'
9
9
 
10
10
  export interface InputToolbarProps<TMessage extends IMessage> {
11
- options?: { [key: string]: () => void }
12
- optionTintColor?: string
11
+ actions?: Array<{ title: string, action: () => void }>
12
+ actionSheetOptionTintColor?: string
13
13
  containerStyle?: StyleProp<ViewStyle>
14
14
  primaryStyle?: StyleProp<ViewStyle>
15
15
  accessoryStyle?: StyleProp<ViewStyle>
@@ -31,8 +31,8 @@ export function InputToolbar<TMessage extends IMessage = IMessage> (
31
31
  renderComposer,
32
32
  renderSend,
33
33
  renderAccessory,
34
- options,
35
- optionTintColor,
34
+ actions,
35
+ actionSheetOptionTintColor,
36
36
  icon,
37
37
  wrapperStyle,
38
38
  containerStyle,
@@ -43,8 +43,8 @@ export function InputToolbar<TMessage extends IMessage = IMessage> (
43
43
  const actionsFragment = useMemo(() => {
44
44
  const props = {
45
45
  onPressActionButton,
46
- options,
47
- optionTintColor,
46
+ actions,
47
+ actionSheetOptionTintColor,
48
48
  icon,
49
49
  wrapperStyle,
50
50
  containerStyle,
@@ -56,8 +56,8 @@ export function InputToolbar<TMessage extends IMessage = IMessage> (
56
56
  }, [
57
57
  renderActions,
58
58
  onPressActionButton,
59
- options,
60
- optionTintColor,
59
+ actions,
60
+ actionSheetOptionTintColor,
61
61
  icon,
62
62
  wrapperStyle,
63
63
  containerStyle,
@@ -40,7 +40,7 @@ const styles = StyleSheet.create({
40
40
  },
41
41
  })
42
42
 
43
- export interface LoadEarlierProps {
43
+ export interface LoadEarlierMessagesProps {
44
44
  isLoadingEarlier?: boolean
45
45
  label?: string
46
46
  containerStyle?: StyleProp<ViewStyle>
@@ -49,12 +49,12 @@ export interface LoadEarlierProps {
49
49
  activityIndicatorStyle?: StyleProp<ViewStyle>
50
50
  activityIndicatorColor?: string
51
51
  activityIndicatorSize?: number | 'small' | 'large'
52
- onLoadEarlier?(): void
52
+ onPress: () => void
53
53
  }
54
54
 
55
- export const LoadEarlier: React.FC<LoadEarlierProps> = ({
55
+ export const LoadEarlierMessages: React.FC<LoadEarlierMessagesProps> = ({
56
56
  isLoadingEarlier = false,
57
- onLoadEarlier = () => {},
57
+ onPress,
58
58
  label = 'Load earlier messages',
59
59
  containerStyle,
60
60
  wrapperStyle,
@@ -83,7 +83,7 @@ export const LoadEarlier: React.FC<LoadEarlierProps> = ({
83
83
  return (
84
84
  <TouchableOpacity
85
85
  style={[styles.container, containerStyle]}
86
- onPress={onLoadEarlier}
86
+ onPress={onPress}
87
87
  disabled={isLoadingEarlier}
88
88
  accessibilityRole='button'
89
89
  >
@@ -0,0 +1,97 @@
1
+ import React, { useMemo } from 'react'
2
+ import {
3
+ ActivityIndicator,
4
+ Platform,
5
+ StyleSheet,
6
+ Text,
7
+ View,
8
+ StyleProp,
9
+ ViewStyle,
10
+ TextStyle,
11
+ } from 'react-native'
12
+ import Color from './Color'
13
+ import { TouchableOpacity } from './components/TouchableOpacity'
14
+ import stylesCommon from './styles'
15
+
16
+ const styles = StyleSheet.create({
17
+ container: {
18
+ alignItems: 'center',
19
+ marginTop: 5,
20
+ marginBottom: 10,
21
+ },
22
+ wrapper: {
23
+ backgroundColor: Color.defaultColor,
24
+ borderRadius: 15,
25
+ height: 30,
26
+ paddingLeft: 10,
27
+ paddingRight: 10,
28
+ },
29
+ text: {
30
+ backgroundColor: Color.backgroundTransparent,
31
+ color: Color.white,
32
+ fontSize: 12,
33
+ },
34
+ activityIndicator: {
35
+ marginTop: Platform.select({
36
+ ios: -14,
37
+ android: -16,
38
+ default: -15,
39
+ }),
40
+ },
41
+ })
42
+
43
+ export interface LoadEarlierMessagesProps {
44
+ isAvailable?: boolean
45
+ isLoading?: boolean
46
+ isInfiniteScrollEnabled?: boolean
47
+ label?: string
48
+ containerStyle?: StyleProp<ViewStyle>
49
+ wrapperStyle?: StyleProp<ViewStyle>
50
+ textStyle?: StyleProp<TextStyle>
51
+ activityIndicatorStyle?: StyleProp<ViewStyle>
52
+ activityIndicatorColor?: string
53
+ activityIndicatorSize?: number | 'small' | 'large'
54
+ onPress: () => void
55
+ }
56
+
57
+ export const LoadEarlierMessages: React.FC<LoadEarlierMessagesProps> = ({
58
+ isLoading = false,
59
+ onPress,
60
+ label = 'Load earlier messages',
61
+ containerStyle,
62
+ wrapperStyle,
63
+ textStyle,
64
+ activityIndicatorColor = 'white',
65
+ activityIndicatorSize = 'small',
66
+ activityIndicatorStyle,
67
+ }) => {
68
+ const loadingContent = useMemo(() => (
69
+ <View>
70
+ <Text style={[styles.text, textStyle, { opacity: 0 }]}>
71
+ {label}
72
+ </Text>
73
+ <ActivityIndicator
74
+ color={activityIndicatorColor!}
75
+ size={activityIndicatorSize!}
76
+ style={[styles.activityIndicator, activityIndicatorStyle]}
77
+ />
78
+ </View>
79
+ ), [label, textStyle, activityIndicatorColor, activityIndicatorSize, activityIndicatorStyle])
80
+
81
+ const labelContent = useMemo(() => (
82
+ <Text style={[styles.text, textStyle]}>{label}</Text>
83
+ ), [label, textStyle])
84
+
85
+ return (
86
+ <TouchableOpacity
87
+ style={[styles.container, containerStyle]}
88
+ onPress={onPress}
89
+ disabled={isLoading}
90
+ accessibilityRole='button'
91
+ >
92
+ <View style={[stylesCommon.centerItems, styles.wrapper, wrapperStyle]}>
93
+ {isLoading ? loadingContent : labelContent}
94
+ </View>
95
+ </TouchableOpacity>
96
+ )
97
+ }
@@ -11,13 +11,13 @@ import { DayAnimatedProps } from './types'
11
11
 
12
12
  export * from './types'
13
13
 
14
- const DayAnimated = ({ scrolledY, daysPositions, listHeight, renderDay, messages, isLoadingEarlier, ...rest }: DayAnimatedProps) => {
14
+ const DayAnimated = ({ scrolledY, daysPositions, listHeight, renderDay, messages, isLoading, ...rest }: DayAnimatedProps) => {
15
15
  const opacity = useSharedValue(0)
16
16
  const fadeOutOpacityTimeoutId = useSharedValue<ReturnType<typeof setTimeout> | undefined>(undefined)
17
17
  const containerHeight = useSharedValue(0)
18
18
 
19
19
  const isScrolledOnMount = useSharedValue(false)
20
- const isLoadingEarlierAnim = useSharedValue(isLoadingEarlier)
20
+ const isLoadingAnim = useSharedValue(isLoading)
21
21
 
22
22
  const daysPositionsArray = useDerivedValue(() => Object.values(daysPositions.value).sort((a, b) => a.y - b.y))
23
23
 
@@ -57,11 +57,11 @@ const DayAnimated = ({ scrolledY, daysPositions, listHeight, renderDay, messages
57
57
  const style = useAnimatedStyle(() => ({
58
58
  top: interpolate(
59
59
  relativeScrolledPositionToBottomOfDay.value,
60
- [-dayTopOffset, -0.0001, 0, isLoadingEarlierAnim.value ? 0 : containerHeight.value + dayTopOffset],
61
- [dayTopOffset, dayTopOffset, -containerHeight.value, isLoadingEarlierAnim.value ? -containerHeight.value : dayTopOffset],
60
+ [-dayTopOffset, -0.0001, 0, isLoadingAnim.value ? 0 : containerHeight.value + dayTopOffset],
61
+ [dayTopOffset, dayTopOffset, -containerHeight.value, isLoadingAnim.value ? -containerHeight.value : dayTopOffset],
62
62
  'clamp'
63
63
  ),
64
- }), [relativeScrolledPositionToBottomOfDay, containerHeight, dayTopOffset, isLoadingEarlierAnim])
64
+ }), [relativeScrolledPositionToBottomOfDay, containerHeight, dayTopOffset, isLoadingAnim])
65
65
 
66
66
  const contentStyle = useAnimatedStyle(() => ({
67
67
  opacity: opacity.value,
@@ -111,8 +111,8 @@ const DayAnimated = ({ scrolledY, daysPositions, listHeight, renderDay, messages
111
111
  )
112
112
 
113
113
  useEffect(() => {
114
- isLoadingEarlierAnim.value = isLoadingEarlier
115
- }, [isLoadingEarlierAnim, isLoadingEarlier])
114
+ isLoadingAnim.value = isLoading
115
+ }, [isLoadingAnim, isLoading])
116
116
 
117
117
  const dayContent = useMemo(() => {
118
118
  if (!createdAt)
@@ -8,5 +8,5 @@ export interface DayAnimatedProps extends Omit<DayProps, 'createdAt'> {
8
8
  listHeight: { value: number }
9
9
  renderDay?: (props: DayProps) => React.ReactNode
10
10
  messages: IMessage[]
11
- isLoadingEarlier: boolean
11
+ isLoading: boolean
12
12
  }
@@ -3,14 +3,13 @@ import {
3
3
  View,
4
4
  Pressable,
5
5
  Text,
6
- Platform,
7
6
  LayoutChangeEvent,
8
7
  ListRenderItemInfo,
9
8
  CellRendererProps,
10
9
  } from 'react-native'
11
10
  import { FlatList } from 'react-native-gesture-handler'
12
11
  import Animated, { runOnJS, useAnimatedScrollHandler, useAnimatedStyle, useSharedValue, withTiming } from 'react-native-reanimated'
13
- import { LoadEarlier } from '../LoadEarlier'
12
+ import { LoadEarlierMessages } from '../LoadEarlierMessages'
14
13
  import { warning } from '../logging'
15
14
  import { ReanimatedScrollEvent } from '../reanimatedCompat'
16
15
 
@@ -27,7 +26,7 @@ import { MessageContainerProps, DaysPositions } from './types'
27
26
 
28
27
  export * from './types'
29
28
 
30
-
29
+
31
30
  const AnimatedFlatList = Animated.createAnimatedComponent(FlatList) as React.ComponentType<any>
32
31
 
33
32
  function MessageContainer<TMessage extends IMessage = IMessage> (props: MessageContainerProps<TMessage>) {
@@ -36,17 +35,14 @@ function MessageContainer<TMessage extends IMessage = IMessage> (props: MessageC
36
35
  user,
37
36
  isTyping = false,
38
37
  renderChatEmpty: renderChatEmptyProp,
39
- onLoadEarlier,
40
38
  inverted = true,
41
- loadEarlier = false,
42
39
  listProps,
43
40
  extraData,
44
41
  isScrollToBottomEnabled = false,
45
42
  scrollToBottomOffset = 200,
46
43
  alignTop = false,
47
44
  scrollToBottomStyle,
48
- infiniteScroll = false,
49
- isLoadingEarlier = false,
45
+ loadEarlierMessagesProps,
50
46
  renderTypingIndicator: renderTypingIndicatorProp,
51
47
  renderFooter: renderFooterProp,
52
48
  renderLoadEarlier: renderLoadEarlierProp,
@@ -83,15 +79,15 @@ function MessageContainer<TMessage extends IMessage = IMessage> (props: MessageC
83
79
  }, [renderFooterProp, renderTypingIndicator, props])
84
80
 
85
81
  const renderLoadEarlier = useCallback(() => {
86
- if (loadEarlier) {
82
+ if (loadEarlierMessagesProps?.onPress && loadEarlierMessagesProps?.isAvailable) {
87
83
  if (renderLoadEarlierProp)
88
- return renderLoadEarlierProp(props)
84
+ return renderLoadEarlierProp(loadEarlierMessagesProps)
89
85
 
90
- return <LoadEarlier {...props} />
86
+ return <LoadEarlierMessages {...loadEarlierMessagesProps} />
91
87
  }
92
88
 
93
89
  return null
94
- }, [loadEarlier, renderLoadEarlierProp, props])
90
+ }, [loadEarlierMessagesProps, renderLoadEarlierProp])
95
91
 
96
92
  const changeScrollToBottomVisibility: (isVisible: boolean) => void = useCallbackThrottled((isVisible: boolean) => {
97
93
  if (isScrollingDown.value && isVisible)
@@ -290,14 +286,13 @@ function MessageContainer<TMessage extends IMessage = IMessage> (props: MessageC
290
286
 
291
287
  const onEndReached = useCallback(() => {
292
288
  if (
293
- infiniteScroll &&
294
- loadEarlier &&
295
- onLoadEarlier &&
296
- !isLoadingEarlier &&
297
- Platform.OS !== 'web'
289
+ loadEarlierMessagesProps?.isInfiniteScrollEnabled &&
290
+ loadEarlierMessagesProps?.onPress &&
291
+ loadEarlierMessagesProps?.isAvailable &&
292
+ !loadEarlierMessagesProps?.isLoading
298
293
  )
299
- onLoadEarlier()
300
- }, [infiniteScroll, loadEarlier, onLoadEarlier, isLoadingEarlier])
294
+ loadEarlierMessagesProps.onPress()
295
+ }, [loadEarlierMessagesProps])
301
296
 
302
297
  const keyExtractor = useCallback((item: unknown) => (item as TMessage)._id.toString(), [])
303
298
 
@@ -420,7 +415,7 @@ function MessageContainer<TMessage extends IMessage = IMessage> (props: MessageC
420
415
  listHeight={listHeight}
421
416
  renderDay={renderDayProp}
422
417
  messages={messages}
423
- isLoadingEarlier={isLoadingEarlier}
418
+ isLoading={loadEarlierMessagesProps?.isLoading ?? false}
424
419
  />
425
420
  </View>
426
421
  )
@@ -6,7 +6,7 @@ import {
6
6
  } from 'react-native'
7
7
  import { FlatList } from 'react-native-gesture-handler'
8
8
 
9
- import { LoadEarlierProps } from '../LoadEarlier'
9
+ import { LoadEarlierMessagesProps } from '../LoadEarlierMessages'
10
10
  import { MessageProps } from '../Message'
11
11
  import { ReanimatedScrollEvent } from '../reanimatedCompat'
12
12
  import { User, IMessage, Reply, DayProps } from '../types'
@@ -28,8 +28,6 @@ export interface MessageContainerProps<TMessage extends IMessage = IMessage>
28
28
  listProps?: ListProps<TMessage>
29
29
  /** Reverses display order of messages; default is true */
30
30
  inverted?: boolean
31
- /** Enables the "Load earlier messages" button */
32
- loadEarlier?: boolean
33
31
  /** Controls whether or not the message bubbles appear at the top of the chat */
34
32
  alignTop?: boolean
35
33
  /** Enables the isScrollToBottomEnabled Component */
@@ -49,19 +47,15 @@ export interface MessageContainerProps<TMessage extends IMessage = IMessage>
49
47
  /** Custom day above a message */
50
48
  renderDay?(props: DayProps): React.ReactNode
51
49
  /** Custom "Load earlier messages" button */
52
- renderLoadEarlier?(props: LoadEarlierProps): React.ReactNode
50
+ renderLoadEarlier?(props: LoadEarlierMessagesProps): React.ReactNode
53
51
  /** Custom typing indicator */
54
52
  renderTypingIndicator?(): React.ReactNode
55
53
  /** Scroll to bottom custom component */
56
54
  scrollToBottomComponent?(): React.ReactNode
57
- /** Callback when loading earlier messages */
58
- onLoadEarlier?(): void
59
55
  /** Callback when quick reply is sent */
60
56
  onQuickReply?(replies: Reply[]): void
61
- /** Infinite scroll up when reach the top of messages container, automatically call onLoadEarlier function if exist */
62
- infiniteScroll?: boolean
63
- /** Display an ActivityIndicator when loading earlier messages */
64
- isLoadingEarlier?: boolean
57
+ /** Props to pass to the LoadEarlierMessages component. The LoadEarlierMessages button is only visible when isAvailable is true. Includes isAvailable (controls button visibility), isInfiniteScrollEnabled (infinite scroll up when reach the top of messages container, automatically call onPress function if it exists - not yet supported for web), onPress (callback when button is pressed), isLoading (display loading indicator), label (override default "Load earlier messages" text), and styling props (containerStyle, wrapperStyle, textStyle, activityIndicatorStyle, activityIndicatorColor, activityIndicatorSize). */
58
+ loadEarlierMessagesProps?: LoadEarlierMessagesProps
65
59
  /** Custom scroll event handler */
66
60
  handleOnScroll?(event: ReanimatedScrollEvent): void
67
61
  /** Style for TypingIndicator component */
@@ -23,7 +23,7 @@ describe('DayAnimated', () => {
23
23
  daysPositions={mockDaysPositions}
24
24
  listHeight={mockListHeight}
25
25
  messages={[mockMessage]}
26
- isLoadingEarlier={false}
26
+ isLoading={false}
27
27
  />
28
28
  )
29
29
  expect(toJSON()).toMatchSnapshot()
@@ -42,7 +42,7 @@ describe('DayAnimated', () => {
42
42
  daysPositions={mockDaysPositions}
43
43
  listHeight={mockListHeight}
44
44
  messages={[mockMessage]}
45
- isLoadingEarlier={false}
45
+ isLoading={false}
46
46
  renderDay={customRenderDay}
47
47
  />
48
48
  )
@@ -1,10 +1,10 @@
1
1
  import React from 'react'
2
2
  import { render } from '@testing-library/react-native'
3
3
 
4
- import { LoadEarlier } from '../GiftedChat'
4
+ import { LoadEarlierMessages } from '../GiftedChat'
5
5
 
6
- it('should render <LoadEarlier /> and compare with snapshot', () => {
7
- const { toJSON } = render(<LoadEarlier />)
6
+ it('should render <LoadEarlierMessages /> and compare with snapshot', () => {
7
+ const { toJSON } = render(<LoadEarlierMessages />)
8
8
 
9
9
  expect(toJSON()).toMatchSnapshot()
10
10
  })
@@ -5,13 +5,6 @@ import { MessageContainer } from '../GiftedChat'
5
5
  import { DEFAULT_TEST_MESSAGE } from './data'
6
6
 
7
7
  it('should render <MessageContainer /> without crashing', () => {
8
- render(
9
- <MessageContainer
10
- messages={[DEFAULT_TEST_MESSAGE]}
11
- user={{ _id: 'test' }}
12
- />
13
- )
14
-
15
8
  // Just verify it renders without throwing
16
9
  expect(() => render(
17
10
  <MessageContainer
@@ -1,6 +1,6 @@
1
1
  // Jest Snapshot v1, https://goo.gl/fbAQLP
2
2
 
3
- exports[`should render <LoadEarlier /> and compare with snapshot 1`] = `
3
+ exports[`should render <LoadEarlierMessages /> and compare with snapshot 1`] = `
4
4
  <View
5
5
  accessibilityRole="button"
6
6
  accessibilityState={
package/src/types.ts CHANGED
@@ -13,7 +13,7 @@ export { ComposerProps } from './Composer'
13
13
  export { DayProps } from './Day'
14
14
  export { GiftedAvatarProps } from './GiftedAvatar'
15
15
  export { InputToolbarProps } from './InputToolbar'
16
- export { LoadEarlierProps } from './LoadEarlier'
16
+ export { LoadEarlierMessagesProps } from './LoadEarlierMessages'
17
17
  export { MessageProps } from './Message'
18
18
  export { MessageContainerProps } from './MessageContainer'
19
19
  export { MessageImageProps } from './MessageImage'
package/src/utils.ts CHANGED
@@ -46,7 +46,6 @@ function processCallbackArguments (args: unknown[]): unknown[] {
46
46
  return params
47
47
  }
48
48
 
49
-
50
49
  export function useCallbackDebounced<T extends (...args: any[]) => any>(callbackFunc: T, deps: React.DependencyList = [], time: number): (...args: Parameters<T>) => void {
51
50
  const timeoutId = useRef<ReturnType<typeof setTimeout>>(undefined)
52
51
 
@@ -70,7 +69,6 @@ export function useCallbackDebounced<T extends (...args: any[]) => any>(callback
70
69
  return savedFunc
71
70
  }
72
71
 
73
-
74
72
  export function useCallbackThrottled<T extends (...args: any[]) => any>(callbackFunc: T, deps: React.DependencyList = [], time: number): (...args: Parameters<T>) => void {
75
73
  const lastExecution = useRef<number>(0)
76
74
  const timeoutId = useRef<ReturnType<typeof setTimeout>>(undefined)