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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-native-gifted-chat",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "3.0.0-alpha.1",
|
|
4
4
|
"description": "The most complete chat UI for React Native",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"android",
|
|
@@ -94,7 +94,7 @@
|
|
|
94
94
|
"react-dom": "19.1.0",
|
|
95
95
|
"react-native": "0.81.5",
|
|
96
96
|
"react-native-gesture-handler": "^2.29.1",
|
|
97
|
-
"react-native-keyboard-controller": "^1.19.
|
|
97
|
+
"react-native-keyboard-controller": "^1.19.6",
|
|
98
98
|
"react-native-reanimated": "^3.19.4",
|
|
99
99
|
"react-native-safe-area-context": "^5.6.2",
|
|
100
100
|
"react-test-renderer": "19.1.0",
|
package/src/Avatar.tsx
CHANGED
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
ViewStyle,
|
|
8
8
|
} from 'react-native'
|
|
9
9
|
import { GiftedAvatar } from './GiftedAvatar'
|
|
10
|
-
import { IMessage, LeftRightStyle, User } from './
|
|
10
|
+
import { IMessage, LeftRightStyle, User } from './Models'
|
|
11
11
|
import { isSameUser, isSameDay } from './utils'
|
|
12
12
|
|
|
13
13
|
interface Styles {
|
|
@@ -57,8 +57,8 @@ export interface AvatarProps<TMessage extends IMessage> {
|
|
|
57
57
|
previousMessage?: TMessage
|
|
58
58
|
nextMessage?: TMessage
|
|
59
59
|
position: 'left' | 'right'
|
|
60
|
-
|
|
61
|
-
|
|
60
|
+
isAvatarOnTop?: boolean
|
|
61
|
+
isAvatarVisibleForEveryMessage?: boolean
|
|
62
62
|
imageStyle?: LeftRightStyle<ImageStyle>
|
|
63
63
|
containerStyle?: LeftRightStyle<ViewStyle>
|
|
64
64
|
textStyle?: TextStyle
|
|
@@ -71,8 +71,8 @@ export function Avatar<TMessage extends IMessage = IMessage> (
|
|
|
71
71
|
props: AvatarProps<TMessage>
|
|
72
72
|
) {
|
|
73
73
|
const {
|
|
74
|
-
|
|
75
|
-
|
|
74
|
+
isAvatarOnTop,
|
|
75
|
+
isAvatarVisibleForEveryMessage,
|
|
76
76
|
containerStyle,
|
|
77
77
|
position,
|
|
78
78
|
currentMessage,
|
|
@@ -84,13 +84,13 @@ export function Avatar<TMessage extends IMessage = IMessage> (
|
|
|
84
84
|
onLongPressAvatar,
|
|
85
85
|
} = props
|
|
86
86
|
|
|
87
|
-
const messageToCompare =
|
|
87
|
+
const messageToCompare = isAvatarOnTop ? previousMessage : nextMessage
|
|
88
88
|
|
|
89
89
|
const renderAvatarComponent = useCallback(() => {
|
|
90
90
|
if (renderAvatar)
|
|
91
91
|
return renderAvatar({
|
|
92
|
-
|
|
93
|
-
|
|
92
|
+
isAvatarOnTop,
|
|
93
|
+
isAvatarVisibleForEveryMessage,
|
|
94
94
|
containerStyle,
|
|
95
95
|
position,
|
|
96
96
|
currentMessage,
|
|
@@ -117,8 +117,8 @@ export function Avatar<TMessage extends IMessage = IMessage> (
|
|
|
117
117
|
return null
|
|
118
118
|
}, [
|
|
119
119
|
renderAvatar,
|
|
120
|
-
|
|
121
|
-
|
|
120
|
+
isAvatarOnTop,
|
|
121
|
+
isAvatarVisibleForEveryMessage,
|
|
122
122
|
containerStyle,
|
|
123
123
|
position,
|
|
124
124
|
currentMessage,
|
|
@@ -133,7 +133,7 @@ export function Avatar<TMessage extends IMessage = IMessage> (
|
|
|
133
133
|
return null
|
|
134
134
|
|
|
135
135
|
if (
|
|
136
|
-
!
|
|
136
|
+
!isAvatarVisibleForEveryMessage &&
|
|
137
137
|
currentMessage &&
|
|
138
138
|
messageToCompare &&
|
|
139
139
|
isSameUser(currentMessage, messageToCompare) &&
|
|
@@ -159,7 +159,7 @@ export function Avatar<TMessage extends IMessage = IMessage> (
|
|
|
159
159
|
<View
|
|
160
160
|
style={[
|
|
161
161
|
styles[position].container,
|
|
162
|
-
|
|
162
|
+
isAvatarOnTop && styles[position].onTop,
|
|
163
163
|
containerStyle?.[position],
|
|
164
164
|
]}
|
|
165
165
|
>
|
package/src/Bubble/index.tsx
CHANGED
|
@@ -10,11 +10,11 @@ import { MessageAudio } from '../MessageAudio'
|
|
|
10
10
|
import { MessageImage } from '../MessageImage'
|
|
11
11
|
import { MessageText } from '../MessageText'
|
|
12
12
|
import { MessageVideo } from '../MessageVideo'
|
|
13
|
+
import { IMessage } from '../Models'
|
|
13
14
|
import { QuickReplies } from '../QuickReplies'
|
|
14
15
|
import stylesCommon from '../styles'
|
|
15
16
|
|
|
16
17
|
import { Time } from '../Time'
|
|
17
|
-
import { IMessage } from '../types'
|
|
18
18
|
import { isSameUser, isSameDay, renderComponentOrElement } from '../utils'
|
|
19
19
|
|
|
20
20
|
import styles from './styles'
|
|
@@ -284,7 +284,7 @@ export const Bubble = <TMessage extends IMessage = IMessage>(props: BubbleProps<
|
|
|
284
284
|
renderUsername,
|
|
285
285
|
} = props
|
|
286
286
|
|
|
287
|
-
if (props.
|
|
287
|
+
if (props.isUsernameVisible && currentMessage) {
|
|
288
288
|
if (user && currentMessage.user._id === user._id)
|
|
289
289
|
return null
|
|
290
290
|
|
package/src/Bubble/types.ts
CHANGED
|
@@ -6,8 +6,6 @@ import {
|
|
|
6
6
|
} from 'react-native'
|
|
7
7
|
import { MessageImageProps } from '../MessageImage'
|
|
8
8
|
import { MessageTextProps } from '../MessageText'
|
|
9
|
-
import { QuickRepliesProps } from '../QuickReplies'
|
|
10
|
-
import { TimeProps } from '../Time'
|
|
11
9
|
import {
|
|
12
10
|
User,
|
|
13
11
|
IMessage,
|
|
@@ -16,7 +14,9 @@ import {
|
|
|
16
14
|
Omit,
|
|
17
15
|
MessageVideoProps,
|
|
18
16
|
MessageAudioProps,
|
|
19
|
-
} from '../
|
|
17
|
+
} from '../Models'
|
|
18
|
+
import { QuickRepliesProps } from '../QuickReplies'
|
|
19
|
+
import { TimeProps } from '../Time'
|
|
20
20
|
|
|
21
21
|
|
|
22
22
|
export type RenderMessageImageProps<TMessage extends IMessage> = Omit<
|
|
@@ -47,9 +47,9 @@ export type RenderMessageTextProps<TMessage extends IMessage> = Omit<
|
|
|
47
47
|
export interface BubbleProps<TMessage extends IMessage> {
|
|
48
48
|
user?: User
|
|
49
49
|
touchableProps?: object
|
|
50
|
-
|
|
50
|
+
isUsernameVisible?: boolean
|
|
51
51
|
isCustomViewBottom?: boolean
|
|
52
|
-
|
|
52
|
+
isInverted?: boolean
|
|
53
53
|
position: 'left' | 'right'
|
|
54
54
|
currentMessage: TMessage
|
|
55
55
|
nextMessage?: TMessage
|
|
@@ -66,25 +66,25 @@ export interface BubbleProps<TMessage extends IMessage> {
|
|
|
66
66
|
quickReplyTextStyle?: StyleProp<TextStyle>
|
|
67
67
|
quickReplyContainerStyle?: StyleProp<ViewStyle>
|
|
68
68
|
messageTextProps?: Partial<MessageTextProps<TMessage>>
|
|
69
|
-
onPressMessage
|
|
70
|
-
onLongPressMessage
|
|
71
|
-
onQuickReply
|
|
72
|
-
renderMessageImage
|
|
69
|
+
onPressMessage?: (context?: unknown, message?: unknown) => void
|
|
70
|
+
onLongPressMessage?: (context?: unknown, message?: unknown) => void
|
|
71
|
+
onQuickReply?: (replies: Reply[]) => void
|
|
72
|
+
renderMessageImage?: (
|
|
73
73
|
props: RenderMessageImageProps<TMessage>,
|
|
74
|
-
)
|
|
75
|
-
renderMessageVideo
|
|
74
|
+
) => React.ReactNode
|
|
75
|
+
renderMessageVideo?: (
|
|
76
76
|
props: RenderMessageVideoProps<TMessage>,
|
|
77
|
-
)
|
|
78
|
-
renderMessageAudio
|
|
77
|
+
) => React.ReactNode
|
|
78
|
+
renderMessageAudio?: (
|
|
79
79
|
props: RenderMessageAudioProps<TMessage>,
|
|
80
|
-
)
|
|
81
|
-
renderMessageText
|
|
82
|
-
renderCustomView
|
|
83
|
-
renderTime
|
|
84
|
-
renderTicks
|
|
85
|
-
renderUsername
|
|
86
|
-
renderQuickReplySend
|
|
87
|
-
renderQuickReplies
|
|
80
|
+
) => React.ReactNode
|
|
81
|
+
renderMessageText?: (props: RenderMessageTextProps<TMessage>) => React.ReactNode
|
|
82
|
+
renderCustomView?: (bubbleProps: BubbleProps<TMessage>) => React.ReactNode
|
|
83
|
+
renderTime?: (timeProps: TimeProps<TMessage>) => React.ReactNode
|
|
84
|
+
renderTicks?: (currentMessage: TMessage) => React.ReactNode
|
|
85
|
+
renderUsername?: (user?: TMessage['user']) => React.ReactNode
|
|
86
|
+
renderQuickReplySend?: () => React.ReactNode
|
|
87
|
+
renderQuickReplies?: (
|
|
88
88
|
quickReplies: QuickRepliesProps<TMessage>,
|
|
89
|
-
)
|
|
89
|
+
) => React.ReactNode
|
|
90
90
|
}
|
package/src/Composer.tsx
CHANGED
|
@@ -16,14 +16,12 @@ export interface ComposerProps {
|
|
|
16
16
|
composerHeight?: number
|
|
17
17
|
text?: string
|
|
18
18
|
textInputProps?: Partial<TextInputProps>
|
|
19
|
-
onTextChanged?(text: string): void
|
|
20
19
|
onInputSizeChanged?(layout: { width: number, height: number }): void
|
|
21
20
|
}
|
|
22
21
|
|
|
23
22
|
export function Composer ({
|
|
24
23
|
composerHeight = MIN_COMPOSER_HEIGHT,
|
|
25
24
|
onInputSizeChanged,
|
|
26
|
-
onTextChanged,
|
|
27
25
|
text = '',
|
|
28
26
|
textInputProps,
|
|
29
27
|
}: ComposerProps): React.ReactElement {
|
|
@@ -67,7 +65,6 @@ export function Composer ({
|
|
|
67
65
|
accessibilityLabel={placeholder}
|
|
68
66
|
placeholderTextColor={textInputProps?.placeholderTextColor ?? (isDark ? '#888' : Color.defaultColor)}
|
|
69
67
|
onContentSizeChange={handleContentSizeChange}
|
|
70
|
-
onChangeText={onTextChanged}
|
|
71
68
|
value={text}
|
|
72
69
|
enablesReturnKeyAutomatically
|
|
73
70
|
underlineColorAndroid='transparent'
|
package/src/GiftedAvatar.tsx
CHANGED
|
@@ -10,8 +10,8 @@ import {
|
|
|
10
10
|
} from 'react-native'
|
|
11
11
|
import { Color } from './Color'
|
|
12
12
|
import { TouchableOpacity } from './components/TouchableOpacity'
|
|
13
|
+
import { User } from './Models'
|
|
13
14
|
import stylesCommon from './styles'
|
|
14
|
-
import { User } from './types'
|
|
15
15
|
|
|
16
16
|
const {
|
|
17
17
|
carrot,
|
package/src/GiftedChat/index.tsx
CHANGED
|
@@ -19,23 +19,14 @@ import {
|
|
|
19
19
|
import dayjs from 'dayjs'
|
|
20
20
|
import localizedFormat from 'dayjs/plugin/localizedFormat'
|
|
21
21
|
import { GestureHandlerRootView } from 'react-native-gesture-handler'
|
|
22
|
-
import {
|
|
23
|
-
import Animated, {
|
|
24
|
-
useAnimatedStyle,
|
|
25
|
-
useAnimatedReaction,
|
|
26
|
-
useSharedValue,
|
|
27
|
-
withTiming,
|
|
28
|
-
runOnJS,
|
|
29
|
-
} from 'react-native-reanimated'
|
|
22
|
+
import { KeyboardAvoidingView, KeyboardProvider } from 'react-native-keyboard-controller'
|
|
30
23
|
import { SafeAreaProvider } from 'react-native-safe-area-context'
|
|
31
24
|
import { MAX_COMPOSER_HEIGHT, MIN_COMPOSER_HEIGHT, TEST_ID } from '../Constant'
|
|
32
25
|
import { GiftedChatContext } from '../GiftedChatContext'
|
|
33
26
|
import { InputToolbar } from '../InputToolbar'
|
|
34
27
|
import { MessageContainer, AnimatedList } from '../MessageContainer'
|
|
28
|
+
import { IMessage } from '../Models'
|
|
35
29
|
import stylesCommon from '../styles'
|
|
36
|
-
import {
|
|
37
|
-
IMessage,
|
|
38
|
-
} from '../types'
|
|
39
30
|
import { renderComponentOrElement } from '../utils'
|
|
40
31
|
import styles from './styles'
|
|
41
32
|
import { GiftedChatProps } from './types'
|
|
@@ -63,13 +54,9 @@ function GiftedChat<TMessage extends IMessage = IMessage> (
|
|
|
63
54
|
textInputProps,
|
|
64
55
|
renderChatFooter,
|
|
65
56
|
renderInputToolbar,
|
|
66
|
-
|
|
67
|
-
focusOnInputWhenOpeningKeyboard = true,
|
|
68
|
-
onInputTextChanged,
|
|
69
|
-
inverted = true,
|
|
57
|
+
isInverted = true,
|
|
70
58
|
minComposerHeight = MIN_COMPOSER_HEIGHT,
|
|
71
59
|
maxComposerHeight = MAX_COMPOSER_HEIGHT,
|
|
72
|
-
isKeyboardInternallyHandled = true,
|
|
73
60
|
} = props
|
|
74
61
|
|
|
75
62
|
const actionSheetRef = useRef<ActionSheetProviderRef>(null)
|
|
@@ -84,37 +71,12 @@ function GiftedChat<TMessage extends IMessage = IMessage> (
|
|
|
84
71
|
[props.textInputRef]
|
|
85
72
|
)
|
|
86
73
|
|
|
87
|
-
const isTextInputWasFocused: RefObject<boolean> = useRef(false)
|
|
88
|
-
|
|
89
74
|
const [isInitialized, setIsInitialized] = useState<boolean>(false)
|
|
90
75
|
const [composerHeight, setComposerHeight] = useState<number>(
|
|
91
76
|
minComposerHeight!
|
|
92
77
|
)
|
|
93
78
|
const [text, setText] = useState<string | undefined>(() => props.text || '')
|
|
94
79
|
|
|
95
|
-
// Always call the hook, but conditionally use its data
|
|
96
|
-
const keyboardControllerData = useReanimatedKeyboardAnimation()
|
|
97
|
-
|
|
98
|
-
// Create a mock keyboard object when keyboard is not internally handled
|
|
99
|
-
const keyboard = useMemo(() => {
|
|
100
|
-
if (!isKeyboardInternallyHandled)
|
|
101
|
-
return { height: { value: 0 } }
|
|
102
|
-
|
|
103
|
-
return keyboardControllerData
|
|
104
|
-
}, [isKeyboardInternallyHandled, keyboardControllerData])
|
|
105
|
-
|
|
106
|
-
const trackingKeyboardMovement = useSharedValue(false)
|
|
107
|
-
const keyboardBottomOffsetAnim = useSharedValue(0)
|
|
108
|
-
|
|
109
|
-
const contentStyleAnim = useAnimatedStyle(
|
|
110
|
-
() => ({
|
|
111
|
-
transform: [
|
|
112
|
-
{ translateY: keyboard.height.value + keyboardBottomOffsetAnim.value },
|
|
113
|
-
],
|
|
114
|
-
}),
|
|
115
|
-
[keyboard, keyboardBottomOffsetAnim]
|
|
116
|
-
)
|
|
117
|
-
|
|
118
80
|
const getTextFromProp = useCallback(
|
|
119
81
|
(fallback: string) => {
|
|
120
82
|
if (props.text === undefined)
|
|
@@ -125,40 +87,12 @@ function GiftedChat<TMessage extends IMessage = IMessage> (
|
|
|
125
87
|
[props.text]
|
|
126
88
|
)
|
|
127
89
|
|
|
128
|
-
/**
|
|
129
|
-
* Store text input focus status when keyboard hide to retrieve
|
|
130
|
-
* it afterwards if needed.
|
|
131
|
-
* `onKeyboardWillHide` may be called twice in sequence so we
|
|
132
|
-
* make a guard condition (eg. showing image picker)
|
|
133
|
-
*/
|
|
134
|
-
const handleTextInputFocusWhenKeyboardHide = useCallback(() => {
|
|
135
|
-
if (!isTextInputWasFocused.current)
|
|
136
|
-
isTextInputWasFocused.current =
|
|
137
|
-
textInputRef.current?.isFocused() || false
|
|
138
|
-
}, [textInputRef])
|
|
139
|
-
|
|
140
|
-
/**
|
|
141
|
-
* Refocus the text input only if it was focused before showing keyboard.
|
|
142
|
-
* This is needed in some cases (eg. showing image picker).
|
|
143
|
-
*/
|
|
144
|
-
const handleTextInputFocusWhenKeyboardShow = useCallback(() => {
|
|
145
|
-
if (
|
|
146
|
-
textInputRef.current &&
|
|
147
|
-
isTextInputWasFocused.current &&
|
|
148
|
-
!textInputRef.current.isFocused()
|
|
149
|
-
)
|
|
150
|
-
textInputRef.current.focus()
|
|
151
|
-
|
|
152
|
-
// Reset the indicator since the keyboard is shown
|
|
153
|
-
isTextInputWasFocused.current = false
|
|
154
|
-
}, [textInputRef])
|
|
155
|
-
|
|
156
90
|
const scrollToBottom = useCallback(
|
|
157
91
|
(isAnimated = true) => {
|
|
158
92
|
if (!messageContainerRef?.current)
|
|
159
93
|
return
|
|
160
94
|
|
|
161
|
-
if (
|
|
95
|
+
if (isInverted) {
|
|
162
96
|
messageContainerRef.current.scrollToOffset({
|
|
163
97
|
offset: 0,
|
|
164
98
|
animated: isAnimated,
|
|
@@ -168,7 +102,7 @@ function GiftedChat<TMessage extends IMessage = IMessage> (
|
|
|
168
102
|
|
|
169
103
|
messageContainerRef.current.scrollToEnd({ animated: isAnimated })
|
|
170
104
|
},
|
|
171
|
-
[
|
|
105
|
+
[isInverted, messageContainerRef]
|
|
172
106
|
)
|
|
173
107
|
|
|
174
108
|
const renderMessages = useMemo(() => {
|
|
@@ -181,7 +115,7 @@ function GiftedChat<TMessage extends IMessage = IMessage> (
|
|
|
181
115
|
<View style={[stylesCommon.fill, messagesContainerStyle]}>
|
|
182
116
|
<MessageContainer<TMessage>
|
|
183
117
|
{...messagesContainerProps}
|
|
184
|
-
|
|
118
|
+
isInverted={isInverted}
|
|
185
119
|
messages={messages}
|
|
186
120
|
forwardRef={messageContainerRef}
|
|
187
121
|
isTyping={isTyping}
|
|
@@ -194,14 +128,14 @@ function GiftedChat<TMessage extends IMessage = IMessage> (
|
|
|
194
128
|
isTyping,
|
|
195
129
|
messages,
|
|
196
130
|
props,
|
|
197
|
-
|
|
131
|
+
isInverted,
|
|
198
132
|
messageContainerRef,
|
|
199
133
|
renderChatFooter,
|
|
200
134
|
])
|
|
201
135
|
|
|
202
136
|
const notifyInputTextReset = useCallback(() => {
|
|
203
|
-
|
|
204
|
-
}, [
|
|
137
|
+
props.textInputProps?.onChangeText?.('')
|
|
138
|
+
}, [props.textInputProps])
|
|
205
139
|
|
|
206
140
|
const resetInputToolbar = useCallback(() => {
|
|
207
141
|
textInputRef.current?.clear()
|
|
@@ -253,15 +187,15 @@ function GiftedChat<TMessage extends IMessage = IMessage> (
|
|
|
253
187
|
[maxComposerHeight, minComposerHeight]
|
|
254
188
|
)
|
|
255
189
|
|
|
256
|
-
const
|
|
190
|
+
const _onChangeText = useCallback(
|
|
257
191
|
(text: string) => {
|
|
258
|
-
|
|
192
|
+
props.textInputProps?.onChangeText?.(text)
|
|
259
193
|
|
|
260
194
|
// Only set state if it's not being overridden by a prop.
|
|
261
195
|
if (props.text === undefined)
|
|
262
196
|
setText(text)
|
|
263
197
|
},
|
|
264
|
-
[
|
|
198
|
+
[props.text, props.textInputProps]
|
|
265
199
|
)
|
|
266
200
|
|
|
267
201
|
const onInitialLayoutViewLayout = useCallback(
|
|
@@ -293,9 +227,9 @@ function GiftedChat<TMessage extends IMessage = IMessage> (
|
|
|
293
227
|
composerHeight: Math.max(minComposerHeight!, composerHeight),
|
|
294
228
|
onSend: _onSend,
|
|
295
229
|
onInputSizeChanged,
|
|
296
|
-
onTextChanged: _onInputTextChanged,
|
|
297
230
|
textInputProps: {
|
|
298
231
|
...textInputProps,
|
|
232
|
+
onChangeText: _onChangeText,
|
|
299
233
|
ref: textInputRef,
|
|
300
234
|
},
|
|
301
235
|
}
|
|
@@ -316,7 +250,7 @@ function GiftedChat<TMessage extends IMessage = IMessage> (
|
|
|
316
250
|
composerHeight,
|
|
317
251
|
textInputRef,
|
|
318
252
|
textInputProps,
|
|
319
|
-
|
|
253
|
+
_onChangeText,
|
|
320
254
|
])
|
|
321
255
|
|
|
322
256
|
const contextValues = useMemo(
|
|
@@ -337,90 +271,67 @@ function GiftedChat<TMessage extends IMessage = IMessage> (
|
|
|
337
271
|
setText(props.text)
|
|
338
272
|
}, [props.text])
|
|
339
273
|
|
|
340
|
-
// Only set up keyboard animation when keyboard is internally handled
|
|
341
|
-
useAnimatedReaction(
|
|
342
|
-
() => isKeyboardInternallyHandled ? keyboard.height.value : 0,
|
|
343
|
-
(value, prevValue) => {
|
|
344
|
-
// Skip keyboard handling when not internally handled
|
|
345
|
-
if (!isKeyboardInternallyHandled)
|
|
346
|
-
return
|
|
347
|
-
|
|
348
|
-
if (prevValue !== null && value !== prevValue) {
|
|
349
|
-
const isKeyboardMovingUp = value < prevValue
|
|
350
|
-
if (isKeyboardMovingUp !== trackingKeyboardMovement.value) {
|
|
351
|
-
trackingKeyboardMovement.value = isKeyboardMovingUp
|
|
352
|
-
keyboardBottomOffsetAnim.value = withTiming(
|
|
353
|
-
isKeyboardMovingUp ? keyboardBottomOffset : 0,
|
|
354
|
-
{
|
|
355
|
-
// If `keyboardBottomOffset` exists, we change the duration to a smaller value to fix the delay in the keyboard animation speed
|
|
356
|
-
duration: keyboardBottomOffset ? 150 : 400,
|
|
357
|
-
}
|
|
358
|
-
)
|
|
359
|
-
|
|
360
|
-
if (focusOnInputWhenOpeningKeyboard)
|
|
361
|
-
if (isKeyboardMovingUp)
|
|
362
|
-
runOnJS(handleTextInputFocusWhenKeyboardShow)()
|
|
363
|
-
else
|
|
364
|
-
runOnJS(handleTextInputFocusWhenKeyboardHide)()
|
|
365
|
-
}
|
|
366
|
-
}
|
|
367
|
-
},
|
|
368
|
-
[
|
|
369
|
-
keyboard,
|
|
370
|
-
trackingKeyboardMovement,
|
|
371
|
-
focusOnInputWhenOpeningKeyboard,
|
|
372
|
-
handleTextInputFocusWhenKeyboardHide,
|
|
373
|
-
handleTextInputFocusWhenKeyboardShow,
|
|
374
|
-
keyboardBottomOffset,
|
|
375
|
-
isKeyboardInternallyHandled,
|
|
376
|
-
]
|
|
377
|
-
)
|
|
378
|
-
|
|
379
274
|
return (
|
|
380
275
|
<GiftedChatContext.Provider value={contextValues}>
|
|
381
276
|
<ActionSheetProvider ref={actionSheetRef}>
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
277
|
+
{/* @ts-expect-error */}
|
|
278
|
+
<KeyboardAvoidingView
|
|
279
|
+
behavior='padding'
|
|
280
|
+
style={stylesCommon.fill}
|
|
281
|
+
{...props.keyboardAvoidingViewProps}
|
|
386
282
|
>
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
283
|
+
<View
|
|
284
|
+
testID={TEST_ID.WRAPPER}
|
|
285
|
+
style={[stylesCommon.fill, styles.contentContainer]}
|
|
286
|
+
onLayout={onInitialLayoutViewLayout}
|
|
287
|
+
>
|
|
288
|
+
{isInitialized
|
|
289
|
+
? (
|
|
290
|
+
<>
|
|
291
|
+
{renderMessages}
|
|
292
|
+
{inputToolbarFragment}
|
|
293
|
+
</>
|
|
294
|
+
)
|
|
295
|
+
: (
|
|
296
|
+
renderComponentOrElement(renderLoading, {})
|
|
297
|
+
)}
|
|
298
|
+
</View>
|
|
299
|
+
</KeyboardAvoidingView>
|
|
398
300
|
</ActionSheetProvider>
|
|
399
301
|
</GiftedChatContext.Provider>
|
|
400
302
|
)
|
|
401
303
|
}
|
|
402
304
|
|
|
403
305
|
function GiftedChatWrapper<TMessage extends IMessage = IMessage> (props: GiftedChatProps<TMessage>) {
|
|
306
|
+
const {
|
|
307
|
+
keyboardProviderProps,
|
|
308
|
+
...rest
|
|
309
|
+
} = props
|
|
310
|
+
|
|
404
311
|
return (
|
|
405
|
-
<
|
|
406
|
-
<
|
|
407
|
-
<
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
312
|
+
<GestureHandlerRootView style={styles.fill}>
|
|
313
|
+
<SafeAreaProvider>
|
|
314
|
+
<KeyboardProvider
|
|
315
|
+
statusBarTranslucent
|
|
316
|
+
navigationBarTranslucent
|
|
317
|
+
{...keyboardProviderProps}
|
|
318
|
+
>
|
|
319
|
+
<GiftedChat<TMessage> {...rest} />
|
|
320
|
+
</KeyboardProvider>
|
|
321
|
+
</SafeAreaProvider>
|
|
322
|
+
</GestureHandlerRootView>
|
|
412
323
|
)
|
|
413
324
|
}
|
|
414
325
|
|
|
415
326
|
GiftedChatWrapper.append = <TMessage extends IMessage>(
|
|
416
327
|
currentMessages: TMessage[] = [],
|
|
417
328
|
messages: TMessage[],
|
|
418
|
-
|
|
329
|
+
isInverted = true
|
|
419
330
|
) => {
|
|
420
331
|
if (!Array.isArray(messages))
|
|
421
332
|
messages = [messages]
|
|
422
333
|
|
|
423
|
-
return
|
|
334
|
+
return isInverted
|
|
424
335
|
? messages.concat(currentMessages)
|
|
425
336
|
: currentMessages.concat(messages)
|
|
426
337
|
}
|
|
@@ -428,12 +339,12 @@ GiftedChatWrapper.append = <TMessage extends IMessage>(
|
|
|
428
339
|
GiftedChatWrapper.prepend = <TMessage extends IMessage>(
|
|
429
340
|
currentMessages: TMessage[] = [],
|
|
430
341
|
messages: TMessage[],
|
|
431
|
-
|
|
342
|
+
isInverted = true
|
|
432
343
|
) => {
|
|
433
344
|
if (!Array.isArray(messages))
|
|
434
345
|
messages = [messages]
|
|
435
346
|
|
|
436
|
-
return
|
|
347
|
+
return isInverted
|
|
437
348
|
? currentMessages.concat(messages)
|
|
438
349
|
: messages.concat(currentMessages)
|
|
439
350
|
}
|