UNPKG

@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
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