react-native-gifted-chat 2.4.0 → 2.5.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 +16 -6
- package/lib/Actions.d.ts +5 -6
- package/lib/Actions.js +16 -13
- package/lib/Actions.js.map +1 -1
- package/lib/Avatar.d.ts +10 -24
- package/lib/Avatar.js +24 -35
- package/lib/Avatar.js.flow +1 -1
- package/lib/Avatar.js.map +1 -1
- package/lib/Bubble.d.ts +30 -29
- package/lib/Bubble.js +90 -72
- package/lib/Bubble.js.flow +3 -1
- package/lib/Bubble.js.map +1 -1
- package/lib/Composer.d.ts +1 -1
- package/lib/Composer.js +30 -32
- package/lib/Composer.js.map +1 -1
- package/lib/Constant.js +1 -0
- package/lib/Constant.js.map +1 -1
- package/lib/Day.d.ts +3 -15
- package/lib/Day.js +2 -14
- package/lib/Day.js.flow +1 -1
- package/lib/Day.js.map +1 -1
- package/lib/GiftedAvatar.d.ts +7 -7
- package/lib/GiftedAvatar.js +30 -29
- package/lib/GiftedAvatar.js.map +1 -1
- package/lib/GiftedChat.d.ts +16 -89
- package/lib/GiftedChat.js +204 -350
- package/lib/GiftedChat.js.flow +3 -3
- package/lib/GiftedChat.js.map +1 -1
- package/lib/GiftedChatContext.d.ts +2 -1
- package/lib/GiftedChatContext.js.map +1 -1
- package/lib/InputToolbar.d.ts +7 -5
- package/lib/InputToolbar.js +41 -34
- package/lib/InputToolbar.js.map +1 -1
- package/lib/LoadEarlier.d.ts +4 -4
- package/lib/LoadEarlier.js +8 -6
- package/lib/LoadEarlier.js.map +1 -1
- package/lib/Message.d.ts +8 -9
- package/lib/Message.js +47 -40
- package/lib/Message.js.flow +1 -1
- package/lib/Message.js.map +1 -1
- package/lib/MessageAudio.d.ts +2 -1
- package/lib/MessageAudio.js +4 -4
- package/lib/MessageAudio.js.flow +1 -1
- package/lib/MessageAudio.js.map +1 -1
- package/lib/MessageContainer.d.ts +17 -17
- package/lib/MessageContainer.js +33 -51
- package/lib/MessageContainer.js.map +1 -1
- package/lib/MessageImage.d.ts +8 -5
- package/lib/MessageImage.js +6 -6
- package/lib/MessageImage.js.flow +1 -1
- package/lib/MessageImage.js.map +1 -1
- package/lib/MessageText.d.ts +11 -10
- package/lib/MessageText.js +5 -10
- package/lib/MessageText.js.flow +1 -1
- package/lib/MessageText.js.map +1 -1
- package/lib/MessageVideo.d.ts +2 -1
- package/lib/MessageVideo.js +4 -4
- package/lib/MessageVideo.js.flow +1 -1
- package/lib/MessageVideo.js.map +1 -1
- package/lib/Models.d.ts +7 -7
- package/lib/QuickReplies.d.ts +4 -3
- package/lib/QuickReplies.js +10 -16
- package/lib/QuickReplies.js.flow +4 -2
- package/lib/QuickReplies.js.map +1 -1
- package/lib/Send.d.ts +4 -4
- package/lib/Send.js +6 -9
- package/lib/Send.js.map +1 -1
- package/lib/SystemMessage.d.ts +6 -5
- package/lib/SystemMessage.js +1 -2
- package/lib/SystemMessage.js.flow +1 -1
- package/lib/SystemMessage.js.map +1 -1
- package/lib/Time.d.ts +7 -6
- package/lib/Time.js +1 -3
- package/lib/Time.js.flow +1 -1
- package/lib/Time.js.map +1 -1
- package/lib/TypingIndicator.d.ts +2 -1
- package/lib/TypingIndicator.js +5 -5
- package/lib/TypingIndicator.js.map +1 -1
- package/lib/hooks/useUpdateLayoutEffect.js +2 -4
- package/lib/hooks/useUpdateLayoutEffect.js.map +1 -1
- package/lib/index.d.ts +1 -0
- package/lib/index.js +1 -0
- package/lib/index.js.map +1 -1
- package/lib/logging.d.ts +2 -2
- package/lib/logging.js.map +1 -1
- package/lib/types.js.flow +1 -1
- package/lib/utils.d.ts +1 -1
- package/lib/utils.js +2 -4
- package/lib/utils.js.map +1 -1
- package/package.json +83 -53
- package/src/Actions.tsx +114 -0
- package/src/Avatar.tsx +178 -0
- package/src/Bubble.tsx +601 -0
- package/src/Color.ts +17 -0
- package/src/Composer.tsx +147 -0
- package/src/Constant.ts +18 -0
- package/src/Day.tsx +71 -0
- package/src/GiftedAvatar.tsx +205 -0
- package/src/GiftedChat.tsx +670 -0
- package/src/GiftedChatContext.ts +23 -0
- package/src/InputToolbar.tsx +113 -0
- package/src/LoadEarlier.tsx +108 -0
- package/src/Message.tsx +229 -0
- package/src/MessageAudio.tsx +19 -0
- package/src/MessageContainer.tsx +362 -0
- package/src/MessageImage.tsx +78 -0
- package/src/MessageText.tsx +187 -0
- package/src/MessageVideo.tsx +19 -0
- package/src/Models.ts +84 -0
- package/src/QuickReplies.tsx +186 -0
- package/src/Send.tsx +102 -0
- package/src/SystemMessage.tsx +61 -0
- package/src/Time.tsx +97 -0
- package/src/TypingIndicator.tsx +108 -0
- package/src/__tests__/Actions.test.tsx +10 -0
- package/src/__tests__/Avatar.test.tsx +13 -0
- package/src/__tests__/Bubble.test.tsx +23 -0
- package/src/__tests__/Color.test.tsx +5 -0
- package/src/__tests__/Composer.test.tsx +11 -0
- package/src/__tests__/Constant.test.tsx +5 -0
- package/src/__tests__/Day.test.tsx +23 -0
- package/src/__tests__/GiftedAvatar.test.tsx +11 -0
- package/src/__tests__/GiftedChat.test.tsx +36 -0
- package/src/__tests__/InputToolbar.test.tsx +11 -0
- package/src/__tests__/LoadEarlier.test.tsx +11 -0
- package/src/__tests__/Message.test.tsx +77 -0
- package/src/__tests__/MessageContainer.test.tsx +11 -0
- package/src/__tests__/MessageImage.test.tsx +27 -0
- package/src/__tests__/MessageText.test.tsx +11 -0
- package/src/__tests__/Send.test.tsx +22 -0
- package/src/__tests__/SystemMessage.test.tsx +27 -0
- package/src/__tests__/Time.test.tsx +29 -0
- package/src/__tests__/__snapshots__/Actions.test.tsx.snap +76 -0
- package/src/__tests__/__snapshots__/Avatar.test.tsx.snap +17 -0
- package/src/__tests__/__snapshots__/Bubble.test.tsx.snap +145 -0
- package/src/__tests__/__snapshots__/Color.test.tsx.snap +21 -0
- package/src/__tests__/__snapshots__/Composer.test.tsx.snap +35 -0
- package/src/__tests__/__snapshots__/Constant.test.tsx.snap +16 -0
- package/src/__tests__/__snapshots__/Day.test.tsx.snap +37 -0
- package/src/__tests__/__snapshots__/GiftedAvatar.test.tsx.snap +22 -0
- package/src/__tests__/__snapshots__/GiftedChat.test.tsx.snap +15 -0
- package/src/__tests__/__snapshots__/InputToolbar.test.tsx.snap +60 -0
- package/src/__tests__/__snapshots__/LoadEarlier.test.tsx.snap +74 -0
- package/src/__tests__/__snapshots__/Message.test.tsx.snap +628 -0
- package/src/__tests__/__snapshots__/MessageContainer.test.tsx.snap +127 -0
- package/src/__tests__/__snapshots__/MessageImage.test.tsx.snap +38 -0
- package/src/__tests__/__snapshots__/MessageText.test.tsx.snap +30 -0
- package/src/__tests__/__snapshots__/Send.test.tsx.snap +129 -0
- package/src/__tests__/__snapshots__/SystemMessage.test.tsx.snap +38 -0
- package/src/__tests__/__snapshots__/Time.test.tsx.snap +33 -0
- package/src/__tests__/data.ts +8 -0
- package/src/__tests__/utils.test.ts +31 -0
- package/src/hooks/useUpdateLayoutEffect.ts +21 -0
- package/src/index.ts +4 -0
- package/src/logging.ts +8 -0
- package/src/utils.ts +39 -0
- package/.eslintignore +0 -2
- package/.eslintrc.js +0 -21
- package/jest.config.js +0 -15
package/lib/GiftedChat.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
+
import React, { createRef, useEffect, useMemo, useRef, useState, useCallback, } from 'react';
|
|
2
|
+
import 'react-native-get-random-values'; // NOTE: FOR "uuid" SUPPORT
|
|
1
3
|
import { ActionSheetProvider, } from '@expo/react-native-action-sheet';
|
|
2
4
|
import dayjs from 'dayjs';
|
|
3
5
|
import localizedFormat from 'dayjs/plugin/localizedFormat';
|
|
4
|
-
import
|
|
5
|
-
import
|
|
6
|
-
import { KeyboardAvoidingView, Platform, StyleSheet, View, } from 'react-native';
|
|
7
|
-
import uuid from 'uuid';
|
|
6
|
+
import { Platform, StyleSheet, View, } from 'react-native';
|
|
7
|
+
import { v4 as uuidv4 } from 'uuid';
|
|
8
8
|
import { Actions } from './Actions';
|
|
9
9
|
import { Avatar } from './Avatar';
|
|
10
10
|
import Bubble from './Bubble';
|
|
@@ -23,425 +23,279 @@ import { Send } from './Send';
|
|
|
23
23
|
import { SystemMessage } from './SystemMessage';
|
|
24
24
|
import { Time } from './Time';
|
|
25
25
|
import * as utils from './utils';
|
|
26
|
+
import Animated, { useAnimatedKeyboard, useAnimatedStyle, useAnimatedReaction, useSharedValue, withTiming, runOnJS, } from 'react-native-reanimated';
|
|
27
|
+
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
|
26
28
|
dayjs.extend(localizedFormat);
|
|
27
29
|
function GiftedChat(props) {
|
|
28
|
-
const { messages = [],
|
|
30
|
+
const { messages = [], initialText = '', isTyping, messageIdGenerator = () => uuidv4(), user = {}, onSend, locale = 'en', renderLoading, actionSheet = null, textInputProps, renderChatFooter = null, renderInputToolbar = null, keyboardShouldPersistTaps = Platform.select({
|
|
29
31
|
ios: 'never',
|
|
30
32
|
android: 'always',
|
|
31
33
|
default: 'never',
|
|
32
|
-
}), onInputTextChanged = null, maxInputLength = null,
|
|
33
|
-
const isMountedRef = useRef(false);
|
|
34
|
-
const keyboardHeightRef = useRef(0);
|
|
35
|
-
const bottomOffsetRef = useRef(0);
|
|
36
|
-
const maxHeightRef = useRef(undefined);
|
|
37
|
-
const isFirstLayoutRef = useRef(true);
|
|
34
|
+
}), onInputTextChanged = null, maxInputLength = null, inverted = true, minComposerHeight = MIN_COMPOSER_HEIGHT, maxComposerHeight = MAX_COMPOSER_HEIGHT, } = props;
|
|
38
35
|
const actionSheetRef = useRef(null);
|
|
39
|
-
|
|
40
|
-
const
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
}
|
|
59
|
-
return () => {
|
|
60
|
-
isMountedRef.current = false;
|
|
61
|
-
};
|
|
62
|
-
}, [messages, text]);
|
|
63
|
-
const getTextFromProp = (fallback) => {
|
|
64
|
-
if (text === undefined) {
|
|
36
|
+
const messageContainerRef = useMemo(() => props.messageContainerRef || createRef(), [props.messageContainerRef]);
|
|
37
|
+
const textInputRef = useMemo(() => props.textInputRef || createRef(), [props.textInputRef]);
|
|
38
|
+
const isTextInputWasFocused = useRef(false);
|
|
39
|
+
const [isInitialized, setIsInitialized] = useState(false);
|
|
40
|
+
const [composerHeight, setComposerHeight] = useState(minComposerHeight);
|
|
41
|
+
const [text, setText] = useState(() => props.text || '');
|
|
42
|
+
const [isTypingDisabled, setIsTypingDisabled] = useState(false);
|
|
43
|
+
const keyboard = useAnimatedKeyboard();
|
|
44
|
+
const trackingKeyboardMovement = useSharedValue(false);
|
|
45
|
+
const debounceEnableTypingTimeoutId = useRef();
|
|
46
|
+
const insets = useSafeAreaInsets();
|
|
47
|
+
const keyboardOffsetBottom = useSharedValue(0);
|
|
48
|
+
const contentStyleAnim = useAnimatedStyle(() => ({
|
|
49
|
+
transform: [
|
|
50
|
+
{ translateY: -keyboard.height.value + keyboardOffsetBottom.value },
|
|
51
|
+
],
|
|
52
|
+
}), [keyboard, keyboardOffsetBottom]);
|
|
53
|
+
const getTextFromProp = useCallback((fallback) => {
|
|
54
|
+
if (props.text === undefined)
|
|
65
55
|
return fallback;
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
};
|
|
69
|
-
const getKeyboardHeight = () => {
|
|
70
|
-
if (Platform.OS === 'android' && !forceGetKeyboardHeight) {
|
|
71
|
-
// For android: on-screen keyboard resized main container and has own height.
|
|
72
|
-
// @see https://developer.android.com/training/keyboard-input/visibility.html
|
|
73
|
-
// So for calculate the messages container height ignore keyboard height.
|
|
74
|
-
return 0;
|
|
75
|
-
}
|
|
76
|
-
return keyboardHeightRef.current;
|
|
77
|
-
};
|
|
78
|
-
const calculateInputToolbarHeight = (composerHeight) => {
|
|
79
|
-
const getMinInputToolbarHeight = renderAccessory
|
|
80
|
-
? minInputToolbarHeight * 2
|
|
81
|
-
: minInputToolbarHeight;
|
|
82
|
-
return composerHeight + (getMinInputToolbarHeight - minComposerHeight);
|
|
83
|
-
};
|
|
84
|
-
/**
|
|
85
|
-
* Returns the height, based on current window size, without taking the keyboard into account.
|
|
86
|
-
*/
|
|
87
|
-
const getBasicMessagesContainerHeight = (composerHeight = state.composerHeight) => {
|
|
88
|
-
return maxHeightRef.current - calculateInputToolbarHeight(composerHeight);
|
|
89
|
-
};
|
|
90
|
-
/**
|
|
91
|
-
* Returns the height, based on current window size, taking the keyboard into account.
|
|
92
|
-
*/
|
|
93
|
-
const getMessagesContainerHeightWithKeyboard = (composerHeight = state.composerHeight) => {
|
|
94
|
-
return (getBasicMessagesContainerHeight(composerHeight) -
|
|
95
|
-
getKeyboardHeight() +
|
|
96
|
-
bottomOffsetRef.current);
|
|
97
|
-
};
|
|
56
|
+
return props.text;
|
|
57
|
+
}, [props.text]);
|
|
98
58
|
/**
|
|
99
59
|
* Store text input focus status when keyboard hide to retrieve
|
|
100
|
-
* it
|
|
60
|
+
* it afterwards if needed.
|
|
101
61
|
* `onKeyboardWillHide` may be called twice in sequence so we
|
|
102
62
|
* make a guard condition (eg. showing image picker)
|
|
103
63
|
*/
|
|
104
|
-
const handleTextInputFocusWhenKeyboardHide = () => {
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
};
|
|
64
|
+
const handleTextInputFocusWhenKeyboardHide = useCallback(() => {
|
|
65
|
+
if (!isTextInputWasFocused.current)
|
|
66
|
+
isTextInputWasFocused.current =
|
|
67
|
+
textInputRef.current?.isFocused() || false;
|
|
68
|
+
}, [textInputRef]);
|
|
110
69
|
/**
|
|
111
70
|
* Refocus the text input only if it was focused before showing keyboard.
|
|
112
71
|
* This is needed in some cases (eg. showing image picker).
|
|
113
72
|
*/
|
|
114
|
-
const handleTextInputFocusWhenKeyboardShow = () => {
|
|
73
|
+
const handleTextInputFocusWhenKeyboardShow = useCallback(() => {
|
|
115
74
|
if (textInputRef.current &&
|
|
116
|
-
|
|
117
|
-
!textInputRef.current.isFocused())
|
|
75
|
+
isTextInputWasFocused &&
|
|
76
|
+
!textInputRef.current.isFocused())
|
|
118
77
|
textInputRef.current.focus();
|
|
119
|
-
}
|
|
120
78
|
// Reset the indicator since the keyboard is shown
|
|
121
|
-
|
|
122
|
-
};
|
|
123
|
-
const
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
if (
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
setState({
|
|
145
|
-
...state,
|
|
146
|
-
typingDisabled: true,
|
|
147
|
-
messagesContainerHeight: newMessagesContainerHeight,
|
|
79
|
+
isTextInputWasFocused.current = false;
|
|
80
|
+
}, [textInputRef]);
|
|
81
|
+
const disableTyping = useCallback(() => {
|
|
82
|
+
clearTimeout(debounceEnableTypingTimeoutId.current);
|
|
83
|
+
setIsTypingDisabled(true);
|
|
84
|
+
}, []);
|
|
85
|
+
const enableTyping = useCallback(() => {
|
|
86
|
+
clearTimeout(debounceEnableTypingTimeoutId.current);
|
|
87
|
+
setIsTypingDisabled(false);
|
|
88
|
+
}, []);
|
|
89
|
+
const debounceEnableTyping = useCallback(() => {
|
|
90
|
+
clearTimeout(debounceEnableTypingTimeoutId.current);
|
|
91
|
+
debounceEnableTypingTimeoutId.current = setTimeout(() => {
|
|
92
|
+
enableTyping();
|
|
93
|
+
}, 50);
|
|
94
|
+
}, [enableTyping]);
|
|
95
|
+
const scrollToBottom = useCallback((isAnimated = true) => {
|
|
96
|
+
if (!messageContainerRef?.current)
|
|
97
|
+
return;
|
|
98
|
+
if (inverted) {
|
|
99
|
+
messageContainerRef.current.scrollToOffset({
|
|
100
|
+
offset: 0,
|
|
101
|
+
animated: isAnimated,
|
|
148
102
|
});
|
|
103
|
+
return;
|
|
149
104
|
}
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
setState({
|
|
156
|
-
...state,
|
|
157
|
-
typingDisabled: false,
|
|
158
|
-
});
|
|
159
|
-
};
|
|
160
|
-
const onKeyboardDidHide = (e) => {
|
|
161
|
-
if (Platform.OS === 'android') {
|
|
162
|
-
onKeyboardWillHide(e);
|
|
163
|
-
}
|
|
164
|
-
setState({
|
|
165
|
-
...state,
|
|
166
|
-
typingDisabled: false,
|
|
167
|
-
});
|
|
168
|
-
};
|
|
169
|
-
const scrollToBottom = (animated = true) => {
|
|
170
|
-
if (messageContainerRef === null || messageContainerRef === void 0 ? void 0 : messageContainerRef.current) {
|
|
171
|
-
if (!inverted) {
|
|
172
|
-
messageContainerRef.current.scrollToEnd({ animated });
|
|
173
|
-
}
|
|
174
|
-
else {
|
|
175
|
-
messageContainerRef.current.scrollToOffset({
|
|
176
|
-
offset: 0,
|
|
177
|
-
animated,
|
|
178
|
-
});
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
};
|
|
182
|
-
const renderMessages = () => {
|
|
105
|
+
messageContainerRef.current.scrollToEnd({ animated: isAnimated });
|
|
106
|
+
}, [inverted, messageContainerRef]);
|
|
107
|
+
const renderMessages = useMemo(() => {
|
|
108
|
+
if (!isInitialized)
|
|
109
|
+
return null;
|
|
183
110
|
const { messagesContainerStyle, ...messagesContainerProps } = props;
|
|
184
|
-
const fragment = (<View style={[
|
|
185
|
-
typeof state.messagesContainerHeight === 'number' && {
|
|
186
|
-
height: state.messagesContainerHeight,
|
|
187
|
-
},
|
|
188
|
-
messagesContainerStyle,
|
|
189
|
-
]}>
|
|
111
|
+
const fragment = (<View style={[styles.fill, messagesContainerStyle]}>
|
|
190
112
|
<MessageContainer {...messagesContainerProps} invertibleScrollViewProps={{
|
|
191
|
-
inverted
|
|
192
|
-
keyboardShouldPersistTaps
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
onKeyboardDidShow: onKeyboardDidShow,
|
|
196
|
-
onKeyboardDidHide: onKeyboardDidHide,
|
|
197
|
-
}} messages={state.messages} forwardRef={messageContainerRef} isTyping={isTyping}/>
|
|
198
|
-
{_renderChatFooter()}
|
|
113
|
+
inverted,
|
|
114
|
+
keyboardShouldPersistTaps,
|
|
115
|
+
}} messages={messages} forwardRef={messageContainerRef} isTyping={isTyping}/>
|
|
116
|
+
{renderChatFooter?.()}
|
|
199
117
|
</View>);
|
|
200
|
-
return
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
|
|
118
|
+
return fragment;
|
|
119
|
+
}, [
|
|
120
|
+
isInitialized,
|
|
121
|
+
isTyping,
|
|
122
|
+
messages,
|
|
123
|
+
props,
|
|
124
|
+
inverted,
|
|
125
|
+
keyboardShouldPersistTaps,
|
|
126
|
+
messageContainerRef,
|
|
127
|
+
renderChatFooter,
|
|
128
|
+
]);
|
|
129
|
+
const notifyInputTextReset = useCallback(() => {
|
|
130
|
+
onInputTextChanged?.('');
|
|
131
|
+
}, [onInputTextChanged]);
|
|
132
|
+
const resetInputToolbar = useCallback(() => {
|
|
133
|
+
textInputRef.current?.clear();
|
|
134
|
+
notifyInputTextReset();
|
|
135
|
+
setComposerHeight(minComposerHeight);
|
|
136
|
+
setText(getTextFromProp(''));
|
|
137
|
+
enableTyping();
|
|
138
|
+
}, [
|
|
139
|
+
minComposerHeight,
|
|
140
|
+
getTextFromProp,
|
|
141
|
+
textInputRef,
|
|
142
|
+
notifyInputTextReset,
|
|
143
|
+
enableTyping,
|
|
144
|
+
]);
|
|
145
|
+
const _onSend = useCallback((messages = [], shouldResetInputToolbar = false) => {
|
|
146
|
+
if (!Array.isArray(messages))
|
|
204
147
|
messages = [messages];
|
|
205
|
-
}
|
|
206
148
|
const newMessages = messages.map(message => {
|
|
207
149
|
return {
|
|
208
150
|
...message,
|
|
209
151
|
user: user,
|
|
210
152
|
createdAt: new Date(),
|
|
211
|
-
_id: messageIdGenerator
|
|
153
|
+
_id: messageIdGenerator?.(),
|
|
212
154
|
};
|
|
213
155
|
});
|
|
214
156
|
if (shouldResetInputToolbar === true) {
|
|
215
|
-
|
|
216
|
-
...state,
|
|
217
|
-
typingDisabled: true,
|
|
218
|
-
});
|
|
157
|
+
disableTyping();
|
|
219
158
|
resetInputToolbar();
|
|
220
159
|
}
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
// if (shouldResetInputToolbar === true) {
|
|
225
|
-
// setTimeout(() => {
|
|
226
|
-
// if (isMountedRef.current === true) {
|
|
227
|
-
// setState({
|
|
228
|
-
// ...state,
|
|
229
|
-
// typingDisabled: false,
|
|
230
|
-
// })
|
|
231
|
-
// }
|
|
232
|
-
// }, 100)
|
|
233
|
-
// }
|
|
234
|
-
};
|
|
235
|
-
const resetInputToolbar = () => {
|
|
236
|
-
if (textInputRef.current) {
|
|
237
|
-
textInputRef.current.clear();
|
|
238
|
-
}
|
|
239
|
-
notifyInputTextReset();
|
|
240
|
-
const newMessagesContainerHeight = getMessagesContainerHeightWithKeyboard(minComposerHeight);
|
|
241
|
-
setState({
|
|
242
|
-
...state,
|
|
243
|
-
text: getTextFromProp(''),
|
|
244
|
-
composerHeight: minComposerHeight,
|
|
245
|
-
messagesContainerHeight: newMessagesContainerHeight,
|
|
246
|
-
});
|
|
247
|
-
};
|
|
248
|
-
const onInputSizeChanged = (size) => {
|
|
160
|
+
onSend?.(newMessages);
|
|
161
|
+
}, [messageIdGenerator, onSend, user, resetInputToolbar, disableTyping]);
|
|
162
|
+
const onInputSizeChanged = useCallback((size) => {
|
|
249
163
|
const newComposerHeight = Math.max(minComposerHeight, Math.min(maxComposerHeight, size.height));
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
messagesContainerHeight: newMessagesContainerHeight,
|
|
255
|
-
});
|
|
256
|
-
};
|
|
257
|
-
const _onInputTextChanged = (_text) => {
|
|
258
|
-
if (state.typingDisabled) {
|
|
164
|
+
setComposerHeight(newComposerHeight);
|
|
165
|
+
}, [maxComposerHeight, minComposerHeight]);
|
|
166
|
+
const _onInputTextChanged = useCallback((_text) => {
|
|
167
|
+
if (isTypingDisabled)
|
|
259
168
|
return;
|
|
260
|
-
|
|
261
|
-
if (onInputTextChanged) {
|
|
262
|
-
onInputTextChanged(_text);
|
|
263
|
-
}
|
|
169
|
+
onInputTextChanged?.(_text);
|
|
264
170
|
// Only set state if it's not being overridden by a prop.
|
|
265
|
-
if (text === undefined)
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
const notifyInputTextReset = () => {
|
|
270
|
-
if (onInputTextChanged) {
|
|
271
|
-
onInputTextChanged('');
|
|
272
|
-
}
|
|
273
|
-
};
|
|
274
|
-
const onInitialLayoutViewLayout = (e) => {
|
|
171
|
+
if (props.text === undefined)
|
|
172
|
+
setText(_text);
|
|
173
|
+
}, [onInputTextChanged, isTypingDisabled, props.text]);
|
|
174
|
+
const onInitialLayoutViewLayout = useCallback((e) => {
|
|
275
175
|
const { layout } = e.nativeEvent;
|
|
276
|
-
if (layout.height <= 0)
|
|
176
|
+
if (layout.height <= 0)
|
|
277
177
|
return;
|
|
278
|
-
}
|
|
279
178
|
notifyInputTextReset();
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
messagesContainerHeight: newMessagesContainerHeight,
|
|
288
|
-
});
|
|
289
|
-
};
|
|
290
|
-
const onMainViewLayout = (e) => {
|
|
291
|
-
// TODO: fix an issue when keyboard is dismissing during the initialization
|
|
292
|
-
const { layout } = e.nativeEvent;
|
|
293
|
-
if (maxHeightRef.current !== layout.height ||
|
|
294
|
-
isFirstLayoutRef.current === true) {
|
|
295
|
-
maxHeightRef.current = layout.height;
|
|
296
|
-
setState({
|
|
297
|
-
...state,
|
|
298
|
-
messagesContainerHeight: keyboardHeightRef.current > 0
|
|
299
|
-
? getMessagesContainerHeightWithKeyboard()
|
|
300
|
-
: getBasicMessagesContainerHeight(),
|
|
301
|
-
});
|
|
302
|
-
}
|
|
303
|
-
if (isFirstLayoutRef.current === true) {
|
|
304
|
-
isFirstLayoutRef.current = false;
|
|
305
|
-
}
|
|
306
|
-
};
|
|
307
|
-
const _renderInputToolbar = () => {
|
|
179
|
+
setIsInitialized(true);
|
|
180
|
+
setComposerHeight(minComposerHeight);
|
|
181
|
+
setText(getTextFromProp(initialText));
|
|
182
|
+
}, [initialText, minComposerHeight, notifyInputTextReset, getTextFromProp]);
|
|
183
|
+
const inputToolbarFragment = useMemo(() => {
|
|
184
|
+
if (!isInitialized)
|
|
185
|
+
return null;
|
|
308
186
|
const inputToolbarProps = {
|
|
309
187
|
...props,
|
|
310
|
-
text: getTextFromProp(
|
|
311
|
-
composerHeight: Math.max(minComposerHeight,
|
|
188
|
+
text: getTextFromProp(text),
|
|
189
|
+
composerHeight: Math.max(minComposerHeight, composerHeight),
|
|
312
190
|
onSend: _onSend,
|
|
313
|
-
onInputSizeChanged
|
|
191
|
+
onInputSizeChanged,
|
|
314
192
|
onTextChanged: _onInputTextChanged,
|
|
315
193
|
textInputProps: {
|
|
316
194
|
...textInputProps,
|
|
317
195
|
ref: textInputRef,
|
|
318
|
-
maxLength:
|
|
196
|
+
maxLength: isTypingDisabled ? 0 : maxInputLength,
|
|
319
197
|
},
|
|
320
198
|
};
|
|
321
|
-
if (renderInputToolbar)
|
|
199
|
+
if (renderInputToolbar)
|
|
322
200
|
return renderInputToolbar(inputToolbarProps);
|
|
323
|
-
}
|
|
324
201
|
return <InputToolbar {...inputToolbarProps}/>;
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
202
|
+
}, [
|
|
203
|
+
isInitialized,
|
|
204
|
+
_onSend,
|
|
205
|
+
getTextFromProp,
|
|
206
|
+
maxInputLength,
|
|
207
|
+
minComposerHeight,
|
|
208
|
+
onInputSizeChanged,
|
|
209
|
+
props,
|
|
210
|
+
text,
|
|
211
|
+
renderInputToolbar,
|
|
212
|
+
composerHeight,
|
|
213
|
+
isTypingDisabled,
|
|
214
|
+
textInputRef,
|
|
215
|
+
textInputProps,
|
|
216
|
+
_onInputTextChanged,
|
|
217
|
+
]);
|
|
338
218
|
const contextValues = useMemo(() => ({
|
|
339
|
-
actionSheet: actionSheet ||
|
|
219
|
+
actionSheet: actionSheet ||
|
|
220
|
+
(() => ({
|
|
221
|
+
showActionSheetWithOptions: actionSheetRef.current.showActionSheetWithOptions,
|
|
222
|
+
})),
|
|
340
223
|
getLocale: () => locale,
|
|
341
224
|
}), [actionSheet, locale]);
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
225
|
+
useEffect(() => {
|
|
226
|
+
if (props.text != null)
|
|
227
|
+
setText(props.text);
|
|
228
|
+
}, [props.text]);
|
|
229
|
+
useEffect(() => {
|
|
230
|
+
if (!inverted && messages?.length)
|
|
231
|
+
setTimeout(() => scrollToBottom(false), 200);
|
|
232
|
+
}, [messages?.length, inverted, scrollToBottom]);
|
|
233
|
+
useAnimatedReaction(() => keyboard.height.value, (value, prevValue) => {
|
|
234
|
+
if (prevValue && value !== prevValue) {
|
|
235
|
+
const isKeyboardMovingUp = value > prevValue;
|
|
236
|
+
if (isKeyboardMovingUp !== trackingKeyboardMovement.value) {
|
|
237
|
+
trackingKeyboardMovement.value = isKeyboardMovingUp;
|
|
238
|
+
keyboardOffsetBottom.value = withTiming(isKeyboardMovingUp ? insets.bottom : 0, {
|
|
239
|
+
duration: 400,
|
|
240
|
+
});
|
|
241
|
+
if (isKeyboardMovingUp)
|
|
242
|
+
runOnJS(handleTextInputFocusWhenKeyboardShow)();
|
|
243
|
+
else
|
|
244
|
+
runOnJS(handleTextInputFocusWhenKeyboardHide)();
|
|
245
|
+
if (value === 0) {
|
|
246
|
+
runOnJS(enableTyping)();
|
|
247
|
+
}
|
|
248
|
+
else {
|
|
249
|
+
runOnJS(disableTyping)();
|
|
250
|
+
runOnJS(debounceEnableTyping)();
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
}, [
|
|
255
|
+
keyboard,
|
|
256
|
+
trackingKeyboardMovement,
|
|
257
|
+
insets,
|
|
258
|
+
handleTextInputFocusWhenKeyboardHide,
|
|
259
|
+
handleTextInputFocusWhenKeyboardShow,
|
|
260
|
+
enableTyping,
|
|
261
|
+
disableTyping,
|
|
262
|
+
debounceEnableTyping,
|
|
263
|
+
]);
|
|
264
|
+
return (<GiftedChatContext.Provider value={contextValues}>
|
|
265
|
+
<ActionSheetProvider ref={actionSheetRef}>
|
|
266
|
+
<View testID={TEST_ID.WRAPPER} style={[styles.fill, styles.contentContainer]} onLayout={onInitialLayoutViewLayout}>
|
|
267
|
+
{isInitialized
|
|
268
|
+
? (<Animated.View style={[styles.fill, contentStyleAnim]}>
|
|
269
|
+
{renderMessages}
|
|
270
|
+
{inputToolbarFragment}
|
|
271
|
+
</Animated.View>)
|
|
272
|
+
: (renderLoading?.())}
|
|
351
273
|
</View>
|
|
352
|
-
</
|
|
353
|
-
|
|
354
|
-
return (<View testID={TEST_ID.LOADING_WRAPPER} style={styles.container} onLayout={onInitialLayoutViewLayout}>
|
|
355
|
-
{_renderLoading()}
|
|
356
|
-
</View>);
|
|
274
|
+
</ActionSheetProvider>
|
|
275
|
+
</GiftedChatContext.Provider>);
|
|
357
276
|
}
|
|
358
|
-
GiftedChat.propTypes = {
|
|
359
|
-
messages: PropTypes.arrayOf(PropTypes.object),
|
|
360
|
-
messagesContainerStyle: utils.StylePropType,
|
|
361
|
-
text: PropTypes.string,
|
|
362
|
-
initialText: PropTypes.string,
|
|
363
|
-
placeholder: PropTypes.string,
|
|
364
|
-
disableComposer: PropTypes.bool,
|
|
365
|
-
messageIdGenerator: PropTypes.func,
|
|
366
|
-
user: PropTypes.object,
|
|
367
|
-
onSend: PropTypes.func,
|
|
368
|
-
locale: PropTypes.string,
|
|
369
|
-
timeFormat: PropTypes.string,
|
|
370
|
-
dateFormat: PropTypes.string,
|
|
371
|
-
isKeyboardInternallyHandled: PropTypes.bool,
|
|
372
|
-
loadEarlier: PropTypes.bool,
|
|
373
|
-
onLoadEarlier: PropTypes.func,
|
|
374
|
-
isLoadingEarlier: PropTypes.bool,
|
|
375
|
-
renderLoading: PropTypes.func,
|
|
376
|
-
renderLoadEarlier: PropTypes.func,
|
|
377
|
-
renderAvatar: PropTypes.func,
|
|
378
|
-
showUserAvatar: PropTypes.bool,
|
|
379
|
-
actionSheet: PropTypes.func,
|
|
380
|
-
onPressAvatar: PropTypes.func,
|
|
381
|
-
onLongPressAvatar: PropTypes.func,
|
|
382
|
-
renderUsernameOnMessage: PropTypes.bool,
|
|
383
|
-
renderAvatarOnTop: PropTypes.bool,
|
|
384
|
-
isCustomViewBottom: PropTypes.bool,
|
|
385
|
-
renderBubble: PropTypes.func,
|
|
386
|
-
renderSystemMessage: PropTypes.func,
|
|
387
|
-
onLongPress: PropTypes.func,
|
|
388
|
-
renderMessage: PropTypes.func,
|
|
389
|
-
renderMessageText: PropTypes.func,
|
|
390
|
-
renderMessageImage: PropTypes.func,
|
|
391
|
-
imageProps: PropTypes.object,
|
|
392
|
-
videoProps: PropTypes.object,
|
|
393
|
-
audioProps: PropTypes.object,
|
|
394
|
-
lightboxProps: PropTypes.object,
|
|
395
|
-
renderCustomView: PropTypes.func,
|
|
396
|
-
renderDay: PropTypes.func,
|
|
397
|
-
renderTime: PropTypes.func,
|
|
398
|
-
renderFooter: PropTypes.func,
|
|
399
|
-
renderChatEmpty: PropTypes.func,
|
|
400
|
-
renderChatFooter: PropTypes.func,
|
|
401
|
-
renderInputToolbar: PropTypes.func,
|
|
402
|
-
renderComposer: PropTypes.func,
|
|
403
|
-
renderActions: PropTypes.func,
|
|
404
|
-
renderSend: PropTypes.func,
|
|
405
|
-
renderAccessory: PropTypes.func,
|
|
406
|
-
onPressActionButton: PropTypes.func,
|
|
407
|
-
bottomOffset: PropTypes.number,
|
|
408
|
-
minInputToolbarHeight: PropTypes.number,
|
|
409
|
-
listViewProps: PropTypes.object,
|
|
410
|
-
keyboardShouldPersistTaps: PropTypes.oneOf(['always', 'never', 'handled']),
|
|
411
|
-
onInputTextChanged: PropTypes.func,
|
|
412
|
-
maxInputLength: PropTypes.number,
|
|
413
|
-
forceGetKeyboardHeight: PropTypes.bool,
|
|
414
|
-
inverted: PropTypes.bool,
|
|
415
|
-
textInputProps: PropTypes.object,
|
|
416
|
-
extraData: PropTypes.object,
|
|
417
|
-
minComposerHeight: PropTypes.number,
|
|
418
|
-
maxComposerHeight: PropTypes.number,
|
|
419
|
-
alignTop: PropTypes.bool,
|
|
420
|
-
};
|
|
421
277
|
GiftedChat.append = (currentMessages = [], messages, inverted = true) => {
|
|
422
|
-
if (!Array.isArray(messages))
|
|
278
|
+
if (!Array.isArray(messages))
|
|
423
279
|
messages = [messages];
|
|
424
|
-
}
|
|
425
280
|
return inverted
|
|
426
281
|
? messages.concat(currentMessages)
|
|
427
282
|
: currentMessages.concat(messages);
|
|
428
283
|
};
|
|
429
284
|
GiftedChat.prepend = (currentMessages = [], messages, inverted = true) => {
|
|
430
|
-
if (!Array.isArray(messages))
|
|
285
|
+
if (!Array.isArray(messages))
|
|
431
286
|
messages = [messages];
|
|
432
|
-
}
|
|
433
287
|
return inverted
|
|
434
288
|
? currentMessages.concat(messages)
|
|
435
289
|
: messages.concat(currentMessages);
|
|
436
290
|
};
|
|
437
291
|
const styles = StyleSheet.create({
|
|
438
|
-
|
|
292
|
+
fill: {
|
|
439
293
|
flex: 1,
|
|
440
294
|
},
|
|
441
|
-
|
|
442
|
-
|
|
295
|
+
contentContainer: {
|
|
296
|
+
overflow: 'hidden',
|
|
443
297
|
},
|
|
444
298
|
});
|
|
445
299
|
export * from './Models';
|
|
446
|
-
export { GiftedChat, Actions, Avatar, Bubble, SystemMessage, MessageImage, MessageText, Composer, Day, InputToolbar, LoadEarlier, Message, MessageContainer, Send, Time, GiftedAvatar, utils
|
|
300
|
+
export { GiftedChat, Actions, Avatar, Bubble, SystemMessage, MessageImage, MessageText, Composer, Day, InputToolbar, LoadEarlier, Message, MessageContainer, Send, Time, GiftedAvatar, utils };
|
|
447
301
|
//# sourceMappingURL=GiftedChat.js.map
|
package/lib/GiftedChat.js.flow
CHANGED
|
@@ -66,7 +66,6 @@ export type GiftedChatProps<TMessage: IMessage = IMessage> = $ReadOnly<{|
|
|
|
66
66
|
inverted?: boolean,
|
|
67
67
|
imageProps?: MessageProps<TMessage>,
|
|
68
68
|
lightboxProps?: any,
|
|
69
|
-
bottomOffset?: number,
|
|
70
69
|
minInputToolbarHeight?: number,
|
|
71
70
|
listViewProps?: $Shape<React.ElementProps<typeof FlatList>>,
|
|
72
71
|
textInputProps?: any,
|
|
@@ -84,6 +83,8 @@ export type GiftedChatProps<TMessage: IMessage = IMessage> = $ReadOnly<{|
|
|
|
84
83
|
},
|
|
85
84
|
optionTintColor?: string,
|
|
86
85
|
quickReplyStyle?: ViewStyleProp,
|
|
86
|
+
quickReplyTextStyle?: TextStyleProp,
|
|
87
|
+
quickReplyContainerStyle?: ViewStyleProp,
|
|
87
88
|
isCustomViewBottom?: boolean,
|
|
88
89
|
timeTextStyle?: LeftRightStyle<TextStyleProp>,
|
|
89
90
|
onPressAvatar?: User => void,
|
|
@@ -128,8 +129,7 @@ export type GiftedChatProps<TMessage: IMessage = IMessage> = $ReadOnly<{|
|
|
|
128
129
|
export type GiftedChatState<TMessage: IMessage = IMessage> = {|
|
|
129
130
|
isInitialized: boolean,
|
|
130
131
|
composerHeight?: number,
|
|
131
|
-
|
|
132
|
-
typingDisabled: boolean,
|
|
132
|
+
isTypingDisabled: boolean,
|
|
133
133
|
text?: string,
|
|
134
134
|
messages?: Array<TMessage>,
|
|
135
135
|
|}
|