react-native-gifted-chat 2.8.2-alpha.6 → 3.0.0-alpha.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.
- package/README.md +190 -269
- package/package.json +2 -1
- package/src/Actions.tsx +1 -1
- package/src/Avatar.tsx +12 -12
- package/src/Bubble/index.tsx +14 -16
- package/src/Bubble/styles.ts +1 -1
- package/src/Bubble/types.ts +26 -27
- package/src/Color.ts +1 -1
- package/src/Composer.tsx +1 -4
- package/src/Day/styles.ts +1 -1
- package/src/GiftedAvatar.tsx +2 -2
- package/src/GiftedChat/index.tsx +26 -61
- package/src/GiftedChat/types.ts +40 -49
- package/src/InputToolbar.tsx +28 -19
- package/src/LoadEarlierMessages.tsx +1 -1
- package/src/Message/index.tsx +11 -14
- package/src/Message/types.ts +8 -12
- package/src/MessageAudio.tsx +1 -1
- package/src/MessageContainer/components/DayAnimated/index.tsx +1 -3
- package/src/MessageContainer/components/DayAnimated/types.ts +1 -1
- package/src/MessageContainer/components/Item/index.tsx +9 -11
- package/src/MessageContainer/components/Item/types.ts +1 -1
- package/src/MessageContainer/index.tsx +32 -36
- package/src/MessageContainer/styles.ts +1 -1
- package/src/MessageContainer/types.ts +12 -16
- package/src/MessageImage.tsx +1 -1
- package/src/MessageText.tsx +1 -1
- package/src/MessageVideo.tsx +1 -1
- package/src/Models.ts +63 -0
- package/src/QuickReplies.tsx +2 -2
- package/src/Send.tsx +31 -32
- package/src/SystemMessage.tsx +2 -2
- package/src/Time.tsx +6 -6
- package/src/TypingIndicator/index.tsx +1 -3
- package/src/TypingIndicator/styles.ts +1 -1
- package/src/__tests__/Actions.test.tsx +1 -1
- package/src/__tests__/Avatar.test.tsx +7 -2
- package/src/__tests__/Bubble.test.tsx +3 -7
- package/src/__tests__/Color.test.tsx +1 -1
- package/src/__tests__/Composer.test.tsx +1 -1
- package/src/__tests__/Day.test.tsx +3 -3
- package/src/__tests__/DayAnimated.test.tsx +5 -11
- package/src/__tests__/GiftedAvatar.test.tsx +1 -1
- package/src/__tests__/GiftedChat.test.tsx +1 -1
- package/src/__tests__/InputToolbar.test.tsx +1 -1
- package/src/__tests__/LoadEarlier.test.tsx +2 -2
- package/src/__tests__/Message.test.tsx +7 -13
- package/src/__tests__/MessageContainer.test.tsx +4 -4
- package/src/__tests__/MessageImage.test.tsx +2 -2
- package/src/__tests__/MessageText.test.tsx +3 -2
- package/src/__tests__/Send.test.tsx +2 -2
- package/src/__tests__/SystemMessage.test.tsx +1 -1
- package/src/__tests__/Time.test.tsx +1 -1
- package/src/__tests__/__snapshots__/Actions.test.tsx.snap +2 -86
- package/src/__tests__/__snapshots__/Bubble.test.tsx.snap +1 -1
- package/src/__tests__/__snapshots__/Day.test.tsx.snap +96 -2
- package/src/__tests__/__snapshots__/InputToolbar.test.tsx.snap +1 -1
- package/src/__tests__/__snapshots__/LoadEarlier.test.tsx.snap +3 -89
- package/src/__tests__/__snapshots__/Message.test.tsx.snap +43 -4
- package/src/__tests__/__snapshots__/MessageText.test.tsx.snap +1 -1
- package/src/__tests__/__snapshots__/Send.test.tsx.snap +10 -142
- package/src/__tests__/data.ts +2 -2
- package/src/components/TouchableOpacity.tsx +19 -8
- package/src/index.ts +19 -1
- package/src/types.ts +1 -63
- package/src/utils.ts +23 -2
package/src/InputToolbar.tsx
CHANGED
|
@@ -2,10 +2,11 @@ import React, { useMemo } from 'react'
|
|
|
2
2
|
import { StyleSheet, View, StyleProp, ViewStyle, useColorScheme } from 'react-native'
|
|
3
3
|
|
|
4
4
|
import { Actions, ActionsProps } from './Actions'
|
|
5
|
-
import Color from './Color'
|
|
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 {
|
|
9
|
+
import { renderComponentOrElement } from './utils'
|
|
9
10
|
|
|
10
11
|
export interface InputToolbarProps<TMessage extends IMessage> {
|
|
11
12
|
actions?: Array<{ title: string, action: () => void }>
|
|
@@ -13,11 +14,11 @@ export interface InputToolbarProps<TMessage extends IMessage> {
|
|
|
13
14
|
containerStyle?: StyleProp<ViewStyle>
|
|
14
15
|
primaryStyle?: StyleProp<ViewStyle>
|
|
15
16
|
accessoryStyle?: StyleProp<ViewStyle>
|
|
16
|
-
renderAccessory
|
|
17
|
-
renderActions
|
|
18
|
-
renderSend
|
|
19
|
-
renderComposer
|
|
20
|
-
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
|
|
21
22
|
icon?: () => React.ReactNode
|
|
22
23
|
wrapperStyle?: StyleProp<ViewStyle>
|
|
23
24
|
}
|
|
@@ -41,7 +42,7 @@ export function InputToolbar<TMessage extends IMessage = IMessage> (
|
|
|
41
42
|
const colorScheme = useColorScheme()
|
|
42
43
|
|
|
43
44
|
const actionsFragment = useMemo(() => {
|
|
44
|
-
const
|
|
45
|
+
const actionsProps = {
|
|
45
46
|
onPressActionButton,
|
|
46
47
|
actions,
|
|
47
48
|
actionSheetOptionTintColor,
|
|
@@ -50,9 +51,13 @@ export function InputToolbar<TMessage extends IMessage = IMessage> (
|
|
|
50
51
|
containerStyle,
|
|
51
52
|
}
|
|
52
53
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
54
|
+
if (renderActions)
|
|
55
|
+
return renderComponentOrElement(renderActions, actionsProps)
|
|
56
|
+
|
|
57
|
+
if (onPressActionButton)
|
|
58
|
+
return <Actions {...actionsProps} />
|
|
59
|
+
|
|
60
|
+
return null
|
|
56
61
|
}, [
|
|
57
62
|
renderActions,
|
|
58
63
|
onPressActionButton,
|
|
@@ -64,15 +69,19 @@ export function InputToolbar<TMessage extends IMessage = IMessage> (
|
|
|
64
69
|
])
|
|
65
70
|
|
|
66
71
|
const composerFragment = useMemo(() => {
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
)
|
|
71
|
-
|
|
72
|
+
const composerProps = props as ComposerProps
|
|
73
|
+
|
|
74
|
+
if (renderComposer)
|
|
75
|
+
return renderComponentOrElement(renderComposer, composerProps)
|
|
76
|
+
|
|
77
|
+
return <Composer {...composerProps} />
|
|
72
78
|
}, [renderComposer, props])
|
|
73
79
|
|
|
74
80
|
const sendFragment = useMemo(() => {
|
|
75
|
-
|
|
81
|
+
if (renderSend)
|
|
82
|
+
return renderComponentOrElement(renderSend, props)
|
|
83
|
+
|
|
84
|
+
return <Send {...props} />
|
|
76
85
|
}, [renderSend, props])
|
|
77
86
|
|
|
78
87
|
const accessoryFragment = useMemo(() => {
|
|
@@ -81,13 +90,13 @@ export function InputToolbar<TMessage extends IMessage = IMessage> (
|
|
|
81
90
|
|
|
82
91
|
return (
|
|
83
92
|
<View style={[styles.accessory, props.accessoryStyle]}>
|
|
84
|
-
{renderAccessory
|
|
93
|
+
{renderComponentOrElement(renderAccessory, props)}
|
|
85
94
|
</View>
|
|
86
95
|
)
|
|
87
96
|
}, [renderAccessory, props])
|
|
88
97
|
|
|
89
98
|
return (
|
|
90
|
-
<View style={[styles.container, styles
|
|
99
|
+
<View style={[styles.container, colorScheme === 'dark' && styles.container_dark, containerStyle]}>
|
|
91
100
|
<View style={[styles.primary, props.primaryStyle]}>
|
|
92
101
|
{actionsFragment}
|
|
93
102
|
{composerFragment}
|
package/src/Message/index.tsx
CHANGED
|
@@ -1,18 +1,17 @@
|
|
|
1
|
-
import React, {
|
|
1
|
+
import React, { useCallback } from 'react'
|
|
2
2
|
import { View } from 'react-native'
|
|
3
3
|
|
|
4
4
|
import { Avatar } from '../Avatar'
|
|
5
|
-
import Bubble from '../Bubble'
|
|
5
|
+
import { Bubble } from '../Bubble'
|
|
6
|
+
import { IMessage } from '../Models'
|
|
6
7
|
import { SystemMessage } from '../SystemMessage'
|
|
7
|
-
|
|
8
|
-
import { IMessage } from '../types'
|
|
9
|
-
import { isSameUser } from '../utils'
|
|
8
|
+
import { isSameUser, renderComponentOrElement } from '../utils'
|
|
10
9
|
import styles from './styles'
|
|
11
10
|
import { MessageProps } from './types'
|
|
12
11
|
|
|
13
12
|
export * from './types'
|
|
14
13
|
|
|
15
|
-
const Message
|
|
14
|
+
export const Message = <TMessage extends IMessage = IMessage>(props: MessageProps<TMessage>) => {
|
|
16
15
|
const {
|
|
17
16
|
currentMessage,
|
|
18
17
|
renderBubble: renderBubbleProp,
|
|
@@ -22,7 +21,7 @@ const Message: React.FC<MessageProps<IMessage>> = (props: MessageProps<IMessage>
|
|
|
22
21
|
position,
|
|
23
22
|
containerStyle,
|
|
24
23
|
user,
|
|
25
|
-
|
|
24
|
+
isUserAvatarVisible,
|
|
26
25
|
} = props
|
|
27
26
|
|
|
28
27
|
const renderBubble = useCallback(() => {
|
|
@@ -35,7 +34,7 @@ const Message: React.FC<MessageProps<IMessage>> = (props: MessageProps<IMessage>
|
|
|
35
34
|
} = props
|
|
36
35
|
|
|
37
36
|
if (renderBubbleProp)
|
|
38
|
-
return renderBubbleProp
|
|
37
|
+
return renderComponentOrElement(renderBubbleProp, rest)
|
|
39
38
|
|
|
40
39
|
return <Bubble {...rest} />
|
|
41
40
|
}, [props, renderBubbleProp])
|
|
@@ -50,7 +49,7 @@ const Message: React.FC<MessageProps<IMessage>> = (props: MessageProps<IMessage>
|
|
|
50
49
|
} = props
|
|
51
50
|
|
|
52
51
|
if (renderSystemMessageProp)
|
|
53
|
-
return renderSystemMessageProp
|
|
52
|
+
return renderComponentOrElement(renderSystemMessageProp, rest)
|
|
54
53
|
|
|
55
54
|
return <SystemMessage {...rest} />
|
|
56
55
|
}, [props, renderSystemMessageProp])
|
|
@@ -60,7 +59,7 @@ const Message: React.FC<MessageProps<IMessage>> = (props: MessageProps<IMessage>
|
|
|
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 @@ const Message: React.FC<MessageProps<IMessage>> = (props: MessageProps<IMessage>
|
|
|
80
79
|
props,
|
|
81
80
|
user,
|
|
82
81
|
currentMessage,
|
|
83
|
-
|
|
82
|
+
isUserAvatarVisible,
|
|
84
83
|
])
|
|
85
84
|
|
|
86
85
|
if (!currentMessage)
|
|
@@ -99,7 +98,7 @@ const Message: React.FC<MessageProps<IMessage>> = (props: MessageProps<IMessage>
|
|
|
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
|
>
|
|
@@ -111,5 +110,3 @@ const Message: React.FC<MessageProps<IMessage>> = (props: MessageProps<IMessage>
|
|
|
111
110
|
</View>
|
|
112
111
|
)
|
|
113
112
|
}
|
|
114
|
-
|
|
115
|
-
export default memo(Message)
|
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
|
}
|
package/src/MessageAudio.tsx
CHANGED
|
@@ -11,7 +11,7 @@ import { DayAnimatedProps } from './types'
|
|
|
11
11
|
|
|
12
12
|
export * from './types'
|
|
13
13
|
|
|
14
|
-
const DayAnimated = ({ scrolledY, daysPositions, listHeight, renderDay, messages, isLoading, ...rest }: DayAnimatedProps) => {
|
|
14
|
+
export 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)
|
|
@@ -145,5 +145,3 @@ const DayAnimated = ({ scrolledY, daysPositions, listHeight, renderDay, messages
|
|
|
145
145
|
</Animated.View>
|
|
146
146
|
)
|
|
147
147
|
}
|
|
148
|
-
|
|
149
|
-
export default DayAnimated
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import React, {
|
|
1
|
+
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,
|
|
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'
|
|
@@ -68,7 +68,7 @@ export const useRelativeScrolledPositionToBottomOfDay = (
|
|
|
68
68
|
return relativeScrolledPositionToBottomOfDay
|
|
69
69
|
}
|
|
70
70
|
|
|
71
|
-
const DayWrapper =
|
|
71
|
+
const DayWrapper = <TMessage extends IMessage>(props: MessageProps<TMessage>) => {
|
|
72
72
|
const {
|
|
73
73
|
renderDay: renderDayProp,
|
|
74
74
|
currentMessage,
|
|
@@ -87,7 +87,7 @@ const DayWrapper = forwardRef<View, MessageProps<IMessage>>((props, ref) => {
|
|
|
87
87
|
} = props
|
|
88
88
|
|
|
89
89
|
return (
|
|
90
|
-
<View
|
|
90
|
+
<View>
|
|
91
91
|
{
|
|
92
92
|
renderDayProp
|
|
93
93
|
? renderDayProp({ ...rest, createdAt: currentMessage.createdAt })
|
|
@@ -95,9 +95,9 @@ const DayWrapper = forwardRef<View, MessageProps<IMessage>>((props, ref) => {
|
|
|
95
95
|
}
|
|
96
96
|
</View>
|
|
97
97
|
)
|
|
98
|
-
}
|
|
98
|
+
}
|
|
99
99
|
|
|
100
|
-
const Item = <TMessage extends IMessage>(props: ItemProps<TMessage>) => {
|
|
100
|
+
export const Item = <TMessage extends IMessage>(props: ItemProps<TMessage>) => {
|
|
101
101
|
const {
|
|
102
102
|
renderMessage: renderMessageProp,
|
|
103
103
|
scrolledY,
|
|
@@ -146,15 +146,13 @@ const Item = <TMessage extends IMessage>(props: ItemProps<TMessage>) => {
|
|
|
146
146
|
style={style}
|
|
147
147
|
onLayout={handleLayoutDayContainer}
|
|
148
148
|
>
|
|
149
|
-
<DayWrapper {...rest as MessageProps<TMessage>} />
|
|
149
|
+
<DayWrapper<TMessage> {...rest as MessageProps<TMessage>} />
|
|
150
150
|
</Animated.View>
|
|
151
151
|
{
|
|
152
152
|
renderMessageProp
|
|
153
153
|
? renderMessageProp(rest as MessageProps<TMessage>)
|
|
154
|
-
: <Message {...rest as MessageProps<TMessage>} />
|
|
154
|
+
: <Message<TMessage> {...rest as MessageProps<TMessage>} />
|
|
155
155
|
}
|
|
156
156
|
</View>
|
|
157
157
|
)
|
|
158
158
|
}
|
|
159
|
-
|
|
160
|
-
export default Item
|
|
@@ -11,15 +11,14 @@ 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 {
|
|
18
|
-
import TypingIndicator from '../TypingIndicator'
|
|
17
|
+
import { TypingIndicator } from '../TypingIndicator'
|
|
19
18
|
import { isSameDay, useCallbackThrottled } from '../utils'
|
|
20
|
-
import DayAnimated from './components/DayAnimated'
|
|
19
|
+
import { DayAnimated } from './components/DayAnimated'
|
|
21
20
|
|
|
22
|
-
import Item from './components/Item'
|
|
21
|
+
import { Item } from './components/Item'
|
|
23
22
|
import { ItemProps } from './components/Item/types'
|
|
24
23
|
import styles from './styles'
|
|
25
24
|
import { MessageContainerProps, DaysPositions } from './types'
|
|
@@ -29,29 +28,29 @@ export * from './types'
|
|
|
29
28
|
|
|
30
29
|
const AnimatedFlatList = Animated.createAnimatedComponent(FlatList) as React.ComponentType<any>
|
|
31
30
|
|
|
32
|
-
|
|
31
|
+
export const MessageContainer = <TMessage extends IMessage>(props: MessageContainerProps<TMessage>) => {
|
|
33
32
|
const {
|
|
34
33
|
messages = [],
|
|
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 @@ function MessageContainer<TMessage extends IMessage = IMessage> (props: MessageC
|
|
|
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 @@ function MessageContainer<TMessage extends IMessage = IMessage> (props: MessageC
|
|
|
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 @@ function MessageContainer<TMessage extends IMessage = IMessage> (props: MessageC
|
|
|
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 @@ function MessageContainer<TMessage extends IMessage = IMessage> (props: MessageC
|
|
|
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 @@ function MessageContainer<TMessage extends IMessage = IMessage> (props: MessageC
|
|
|
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 @@ function MessageContainer<TMessage extends IMessage = IMessage> (props: MessageC
|
|
|
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 @@ function MessageContainer<TMessage extends IMessage = IMessage> (props: MessageC
|
|
|
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 @@ function MessageContainer<TMessage extends IMessage = IMessage> (props: MessageC
|
|
|
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 @@ function MessageContainer<TMessage extends IMessage = IMessage> (props: MessageC
|
|
|
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 @@ function MessageContainer<TMessage extends IMessage = IMessage> (props: MessageC
|
|
|
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 @@ function MessageContainer<TMessage extends IMessage = IMessage> (props: MessageC
|
|
|
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 @@ function MessageContainer<TMessage extends IMessage = IMessage> (props: MessageC
|
|
|
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,35 @@ function MessageContainer<TMessage extends IMessage = IMessage> (props: MessageC
|
|
|
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}
|
|
407
404
|
{...listProps}
|
|
405
|
+
onScroll={scrollHandler}
|
|
408
406
|
onLayout={onLayoutList}
|
|
409
407
|
CellRendererComponent={renderCell}
|
|
410
408
|
/>
|
|
@@ -420,5 +418,3 @@ function MessageContainer<TMessage extends IMessage = IMessage> (props: MessageC
|
|
|
420
418
|
</View>
|
|
421
419
|
)
|
|
422
420
|
}
|
|
423
|
-
|
|
424
|
-
export default MessageContainer
|
|
@@ -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
|
}
|
package/src/MessageImage.tsx
CHANGED
|
@@ -16,8 +16,8 @@ import {
|
|
|
16
16
|
import { BaseButton, GestureHandlerRootView, Text } from 'react-native-gesture-handler'
|
|
17
17
|
import { useSafeAreaInsets } from 'react-native-safe-area-context'
|
|
18
18
|
import Zoom from 'react-native-zoom-reanimated'
|
|
19
|
+
import { IMessage } from './Models'
|
|
19
20
|
import commonStyles from './styles'
|
|
20
|
-
import { IMessage } from './types'
|
|
21
21
|
|
|
22
22
|
const styles = StyleSheet.create({
|
|
23
23
|
image: {
|
package/src/MessageText.tsx
CHANGED
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
|
|
10
10
|
import { Match } from 'autolinker/dist/es2015'
|
|
11
11
|
import Autolink, { AutolinkProps } from 'react-native-autolink'
|
|
12
|
-
import { LeftRightStyle, IMessage } from './
|
|
12
|
+
import { LeftRightStyle, IMessage } from './Models'
|
|
13
13
|
|
|
14
14
|
export type MessageTextProps<TMessage extends IMessage> = {
|
|
15
15
|
position?: 'left' | 'right'
|