@sendbird/uikit-react-native
Version:
Sendbird UIKit for React Native: A feature-rich and customizable chat UI kit with messaging, channel management, and user authentication.
130 lines (122 loc) • 5.57 kB
JavaScript
import { useEffect, useRef, useState } from 'react';
import { Platform } from 'react-native';
import { replace, useFreshCallback } from '@sendbird/uikit-utils';
import { useSendbirdChat } from './useContext';
// Note: The selection change with the keyboard cursor might not work properly with RTL languages
const useMentionTextInput = params => {
const {
mentionManager,
sbOptions
} = useSendbirdChat();
const mentionedUsersRef = useRef([]);
const textInputRef = useRef();
const [text, setText] = useState('');
const [selection, setSelection] = useState({
start: 0,
end: 0
});
// TODO: Refactor text edit logic more clearly
useEffect(() => {
if (mentionManager.shouldUseMentionedMessageTemplate(params.messageToEdit, sbOptions.uikit.groupChannel.channel.enableMention)) {
var _params$messageToEdit, _params$messageToEdit2;
const result = mentionManager.templateToTextAndMentionedUsers(((_params$messageToEdit = params.messageToEdit) === null || _params$messageToEdit === void 0 ? void 0 : _params$messageToEdit.mentionedMessageTemplate) ?? '', ((_params$messageToEdit2 = params.messageToEdit) === null || _params$messageToEdit2 === void 0 ? void 0 : _params$messageToEdit2.mentionedUsers) ?? []);
mentionedUsersRef.current = result.mentionedUsers;
setText(result.mentionedText);
} else {
var _params$messageToEdit3;
mentionedUsersRef.current = [];
if ((_params$messageToEdit3 = params.messageToEdit) !== null && _params$messageToEdit3 !== void 0 && _params$messageToEdit3.isUserMessage()) {
setText(params.messageToEdit.message);
}
}
}, [params.messageToEdit]);
const onChangeText = useFreshCallback((_nextText, addedMentionedUser) => {
const prevText = text;
let nextText = _nextText;
let offset = nextText.length - prevText.length;
// Text clear
if (nextText === '') {
mentionedUsersRef.current = [];
}
// Text add
else if (offset > 0) {
/** Add mentioned user **/
if (addedMentionedUser) mentionedUsersRef.current.push(addedMentionedUser);
/** Reconcile mentioned users range on the right side of the selection **/
mentionedUsersRef.current = mentionManager.reconcileRangeOfMentionedUsers(offset, selection.end, mentionedUsersRef.current);
}
// Text remove
else if (offset < 0) {
// Ranged remove
if (selection.start !== selection.end) {
/** Filter mentioned users in selection range **/
const {
filtered,
lastSelection
} = mentionManager.removeMentionedUsersInSelection(selection, mentionedUsersRef.current);
/** Reconcile mentioned users range on the right side of the selection **/
mentionedUsersRef.current = mentionManager.reconcileRangeOfMentionedUsers(offset, Math.max(selection.end, lastSelection), filtered);
}
// Single remove
else {
/** Find mentioned user who ranges in removed selection **/
const foundIndex = mentionedUsersRef.current.findIndex(it => mentionManager.rangeHelpers.overlaps(it.range, selection, 'underMore'));
/** If found, remove from the mentioned user list and remove remainder text **/
if (foundIndex > -1) {
const it = mentionedUsersRef.current[foundIndex];
const remainderLength = it.range.end - it.range.start + offset;
offset = -remainderLength + offset;
nextText = replace(nextText, it.range.start, it.range.start + remainderLength, '');
mentionedUsersRef.current.splice(foundIndex, 1);
}
/** Reconcile mentioned users range on the right side of the selection **/
mentionedUsersRef.current = mentionManager.reconcileRangeOfMentionedUsers(offset, selection.end, mentionedUsersRef.current);
}
}
setText(nextText);
});
return {
textInputRef,
selection,
onSelectionChange: useFreshCallback(e => {
const nativeSelection = {
...e.nativeEvent.selection
};
// NOTE: To synchronize call onSelectionChange after onChangeText called on each platform.
setTimeout(() => {
const mentionedUser = mentionedUsersRef.current.find(it => mentionManager.rangeHelpers.overlaps(it.range, nativeSelection));
// Selection should be blocked if changed into mentioned area
if (mentionedUser) {
var _textInputRef$current;
const selectionBlock = {
start: mentionedUser.range.start,
end: mentionedUser.range.end
};
(_textInputRef$current = textInputRef.current) === null || _textInputRef$current === void 0 || _textInputRef$current.setNativeProps({
selection: selectionBlock
});
// BUG: setNativeProps called again when invoked onChangeText
// https://github.com/facebook/react-native/issues/33520
if (Platform.OS === 'android') {
setTimeout(() => {
var _textInputRef$current2;
(_textInputRef$current2 = textInputRef.current) === null || _textInputRef$current2 === void 0 || _textInputRef$current2.setNativeProps({
selection: {
start: 0
}
});
}, 250);
}
setSelection(selectionBlock);
} else {
setSelection(nativeSelection);
}
}, 10);
}),
text,
onChangeText,
mentionedUsers: mentionedUsersRef.current
};
};
export default useMentionTextInput;
//# sourceMappingURL=useMentionTextInput.js.map