react-native-gifted-chat 2.4.1 → 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.
Files changed (159) hide show
  1. package/README.md +1 -3
  2. package/lib/Actions.d.ts +5 -6
  3. package/lib/Actions.js +16 -13
  4. package/lib/Actions.js.map +1 -1
  5. package/lib/Avatar.d.ts +9 -25
  6. package/lib/Avatar.js +12 -18
  7. package/lib/Avatar.js.flow +1 -1
  8. package/lib/Avatar.js.map +1 -1
  9. package/lib/Bubble.d.ts +29 -29
  10. package/lib/Bubble.js +88 -70
  11. package/lib/Bubble.js.flow +1 -1
  12. package/lib/Bubble.js.map +1 -1
  13. package/lib/Composer.d.ts +1 -1
  14. package/lib/Composer.js +30 -32
  15. package/lib/Composer.js.map +1 -1
  16. package/lib/Constant.js +1 -0
  17. package/lib/Constant.js.map +1 -1
  18. package/lib/Day.d.ts +3 -15
  19. package/lib/Day.js +2 -14
  20. package/lib/Day.js.flow +1 -1
  21. package/lib/Day.js.map +1 -1
  22. package/lib/GiftedAvatar.d.ts +7 -7
  23. package/lib/GiftedAvatar.js +30 -29
  24. package/lib/GiftedAvatar.js.map +1 -1
  25. package/lib/GiftedChat.d.ts +15 -89
  26. package/lib/GiftedChat.js +204 -350
  27. package/lib/GiftedChat.js.flow +1 -3
  28. package/lib/GiftedChat.js.map +1 -1
  29. package/lib/GiftedChatContext.d.ts +2 -1
  30. package/lib/GiftedChatContext.js.map +1 -1
  31. package/lib/InputToolbar.d.ts +7 -5
  32. package/lib/InputToolbar.js +41 -34
  33. package/lib/InputToolbar.js.map +1 -1
  34. package/lib/LoadEarlier.d.ts +4 -4
  35. package/lib/LoadEarlier.js +8 -6
  36. package/lib/LoadEarlier.js.map +1 -1
  37. package/lib/Message.d.ts +8 -9
  38. package/lib/Message.js +47 -40
  39. package/lib/Message.js.flow +1 -1
  40. package/lib/Message.js.map +1 -1
  41. package/lib/MessageAudio.d.ts +2 -1
  42. package/lib/MessageAudio.js +4 -4
  43. package/lib/MessageAudio.js.flow +1 -1
  44. package/lib/MessageAudio.js.map +1 -1
  45. package/lib/MessageContainer.d.ts +17 -17
  46. package/lib/MessageContainer.js +33 -51
  47. package/lib/MessageContainer.js.map +1 -1
  48. package/lib/MessageImage.d.ts +5 -4
  49. package/lib/MessageImage.js +4 -5
  50. package/lib/MessageImage.js.flow +1 -1
  51. package/lib/MessageImage.js.map +1 -1
  52. package/lib/MessageText.d.ts +11 -10
  53. package/lib/MessageText.js +5 -10
  54. package/lib/MessageText.js.flow +1 -1
  55. package/lib/MessageText.js.map +1 -1
  56. package/lib/MessageVideo.d.ts +2 -1
  57. package/lib/MessageVideo.js +4 -4
  58. package/lib/MessageVideo.js.flow +1 -1
  59. package/lib/MessageVideo.js.map +1 -1
  60. package/lib/Models.d.ts +7 -7
  61. package/lib/QuickReplies.d.ts +3 -3
  62. package/lib/QuickReplies.js +8 -14
  63. package/lib/QuickReplies.js.flow +1 -1
  64. package/lib/QuickReplies.js.map +1 -1
  65. package/lib/Send.d.ts +4 -4
  66. package/lib/Send.js +6 -9
  67. package/lib/Send.js.map +1 -1
  68. package/lib/SystemMessage.d.ts +6 -5
  69. package/lib/SystemMessage.js +1 -2
  70. package/lib/SystemMessage.js.flow +1 -1
  71. package/lib/SystemMessage.js.map +1 -1
  72. package/lib/Time.d.ts +7 -6
  73. package/lib/Time.js +1 -3
  74. package/lib/Time.js.flow +1 -1
  75. package/lib/Time.js.map +1 -1
  76. package/lib/TypingIndicator.d.ts +2 -1
  77. package/lib/TypingIndicator.js +5 -5
  78. package/lib/TypingIndicator.js.map +1 -1
  79. package/lib/hooks/useUpdateLayoutEffect.js +2 -4
  80. package/lib/hooks/useUpdateLayoutEffect.js.map +1 -1
  81. package/lib/index.d.ts +1 -0
  82. package/lib/index.js +1 -0
  83. package/lib/index.js.map +1 -1
  84. package/lib/logging.d.ts +2 -2
  85. package/lib/logging.js.map +1 -1
  86. package/lib/types.js.flow +1 -1
  87. package/lib/utils.d.ts +1 -1
  88. package/lib/utils.js +2 -4
  89. package/lib/utils.js.map +1 -1
  90. package/package.json +83 -53
  91. package/src/Actions.tsx +114 -0
  92. package/src/Avatar.tsx +178 -0
  93. package/src/Bubble.tsx +601 -0
  94. package/src/Color.ts +17 -0
  95. package/src/Composer.tsx +147 -0
  96. package/src/Constant.ts +18 -0
  97. package/src/Day.tsx +71 -0
  98. package/src/GiftedAvatar.tsx +205 -0
  99. package/src/GiftedChat.tsx +670 -0
  100. package/src/GiftedChatContext.ts +23 -0
  101. package/src/InputToolbar.tsx +113 -0
  102. package/src/LoadEarlier.tsx +108 -0
  103. package/src/Message.tsx +229 -0
  104. package/src/MessageAudio.tsx +19 -0
  105. package/src/MessageContainer.tsx +362 -0
  106. package/src/MessageImage.tsx +78 -0
  107. package/src/MessageText.tsx +187 -0
  108. package/src/MessageVideo.tsx +19 -0
  109. package/src/Models.ts +84 -0
  110. package/src/QuickReplies.tsx +186 -0
  111. package/src/Send.tsx +102 -0
  112. package/src/SystemMessage.tsx +61 -0
  113. package/src/Time.tsx +97 -0
  114. package/src/TypingIndicator.tsx +108 -0
  115. package/src/__tests__/Actions.test.tsx +10 -0
  116. package/src/__tests__/Avatar.test.tsx +13 -0
  117. package/src/__tests__/Bubble.test.tsx +23 -0
  118. package/src/__tests__/Color.test.tsx +5 -0
  119. package/src/__tests__/Composer.test.tsx +11 -0
  120. package/src/__tests__/Constant.test.tsx +5 -0
  121. package/src/__tests__/Day.test.tsx +23 -0
  122. package/src/__tests__/GiftedAvatar.test.tsx +11 -0
  123. package/src/__tests__/GiftedChat.test.tsx +36 -0
  124. package/src/__tests__/InputToolbar.test.tsx +11 -0
  125. package/src/__tests__/LoadEarlier.test.tsx +11 -0
  126. package/src/__tests__/Message.test.tsx +77 -0
  127. package/src/__tests__/MessageContainer.test.tsx +11 -0
  128. package/src/__tests__/MessageImage.test.tsx +27 -0
  129. package/src/__tests__/MessageText.test.tsx +11 -0
  130. package/src/__tests__/Send.test.tsx +22 -0
  131. package/src/__tests__/SystemMessage.test.tsx +27 -0
  132. package/src/__tests__/Time.test.tsx +29 -0
  133. package/src/__tests__/__snapshots__/Actions.test.tsx.snap +76 -0
  134. package/src/__tests__/__snapshots__/Avatar.test.tsx.snap +17 -0
  135. package/src/__tests__/__snapshots__/Bubble.test.tsx.snap +145 -0
  136. package/src/__tests__/__snapshots__/Color.test.tsx.snap +21 -0
  137. package/src/__tests__/__snapshots__/Composer.test.tsx.snap +35 -0
  138. package/src/__tests__/__snapshots__/Constant.test.tsx.snap +16 -0
  139. package/src/__tests__/__snapshots__/Day.test.tsx.snap +37 -0
  140. package/src/__tests__/__snapshots__/GiftedAvatar.test.tsx.snap +22 -0
  141. package/src/__tests__/__snapshots__/GiftedChat.test.tsx.snap +15 -0
  142. package/src/__tests__/__snapshots__/InputToolbar.test.tsx.snap +60 -0
  143. package/src/__tests__/__snapshots__/LoadEarlier.test.tsx.snap +74 -0
  144. package/src/__tests__/__snapshots__/Message.test.tsx.snap +628 -0
  145. package/src/__tests__/__snapshots__/MessageContainer.test.tsx.snap +127 -0
  146. package/src/__tests__/__snapshots__/MessageImage.test.tsx.snap +38 -0
  147. package/src/__tests__/__snapshots__/MessageText.test.tsx.snap +30 -0
  148. package/src/__tests__/__snapshots__/Send.test.tsx.snap +129 -0
  149. package/src/__tests__/__snapshots__/SystemMessage.test.tsx.snap +38 -0
  150. package/src/__tests__/__snapshots__/Time.test.tsx.snap +33 -0
  151. package/src/__tests__/data.ts +8 -0
  152. package/src/__tests__/utils.test.ts +31 -0
  153. package/src/hooks/useUpdateLayoutEffect.ts +21 -0
  154. package/src/index.ts +4 -0
  155. package/src/logging.ts +8 -0
  156. package/src/utils.ts +39 -0
  157. package/.eslintignore +0 -2
  158. package/.eslintrc.js +0 -21
  159. 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 PropTypes from 'prop-types';
5
- import React, { createRef, useEffect, useMemo, useRef, useState } from 'react';
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 = [], text = undefined, initialText = '', isTyping, messageIdGenerator = () => uuid.v4(), user = {}, onSend = () => { }, locale = 'en', renderLoading = null, actionSheet = null, textInputProps = {}, renderChatFooter = null, renderInputToolbar = null, renderAccessory = null, isKeyboardInternallyHandled = true, bottomOffset = null, minInputToolbarHeight = 44, keyboardShouldPersistTaps = Platform.select({
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, forceGetKeyboardHeight = false, inverted = true, minComposerHeight = MIN_COMPOSER_HEIGHT, maxComposerHeight = MAX_COMPOSER_HEIGHT, messageContainerRef = createRef(), textInputRef = createRef(), } = props;
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
- let _isTextInputWasFocused = false;
40
- const [state, setState] = useState({
41
- isInitialized: false,
42
- composerHeight: minComposerHeight,
43
- messagesContainerHeight: undefined,
44
- typingDisabled: false,
45
- text: undefined,
46
- messages: undefined,
47
- });
48
- useEffect(() => {
49
- isMountedRef.current = true;
50
- setState({
51
- ...state,
52
- messages,
53
- // Text prop takes precedence over state.
54
- ...(text !== undefined && text !== state.text && { text: text }),
55
- });
56
- if (inverted === false && (messages === null || messages === void 0 ? void 0 : messages.length)) {
57
- setTimeout(() => scrollToBottom(false), 200);
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
- return text;
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 after wards if needed.
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
- var _a;
106
- if (!_isTextInputWasFocused) {
107
- _isTextInputWasFocused = ((_a = textInputRef.current) === null || _a === void 0 ? void 0 : _a.isFocused()) || false;
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
- _isTextInputWasFocused &&
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
- _isTextInputWasFocused = false;
122
- };
123
- const onKeyboardWillShow = (e) => {
124
- handleTextInputFocusWhenKeyboardShow();
125
- if (isKeyboardInternallyHandled) {
126
- keyboardHeightRef.current = e.endCoordinates
127
- ? e.endCoordinates.height
128
- : e.end.height;
129
- bottomOffsetRef.current = bottomOffset != null ? bottomOffset : 1;
130
- const newMessagesContainerHeight = getMessagesContainerHeightWithKeyboard();
131
- setState({
132
- ...state,
133
- typingDisabled: true,
134
- messagesContainerHeight: newMessagesContainerHeight,
135
- });
136
- }
137
- };
138
- const onKeyboardWillHide = (_e) => {
139
- handleTextInputFocusWhenKeyboardHide();
140
- if (isKeyboardInternallyHandled) {
141
- keyboardHeightRef.current = 0;
142
- bottomOffsetRef.current = 0;
143
- const newMessagesContainerHeight = getBasicMessagesContainerHeight();
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
- const onKeyboardDidShow = (e) => {
152
- if (Platform.OS === 'android') {
153
- onKeyboardWillShow(e);
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: inverted,
192
- keyboardShouldPersistTaps: keyboardShouldPersistTaps,
193
- onKeyboardWillShow: onKeyboardWillShow,
194
- onKeyboardWillHide: onKeyboardWillHide,
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 isKeyboardInternallyHandled ? (<KeyboardAvoidingView enabled>{fragment}</KeyboardAvoidingView>) : (fragment);
201
- };
202
- const _onSend = (messages = [], shouldResetInputToolbar = false) => {
203
- if (!Array.isArray(messages)) {
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 && messageIdGenerator(),
153
+ _id: messageIdGenerator?.(),
212
154
  };
213
155
  });
214
156
  if (shouldResetInputToolbar === true) {
215
- setState({
216
- ...state,
217
- typingDisabled: true,
218
- });
157
+ disableTyping();
219
158
  resetInputToolbar();
220
159
  }
221
- if (onSend) {
222
- onSend(newMessages);
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
- const newMessagesContainerHeight = getMessagesContainerHeightWithKeyboard(newComposerHeight);
251
- setState({
252
- ...state,
253
- composerHeight: newComposerHeight,
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
- setState({ ...state, text: _text });
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
- maxHeightRef.current = layout.height;
281
- const newMessagesContainerHeight = getMessagesContainerHeightWithKeyboard(minComposerHeight);
282
- setState({
283
- ...state,
284
- isInitialized: true,
285
- text: getTextFromProp(initialText),
286
- composerHeight: minComposerHeight,
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(state.text),
311
- composerHeight: Math.max(minComposerHeight, state.composerHeight),
188
+ text: getTextFromProp(text),
189
+ composerHeight: Math.max(minComposerHeight, composerHeight),
312
190
  onSend: _onSend,
313
- onInputSizeChanged: onInputSizeChanged,
191
+ onInputSizeChanged,
314
192
  onTextChanged: _onInputTextChanged,
315
193
  textInputProps: {
316
194
  ...textInputProps,
317
195
  ref: textInputRef,
318
- maxLength: state.typingDisabled ? 0 : maxInputLength,
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
- const _renderChatFooter = () => {
327
- if (renderChatFooter) {
328
- return renderChatFooter();
329
- }
330
- return null;
331
- };
332
- const _renderLoading = () => {
333
- if (renderLoading) {
334
- return renderLoading();
335
- }
336
- return null;
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 || (() => { var _a; return (_a = actionSheetRef.current) === null || _a === void 0 ? void 0 : _a.getContext(); }),
219
+ actionSheet: actionSheet ||
220
+ (() => ({
221
+ showActionSheetWithOptions: actionSheetRef.current.showActionSheetWithOptions,
222
+ })),
340
223
  getLocale: () => locale,
341
224
  }), [actionSheet, locale]);
342
- if (state.isInitialized === true) {
343
- return (<GiftedChatContext.Provider value={contextValues}>
344
- <View testID={TEST_ID.WRAPPER} style={styles.wrapper}>
345
- <ActionSheetProvider ref={actionSheetRef}>
346
- <View style={styles.container} onLayout={onMainViewLayout}>
347
- {renderMessages()}
348
- {_renderInputToolbar()}
349
- </View>
350
- </ActionSheetProvider>
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
- </GiftedChatContext.Provider>);
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
- container: {
292
+ fill: {
439
293
  flex: 1,
440
294
  },
441
- wrapper: {
442
- flex: 1,
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
@@ -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,
@@ -130,8 +129,7 @@ export type GiftedChatProps<TMessage: IMessage = IMessage> = $ReadOnly<{|
130
129
  export type GiftedChatState<TMessage: IMessage = IMessage> = {|
131
130
  isInitialized: boolean,
132
131
  composerHeight?: number,
133
- messagesContainerHeight?: number | AnimatedValue,
134
- typingDisabled: boolean,
132
+ isTypingDisabled: boolean,
135
133
  text?: string,
136
134
  messages?: Array<TMessage>,
137
135
  |}