react-native-gifted-chat 2.9.0-alpha.0 → 3.0.0-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 +234 -319
- package/package.json +2 -2
- package/src/Avatar.tsx +12 -12
- package/src/Bubble/index.tsx +2 -2
- package/src/Bubble/types.ts +22 -22
- package/src/Composer.tsx +0 -3
- package/src/GiftedAvatar.tsx +1 -1
- package/src/GiftedChat/index.tsx +56 -145
- package/src/GiftedChat/types.ts +40 -52
- package/src/InputToolbar.tsx +6 -6
- package/src/Message/index.tsx +5 -6
- package/src/Message/types.ts +8 -12
- package/src/MessageContainer/components/DayAnimated/types.ts +1 -1
- package/src/MessageContainer/components/Item/index.tsx +2 -2
- package/src/MessageContainer/components/Item/types.ts +1 -1
- package/src/MessageContainer/index.tsx +30 -30
- package/src/MessageContainer/styles.ts +12 -5
- package/src/MessageContainer/types.ts +12 -16
- package/src/MessageImage.tsx +100 -67
- package/src/MessageText.tsx +1 -1
- package/src/Models.ts +63 -0
- package/src/QuickReplies.tsx +1 -1
- package/src/Send.tsx +30 -31
- package/src/SystemMessage.tsx +1 -1
- package/src/Time.tsx +1 -1
- package/src/__tests__/GiftedChat.test.tsx +0 -28
- package/src/__tests__/Message.test.tsx +2 -2
- package/src/__tests__/Send.test.tsx +1 -1
- package/src/__tests__/__snapshots__/Actions.test.tsx.snap +2 -86
- package/src/__tests__/__snapshots__/GiftedChat.test.tsx.snap +31 -54
- package/src/__tests__/__snapshots__/LoadEarlier.test.tsx.snap +3 -89
- package/src/__tests__/__snapshots__/Message.test.tsx.snap +2 -2
- package/src/__tests__/__snapshots__/MessageImage.test.tsx.snap +1 -1
- package/src/__tests__/__snapshots__/Send.test.tsx.snap +10 -142
- package/src/__tests__/data.ts +1 -1
- package/src/components/TouchableOpacity.tsx +19 -8
- package/src/types.ts +1 -63
- package/src/utils.ts +1 -1
package/src/GiftedChat/types.ts
CHANGED
|
@@ -8,26 +8,26 @@ import {
|
|
|
8
8
|
import {
|
|
9
9
|
ActionSheetOptions,
|
|
10
10
|
} from '@expo/react-native-action-sheet'
|
|
11
|
+
import { KeyboardProvider, KeyboardAvoidingViewProps } from 'react-native-keyboard-controller'
|
|
11
12
|
import { ActionsProps } from '../Actions'
|
|
12
13
|
import { AvatarProps } from '../Avatar'
|
|
13
14
|
import { BubbleProps } from '../Bubble'
|
|
14
15
|
import { ComposerProps } from '../Composer'
|
|
15
16
|
import { InputToolbarProps } from '../InputToolbar'
|
|
16
|
-
import { MessageProps } from '../Message'
|
|
17
17
|
import { AnimatedList, MessageContainerProps } from '../MessageContainer'
|
|
18
18
|
import { MessageImageProps } from '../MessageImage'
|
|
19
19
|
import { MessageTextProps } from '../MessageText'
|
|
20
|
-
import { QuickRepliesProps } from '../QuickReplies'
|
|
21
|
-
import { SendProps } from '../Send'
|
|
22
|
-
import { SystemMessageProps } from '../SystemMessage'
|
|
23
|
-
import { TimeProps } from '../Time'
|
|
24
20
|
import {
|
|
25
21
|
IMessage,
|
|
26
22
|
LeftRightStyle,
|
|
27
23
|
MessageAudioProps,
|
|
28
24
|
MessageVideoProps,
|
|
29
25
|
User,
|
|
30
|
-
} from '../
|
|
26
|
+
} from '../Models'
|
|
27
|
+
import { QuickRepliesProps } from '../QuickReplies'
|
|
28
|
+
import { SendProps } from '../Send'
|
|
29
|
+
import { SystemMessageProps } from '../SystemMessage'
|
|
30
|
+
import { TimeProps } from '../Time'
|
|
31
31
|
|
|
32
32
|
export interface GiftedChatProps<TMessage extends IMessage> extends Partial<MessageContainerProps<TMessage>> {
|
|
33
33
|
/* Message container ref */
|
|
@@ -35,7 +35,7 @@ export interface GiftedChatProps<TMessage extends IMessage> extends Partial<Mess
|
|
|
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 */
|
|
38
|
-
|
|
38
|
+
isUsernameVisible?: boolean
|
|
39
39
|
/* Messages container style */
|
|
40
40
|
messagesContainerStyle?: StyleProp<ViewStyle>
|
|
41
41
|
/* Input text; default is undefined, but if specified, it will override GiftedChat's internal state */
|
|
@@ -51,26 +51,20 @@ export interface GiftedChatProps<TMessage extends IMessage> extends Partial<Mess
|
|
|
51
51
|
dateFormat?: string
|
|
52
52
|
/* Format to use for rendering relative times; Today - for now. See more: https://day.js.org/docs/en/plugin/calendar */
|
|
53
53
|
dateFormatCalendar?: object
|
|
54
|
-
/* 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` */
|
|
55
|
-
isKeyboardInternallyHandled?: boolean
|
|
56
54
|
/* Whether to render an avatar for the current user; default is false, only show avatars for other users */
|
|
57
|
-
|
|
55
|
+
isUserAvatarVisible?: boolean
|
|
58
56
|
/* When false, avatars will only be displayed when a consecutive message is from the same user on the same day; default is false */
|
|
59
|
-
|
|
57
|
+
isAvatarVisibleForEveryMessage?: boolean
|
|
60
58
|
/* Render the message avatar at the top of consecutive messages, rather than the bottom; default is false */
|
|
61
|
-
|
|
59
|
+
isAvatarOnTop?: boolean
|
|
62
60
|
/* Extra props to be passed to the <Image> component created by the default renderMessageImage */
|
|
63
61
|
imageProps?: MessageImageProps<TMessage>
|
|
64
|
-
/* Distance of the chat from the bottom of the screen (e.g. useful if you display a tab bar); default is 0 */
|
|
65
|
-
keyboardBottomOffset?: number
|
|
66
|
-
/* Focus on <TextInput> automatically when opening the keyboard; default is true */
|
|
67
|
-
focusOnInputWhenOpeningKeyboard?: boolean
|
|
68
62
|
/* Minimum height of the input toolbar; default is 44 */
|
|
69
63
|
minInputToolbarHeight?: number
|
|
70
64
|
/* Extra props to be passed to the <TextInput>. See https://reactnative.dev/docs/textinput */
|
|
71
65
|
textInputProps?: Partial<React.ComponentProps<typeof TextInput>>
|
|
72
66
|
/* Force send button */
|
|
73
|
-
|
|
67
|
+
isSendButtonAlwaysVisible?: boolean
|
|
74
68
|
/* Image style */
|
|
75
69
|
imageStyle?: StyleProp<ViewStyle>
|
|
76
70
|
/* composer min Height */
|
|
@@ -86,73 +80,67 @@ export interface GiftedChatProps<TMessage extends IMessage> extends Partial<Mess
|
|
|
86
80
|
isCustomViewBottom?: boolean
|
|
87
81
|
timeTextStyle?: LeftRightStyle<TextStyle>
|
|
88
82
|
/* Custom action sheet */
|
|
89
|
-
actionSheet
|
|
83
|
+
actionSheet?: () => {
|
|
90
84
|
showActionSheetWithOptions: (
|
|
91
85
|
options: ActionSheetOptions,
|
|
92
86
|
callback: (buttonIndex: number) => void | Promise<void>,
|
|
93
87
|
) => void
|
|
94
88
|
}
|
|
95
89
|
/* Callback when a message avatar is tapped */
|
|
96
|
-
onPressAvatar
|
|
90
|
+
onPressAvatar?: (user: User) => void
|
|
97
91
|
/* Callback when a message avatar is tapped */
|
|
98
|
-
onLongPressAvatar
|
|
99
|
-
/* Generate an id for new messages. Defaults to
|
|
100
|
-
messageIdGenerator
|
|
92
|
+
onLongPressAvatar?: (user: User) => void
|
|
93
|
+
/* Generate an id for new messages. Defaults to a simple random string generator */
|
|
94
|
+
messageIdGenerator?: (message?: TMessage) => string
|
|
101
95
|
/* Callback when sending a message */
|
|
102
|
-
onSend
|
|
96
|
+
onSend?: (messages: TMessage[]) => void
|
|
103
97
|
/* Render a loading view when initializing */
|
|
104
|
-
renderLoading
|
|
98
|
+
renderLoading?: () => React.ReactNode
|
|
105
99
|
/* Custom message avatar; set to null to not render any avatar for the message */
|
|
106
100
|
renderAvatar?: null | ((props: AvatarProps<TMessage>) => React.ReactNode)
|
|
107
101
|
/* Custom message bubble */
|
|
108
|
-
renderBubble
|
|
102
|
+
renderBubble?: (props: BubbleProps<TMessage>) => React.ReactNode
|
|
109
103
|
/* Custom system message */
|
|
110
|
-
renderSystemMessage
|
|
104
|
+
renderSystemMessage?: (props: SystemMessageProps<TMessage>) => React.ReactNode
|
|
111
105
|
/* Callback when a message bubble is pressed; default is to do nothing */
|
|
112
|
-
onPressMessage
|
|
106
|
+
onPressMessage?: (context: unknown, message: TMessage) => void
|
|
113
107
|
/* Callback when a message bubble is long-pressed; default is to show an ActionSheet with "Copy Text" (see example using showActionSheetWithOptions()) */
|
|
114
|
-
onLongPressMessage
|
|
108
|
+
onLongPressMessage?: (context: unknown, message: TMessage) => void
|
|
115
109
|
/* Custom Username container */
|
|
116
|
-
renderUsername
|
|
110
|
+
renderUsername?: (user: User) => React.ReactNode
|
|
117
111
|
/* Reverses display order of messages; default is true */
|
|
118
112
|
/* Custom message text */
|
|
119
|
-
renderMessageText
|
|
113
|
+
renderMessageText?: (messageText: MessageTextProps<TMessage>) => React.ReactNode
|
|
120
114
|
/* Custom message image */
|
|
121
|
-
renderMessageImage
|
|
115
|
+
renderMessageImage?: (props: MessageImageProps<TMessage>) => React.ReactNode
|
|
122
116
|
/* Custom message video */
|
|
123
|
-
renderMessageVideo
|
|
117
|
+
renderMessageVideo?: (props: MessageVideoProps<TMessage>) => React.ReactNode
|
|
124
118
|
/* Custom message video */
|
|
125
|
-
renderMessageAudio
|
|
119
|
+
renderMessageAudio?: (props: MessageAudioProps<TMessage>) => React.ReactNode
|
|
126
120
|
/* Custom view inside the bubble */
|
|
127
|
-
renderCustomView
|
|
121
|
+
renderCustomView?: (props: BubbleProps<TMessage>) => React.ReactNode
|
|
128
122
|
/* Custom time inside a message */
|
|
129
|
-
renderTime
|
|
123
|
+
renderTime?: (props: TimeProps<TMessage>) => React.ReactNode
|
|
130
124
|
/* Custom component to render below the MessageContainer (separate from the ListView) */
|
|
131
|
-
renderChatFooter
|
|
125
|
+
renderChatFooter?: () => React.ReactNode
|
|
132
126
|
/* Custom message composer container. Can be a component, element, render function, or null */
|
|
133
127
|
renderInputToolbar?: React.ComponentType<InputToolbarProps<TMessage>> | React.ReactElement | ((props: InputToolbarProps<TMessage>) => React.ReactNode) | null
|
|
134
128
|
/* Custom text input message composer */
|
|
135
|
-
renderComposer
|
|
129
|
+
renderComposer?: (props: ComposerProps) => React.ReactNode
|
|
136
130
|
/* Custom action button on the left of the message composer */
|
|
137
|
-
renderActions
|
|
131
|
+
renderActions?: (props: ActionsProps) => React.ReactNode
|
|
138
132
|
/* Custom send button; you can pass children to the original Send component quite easily, for example to use a custom icon (example) */
|
|
139
|
-
renderSend
|
|
133
|
+
renderSend?: (props: SendProps<TMessage>) => React.ReactNode
|
|
140
134
|
/* Custom second line of actions below the message composer */
|
|
141
|
-
renderAccessory
|
|
135
|
+
renderAccessory?: (props: InputToolbarProps<TMessage>) => React.ReactNode
|
|
142
136
|
/* Callback when the Action button is pressed (if set, the default actionSheet will not be used) */
|
|
143
|
-
onPressActionButton
|
|
144
|
-
/* Callback when the input text changes */
|
|
145
|
-
onInputTextChanged?(text: string): void
|
|
137
|
+
onPressActionButton?: () => void
|
|
146
138
|
/* Extra props to be passed to the MessageText component */
|
|
147
139
|
messageTextProps?: Partial<MessageTextProps<TMessage>>
|
|
148
|
-
|
|
149
|
-
matchers?: MessageTextProps<TMessage>['matchers']
|
|
150
|
-
renderQuickReplies?(
|
|
140
|
+
renderQuickReplies?: (
|
|
151
141
|
quickReplies: QuickRepliesProps<TMessage>,
|
|
152
|
-
)
|
|
153
|
-
renderQuickReplySend
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
nextProps: MessageProps<TMessage>,
|
|
157
|
-
): boolean
|
|
142
|
+
) => React.ReactNode
|
|
143
|
+
renderQuickReplySend?: () => React.ReactNode
|
|
144
|
+
keyboardProviderProps?: React.ComponentProps<typeof KeyboardProvider>
|
|
145
|
+
keyboardAvoidingViewProps?: KeyboardAvoidingViewProps
|
|
158
146
|
}
|
package/src/InputToolbar.tsx
CHANGED
|
@@ -4,8 +4,8 @@ import { StyleSheet, View, StyleProp, ViewStyle, useColorScheme } from 'react-na
|
|
|
4
4
|
import { Actions, ActionsProps } from './Actions'
|
|
5
5
|
import { Color } from './Color'
|
|
6
6
|
import { Composer, ComposerProps } from './Composer'
|
|
7
|
+
import { IMessage } from './Models'
|
|
7
8
|
import { Send, SendProps } from './Send'
|
|
8
|
-
import { IMessage } from './types'
|
|
9
9
|
import { renderComponentOrElement } from './utils'
|
|
10
10
|
|
|
11
11
|
export interface InputToolbarProps<TMessage extends IMessage> {
|
|
@@ -14,11 +14,11 @@ export interface InputToolbarProps<TMessage extends IMessage> {
|
|
|
14
14
|
containerStyle?: StyleProp<ViewStyle>
|
|
15
15
|
primaryStyle?: StyleProp<ViewStyle>
|
|
16
16
|
accessoryStyle?: StyleProp<ViewStyle>
|
|
17
|
-
renderAccessory
|
|
18
|
-
renderActions
|
|
19
|
-
renderSend
|
|
20
|
-
renderComposer
|
|
21
|
-
onPressActionButton
|
|
17
|
+
renderAccessory?: (props: InputToolbarProps<TMessage>) => React.ReactNode
|
|
18
|
+
renderActions?: (props: ActionsProps) => React.ReactNode
|
|
19
|
+
renderSend?: (props: SendProps<TMessage>) => React.ReactNode
|
|
20
|
+
renderComposer?: (props: ComposerProps) => React.ReactNode
|
|
21
|
+
onPressActionButton?: () => void
|
|
22
22
|
icon?: () => React.ReactNode
|
|
23
23
|
wrapperStyle?: StyleProp<ViewStyle>
|
|
24
24
|
}
|
package/src/Message/index.tsx
CHANGED
|
@@ -3,9 +3,8 @@ import { View } from 'react-native'
|
|
|
3
3
|
|
|
4
4
|
import { Avatar } from '../Avatar'
|
|
5
5
|
import { Bubble } from '../Bubble'
|
|
6
|
+
import { IMessage } from '../Models'
|
|
6
7
|
import { SystemMessage } from '../SystemMessage'
|
|
7
|
-
|
|
8
|
-
import { IMessage } from '../types'
|
|
9
8
|
import { isSameUser, renderComponentOrElement } from '../utils'
|
|
10
9
|
import styles from './styles'
|
|
11
10
|
import { MessageProps } from './types'
|
|
@@ -22,7 +21,7 @@ export const Message = <TMessage extends IMessage = IMessage>(props: MessageProp
|
|
|
22
21
|
position,
|
|
23
22
|
containerStyle,
|
|
24
23
|
user,
|
|
25
|
-
|
|
24
|
+
isUserAvatarVisible,
|
|
26
25
|
} = props
|
|
27
26
|
|
|
28
27
|
const renderBubble = useCallback(() => {
|
|
@@ -60,7 +59,7 @@ export const Message = <TMessage extends IMessage = IMessage>(props: MessageProp
|
|
|
60
59
|
user?._id &&
|
|
61
60
|
currentMessage?.user &&
|
|
62
61
|
user._id === currentMessage.user._id &&
|
|
63
|
-
!
|
|
62
|
+
!isUserAvatarVisible
|
|
64
63
|
)
|
|
65
64
|
return null
|
|
66
65
|
|
|
@@ -80,7 +79,7 @@ export const Message = <TMessage extends IMessage = IMessage>(props: MessageProp
|
|
|
80
79
|
props,
|
|
81
80
|
user,
|
|
82
81
|
currentMessage,
|
|
83
|
-
|
|
82
|
+
isUserAvatarVisible,
|
|
84
83
|
])
|
|
85
84
|
|
|
86
85
|
if (!currentMessage)
|
|
@@ -99,7 +98,7 @@ export const Message = <TMessage extends IMessage = IMessage>(props: MessageProp
|
|
|
99
98
|
style={[
|
|
100
99
|
styles[position].container,
|
|
101
100
|
{ marginBottom: sameUser ? 2 : 10 },
|
|
102
|
-
!props.
|
|
101
|
+
!props.isInverted && { marginBottom: 2 },
|
|
103
102
|
containerStyle?.[position],
|
|
104
103
|
]}
|
|
105
104
|
>
|
package/src/Message/types.ts
CHANGED
|
@@ -2,25 +2,21 @@ import { ViewStyle, LayoutChangeEvent } from 'react-native'
|
|
|
2
2
|
import { AvatarProps } from '../Avatar'
|
|
3
3
|
import { BubbleProps } from '../Bubble'
|
|
4
4
|
import { DayProps } from '../Day'
|
|
5
|
+
import { IMessage, User, LeftRightStyle } from '../Models'
|
|
5
6
|
import { SystemMessageProps } from '../SystemMessage'
|
|
6
|
-
import { IMessage, User, LeftRightStyle } from '../types'
|
|
7
7
|
|
|
8
8
|
export interface MessageProps<TMessage extends IMessage> {
|
|
9
|
-
|
|
9
|
+
isUserAvatarVisible?: boolean
|
|
10
10
|
position: 'left' | 'right'
|
|
11
11
|
currentMessage: TMessage
|
|
12
12
|
nextMessage?: TMessage
|
|
13
13
|
previousMessage?: TMessage
|
|
14
14
|
user: User
|
|
15
|
-
|
|
15
|
+
isInverted?: boolean
|
|
16
16
|
containerStyle?: LeftRightStyle<ViewStyle>
|
|
17
|
-
renderBubble
|
|
18
|
-
renderDay
|
|
19
|
-
renderSystemMessage
|
|
20
|
-
renderAvatar
|
|
21
|
-
|
|
22
|
-
props: MessageProps<IMessage>,
|
|
23
|
-
nextProps: MessageProps<IMessage>,
|
|
24
|
-
): boolean
|
|
25
|
-
onMessageLayout?(event: LayoutChangeEvent): void
|
|
17
|
+
renderBubble?: (props: BubbleProps<TMessage>) => React.ReactNode
|
|
18
|
+
renderDay?: (props: DayProps) => React.ReactNode
|
|
19
|
+
renderSystemMessage?: (props: SystemMessageProps<TMessage>) => React.ReactNode
|
|
20
|
+
renderAvatar?: (props: AvatarProps<TMessage>) => React.ReactNode
|
|
21
|
+
onMessageLayout?: (event: LayoutChangeEvent) => void
|
|
26
22
|
}
|
|
@@ -2,8 +2,8 @@ import React, { useCallback, useMemo } from 'react'
|
|
|
2
2
|
import { LayoutChangeEvent, View } from 'react-native'
|
|
3
3
|
import Animated, { interpolate, useAnimatedStyle, useDerivedValue, useSharedValue } from 'react-native-reanimated'
|
|
4
4
|
import { Day } from '../../../Day'
|
|
5
|
-
import { Message,MessageProps } from '../../../Message'
|
|
6
|
-
import { IMessage } from '../../../
|
|
5
|
+
import { Message, MessageProps } from '../../../Message'
|
|
6
|
+
import { IMessage } from '../../../Models'
|
|
7
7
|
import { isSameDay } from '../../../utils'
|
|
8
8
|
import { DaysPositions } from '../../types'
|
|
9
9
|
import { ItemProps } from './types'
|
|
@@ -11,10 +11,9 @@ import { FlatList } from 'react-native-gesture-handler'
|
|
|
11
11
|
import Animated, { runOnJS, useAnimatedScrollHandler, useAnimatedStyle, useSharedValue, withTiming } from 'react-native-reanimated'
|
|
12
12
|
import { LoadEarlierMessages } from '../LoadEarlierMessages'
|
|
13
13
|
import { warning } from '../logging'
|
|
14
|
+
import { IMessage } from '../Models'
|
|
14
15
|
import { ReanimatedScrollEvent } from '../reanimatedCompat'
|
|
15
|
-
|
|
16
16
|
import stylesCommon from '../styles'
|
|
17
|
-
import { IMessage } from '../types'
|
|
18
17
|
import { TypingIndicator } from '../TypingIndicator'
|
|
19
18
|
import { isSameDay, useCallbackThrottled } from '../utils'
|
|
20
19
|
import { DayAnimated } from './components/DayAnimated'
|
|
@@ -35,23 +34,23 @@ export const MessageContainer = <TMessage extends IMessage>(props: MessageContai
|
|
|
35
34
|
user,
|
|
36
35
|
isTyping = false,
|
|
37
36
|
renderChatEmpty: renderChatEmptyProp,
|
|
38
|
-
|
|
37
|
+
isInverted = true,
|
|
39
38
|
listProps,
|
|
40
|
-
extraData,
|
|
41
39
|
isScrollToBottomEnabled = false,
|
|
42
40
|
scrollToBottomOffset = 200,
|
|
43
|
-
|
|
41
|
+
isAlignedTop = false,
|
|
44
42
|
scrollToBottomStyle,
|
|
45
43
|
loadEarlierMessagesProps,
|
|
46
44
|
renderTypingIndicator: renderTypingIndicatorProp,
|
|
47
45
|
renderFooter: renderFooterProp,
|
|
48
46
|
renderLoadEarlier: renderLoadEarlierProp,
|
|
49
47
|
forwardRef,
|
|
50
|
-
handleOnScroll: handleOnScrollProp,
|
|
51
48
|
scrollToBottomComponent: scrollToBottomComponentProp,
|
|
52
49
|
renderDay: renderDayProp,
|
|
53
50
|
} = props
|
|
54
51
|
|
|
52
|
+
const listPropsOnScrollProp = listProps?.onScroll
|
|
53
|
+
|
|
55
54
|
const scrollToBottomOpacity = useSharedValue(0)
|
|
56
55
|
const isScrollingDown = useSharedValue(false)
|
|
57
56
|
const lastScrolledY = useSharedValue(0)
|
|
@@ -111,14 +110,14 @@ export const MessageContainer = <TMessage extends IMessage>(props: MessageContai
|
|
|
111
110
|
isScrollingDown.value = true
|
|
112
111
|
changeScrollToBottomVisibility(false)
|
|
113
112
|
|
|
114
|
-
if (
|
|
113
|
+
if (isInverted)
|
|
115
114
|
scrollTo({ offset: 0, animated })
|
|
116
115
|
else if (forwardRef?.current)
|
|
117
116
|
forwardRef.current.scrollToEnd({ animated })
|
|
118
|
-
}, [forwardRef,
|
|
117
|
+
}, [forwardRef, isInverted, scrollTo, isScrollingDown, changeScrollToBottomVisibility])
|
|
119
118
|
|
|
120
119
|
const handleOnScroll = useCallback((event: ReanimatedScrollEvent) => {
|
|
121
|
-
|
|
120
|
+
listPropsOnScrollProp?.(event as any)
|
|
122
121
|
|
|
123
122
|
const {
|
|
124
123
|
contentOffset: { y: contentOffsetY },
|
|
@@ -127,12 +126,12 @@ export const MessageContainer = <TMessage extends IMessage>(props: MessageContai
|
|
|
127
126
|
} = event
|
|
128
127
|
|
|
129
128
|
isScrollingDown.value =
|
|
130
|
-
(
|
|
131
|
-
(!
|
|
129
|
+
(isInverted && lastScrolledY.value > contentOffsetY) ||
|
|
130
|
+
(!isInverted && lastScrolledY.value < contentOffsetY)
|
|
132
131
|
|
|
133
132
|
lastScrolledY.value = contentOffsetY
|
|
134
133
|
|
|
135
|
-
if (
|
|
134
|
+
if (isInverted)
|
|
136
135
|
if (contentOffsetY > scrollToBottomOffset!)
|
|
137
136
|
changeScrollToBottomVisibility(true)
|
|
138
137
|
else
|
|
@@ -144,7 +143,7 @@ export const MessageContainer = <TMessage extends IMessage>(props: MessageContai
|
|
|
144
143
|
changeScrollToBottomVisibility(false)
|
|
145
144
|
else
|
|
146
145
|
changeScrollToBottomVisibility(false)
|
|
147
|
-
}, [
|
|
146
|
+
}, [isInverted, scrollToBottomOffset, changeScrollToBottomVisibility, isScrollingDown, lastScrolledY, listPropsOnScrollProp])
|
|
148
147
|
|
|
149
148
|
const restProps = useMemo(() => {
|
|
150
149
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
@@ -170,9 +169,9 @@ export const MessageContainer = <TMessage extends IMessage>(props: MessageContai
|
|
|
170
169
|
|
|
171
170
|
if (messages && user) {
|
|
172
171
|
const previousMessage =
|
|
173
|
-
(
|
|
172
|
+
(isInverted ? messages[index + 1] : messages[index - 1]) || {}
|
|
174
173
|
const nextMessage =
|
|
175
|
-
(
|
|
174
|
+
(isInverted ? messages[index - 1] : messages[index + 1]) || {}
|
|
176
175
|
|
|
177
176
|
const messageProps: ItemProps<TMessage> = {
|
|
178
177
|
...restProps,
|
|
@@ -191,7 +190,7 @@ export const MessageContainer = <TMessage extends IMessage>(props: MessageContai
|
|
|
191
190
|
}
|
|
192
191
|
|
|
193
192
|
return null
|
|
194
|
-
}, [messages, restProps,
|
|
193
|
+
}, [messages, restProps, isInverted, scrolledY, daysPositions, listHeight, user])
|
|
195
194
|
|
|
196
195
|
const emptyContent = useMemo(() => {
|
|
197
196
|
if (!renderChatEmptyProp)
|
|
@@ -202,7 +201,7 @@ export const MessageContainer = <TMessage extends IMessage>(props: MessageContai
|
|
|
202
201
|
|
|
203
202
|
const renderChatEmpty = useCallback(() => {
|
|
204
203
|
if (renderChatEmptyProp)
|
|
205
|
-
return
|
|
204
|
+
return isInverted
|
|
206
205
|
? (
|
|
207
206
|
emptyContent
|
|
208
207
|
)
|
|
@@ -213,7 +212,7 @@ export const MessageContainer = <TMessage extends IMessage>(props: MessageContai
|
|
|
213
212
|
)
|
|
214
213
|
|
|
215
214
|
return <View style={stylesCommon.fill} />
|
|
216
|
-
}, [
|
|
215
|
+
}, [isInverted, renderChatEmptyProp, emptyContent])
|
|
217
216
|
|
|
218
217
|
const ListHeaderComponent = useMemo(() => {
|
|
219
218
|
const content = renderLoadEarlier()
|
|
@@ -273,7 +272,7 @@ export const MessageContainer = <TMessage extends IMessage>(props: MessageContai
|
|
|
273
272
|
listHeight.value = event.nativeEvent.layout.height
|
|
274
273
|
|
|
275
274
|
if (
|
|
276
|
-
!
|
|
275
|
+
!isInverted &&
|
|
277
276
|
messages?.length &&
|
|
278
277
|
isScrollToBottomEnabled
|
|
279
278
|
)
|
|
@@ -282,7 +281,7 @@ export const MessageContainer = <TMessage extends IMessage>(props: MessageContai
|
|
|
282
281
|
}, 500)
|
|
283
282
|
|
|
284
283
|
listProps?.onLayout?.(event)
|
|
285
|
-
}, [
|
|
284
|
+
}, [isInverted, messages, doScrollToBottom, listHeight, listProps, isScrollToBottomEnabled])
|
|
286
285
|
|
|
287
286
|
const onEndReached = useCallback(() => {
|
|
288
287
|
if (
|
|
@@ -326,7 +325,7 @@ export const MessageContainer = <TMessage extends IMessage>(props: MessageContai
|
|
|
326
325
|
}
|
|
327
326
|
|
|
328
327
|
for (const [key, item] of Object.entries(value))
|
|
329
|
-
if (isSameDay(newValue.createdAt, item.createdAt) && (
|
|
328
|
+
if (isSameDay(newValue.createdAt, item.createdAt) && (isInverted ? item.y <= newValue.y : item.y >= newValue.y)) {
|
|
330
329
|
delete value[key]
|
|
331
330
|
break
|
|
332
331
|
}
|
|
@@ -345,7 +344,7 @@ export const MessageContainer = <TMessage extends IMessage>(props: MessageContai
|
|
|
345
344
|
{children}
|
|
346
345
|
</View>
|
|
347
346
|
)
|
|
348
|
-
}, [daysPositions,
|
|
347
|
+
}, [daysPositions, isInverted])
|
|
349
348
|
|
|
350
349
|
const scrollHandler = useAnimatedScrollHandler({
|
|
351
350
|
onScroll: event => {
|
|
@@ -362,7 +361,7 @@ export const MessageContainer = <TMessage extends IMessage>(props: MessageContai
|
|
|
362
361
|
let shouldRemove = messageIndex === -1
|
|
363
362
|
|
|
364
363
|
if (!shouldRemove) {
|
|
365
|
-
const prevMessage = messages[messageIndex + (
|
|
364
|
+
const prevMessage = messages[messageIndex + (isInverted ? 1 : -1)]
|
|
366
365
|
const message = messages[messageIndex]
|
|
367
366
|
shouldRemove = !!prevMessage && isSameDay(message, prevMessage)
|
|
368
367
|
}
|
|
@@ -375,36 +374,37 @@ export const MessageContainer = <TMessage extends IMessage>(props: MessageContai
|
|
|
375
374
|
return value
|
|
376
375
|
})
|
|
377
376
|
})
|
|
378
|
-
}, [messages, daysPositions,
|
|
377
|
+
}, [messages, daysPositions, isInverted])
|
|
379
378
|
|
|
380
379
|
return (
|
|
381
380
|
<View
|
|
382
381
|
style={[
|
|
383
382
|
styles.contentContainerStyle,
|
|
384
|
-
|
|
383
|
+
isAlignedTop ? styles.containerAlignTop : stylesCommon.fill,
|
|
385
384
|
]}
|
|
386
385
|
>
|
|
387
386
|
<AnimatedFlatList
|
|
388
|
-
extraData={extraData}
|
|
389
387
|
ref={forwardRef}
|
|
390
388
|
keyExtractor={keyExtractor}
|
|
391
389
|
data={messages}
|
|
392
390
|
renderItem={renderItem}
|
|
393
|
-
inverted={
|
|
391
|
+
inverted={isInverted}
|
|
394
392
|
automaticallyAdjustContentInsets={false}
|
|
395
393
|
style={stylesCommon.fill}
|
|
396
394
|
ListEmptyComponent={renderChatEmpty}
|
|
397
395
|
ListFooterComponent={
|
|
398
|
-
|
|
396
|
+
isInverted ? ListHeaderComponent : ListFooterComponent
|
|
399
397
|
}
|
|
400
398
|
ListHeaderComponent={
|
|
401
|
-
|
|
399
|
+
isInverted ? ListFooterComponent : ListHeaderComponent
|
|
402
400
|
}
|
|
403
|
-
onScroll={scrollHandler}
|
|
404
401
|
scrollEventThrottle={1}
|
|
405
402
|
onEndReached={onEndReached}
|
|
406
403
|
onEndReachedThreshold={0.1}
|
|
404
|
+
keyboardDismissMode='interactive'
|
|
405
|
+
keyboardShouldPersistTaps='handled'
|
|
407
406
|
{...listProps}
|
|
407
|
+
onScroll={scrollHandler}
|
|
408
408
|
onLayout={onLayoutList}
|
|
409
409
|
CellRendererComponent={renderCell}
|
|
410
410
|
/>
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { StyleSheet } from 'react-native'
|
|
1
|
+
import { Platform, StyleSheet } from 'react-native'
|
|
2
2
|
import { Color } from '../Color'
|
|
3
3
|
|
|
4
4
|
export default StyleSheet.create({
|
|
@@ -24,9 +24,16 @@ export default StyleSheet.create({
|
|
|
24
24
|
width: 40,
|
|
25
25
|
borderRadius: 20,
|
|
26
26
|
backgroundColor: Color.white,
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
27
|
+
...Platform.select({
|
|
28
|
+
ios: {
|
|
29
|
+
shadowColor: Color.black,
|
|
30
|
+
shadowOpacity: 0.5,
|
|
31
|
+
shadowOffset: { width: 0, height: 0 },
|
|
32
|
+
shadowRadius: 1,
|
|
33
|
+
},
|
|
34
|
+
android: {
|
|
35
|
+
elevation: 5,
|
|
36
|
+
},
|
|
37
|
+
}),
|
|
31
38
|
},
|
|
32
39
|
})
|
|
@@ -6,10 +6,10 @@ import {
|
|
|
6
6
|
} from 'react-native'
|
|
7
7
|
import { FlatList } from 'react-native-gesture-handler'
|
|
8
8
|
|
|
9
|
+
import { DayProps } from '../Day'
|
|
9
10
|
import { LoadEarlierMessagesProps } from '../LoadEarlierMessages'
|
|
10
11
|
import { MessageProps } from '../Message'
|
|
11
|
-
import {
|
|
12
|
-
import { User, IMessage, Reply, DayProps } from '../types'
|
|
12
|
+
import { User, IMessage, Reply } from '../Models'
|
|
13
13
|
import { TypingIndicatorProps } from '../TypingIndicator/types'
|
|
14
14
|
|
|
15
15
|
export type ListProps<TMessage extends IMessage = IMessage> = Partial<FlatListProps<TMessage>>
|
|
@@ -27,37 +27,33 @@ export interface MessageContainerProps<TMessage extends IMessage = IMessage>
|
|
|
27
27
|
/** Additional props for FlatList */
|
|
28
28
|
listProps?: ListProps<TMessage>
|
|
29
29
|
/** Reverses display order of messages; default is true */
|
|
30
|
-
|
|
30
|
+
isInverted?: boolean
|
|
31
31
|
/** Controls whether or not the message bubbles appear at the top of the chat */
|
|
32
|
-
|
|
32
|
+
isAlignedTop?: boolean
|
|
33
33
|
/** Enables the isScrollToBottomEnabled Component */
|
|
34
34
|
isScrollToBottomEnabled?: boolean
|
|
35
35
|
/** Scroll to bottom wrapper style */
|
|
36
36
|
scrollToBottomStyle?: StyleProp<ViewStyle>
|
|
37
|
-
/** This can be used to pass unknown data which needs to be re-rendered */
|
|
38
|
-
extraData?: object
|
|
39
37
|
/** Distance from bottom before showing scroll to bottom button */
|
|
40
38
|
scrollToBottomOffset?: number
|
|
41
39
|
/** Custom component to render when messages are empty */
|
|
42
|
-
renderChatEmpty
|
|
40
|
+
renderChatEmpty?: () => React.ReactNode
|
|
43
41
|
/** Custom footer component on the ListView, e.g. 'User is typing...' */
|
|
44
|
-
renderFooter
|
|
42
|
+
renderFooter?: (props: MessageContainerProps<TMessage>) => React.ReactNode
|
|
45
43
|
/** Custom message container */
|
|
46
|
-
renderMessage
|
|
44
|
+
renderMessage?: (props: MessageProps<TMessage>) => React.ReactElement
|
|
47
45
|
/** Custom day above a message */
|
|
48
|
-
renderDay
|
|
46
|
+
renderDay?: (props: DayProps) => React.ReactNode
|
|
49
47
|
/** Custom "Load earlier messages" button */
|
|
50
|
-
renderLoadEarlier
|
|
48
|
+
renderLoadEarlier?: (props: LoadEarlierMessagesProps) => React.ReactNode
|
|
51
49
|
/** Custom typing indicator */
|
|
52
|
-
renderTypingIndicator
|
|
50
|
+
renderTypingIndicator?: () => React.ReactNode
|
|
53
51
|
/** Scroll to bottom custom component */
|
|
54
|
-
scrollToBottomComponent
|
|
52
|
+
scrollToBottomComponent?: () => React.ReactNode
|
|
55
53
|
/** Callback when quick reply is sent */
|
|
56
|
-
onQuickReply
|
|
54
|
+
onQuickReply?: (replies: Reply[]) => void
|
|
57
55
|
/** 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
56
|
loadEarlierMessagesProps?: LoadEarlierMessagesProps
|
|
59
|
-
/** Custom scroll event handler */
|
|
60
|
-
handleOnScroll?(event: ReanimatedScrollEvent): void
|
|
61
57
|
/** Style for TypingIndicator component */
|
|
62
58
|
typingIndicatorStyle?: StyleProp<ViewStyle>
|
|
63
59
|
}
|