@sendbird/uikit-react-native 3.10.0 → 3.10.2
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/lib/commonjs/components/ChannelInput/EditInput.js +5 -3
- package/lib/commonjs/components/ChannelInput/EditInput.js.map +1 -1
- package/lib/commonjs/components/ChannelInput/SendInput.js +19 -4
- package/lib/commonjs/components/ChannelInput/SendInput.js.map +1 -1
- package/lib/commonjs/components/ChannelInput/index.js +25 -2
- package/lib/commonjs/components/ChannelInput/index.js.map +1 -1
- package/lib/commonjs/domain/groupChannel/component/GroupChannelMessageList.js +29 -5
- package/lib/commonjs/domain/groupChannel/component/GroupChannelMessageList.js.map +1 -1
- package/lib/commonjs/domain/groupChannel/module/moduleContext.js +36 -2
- package/lib/commonjs/domain/groupChannel/module/moduleContext.js.map +1 -1
- package/lib/commonjs/domain/groupChannel/types.js.map +1 -1
- package/lib/commonjs/fragments/createGroupChannelFragment.js +3 -1
- package/lib/commonjs/fragments/createGroupChannelFragment.js.map +1 -1
- package/lib/commonjs/version.js +1 -1
- package/lib/commonjs/version.js.map +1 -1
- package/lib/module/components/ChannelInput/EditInput.js +5 -3
- package/lib/module/components/ChannelInput/EditInput.js.map +1 -1
- package/lib/module/components/ChannelInput/SendInput.js +20 -5
- package/lib/module/components/ChannelInput/SendInput.js.map +1 -1
- package/lib/module/components/ChannelInput/index.js +26 -3
- package/lib/module/components/ChannelInput/index.js.map +1 -1
- package/lib/module/domain/groupChannel/component/GroupChannelMessageList.js +29 -5
- package/lib/module/domain/groupChannel/component/GroupChannelMessageList.js.map +1 -1
- package/lib/module/domain/groupChannel/module/moduleContext.js +36 -2
- package/lib/module/domain/groupChannel/module/moduleContext.js.map +1 -1
- package/lib/module/domain/groupChannel/types.js.map +1 -1
- package/lib/module/fragments/createGroupChannelFragment.js +3 -1
- package/lib/module/fragments/createGroupChannelFragment.js.map +1 -1
- package/lib/module/version.js +1 -1
- package/lib/module/version.js.map +1 -1
- package/lib/typescript/src/components/ChannelInput/index.d.ts +2 -1
- package/lib/typescript/src/containers/SendbirdUIKitContainer.d.ts +1 -1
- package/lib/typescript/src/domain/groupChannel/component/GroupChannelInput.d.ts +1 -1
- package/lib/typescript/src/domain/groupChannel/types.d.ts +12 -1
- package/lib/typescript/src/version.d.ts +1 -1
- package/package.json +6 -6
- package/src/components/ChannelInput/EditInput.tsx +2 -0
- package/src/components/ChannelInput/SendInput.tsx +16 -1
- package/src/components/ChannelInput/index.tsx +50 -7
- package/src/domain/groupChannel/component/GroupChannelMessageList.tsx +25 -4
- package/src/domain/groupChannel/module/moduleContext.tsx +40 -5
- package/src/domain/groupChannel/types.ts +14 -1
- package/src/fragments/createGroupChannelFragment.tsx +2 -0
- package/src/version.ts +1 -1
|
@@ -31,6 +31,7 @@ export interface GroupChannelProps {
|
|
|
31
31
|
flatListProps?: GroupChannelProps['MessageList']['flatListProps'];
|
|
32
32
|
sortComparator?: UseGroupChannelMessagesOptions['sortComparator'];
|
|
33
33
|
searchItem?: GroupChannelProps['MessageList']['searchItem'];
|
|
34
|
+
partialTextInputProps?: GroupChannelProps['Input']['partialTextInputProps'];
|
|
34
35
|
/**
|
|
35
36
|
* @description You can specify the query parameters for the message list.
|
|
36
37
|
* @example
|
|
@@ -57,7 +58,7 @@ export interface GroupChannelProps {
|
|
|
57
58
|
onNewLineSeenChange?: (hasSeenNewLine: boolean) => void;
|
|
58
59
|
onUserMarkedAsUnreadChange?: (hasUserMarkedAsUnread: boolean) => void;
|
|
59
60
|
};
|
|
60
|
-
Input: PickPartial<ChannelInputProps, 'shouldRenderInput' | 'onPressSendUserMessage' | 'onPressSendFileMessage' | 'onPressUpdateUserMessage' | 'onPressUpdateFileMessage' | 'SuggestedMentionList' | 'AttachmentsButton', 'inputDisabled'>;
|
|
61
|
+
Input: PickPartial<ChannelInputProps, 'shouldRenderInput' | 'onPressSendUserMessage' | 'onPressSendFileMessage' | 'onPressUpdateUserMessage' | 'onPressUpdateFileMessage' | 'SuggestedMentionList' | 'AttachmentsButton' | 'partialTextInputProps', 'inputDisabled'>;
|
|
61
62
|
SuggestedMentionList: SuggestedMentionListProps;
|
|
62
63
|
Provider: {
|
|
63
64
|
channel: SendbirdGroupChannel;
|
|
@@ -129,6 +130,16 @@ export interface GroupChannelContextsType {
|
|
|
129
130
|
timeout?: number;
|
|
130
131
|
viewPosition?: number;
|
|
131
132
|
}) => void;
|
|
133
|
+
/**
|
|
134
|
+
* Call the FlatList function asynchronously to scroll to a message by messageId lazily.
|
|
135
|
+
* to avoid scrolling before data rendering has been committed.
|
|
136
|
+
* */
|
|
137
|
+
lazyScrollToMessageId: (params?: {
|
|
138
|
+
messageId?: number;
|
|
139
|
+
animated?: boolean;
|
|
140
|
+
timeout?: number;
|
|
141
|
+
viewPosition?: number;
|
|
142
|
+
}) => void;
|
|
132
143
|
onPressReplyMessageInThread?: (parentMessage: SendbirdSendableMessage, startingPoint?: number) => void;
|
|
133
144
|
}>;
|
|
134
145
|
}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
declare const VERSION = "3.10.
|
|
1
|
+
declare const VERSION = "3.10.2";
|
|
2
2
|
export default VERSION;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sendbird/uikit-react-native",
|
|
3
|
-
"version": "3.10.
|
|
3
|
+
"version": "3.10.2",
|
|
4
4
|
"description": "Sendbird UIKit for React Native: A feature-rich and customizable chat UI kit with messaging, channel management, and user authentication.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"sendbird",
|
|
@@ -60,10 +60,10 @@
|
|
|
60
60
|
},
|
|
61
61
|
"dependencies": {
|
|
62
62
|
"@openspacelabs/react-native-zoomable-view": "^2.1.5",
|
|
63
|
-
"@sendbird/uikit-chat-hooks": "3.10.
|
|
64
|
-
"@sendbird/uikit-react-native-foundation": "3.10.
|
|
65
|
-
"@sendbird/uikit-tools": "0.0.
|
|
66
|
-
"@sendbird/uikit-utils": "3.10.
|
|
63
|
+
"@sendbird/uikit-chat-hooks": "3.10.2",
|
|
64
|
+
"@sendbird/uikit-react-native-foundation": "3.10.2",
|
|
65
|
+
"@sendbird/uikit-tools": "0.0.15",
|
|
66
|
+
"@sendbird/uikit-utils": "3.10.2"
|
|
67
67
|
},
|
|
68
68
|
"devDependencies": {
|
|
69
69
|
"@bam.tech/react-native-image-resizer": "^3.0.4",
|
|
@@ -218,5 +218,5 @@
|
|
|
218
218
|
]
|
|
219
219
|
]
|
|
220
220
|
},
|
|
221
|
-
"gitHead": "
|
|
221
|
+
"gitHead": "229779f3a5f52aeb3eb271d077398ad35461d9ff"
|
|
222
222
|
}
|
|
@@ -31,6 +31,7 @@ const EditInput = forwardRef<RNTextInput, EditInputProps>(function EditInput(
|
|
|
31
31
|
autoFocus,
|
|
32
32
|
mentionedUsers,
|
|
33
33
|
inputDisabled,
|
|
34
|
+
partialTextInputProps,
|
|
34
35
|
},
|
|
35
36
|
ref,
|
|
36
37
|
) {
|
|
@@ -70,6 +71,7 @@ const EditInput = forwardRef<RNTextInput, EditInputProps>(function EditInput(
|
|
|
70
71
|
<View style={styles.editInputContainer}>
|
|
71
72
|
<View style={styles.inputWrapper}>
|
|
72
73
|
<TextInput
|
|
74
|
+
{...partialTextInputProps}
|
|
73
75
|
ref={ref}
|
|
74
76
|
multiline
|
|
75
77
|
disableFullscreenUI
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import React, { forwardRef } from 'react';
|
|
2
2
|
import {
|
|
3
3
|
NativeSyntheticEvent,
|
|
4
|
+
Platform,
|
|
4
5
|
TextInput as RNTextInput,
|
|
5
6
|
TextInputSelectionChangeEventData,
|
|
6
7
|
TouchableOpacity,
|
|
@@ -54,6 +55,7 @@ const SendInput = forwardRef<RNTextInput, SendInputProps>(function SendInput(
|
|
|
54
55
|
messageToReply,
|
|
55
56
|
setMessageToReply,
|
|
56
57
|
messageForThread,
|
|
58
|
+
partialTextInputProps,
|
|
57
59
|
},
|
|
58
60
|
ref,
|
|
59
61
|
) {
|
|
@@ -116,7 +118,19 @@ const SendInput = forwardRef<RNTextInput, SendInputProps>(function SendInput(
|
|
|
116
118
|
...messageReplyParams,
|
|
117
119
|
}).catch(onFailureToSend);
|
|
118
120
|
|
|
119
|
-
onChangeText('')
|
|
121
|
+
// On iOS with autoCorrect enabled, calling onChangeText('') immediately after sending
|
|
122
|
+
// can be ignored due to the keyboard's autocorrect not being committed yet.
|
|
123
|
+
// Delay the clear call slightly to allow the autocorrected text to be applied first.
|
|
124
|
+
if (Platform.OS === 'ios') {
|
|
125
|
+
const textInputRef = ref as React.MutableRefObject<RNTextInput | undefined>;
|
|
126
|
+
if (textInputRef.current) {
|
|
127
|
+
setTimeout(() => {
|
|
128
|
+
onChangeText('');
|
|
129
|
+
}, 10);
|
|
130
|
+
}
|
|
131
|
+
} else {
|
|
132
|
+
onChangeText('');
|
|
133
|
+
}
|
|
120
134
|
setMessageToReply?.();
|
|
121
135
|
};
|
|
122
136
|
|
|
@@ -186,6 +200,7 @@ const SendInput = forwardRef<RNTextInput, SendInputProps>(function SendInput(
|
|
|
186
200
|
<View style={styles.sendInputContainer}>
|
|
187
201
|
{AttachmentsButton && <AttachmentsButton onPress={() => openSheet({ sheetItems })} disabled={inputDisabled} />}
|
|
188
202
|
<TextInput
|
|
203
|
+
{...partialTextInputProps}
|
|
189
204
|
ref={ref}
|
|
190
205
|
multiline
|
|
191
206
|
disableFullscreenUI
|
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
import React, { useEffect, useMemo, useState } from 'react';
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
Keyboard,
|
|
4
|
+
KeyboardAvoidingView,
|
|
5
|
+
Platform,
|
|
6
|
+
StyleProp,
|
|
7
|
+
StyleSheet,
|
|
8
|
+
TextInput,
|
|
9
|
+
TextInputProps,
|
|
10
|
+
TextStyle,
|
|
11
|
+
View,
|
|
12
|
+
} from 'react-native';
|
|
3
13
|
|
|
4
14
|
import { createStyleSheet, useUIKitTheme } from '@sendbird/uikit-react-native-foundation';
|
|
5
15
|
import {
|
|
@@ -79,10 +89,18 @@ export type ChannelInputProps = {
|
|
|
79
89
|
AttachmentsButton?: (props: AttachmentsButtonProps) => React.ReactNode | null;
|
|
80
90
|
MessageToReplyPreview?: (props: MessageToReplyPreviewProps) => React.ReactNode | null;
|
|
81
91
|
VoiceMessageInput?: (props: VoiceMessageInputProps) => React.ReactNode | null;
|
|
92
|
+
|
|
93
|
+
// TextInput props - only safe properties that don't interfere with UIKit functionality
|
|
94
|
+
partialTextInputProps?: Partial<Pick<TextInputProps, 'autoCorrect'>>;
|
|
82
95
|
};
|
|
83
96
|
|
|
84
97
|
const AUTO_FOCUS = Platform.select({ ios: false, android: true, default: false });
|
|
85
|
-
const
|
|
98
|
+
const isAndroidApi35Plus = Platform.OS === 'android' && Platform.Version >= 35;
|
|
99
|
+
const KEYBOARD_AVOID_VIEW_BEHAVIOR = Platform.select({
|
|
100
|
+
ios: 'padding' as const,
|
|
101
|
+
android: isAndroidApi35Plus ? ('padding' as const) : undefined,
|
|
102
|
+
default: undefined,
|
|
103
|
+
});
|
|
86
104
|
|
|
87
105
|
// FIXME(iOS): Dynamic style does not work properly when typing the CJK. (https://github.com/facebook/react-native/issues/26107)
|
|
88
106
|
// To workaround temporarily, change the key for re-mount the component.
|
|
@@ -96,6 +114,17 @@ const ChannelInput = (props: ChannelInputProps) => {
|
|
|
96
114
|
const { channel, keyboardAvoidOffset, messageToEdit, setMessageToEdit } = props;
|
|
97
115
|
|
|
98
116
|
const safeArea = useSafeAreaPadding(['top', 'left', 'right', 'bottom']);
|
|
117
|
+
|
|
118
|
+
// Android API 35+ keyboard avoidance handling
|
|
119
|
+
/**
|
|
120
|
+
* Android API 35+ introduced edge-to-edge layouts, which changed how keyboard avoidance should be handled.
|
|
121
|
+
* For API 35+, the system manages insets automatically, so we use the provided keyboardAvoidOffset directly.
|
|
122
|
+
* For older Android versions, we manually subtract the safe area bottom padding to avoid overlapping with system UI.
|
|
123
|
+
* See: https://developer.android.com/develop/ui/views/layout/edge-to-edge
|
|
124
|
+
*/
|
|
125
|
+
const keyboardVerticalOffset = isAndroidApi35Plus
|
|
126
|
+
? keyboardAvoidOffset
|
|
127
|
+
: -safeArea.paddingBottom + keyboardAvoidOffset;
|
|
99
128
|
const { colors, typography } = useUIKitTheme();
|
|
100
129
|
const { sbOptions, mentionManager } = useSendbirdChat();
|
|
101
130
|
|
|
@@ -132,16 +161,30 @@ const ChannelInput = (props: ChannelInputProps) => {
|
|
|
132
161
|
onChangeText(replace(text, searchStringRange.start, searchStringRange.end, mentionedMessageText), { user, range });
|
|
133
162
|
};
|
|
134
163
|
|
|
164
|
+
const [keyboardShown, setKeyboardShown] = useState(false);
|
|
165
|
+
|
|
166
|
+
useEffect(() => {
|
|
167
|
+
const keyboardDidShow = () => setKeyboardShown(true);
|
|
168
|
+
const keyboardDidHide = () => setKeyboardShown(false);
|
|
169
|
+
|
|
170
|
+
const showSubscription = Keyboard.addListener('keyboardDidShow', keyboardDidShow);
|
|
171
|
+
const hideSubscription = Keyboard.addListener('keyboardDidHide', keyboardDidHide);
|
|
172
|
+
|
|
173
|
+
return () => {
|
|
174
|
+
showSubscription.remove();
|
|
175
|
+
hideSubscription.remove();
|
|
176
|
+
};
|
|
177
|
+
}, []);
|
|
178
|
+
|
|
179
|
+
const shouldShowSafeAreaBottom = !isAndroidApi35Plus || (isAndroidApi35Plus && !keyboardShown);
|
|
180
|
+
|
|
135
181
|
if (!props.shouldRenderInput) {
|
|
136
182
|
return <SafeAreaBottom height={safeArea.paddingBottom} />;
|
|
137
183
|
}
|
|
138
184
|
|
|
139
185
|
return (
|
|
140
186
|
<>
|
|
141
|
-
<KeyboardAvoidingView
|
|
142
|
-
keyboardVerticalOffset={-safeArea.paddingBottom + keyboardAvoidOffset}
|
|
143
|
-
behavior={KEYBOARD_AVOID_VIEW_BEHAVIOR}
|
|
144
|
-
>
|
|
187
|
+
<KeyboardAvoidingView keyboardVerticalOffset={keyboardVerticalOffset} behavior={KEYBOARD_AVOID_VIEW_BEHAVIOR}>
|
|
145
188
|
<View
|
|
146
189
|
style={{
|
|
147
190
|
paddingStart: safeArea.paddingStart,
|
|
@@ -181,7 +224,7 @@ const ChannelInput = (props: ChannelInputProps) => {
|
|
|
181
224
|
/>
|
|
182
225
|
)}
|
|
183
226
|
</View>
|
|
184
|
-
<SafeAreaBottom height={safeArea.paddingBottom} />
|
|
227
|
+
{shouldShowSafeAreaBottom && <SafeAreaBottom height={safeArea.paddingBottom} />}
|
|
185
228
|
</View>
|
|
186
229
|
</KeyboardAvoidingView>
|
|
187
230
|
{mentionAvailable && props.SuggestedMentionList && (
|
|
@@ -26,7 +26,7 @@ const GroupChannelMessageList = (props: GroupChannelProps['MessageList']) => {
|
|
|
26
26
|
const { sdk, sbOptions, groupChannelFragmentOptions } = useSendbirdChat();
|
|
27
27
|
const { setMessageToEdit, setMessageToReply } = useContext(GroupChannelContexts.Fragment);
|
|
28
28
|
const groupChannelPubSub = useContext(GroupChannelContexts.PubSub);
|
|
29
|
-
const { flatListRef, lazyScrollToBottom,
|
|
29
|
+
const { flatListRef, lazyScrollToBottom, lazyScrollToMessageId, onPressReplyMessageInThread } = useContext(
|
|
30
30
|
GroupChannelContexts.MessageList,
|
|
31
31
|
);
|
|
32
32
|
|
|
@@ -40,6 +40,7 @@ const GroupChannelMessageList = (props: GroupChannelProps['MessageList']) => {
|
|
|
40
40
|
const viewableMessages = useRef<SendbirdMessage[]>();
|
|
41
41
|
const hasUserMarkedAsUnreadRef = useRef(false);
|
|
42
42
|
const [unreadFirstMessage, setUnreadFirstMessage] = useState<SendbirdMessage | undefined>(undefined);
|
|
43
|
+
const pendingBottomReachedRef = useRef<{ timeout: number; timestamp: number } | null>(null);
|
|
43
44
|
|
|
44
45
|
const updateHasSeenNewLine = useCallback(
|
|
45
46
|
(hasSeenNewLine: boolean) => {
|
|
@@ -63,14 +64,16 @@ const GroupChannelMessageList = (props: GroupChannelProps['MessageList']) => {
|
|
|
63
64
|
|
|
64
65
|
const scrollToMessageWithCreatedAt = useFreshCallback(
|
|
65
66
|
(createdAt: number, focusAnimated: boolean, timeout: number): boolean => {
|
|
66
|
-
const
|
|
67
|
-
const isIncludedInList =
|
|
67
|
+
const foundMessage = props.messages.find((it) => it.createdAt === createdAt);
|
|
68
|
+
const isIncludedInList = !!foundMessage;
|
|
69
|
+
pendingBottomReachedRef.current = null;
|
|
68
70
|
|
|
69
71
|
if (isIncludedInList) {
|
|
70
72
|
if (focusAnimated) {
|
|
71
73
|
setTimeout(() => props.onUpdateSearchItem({ startingPoint: createdAt }), MESSAGE_FOCUS_ANIMATION_DELAY);
|
|
72
74
|
}
|
|
73
|
-
|
|
75
|
+
pendingBottomReachedRef.current = { timeout, timestamp: Date.now() };
|
|
76
|
+
lazyScrollToMessageId({ messageId: foundMessage.messageId, animated: true, timeout });
|
|
74
77
|
} else {
|
|
75
78
|
if (props.channel.messageOffsetTimestamp <= createdAt) {
|
|
76
79
|
if (focusAnimated) {
|
|
@@ -346,6 +349,23 @@ const GroupChannelMessageList = (props: GroupChannelProps['MessageList']) => {
|
|
|
346
349
|
},
|
|
347
350
|
);
|
|
348
351
|
|
|
352
|
+
const onBottomReached = useFreshCallback(() => {
|
|
353
|
+
if (props.hasNext()) {
|
|
354
|
+
if (pendingBottomReachedRef.current) {
|
|
355
|
+
const currentTime = Date.now();
|
|
356
|
+
const elapsedTime = currentTime - pendingBottomReachedRef.current.timestamp;
|
|
357
|
+
|
|
358
|
+
const timeoutThreshold = 500;
|
|
359
|
+
if (elapsedTime >= pendingBottomReachedRef.current.timeout + timeoutThreshold) {
|
|
360
|
+
props.onBottomReached?.();
|
|
361
|
+
pendingBottomReachedRef.current = null;
|
|
362
|
+
}
|
|
363
|
+
} else {
|
|
364
|
+
props.onBottomReached?.();
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
});
|
|
368
|
+
|
|
349
369
|
return (
|
|
350
370
|
<ChannelMessageList
|
|
351
371
|
{...props}
|
|
@@ -359,6 +379,7 @@ const GroupChannelMessageList = (props: GroupChannelProps['MessageList']) => {
|
|
|
359
379
|
onPressNewMessagesButton={scrollToBottom}
|
|
360
380
|
onPressScrollToBottomButton={scrollToBottom}
|
|
361
381
|
onPressMarkAsUnreadMessage={onPressMarkAsUnreadMessage}
|
|
382
|
+
onBottomReached={onBottomReached}
|
|
362
383
|
unreadFirstMessage={unreadFirstMessage}
|
|
363
384
|
unreadMessagesFloatingProps={unreadMessagesFloatingPropsRef.current}
|
|
364
385
|
/>
|
|
@@ -46,6 +46,9 @@ export const GroupChannelContexts: GroupChannelContextsType = {
|
|
|
46
46
|
lazyScrollToIndex: () => {
|
|
47
47
|
// noop
|
|
48
48
|
},
|
|
49
|
+
lazyScrollToMessageId: () => {
|
|
50
|
+
// noop
|
|
51
|
+
},
|
|
49
52
|
} as MessageListContextValue),
|
|
50
53
|
};
|
|
51
54
|
|
|
@@ -68,10 +71,11 @@ export const GroupChannelContextsProvider: GroupChannelModule['Provider'] = ({
|
|
|
68
71
|
const [messageToEdit, setMessageToEdit] = useState<SendbirdUserMessage | SendbirdFileMessage>();
|
|
69
72
|
const [messageToReply, setMessageToReply] = useState<SendbirdUserMessage | SendbirdFileMessage>();
|
|
70
73
|
|
|
71
|
-
const { flatListRef, lazyScrollToIndex, lazyScrollToBottom, scrollToMessage } =
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
74
|
+
const { flatListRef, lazyScrollToIndex, lazyScrollToBottom, scrollToMessage, lazyScrollToMessageId } =
|
|
75
|
+
useScrollActions({
|
|
76
|
+
messages,
|
|
77
|
+
onUpdateSearchItem,
|
|
78
|
+
});
|
|
75
79
|
|
|
76
80
|
const updateInputMode = (mode: 'send' | 'edit' | 'reply', message?: SendbirdUserMessage | SendbirdFileMessage) => {
|
|
77
81
|
if (mode === 'send' || !message) {
|
|
@@ -143,6 +147,7 @@ export const GroupChannelContextsProvider: GroupChannelModule['Provider'] = ({
|
|
|
143
147
|
scrollToMessage,
|
|
144
148
|
lazyScrollToIndex,
|
|
145
149
|
lazyScrollToBottom,
|
|
150
|
+
lazyScrollToMessageId,
|
|
146
151
|
onPressReplyMessageInThread,
|
|
147
152
|
}}
|
|
148
153
|
>
|
|
@@ -159,9 +164,11 @@ type MessageListContextValue = ContextValue<GroupChannelContextsType['MessageLis
|
|
|
159
164
|
const useScrollActions = (params: Pick<GroupChannelProps['Provider'], 'messages' | 'onUpdateSearchItem'>) => {
|
|
160
165
|
const { messages, onUpdateSearchItem } = params;
|
|
161
166
|
const flatListRef = useRef<FlatList<SendbirdMessage>>(null);
|
|
167
|
+
const messagesRef = useRef(messages);
|
|
168
|
+
messagesRef.current = messages;
|
|
162
169
|
|
|
163
170
|
// FIXME: Workaround, should run after data has been applied to UI.
|
|
164
|
-
const lazyScrollToBottom = useFreshCallback<MessageListContextValue['
|
|
171
|
+
const lazyScrollToBottom = useFreshCallback<MessageListContextValue['lazyScrollToBottom']>((params) => {
|
|
165
172
|
if (!flatListRef.current) {
|
|
166
173
|
logFlatListRefWarning();
|
|
167
174
|
return;
|
|
@@ -188,6 +195,33 @@ const useScrollActions = (params: Pick<GroupChannelProps['Provider'], 'messages'
|
|
|
188
195
|
}, params?.timeout ?? 0);
|
|
189
196
|
});
|
|
190
197
|
|
|
198
|
+
// FIXME: Workaround, should run after data has been applied to UI.
|
|
199
|
+
const lazyScrollToMessageId = useFreshCallback<MessageListContextValue['lazyScrollToMessageId']>((params) => {
|
|
200
|
+
if (!flatListRef.current) {
|
|
201
|
+
logFlatListRefWarning();
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
setTimeout(() => {
|
|
206
|
+
let messageIndex = 0;
|
|
207
|
+
if (params?.messageId) {
|
|
208
|
+
const foundMessageIndex = messagesRef.current.findIndex((it) => it.messageId === params.messageId);
|
|
209
|
+
if (foundMessageIndex > -1) {
|
|
210
|
+
messageIndex = foundMessageIndex;
|
|
211
|
+
} else {
|
|
212
|
+
Logger.warn('Message with messageId not found:', params.messageId);
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
flatListRef.current?.scrollToIndex({
|
|
218
|
+
index: messageIndex,
|
|
219
|
+
animated: params?.animated ?? false,
|
|
220
|
+
viewPosition: params?.viewPosition ?? 0.5,
|
|
221
|
+
});
|
|
222
|
+
}, params?.timeout ?? 0);
|
|
223
|
+
});
|
|
224
|
+
|
|
191
225
|
const scrollToMessage = useFreshCallback<MessageListContextValue['scrollToMessage']>((messageId, options) => {
|
|
192
226
|
if (!flatListRef.current) {
|
|
193
227
|
logFlatListRefWarning();
|
|
@@ -221,6 +255,7 @@ const useScrollActions = (params: Pick<GroupChannelProps['Provider'], 'messages'
|
|
|
221
255
|
lazyScrollToIndex,
|
|
222
256
|
lazyScrollToBottom,
|
|
223
257
|
scrollToMessage,
|
|
258
|
+
lazyScrollToMessageId,
|
|
224
259
|
};
|
|
225
260
|
};
|
|
226
261
|
|
|
@@ -53,6 +53,8 @@ export interface GroupChannelProps {
|
|
|
53
53
|
|
|
54
54
|
searchItem?: GroupChannelProps['MessageList']['searchItem'];
|
|
55
55
|
|
|
56
|
+
partialTextInputProps?: GroupChannelProps['Input']['partialTextInputProps'];
|
|
57
|
+
|
|
56
58
|
/**
|
|
57
59
|
* @description You can specify the query parameters for the message list.
|
|
58
60
|
* @example
|
|
@@ -111,7 +113,8 @@ export interface GroupChannelProps {
|
|
|
111
113
|
| 'onPressUpdateUserMessage'
|
|
112
114
|
| 'onPressUpdateFileMessage'
|
|
113
115
|
| 'SuggestedMentionList'
|
|
114
|
-
| 'AttachmentsButton'
|
|
116
|
+
| 'AttachmentsButton'
|
|
117
|
+
| 'partialTextInputProps',
|
|
115
118
|
'inputDisabled'
|
|
116
119
|
>;
|
|
117
120
|
|
|
@@ -183,6 +186,16 @@ export interface GroupChannelContextsType {
|
|
|
183
186
|
timeout?: number;
|
|
184
187
|
viewPosition?: number;
|
|
185
188
|
}) => void;
|
|
189
|
+
/**
|
|
190
|
+
* Call the FlatList function asynchronously to scroll to a message by messageId lazily.
|
|
191
|
+
* to avoid scrolling before data rendering has been committed.
|
|
192
|
+
* */
|
|
193
|
+
lazyScrollToMessageId: (params?: {
|
|
194
|
+
messageId?: number;
|
|
195
|
+
animated?: boolean;
|
|
196
|
+
timeout?: number;
|
|
197
|
+
viewPosition?: number;
|
|
198
|
+
}) => void;
|
|
186
199
|
|
|
187
200
|
onPressReplyMessageInThread?: (parentMessage: SendbirdSendableMessage, startingPoint?: number) => void;
|
|
188
201
|
}>;
|
|
@@ -70,6 +70,7 @@ const createGroupChannelFragment = (initModule?: Partial<GroupChannelModule>): G
|
|
|
70
70
|
flatListComponent,
|
|
71
71
|
flatListProps,
|
|
72
72
|
messageListQueryParams,
|
|
73
|
+
partialTextInputProps,
|
|
73
74
|
collectionCreator,
|
|
74
75
|
}) => {
|
|
75
76
|
const { playerService, recorderService } = usePlatformService();
|
|
@@ -341,6 +342,7 @@ const createGroupChannelFragment = (initModule?: Partial<GroupChannelModule>): G
|
|
|
341
342
|
onPressSendFileMessage={onPressSendFileMessage}
|
|
342
343
|
onPressUpdateUserMessage={onPressUpdateUserMessage}
|
|
343
344
|
onPressUpdateFileMessage={onPressUpdateFileMessage}
|
|
345
|
+
partialTextInputProps={partialTextInputProps}
|
|
344
346
|
/>
|
|
345
347
|
</StatusComposition>
|
|
346
348
|
</GroupChannelModule.Provider>
|
package/src/version.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
const VERSION = '3.10.
|
|
1
|
+
const VERSION = '3.10.2';
|
|
2
2
|
export default VERSION;
|