react-native-gifted-chat 3.0.0-alpha.0 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +295 -0
- package/README.md +71 -95
- package/package.json +2 -2
- package/src/Actions.tsx +27 -18
- package/src/Composer.tsx +21 -84
- package/src/Constant.ts +0 -9
- package/src/GiftedChat/index.tsx +52 -163
- package/src/GiftedChat/types.ts +11 -12
- package/src/InputToolbar.tsx +6 -11
- package/src/MessageImage.tsx +135 -65
- package/src/{MessageContainer → MessagesContainer}/components/Item/index.tsx +21 -17
- package/src/{MessageContainer → MessagesContainer}/components/Item/types.ts +3 -2
- package/src/{MessageContainer → MessagesContainer}/index.tsx +18 -14
- package/src/{MessageContainer → MessagesContainer}/styles.ts +12 -5
- package/src/{MessageContainer → MessagesContainer}/types.ts +4 -2
- package/src/Send.tsx +40 -22
- package/src/__tests__/DayAnimated.test.tsx +1 -1
- package/src/__tests__/GiftedChat.test.tsx +0 -28
- package/src/__tests__/{MessageContainer.test.tsx → MessagesContainer.test.tsx} +7 -7
- package/src/__tests__/__snapshots__/Actions.test.tsx.snap +31 -23
- package/src/__tests__/__snapshots__/Composer.test.tsx.snap +29 -30
- package/src/__tests__/__snapshots__/Constant.test.tsx.snap +0 -2
- package/src/__tests__/__snapshots__/GiftedChat.test.tsx.snap +31 -54
- package/src/__tests__/__snapshots__/InputToolbar.test.tsx.snap +102 -31
- package/src/__tests__/__snapshots__/MessageImage.test.tsx.snap +252 -1
- package/src/__tests__/__snapshots__/Send.test.tsx.snap +189 -49
- package/src/index.ts +1 -1
- package/src/styles.ts +5 -0
- package/src/types.ts +1 -1
- package/CHANGELOG_2.8.1_to_2.8.2-alpha.5.md +0 -374
- /package/src/{MessageContainer → MessagesContainer}/components/DayAnimated/index.tsx +0 -0
- /package/src/{MessageContainer → MessagesContainer}/components/DayAnimated/styles.ts +0 -0
- /package/src/{MessageContainer → MessagesContainer}/components/DayAnimated/types.ts +0 -0
package/src/Actions.tsx
CHANGED
|
@@ -19,7 +19,7 @@ export interface ActionsProps {
|
|
|
19
19
|
icon?: () => ReactNode
|
|
20
20
|
wrapperStyle?: StyleProp<ViewStyle>
|
|
21
21
|
iconTextStyle?: StyleProp<TextStyle>
|
|
22
|
-
|
|
22
|
+
buttonStyle?: StyleProp<ViewStyle>
|
|
23
23
|
onPressActionButton?(): void
|
|
24
24
|
}
|
|
25
25
|
|
|
@@ -30,11 +30,16 @@ export function Actions ({
|
|
|
30
30
|
wrapperStyle,
|
|
31
31
|
iconTextStyle,
|
|
32
32
|
onPressActionButton,
|
|
33
|
-
|
|
33
|
+
buttonStyle,
|
|
34
34
|
}: ActionsProps) {
|
|
35
35
|
const { actionSheet } = useChatContext()
|
|
36
36
|
|
|
37
|
-
const
|
|
37
|
+
const handlePress = useCallback(() => {
|
|
38
|
+
if (onPressActionButton) {
|
|
39
|
+
onPressActionButton()
|
|
40
|
+
return
|
|
41
|
+
}
|
|
42
|
+
|
|
38
43
|
if (!actions?.length)
|
|
39
44
|
return
|
|
40
45
|
|
|
@@ -54,47 +59,51 @@ export function Actions ({
|
|
|
54
59
|
item.action?.()
|
|
55
60
|
}
|
|
56
61
|
)
|
|
57
|
-
}, [actionSheet, actions, actionSheetOptionTintColor])
|
|
62
|
+
}, [actionSheet, actions, actionSheetOptionTintColor, onPressActionButton])
|
|
58
63
|
|
|
59
64
|
const renderIcon = useCallback(() => {
|
|
60
65
|
if (icon)
|
|
61
66
|
return icon()
|
|
62
67
|
|
|
63
68
|
return (
|
|
64
|
-
<View style={[stylesCommon.
|
|
69
|
+
<View style={[stylesCommon.centerItems, styles.wrapper, wrapperStyle]}>
|
|
65
70
|
<Text style={[styles.iconText, iconTextStyle]}>{'+'}</Text>
|
|
66
71
|
</View>
|
|
67
72
|
)
|
|
68
73
|
}, [icon, iconTextStyle, wrapperStyle])
|
|
69
74
|
|
|
70
75
|
return (
|
|
71
|
-
<
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
76
|
+
<View style={styles.container}>
|
|
77
|
+
<TouchableOpacity
|
|
78
|
+
onPress={handlePress}
|
|
79
|
+
style={[styles.button, buttonStyle]}
|
|
80
|
+
>
|
|
81
|
+
{renderIcon()}
|
|
82
|
+
</TouchableOpacity>
|
|
83
|
+
</View>
|
|
77
84
|
)
|
|
78
85
|
}
|
|
79
86
|
|
|
80
87
|
const styles = StyleSheet.create({
|
|
81
88
|
container: {
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
89
|
+
alignItems: 'flex-end',
|
|
90
|
+
},
|
|
91
|
+
button: {
|
|
92
|
+
paddingHorizontal: 8,
|
|
93
|
+
paddingVertical: 7,
|
|
86
94
|
},
|
|
95
|
+
|
|
87
96
|
wrapper: {
|
|
88
|
-
borderRadius: 13,
|
|
89
97
|
borderColor: Color.defaultColor,
|
|
90
98
|
borderWidth: 2,
|
|
99
|
+
width: 26,
|
|
100
|
+
height: 26,
|
|
101
|
+
borderRadius: 13,
|
|
91
102
|
},
|
|
92
103
|
iconText: {
|
|
93
104
|
color: Color.defaultColor,
|
|
94
105
|
fontWeight: 'bold',
|
|
95
106
|
fontSize: 16,
|
|
96
107
|
lineHeight: 16,
|
|
97
|
-
backgroundColor: Color.backgroundTransparent,
|
|
98
|
-
textAlign: 'center',
|
|
99
108
|
},
|
|
100
109
|
})
|
package/src/Composer.tsx
CHANGED
|
@@ -1,118 +1,55 @@
|
|
|
1
|
-
import React
|
|
1
|
+
import React from 'react'
|
|
2
2
|
import {
|
|
3
|
-
Platform,
|
|
4
3
|
StyleSheet,
|
|
5
4
|
TextInput,
|
|
6
5
|
TextInputProps,
|
|
7
|
-
NativeSyntheticEvent,
|
|
8
|
-
TextInputContentSizeChangeEventData,
|
|
9
6
|
useColorScheme,
|
|
7
|
+
View,
|
|
10
8
|
} from 'react-native'
|
|
11
9
|
import { Color } from './Color'
|
|
12
|
-
import {
|
|
13
|
-
import stylesCommon from './styles'
|
|
10
|
+
import stylesCommon, { getColorSchemeStyle } from './styles'
|
|
14
11
|
|
|
15
12
|
export interface ComposerProps {
|
|
16
13
|
composerHeight?: number
|
|
17
14
|
text?: string
|
|
18
15
|
textInputProps?: Partial<TextInputProps>
|
|
19
|
-
onInputSizeChanged?(layout: { width: number, height: number }): void
|
|
20
16
|
}
|
|
21
17
|
|
|
22
18
|
export function Composer ({
|
|
23
|
-
composerHeight = MIN_COMPOSER_HEIGHT,
|
|
24
|
-
onInputSizeChanged,
|
|
25
19
|
text = '',
|
|
26
20
|
textInputProps,
|
|
27
21
|
}: ComposerProps): React.ReactElement {
|
|
28
|
-
const dimensionsRef = useRef<{ width: number, height: number }>(null)
|
|
29
22
|
const colorScheme = useColorScheme()
|
|
30
23
|
const isDark = colorScheme === 'dark'
|
|
31
24
|
|
|
32
|
-
const determineInputSizeChange = useCallback(
|
|
33
|
-
(dimensions: { width: number, height: number }) => {
|
|
34
|
-
// Support earlier versions of React Native on Android.
|
|
35
|
-
if (!dimensions)
|
|
36
|
-
return
|
|
37
|
-
|
|
38
|
-
if (
|
|
39
|
-
!dimensionsRef.current ||
|
|
40
|
-
(dimensionsRef.current &&
|
|
41
|
-
(dimensionsRef.current.width !== dimensions.width ||
|
|
42
|
-
dimensionsRef.current.height !== dimensions.height))
|
|
43
|
-
) {
|
|
44
|
-
dimensionsRef.current = dimensions
|
|
45
|
-
onInputSizeChanged?.(dimensions)
|
|
46
|
-
}
|
|
47
|
-
},
|
|
48
|
-
[onInputSizeChanged]
|
|
49
|
-
)
|
|
50
|
-
|
|
51
|
-
const handleContentSizeChange = useCallback(
|
|
52
|
-
({
|
|
53
|
-
nativeEvent: { contentSize },
|
|
54
|
-
}: NativeSyntheticEvent<TextInputContentSizeChangeEventData>) =>
|
|
55
|
-
determineInputSizeChange(contentSize),
|
|
56
|
-
[determineInputSizeChange]
|
|
57
|
-
)
|
|
58
|
-
|
|
59
25
|
const placeholder = textInputProps?.placeholder ?? 'Type a message...'
|
|
60
26
|
|
|
61
27
|
return (
|
|
62
|
-
<
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
styles[`textInput_${colorScheme}`],
|
|
79
|
-
textInputProps?.style,
|
|
80
|
-
{
|
|
81
|
-
height: composerHeight,
|
|
82
|
-
...Platform.select({
|
|
83
|
-
web: {
|
|
84
|
-
outlineWidth: 0,
|
|
85
|
-
outlineColor: 'transparent',
|
|
86
|
-
outlineOffset: 0,
|
|
87
|
-
},
|
|
88
|
-
}),
|
|
89
|
-
},
|
|
90
|
-
]}
|
|
91
|
-
/>
|
|
28
|
+
<View style={stylesCommon.fill}>
|
|
29
|
+
<TextInput
|
|
30
|
+
testID={placeholder}
|
|
31
|
+
accessible
|
|
32
|
+
accessibilityLabel={placeholder}
|
|
33
|
+
placeholderTextColor={textInputProps?.placeholderTextColor ?? (isDark ? '#888' : Color.defaultColor)}
|
|
34
|
+
value={text}
|
|
35
|
+
enablesReturnKeyAutomatically
|
|
36
|
+
underlineColorAndroid='transparent'
|
|
37
|
+
keyboardAppearance={isDark ? 'dark' : 'default'}
|
|
38
|
+
multiline
|
|
39
|
+
placeholder={placeholder}
|
|
40
|
+
{...textInputProps}
|
|
41
|
+
style={getColorSchemeStyle(styles, 'textInput', colorScheme)}
|
|
42
|
+
/>
|
|
43
|
+
</View>
|
|
92
44
|
)
|
|
93
45
|
}
|
|
94
46
|
|
|
95
47
|
const styles = StyleSheet.create({
|
|
96
48
|
textInput: {
|
|
97
|
-
marginLeft: 10,
|
|
98
49
|
fontSize: 16,
|
|
99
50
|
lineHeight: 22,
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
paddingTop: 6,
|
|
103
|
-
paddingLeft: 4,
|
|
104
|
-
},
|
|
105
|
-
}),
|
|
106
|
-
marginTop: Platform.select({
|
|
107
|
-
ios: 6,
|
|
108
|
-
android: 0,
|
|
109
|
-
web: 6,
|
|
110
|
-
}),
|
|
111
|
-
marginBottom: Platform.select({
|
|
112
|
-
ios: 5,
|
|
113
|
-
android: 3,
|
|
114
|
-
web: 4,
|
|
115
|
-
}),
|
|
51
|
+
paddingTop: 8,
|
|
52
|
+
paddingBottom: 10,
|
|
116
53
|
},
|
|
117
54
|
textInput_dark: {
|
|
118
55
|
color: '#fff',
|
package/src/Constant.ts
CHANGED
|
@@ -1,12 +1,3 @@
|
|
|
1
|
-
import { Platform } from 'react-native'
|
|
2
|
-
|
|
3
|
-
export const MIN_COMPOSER_HEIGHT = Platform.select({
|
|
4
|
-
ios: 33,
|
|
5
|
-
android: 41,
|
|
6
|
-
web: 34,
|
|
7
|
-
windows: 34,
|
|
8
|
-
})
|
|
9
|
-
export const MAX_COMPOSER_HEIGHT = 200
|
|
10
1
|
export const DATE_FORMAT = 'D MMMM'
|
|
11
2
|
export const TIME_FORMAT = 'LT'
|
|
12
3
|
|
package/src/GiftedChat/index.tsx
CHANGED
|
@@ -19,19 +19,12 @@ 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
|
-
import {
|
|
24
|
+
import { TEST_ID } from '../Constant'
|
|
32
25
|
import { GiftedChatContext } from '../GiftedChatContext'
|
|
33
26
|
import { InputToolbar } from '../InputToolbar'
|
|
34
|
-
import {
|
|
27
|
+
import { MessagesContainer, AnimatedList } from '../MessagesContainer'
|
|
35
28
|
import { IMessage } from '../Models'
|
|
36
29
|
import stylesCommon from '../styles'
|
|
37
30
|
import { renderComponentOrElement } from '../utils'
|
|
@@ -61,19 +54,14 @@ function GiftedChat<TMessage extends IMessage = IMessage> (
|
|
|
61
54
|
textInputProps,
|
|
62
55
|
renderChatFooter,
|
|
63
56
|
renderInputToolbar,
|
|
64
|
-
keyboardBottomOffset = 0,
|
|
65
|
-
shouldFocusInputOnKeyboardOpen = true,
|
|
66
57
|
isInverted = true,
|
|
67
|
-
minComposerHeight = MIN_COMPOSER_HEIGHT,
|
|
68
|
-
maxComposerHeight = MAX_COMPOSER_HEIGHT,
|
|
69
|
-
isKeyboardInternallyHandled = true,
|
|
70
58
|
} = props
|
|
71
59
|
|
|
72
60
|
const actionSheetRef = useRef<ActionSheetProviderRef>(null)
|
|
73
61
|
|
|
74
|
-
const
|
|
75
|
-
() => props.
|
|
76
|
-
[props.
|
|
62
|
+
const messagesContainerRef = useMemo(
|
|
63
|
+
() => props.messagesContainerRef || createRef<AnimatedList<TMessage>>(),
|
|
64
|
+
[props.messagesContainerRef]
|
|
77
65
|
) as RefObject<AnimatedList<TMessage>>
|
|
78
66
|
|
|
79
67
|
const textInputRef = useMemo(
|
|
@@ -81,37 +69,9 @@ function GiftedChat<TMessage extends IMessage = IMessage> (
|
|
|
81
69
|
[props.textInputRef]
|
|
82
70
|
)
|
|
83
71
|
|
|
84
|
-
const isTextInputWasFocused: RefObject<boolean> = useRef(false)
|
|
85
|
-
|
|
86
72
|
const [isInitialized, setIsInitialized] = useState<boolean>(false)
|
|
87
|
-
const [composerHeight, setComposerHeight] = useState<number>(
|
|
88
|
-
minComposerHeight!
|
|
89
|
-
)
|
|
90
73
|
const [text, setText] = useState<string | undefined>(() => props.text || '')
|
|
91
74
|
|
|
92
|
-
// Always call the hook, but conditionally use its data
|
|
93
|
-
const keyboardControllerData = useReanimatedKeyboardAnimation()
|
|
94
|
-
|
|
95
|
-
// Create a mock keyboard object when keyboard is not internally handled
|
|
96
|
-
const keyboard = useMemo(() => {
|
|
97
|
-
if (!isKeyboardInternallyHandled)
|
|
98
|
-
return { height: { value: 0 } }
|
|
99
|
-
|
|
100
|
-
return keyboardControllerData
|
|
101
|
-
}, [isKeyboardInternallyHandled, keyboardControllerData])
|
|
102
|
-
|
|
103
|
-
const trackingKeyboardMovement = useSharedValue(false)
|
|
104
|
-
const keyboardBottomOffsetAnim = useSharedValue(0)
|
|
105
|
-
|
|
106
|
-
const contentStyleAnim = useAnimatedStyle(
|
|
107
|
-
() => ({
|
|
108
|
-
transform: [
|
|
109
|
-
{ translateY: keyboard.height.value + keyboardBottomOffsetAnim.value },
|
|
110
|
-
],
|
|
111
|
-
}),
|
|
112
|
-
[keyboard, keyboardBottomOffsetAnim]
|
|
113
|
-
)
|
|
114
|
-
|
|
115
75
|
const getTextFromProp = useCallback(
|
|
116
76
|
(fallback: string) => {
|
|
117
77
|
if (props.text === undefined)
|
|
@@ -122,50 +82,22 @@ function GiftedChat<TMessage extends IMessage = IMessage> (
|
|
|
122
82
|
[props.text]
|
|
123
83
|
)
|
|
124
84
|
|
|
125
|
-
/**
|
|
126
|
-
* Store text input focus status when keyboard hide to retrieve
|
|
127
|
-
* it afterwards if needed.
|
|
128
|
-
* `onKeyboardWillHide` may be called twice in sequence so we
|
|
129
|
-
* make a guard condition (eg. showing image picker)
|
|
130
|
-
*/
|
|
131
|
-
const handleTextInputFocusWhenKeyboardHide = useCallback(() => {
|
|
132
|
-
if (!isTextInputWasFocused.current)
|
|
133
|
-
isTextInputWasFocused.current =
|
|
134
|
-
textInputRef.current?.isFocused() || false
|
|
135
|
-
}, [textInputRef])
|
|
136
|
-
|
|
137
|
-
/**
|
|
138
|
-
* Refocus the text input only if it was focused before showing keyboard.
|
|
139
|
-
* This is needed in some cases (eg. showing image picker).
|
|
140
|
-
*/
|
|
141
|
-
const handleTextInputFocusWhenKeyboardShow = useCallback(() => {
|
|
142
|
-
if (
|
|
143
|
-
textInputRef.current &&
|
|
144
|
-
isTextInputWasFocused.current &&
|
|
145
|
-
!textInputRef.current.isFocused()
|
|
146
|
-
)
|
|
147
|
-
textInputRef.current.focus()
|
|
148
|
-
|
|
149
|
-
// Reset the indicator since the keyboard is shown
|
|
150
|
-
isTextInputWasFocused.current = false
|
|
151
|
-
}, [textInputRef])
|
|
152
|
-
|
|
153
85
|
const scrollToBottom = useCallback(
|
|
154
86
|
(isAnimated = true) => {
|
|
155
|
-
if (!
|
|
87
|
+
if (!messagesContainerRef?.current)
|
|
156
88
|
return
|
|
157
89
|
|
|
158
90
|
if (isInverted) {
|
|
159
|
-
|
|
91
|
+
messagesContainerRef.current.scrollToOffset({
|
|
160
92
|
offset: 0,
|
|
161
93
|
animated: isAnimated,
|
|
162
94
|
})
|
|
163
95
|
return
|
|
164
96
|
}
|
|
165
97
|
|
|
166
|
-
|
|
98
|
+
messagesContainerRef.current.scrollToEnd({ animated: isAnimated })
|
|
167
99
|
},
|
|
168
|
-
[isInverted,
|
|
100
|
+
[isInverted, messagesContainerRef]
|
|
169
101
|
)
|
|
170
102
|
|
|
171
103
|
const renderMessages = useMemo(() => {
|
|
@@ -176,11 +108,11 @@ function GiftedChat<TMessage extends IMessage = IMessage> (
|
|
|
176
108
|
|
|
177
109
|
return (
|
|
178
110
|
<View style={[stylesCommon.fill, messagesContainerStyle]}>
|
|
179
|
-
<
|
|
111
|
+
<MessagesContainer<TMessage>
|
|
180
112
|
{...messagesContainerProps}
|
|
181
113
|
isInverted={isInverted}
|
|
182
114
|
messages={messages}
|
|
183
|
-
forwardRef={
|
|
115
|
+
forwardRef={messagesContainerRef}
|
|
184
116
|
isTyping={isTyping}
|
|
185
117
|
/>
|
|
186
118
|
{renderComponentOrElement(renderChatFooter, {})}
|
|
@@ -192,7 +124,7 @@ function GiftedChat<TMessage extends IMessage = IMessage> (
|
|
|
192
124
|
messages,
|
|
193
125
|
props,
|
|
194
126
|
isInverted,
|
|
195
|
-
|
|
127
|
+
messagesContainerRef,
|
|
196
128
|
renderChatFooter,
|
|
197
129
|
])
|
|
198
130
|
|
|
@@ -205,10 +137,8 @@ function GiftedChat<TMessage extends IMessage = IMessage> (
|
|
|
205
137
|
|
|
206
138
|
notifyInputTextReset()
|
|
207
139
|
|
|
208
|
-
setComposerHeight(minComposerHeight!)
|
|
209
140
|
setText(getTextFromProp(''))
|
|
210
141
|
}, [
|
|
211
|
-
minComposerHeight,
|
|
212
142
|
getTextFromProp,
|
|
213
143
|
textInputRef,
|
|
214
144
|
notifyInputTextReset,
|
|
@@ -238,18 +168,6 @@ function GiftedChat<TMessage extends IMessage = IMessage> (
|
|
|
238
168
|
[messageIdGenerator, onSend, user, resetInputToolbar, scrollToBottom]
|
|
239
169
|
)
|
|
240
170
|
|
|
241
|
-
const onInputSizeChanged = useCallback(
|
|
242
|
-
(size: { height: number }) => {
|
|
243
|
-
const newComposerHeight = Math.max(
|
|
244
|
-
minComposerHeight!,
|
|
245
|
-
Math.min(maxComposerHeight!, size.height)
|
|
246
|
-
)
|
|
247
|
-
|
|
248
|
-
setComposerHeight(newComposerHeight)
|
|
249
|
-
},
|
|
250
|
-
[maxComposerHeight, minComposerHeight]
|
|
251
|
-
)
|
|
252
|
-
|
|
253
171
|
const _onChangeText = useCallback(
|
|
254
172
|
(text: string) => {
|
|
255
173
|
props.textInputProps?.onChangeText?.(text)
|
|
@@ -274,10 +192,9 @@ function GiftedChat<TMessage extends IMessage = IMessage> (
|
|
|
274
192
|
notifyInputTextReset()
|
|
275
193
|
|
|
276
194
|
setIsInitialized(true)
|
|
277
|
-
setComposerHeight(minComposerHeight!)
|
|
278
195
|
setText(getTextFromProp(initialText))
|
|
279
196
|
},
|
|
280
|
-
[isInitialized, initialText,
|
|
197
|
+
[isInitialized, initialText, notifyInputTextReset, getTextFromProp]
|
|
281
198
|
)
|
|
282
199
|
|
|
283
200
|
const inputToolbarFragment = useMemo(() => {
|
|
@@ -287,9 +204,7 @@ function GiftedChat<TMessage extends IMessage = IMessage> (
|
|
|
287
204
|
const inputToolbarProps = {
|
|
288
205
|
...props,
|
|
289
206
|
text: getTextFromProp(text!),
|
|
290
|
-
composerHeight: Math.max(minComposerHeight!, composerHeight),
|
|
291
207
|
onSend: _onSend,
|
|
292
|
-
onInputSizeChanged,
|
|
293
208
|
textInputProps: {
|
|
294
209
|
...textInputProps,
|
|
295
210
|
onChangeText: _onChangeText,
|
|
@@ -305,12 +220,9 @@ function GiftedChat<TMessage extends IMessage = IMessage> (
|
|
|
305
220
|
isInitialized,
|
|
306
221
|
_onSend,
|
|
307
222
|
getTextFromProp,
|
|
308
|
-
minComposerHeight,
|
|
309
|
-
onInputSizeChanged,
|
|
310
223
|
props,
|
|
311
224
|
text,
|
|
312
225
|
renderInputToolbar,
|
|
313
|
-
composerHeight,
|
|
314
226
|
textInputRef,
|
|
315
227
|
textInputProps,
|
|
316
228
|
_onChangeText,
|
|
@@ -334,78 +246,55 @@ function GiftedChat<TMessage extends IMessage = IMessage> (
|
|
|
334
246
|
setText(props.text)
|
|
335
247
|
}, [props.text])
|
|
336
248
|
|
|
337
|
-
// Only set up keyboard animation when keyboard is internally handled
|
|
338
|
-
useAnimatedReaction(
|
|
339
|
-
() => isKeyboardInternallyHandled ? keyboard.height.value : 0,
|
|
340
|
-
(value, prevValue) => {
|
|
341
|
-
// Skip keyboard handling when not internally handled
|
|
342
|
-
if (!isKeyboardInternallyHandled)
|
|
343
|
-
return
|
|
344
|
-
|
|
345
|
-
if (prevValue !== null && value !== prevValue) {
|
|
346
|
-
const isKeyboardMovingUp = value < prevValue
|
|
347
|
-
if (isKeyboardMovingUp !== trackingKeyboardMovement.value) {
|
|
348
|
-
trackingKeyboardMovement.value = isKeyboardMovingUp
|
|
349
|
-
keyboardBottomOffsetAnim.value = withTiming(
|
|
350
|
-
isKeyboardMovingUp ? keyboardBottomOffset : 0,
|
|
351
|
-
{
|
|
352
|
-
// If `keyboardBottomOffset` exists, we change the duration to a smaller value to fix the delay in the keyboard animation speed
|
|
353
|
-
duration: keyboardBottomOffset ? 150 : 400,
|
|
354
|
-
}
|
|
355
|
-
)
|
|
356
|
-
|
|
357
|
-
if (shouldFocusInputOnKeyboardOpen)
|
|
358
|
-
if (isKeyboardMovingUp)
|
|
359
|
-
runOnJS(handleTextInputFocusWhenKeyboardShow)()
|
|
360
|
-
else
|
|
361
|
-
runOnJS(handleTextInputFocusWhenKeyboardHide)()
|
|
362
|
-
}
|
|
363
|
-
}
|
|
364
|
-
},
|
|
365
|
-
[
|
|
366
|
-
keyboard,
|
|
367
|
-
trackingKeyboardMovement,
|
|
368
|
-
shouldFocusInputOnKeyboardOpen,
|
|
369
|
-
handleTextInputFocusWhenKeyboardHide,
|
|
370
|
-
handleTextInputFocusWhenKeyboardShow,
|
|
371
|
-
keyboardBottomOffset,
|
|
372
|
-
isKeyboardInternallyHandled,
|
|
373
|
-
]
|
|
374
|
-
)
|
|
375
|
-
|
|
376
249
|
return (
|
|
377
250
|
<GiftedChatContext.Provider value={contextValues}>
|
|
378
251
|
<ActionSheetProvider ref={actionSheetRef}>
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
252
|
+
{/* @ts-expect-error */}
|
|
253
|
+
<KeyboardAvoidingView
|
|
254
|
+
behavior='padding'
|
|
255
|
+
style={stylesCommon.fill}
|
|
256
|
+
{...props.keyboardAvoidingViewProps}
|
|
383
257
|
>
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
258
|
+
<View
|
|
259
|
+
testID={TEST_ID.WRAPPER}
|
|
260
|
+
style={[stylesCommon.fill, styles.contentContainer]}
|
|
261
|
+
onLayout={onInitialLayoutViewLayout}
|
|
262
|
+
>
|
|
263
|
+
{isInitialized
|
|
264
|
+
? (
|
|
265
|
+
<>
|
|
266
|
+
{renderMessages}
|
|
267
|
+
{inputToolbarFragment}
|
|
268
|
+
</>
|
|
269
|
+
)
|
|
270
|
+
: (
|
|
271
|
+
renderComponentOrElement(renderLoading, {})
|
|
272
|
+
)}
|
|
273
|
+
</View>
|
|
274
|
+
</KeyboardAvoidingView>
|
|
395
275
|
</ActionSheetProvider>
|
|
396
276
|
</GiftedChatContext.Provider>
|
|
397
277
|
)
|
|
398
278
|
}
|
|
399
279
|
|
|
400
280
|
function GiftedChatWrapper<TMessage extends IMessage = IMessage> (props: GiftedChatProps<TMessage>) {
|
|
281
|
+
const {
|
|
282
|
+
keyboardProviderProps,
|
|
283
|
+
...rest
|
|
284
|
+
} = props
|
|
285
|
+
|
|
401
286
|
return (
|
|
402
|
-
<
|
|
403
|
-
<
|
|
404
|
-
<
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
287
|
+
<GestureHandlerRootView style={styles.fill}>
|
|
288
|
+
<SafeAreaProvider>
|
|
289
|
+
<KeyboardProvider
|
|
290
|
+
statusBarTranslucent
|
|
291
|
+
navigationBarTranslucent
|
|
292
|
+
{...keyboardProviderProps}
|
|
293
|
+
>
|
|
294
|
+
<GiftedChat<TMessage> {...rest} />
|
|
295
|
+
</KeyboardProvider>
|
|
296
|
+
</SafeAreaProvider>
|
|
297
|
+
</GestureHandlerRootView>
|
|
409
298
|
)
|
|
410
299
|
}
|
|
411
300
|
|
package/src/GiftedChat/types.ts
CHANGED
|
@@ -8,13 +8,14 @@ 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 { AnimatedList, MessageContainerProps } from '../MessageContainer'
|
|
17
17
|
import { MessageImageProps } from '../MessageImage'
|
|
18
|
+
import { AnimatedList, MessagesContainerProps } from '../MessagesContainer'
|
|
18
19
|
import { MessageTextProps } from '../MessageText'
|
|
19
20
|
import {
|
|
20
21
|
IMessage,
|
|
@@ -28,9 +29,9 @@ import { SendProps } from '../Send'
|
|
|
28
29
|
import { SystemMessageProps } from '../SystemMessage'
|
|
29
30
|
import { TimeProps } from '../Time'
|
|
30
31
|
|
|
31
|
-
export interface GiftedChatProps<TMessage extends IMessage> extends Partial<
|
|
32
|
-
/*
|
|
33
|
-
|
|
32
|
+
export interface GiftedChatProps<TMessage extends IMessage> extends Partial<MessagesContainerProps<TMessage>> {
|
|
33
|
+
/* Messages container ref */
|
|
34
|
+
messagesContainerRef?: RefObject<AnimatedList<TMessage>>
|
|
34
35
|
/* text input ref */
|
|
35
36
|
textInputRef?: RefObject<TextInput>
|
|
36
37
|
/* Controls whether or not to show user.name property in the message bubble */
|
|
@@ -50,8 +51,6 @@ export interface GiftedChatProps<TMessage extends IMessage> extends Partial<Mess
|
|
|
50
51
|
dateFormat?: string
|
|
51
52
|
/* Format to use for rendering relative times; Today - for now. See more: https://day.js.org/docs/en/plugin/calendar */
|
|
52
53
|
dateFormatCalendar?: object
|
|
53
|
-
/* 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` */
|
|
54
|
-
isKeyboardInternallyHandled?: boolean
|
|
55
54
|
/* Whether to render an avatar for the current user; default is false, only show avatars for other users */
|
|
56
55
|
isUserAvatarVisible?: boolean
|
|
57
56
|
/* When false, avatars will only be displayed when a consecutive message is from the same user on the same day; default is false */
|
|
@@ -60,10 +59,6 @@ export interface GiftedChatProps<TMessage extends IMessage> extends Partial<Mess
|
|
|
60
59
|
isAvatarOnTop?: boolean
|
|
61
60
|
/* Extra props to be passed to the <Image> component created by the default renderMessageImage */
|
|
62
61
|
imageProps?: MessageImageProps<TMessage>
|
|
63
|
-
/* Distance of the chat from the bottom of the screen (e.g. useful if you display a tab bar); default is 0 */
|
|
64
|
-
keyboardBottomOffset?: number
|
|
65
|
-
/* Focus on <TextInput> automatically when opening the keyboard; default is true */
|
|
66
|
-
shouldFocusInputOnKeyboardOpen?: boolean
|
|
67
62
|
/* Minimum height of the input toolbar; default is 44 */
|
|
68
63
|
minInputToolbarHeight?: number
|
|
69
64
|
/* Extra props to be passed to the <TextInput>. See https://reactnative.dev/docs/textinput */
|
|
@@ -120,13 +115,13 @@ export interface GiftedChatProps<TMessage extends IMessage> extends Partial<Mess
|
|
|
120
115
|
renderMessageImage?: (props: MessageImageProps<TMessage>) => React.ReactNode
|
|
121
116
|
/* Custom message video */
|
|
122
117
|
renderMessageVideo?: (props: MessageVideoProps<TMessage>) => React.ReactNode
|
|
123
|
-
/* Custom message
|
|
118
|
+
/* Custom message audio */
|
|
124
119
|
renderMessageAudio?: (props: MessageAudioProps<TMessage>) => React.ReactNode
|
|
125
120
|
/* Custom view inside the bubble */
|
|
126
121
|
renderCustomView?: (props: BubbleProps<TMessage>) => React.ReactNode
|
|
127
122
|
/* Custom time inside a message */
|
|
128
123
|
renderTime?: (props: TimeProps<TMessage>) => React.ReactNode
|
|
129
|
-
/* Custom component to render below the
|
|
124
|
+
/* Custom component to render below the MessagesContainer */
|
|
130
125
|
renderChatFooter?: () => React.ReactNode
|
|
131
126
|
/* Custom message composer container. Can be a component, element, render function, or null */
|
|
132
127
|
renderInputToolbar?: React.ComponentType<InputToolbarProps<TMessage>> | React.ReactElement | ((props: InputToolbarProps<TMessage>) => React.ReactNode) | null
|
|
@@ -146,4 +141,8 @@ export interface GiftedChatProps<TMessage extends IMessage> extends Partial<Mess
|
|
|
146
141
|
quickReplies: QuickRepliesProps<TMessage>,
|
|
147
142
|
) => React.ReactNode
|
|
148
143
|
renderQuickReplySend?: () => React.ReactNode
|
|
144
|
+
keyboardProviderProps?: React.ComponentProps<typeof KeyboardProvider>
|
|
145
|
+
keyboardAvoidingViewProps?: KeyboardAvoidingViewProps
|
|
146
|
+
/** Enable animated day label that appears on scroll; default is true */
|
|
147
|
+
isDayAnimationEnabled?: boolean
|
|
149
148
|
}
|