UNPKG

@sendbird/uikit-react

Version:

Sendbird UIKit for React: A feature-rich and customizable chat UI kit with messaging, channel management, and user authentication.

341 lines (338 loc) 20.8 kB
import { a as __awaiter, c as __generator, e as __spreadArray } from '../../chunks/bundle-Bpofr334.js'; import React__default, { useRef, useState, useCallback, useEffect } from 'react'; import { u as useTypingLifecycle } from '../../chunks/bundle-AsQ1wnFm.js'; import { MutedState } from '@sendbird/chat/groupChannel'; import { u as useMediaQueryContext } from '../../chunks/bundle-C2ARCMSL.js'; import { u as useLocalization } from '../../chunks/bundle-Cdqsdoa8.js'; import { useGlobalModalContext } from '../../hooks/useModal.js'; import { c as checkIfFileUploadEnabled, M as MessageInput } from '../../chunks/bundle-BCjR1Qiq.js'; import { a as usePendingFiles, u as useDragAndDrop } from '../../chunks/bundle-ExNQo0Ly.js'; import { i as isChannelTypeSupportsMultipleFilesMessage } from '../../chunks/bundle-C5-D2BAP.js'; import { M as MessageInputKeys } from '../../chunks/bundle-BEPoP7sp.js'; import { S as SuggestedMentionList } from '../../chunks/bundle-DFWI31lg.js'; import { V as VoiceMessageInputWrapper } from '../../chunks/bundle-CwMNZmx9.js'; import '../../chunks/bundle-B56O1y8C.js'; import '../../chunks/bundle-CFc2hy8g.js'; import { u as useSendbird } from '../../chunks/bundle-4clodtJA.js'; import '../../chunks/bundle-BLMU9f-F.js'; import '../../chunks/bundle-lqEjT2ED.js'; import '@sendbird/chat/message'; import '../../chunks/bundle-YDriVB8K.js'; import { R as Role } from '../../chunks/bundle-DVdeXT-4.js'; import { useDirtyGetMentions } from '../../Message/hooks/useDirtyGetMentions.js'; import { b as isDisabledBecauseFrozen, c as isDisabledBecauseMuted } from '../../chunks/bundle-ChLik1Zs.js'; import { c as classnames } from '../../chunks/bundle-DX6fRIJl.js'; import { u as useThread } from '../../chunks/bundle-Gpc6ZS8v.js'; import { c as compressImages } from '../../chunks/bundle-BfgSx7DM.js'; import '../../chunks/bundle-oM0Fxt9G.js'; import '../../chunks/bundle-DmnXeBdU.js'; import '../../chunks/bundle-C4anRHWY.js'; import 'react-dom'; import '../../ui/IconButton.js'; import '../../ui/Button.js'; import '../../chunks/bundle-Cdplrrlw.js'; import '../../ui/Icon.js'; import '../../chunks/bundle-LLA95Pqf.js'; import '../../chunks/bundle-JhKiWlXT.js'; import '@sendbird/chat'; import '@sendbird/chat/openChannel'; import '../../chunks/bundle-BZSLsKkw.js'; import '../../utils/message/getOutgoingMessageState.js'; import '../../chunks/bundle-CglqREVl.js'; import '../../chunks/bundle-BqKoZDqX.js'; import '../../chunks/bundle-EKFQIahk.js'; import '../../chunks/bundle-B5xkbY4c.js'; import 'dompurify'; import '../../chunks/bundle-6I0gJidJ.js'; import '../../chunks/bundle-B40pTdZv.js'; import '../../chunks/bundle-iPLJ9a5J.js'; import '../../chunks/bundle-CnFrQOtC.js'; import '../../chunks/bundle-BJShQs4P.js'; import '../../ui/ImageRenderer.js'; import '../../chunks/bundle-F_R9C4cW.js'; import '../../chunks/bundle-DiO7lolz.js'; import '../../GroupChannel/components/SuggestedMentionList.js'; import '../../ui/QuoteMessageInput.js'; import '../../chunks/bundle-CD4RzjMA.js'; import '../../VoicePlayer/useVoicePlayer.js'; import '../../chunks/bundle-DWURNKdQ.js'; import '../../VoiceRecorder/context.js'; import '../../VoiceRecorder/useVoiceRecorder.js'; import '../../chunks/bundle-DzB_38co.js'; import '../../ui/PlaybackTime.js'; import '../../ui/ProgressBar.js'; import '../../ui/TextButton.js'; import '../../chunks/bundle-C1npFBfj.js'; import '@sendbird/uikit-tools'; import '../../chunks/bundle-B5LKcMN_.js'; import '../../chunks/bundle-CAshTR7h.js'; import '../../chunks/bundle-BRJdb8OX.js'; import '../../chunks/bundle-BdprN8pt.js'; import '../../chunks/bundle-BsY2Dawk.js'; import '../../chunks/bundle-XzHJwv9S.js'; import '../../chunks/bundle-DpOgiF9r.js'; import '../context/types.js'; var ThreadMessageInput = function (props, ref) { var _a; var className = props.className, renderFileUploadIcon = props.renderFileUploadIcon, renderVoiceMessageIcon = props.renderVoiceMessageIcon, renderSendMessageIcon = props.renderSendMessageIcon, acceptableMimeTypes = props.acceptableMimeTypes; var config = useSendbird().state.config; var isMobile = useMediaQueryContext().isMobile; var stringSet = useLocalization().stringSet; var isOnline = config.isOnline, userMention = config.userMention, logger = config.logger, groupChannel = config.groupChannel, imageCompression = config.imageCompression; var threadContext = useThread(); var _b = threadContext.state, currentChannel = _b.currentChannel, parentMessage = _b.parentMessage, isMuted = _b.isMuted, isChannelFrozen = _b.isChannelFrozen, allThreadMessages = _b.allThreadMessages, _c = threadContext.actions, sendMessage = _c.sendMessage, sendFileMessage = _c.sendFileMessage, sendVoiceMessage = _c.sendVoiceMessage, sendMultipleFilesMessage = _c.sendMultipleFilesMessage; var messageInputRef = useRef(); var isMentionEnabled = groupChannel.enableMention; var isVoiceMessageEnabled = groupChannel.enableVoiceMessage; var isMultipleFilesMessageEnabled = (_a = threadContext.state.isMultipleFilesMessageEnabled) !== null && _a !== void 0 ? _a : config.isMultipleFilesMessageEnabled; var threadInputDisabled = props.disabled || !isOnline || isMuted || (!((currentChannel === null || currentChannel === void 0 ? void 0 : currentChannel.myRole) === Role.OPERATOR) && isChannelFrozen) || parentMessage === null; // mention var _d = useState(''), mentionNickname = _d[0], setMentionNickname = _d[1]; var _e = useState([]), mentionedUsers = _e[0], setMentionedUsers = _e[1]; var _f = useState([]), mentionedUserIds = _f[0], setMentionedUserIds = _f[1]; var _g = useState(null), selectedUser = _g[0], setSelectedUser = _g[1]; var _h = useState([]), mentionSuggestedUsers = _h[0], setMentionSuggestedUsers = _h[1]; var _j = useState(null), messageInputEvent = _j[0], setMessageInputEvent = _j[1]; var _k = useState(false), showVoiceMessageInput = _k[0], setShowVoiceMessageInput = _k[1]; // Composer staging var openModal = useGlobalModalContext().openModal; var uikitUploadSizeLimit = config.uikitUploadSizeLimit, uikitMultipleFilesMessageLimit = config.uikitMultipleFilesMessageLimit; var allowMultipleFiles = Boolean(isMultipleFilesMessageEnabled) && Boolean(currentChannel) && isChannelTypeSupportsMultipleFilesMessage(currentChannel); var effectiveMultiLimit = allowMultipleFiles ? uikitMultipleFilesMessageLimit : 1; var _l = usePendingFiles({ uikitUploadSizeLimit: uikitUploadSizeLimit, uikitMultipleFilesMessageLimit: effectiveMultiLimit, acceptableMimeTypes: acceptableMimeTypes, openModal: openModal, stringSet: stringSet, logger: logger, }), pendingFiles = _l.pendingFiles, addFiles = _l.addFiles, removeFile = _l.removeFile, clearPendingFiles = _l.clear; // Window-level drop target — only consume drops that land inside the // thread panel (.sendbird-thread-ui). Drops elsewhere are picked up by the // main channel composer's hook instance. var isFileUploadEnabled = checkIfFileUploadEnabled({ channel: currentChannel !== null && currentChannel !== void 0 ? currentChannel : undefined, config: config }); useDragAndDrop({ onAddFiles: addFiles, disabled: isMobile || threadInputDisabled || showVoiceMessageInput || !isFileUploadEnabled, shouldAccept: function (event) { var target = event.target; if (!(target instanceof Element)) return false; return Boolean(target.closest('.sendbird-thread-ui')); }, }); var _m = useTypingLifecycle(currentChannel), startTyping = _m.startTyping, stopTyping = _m.stopTyping; // Submit handler. Files and body text do not coexist: when files are // present, the composer's text is suppressed at the UI level and again // here. parentMessage is always the thread parent so each send threads // correctly. The caption read path is preserved elsewhere for historical // file messages that still carry a body. var isSubmittingFilesRef = useRef(false); var handleSubmit = useCallback(function (_a) { return __awaiter(void 0, [_a], void 0, function (_b) { var trimmed, rawImageFiles, otherFiles, compressedImageFiles_1, tasks_1, useMFMBatch; var message = _b.message, mentionTemplate = _b.mentionTemplate, files = _b.files; return __generator(this, function (_c) { switch (_c.label) { case 0: trimmed = message.trim(); if (files.length === 0) { if (trimmed.length === 0) return [2 /*return*/]; sendMessage({ message: message, mentionedUsers: mentionedUsers, mentionTemplate: mentionTemplate, quoteMessage: parentMessage, }); setMentionNickname(''); setMentionedUsers([]); stopTyping(); return [2 /*return*/]; } if (isSubmittingFilesRef.current) return [2 /*return*/]; isSubmittingFilesRef.current = true; setMentionNickname(''); setMentionedUsers([]); stopTyping(); clearPendingFiles(); _c.label = 1; case 1: _c.trys.push([1, , 3, 4]); rawImageFiles = files.filter(function (entry) { return entry.isImage; }).map(function (entry) { return entry.file; }); otherFiles = files.filter(function (entry) { return !entry.isImage; }).map(function (entry) { return entry.file; }); return [4 /*yield*/, compressImages({ files: rawImageFiles, imageCompression: imageCompression, logger: logger, })]; case 2: compressedImageFiles_1 = (_c.sent()).compressedFiles; tasks_1 = []; useMFMBatch = isMultipleFilesMessageEnabled && compressedImageFiles_1.length > 1; if (useMFMBatch) { tasks_1.push(function () { return sendMultipleFilesMessage(compressedImageFiles_1, parentMessage !== null && parentMessage !== void 0 ? parentMessage : undefined); }); } else if (compressedImageFiles_1.length === 1) { tasks_1.push(function () { return sendFileMessage(compressedImageFiles_1[0], parentMessage !== null && parentMessage !== void 0 ? parentMessage : undefined); }); } else if (compressedImageFiles_1.length > 1) { compressedImageFiles_1.forEach(function (file) { tasks_1.push(function () { return sendFileMessage(file, parentMessage !== null && parentMessage !== void 0 ? parentMessage : undefined); }); }); } otherFiles.forEach(function (file) { tasks_1.push(function () { return sendFileMessage(file, parentMessage !== null && parentMessage !== void 0 ? parentMessage : undefined); }); }); // Sequential dispatch with per-task error isolation. (function () { return __awaiter(void 0, void 0, void 0, function () { var _i, tasks_2, task, error_1; var _a; return __generator(this, function (_b) { switch (_b.label) { case 0: _i = 0, tasks_2 = tasks_1; _b.label = 1; case 1: if (!(_i < tasks_2.length)) return [3 /*break*/, 6]; task = tasks_2[_i]; _b.label = 2; case 2: _b.trys.push([2, 4, , 5]); return [4 /*yield*/, task()]; case 3: _b.sent(); return [3 /*break*/, 5]; case 4: error_1 = _b.sent(); (_a = logger.warning) === null || _a === void 0 ? void 0 : _a.call(logger, 'Thread|composer: file send failed', error_1); return [3 /*break*/, 5]; case 5: _i++; return [3 /*break*/, 1]; case 6: return [2 /*return*/]; } }); }); })(); return [3 /*break*/, 4]; case 3: isSubmittingFilesRef.current = false; return [7 /*endfinally*/]; case 4: return [2 /*return*/]; } }); }); }, [ sendMessage, sendFileMessage, sendMultipleFilesMessage, mentionedUsers, parentMessage, currentChannel, stopTyping, clearPendingFiles, isMultipleFilesMessageEnabled, imageCompression, logger, ]); var displaySuggestedMentionList = isOnline && isMentionEnabled && mentionNickname.length > 0 && !isDisabledBecauseFrozen(currentChannel) && !isDisabledBecauseMuted(currentChannel) && !(currentChannel === null || currentChannel === void 0 ? void 0 : currentChannel.isBroadcast); var stashedMentionedUsersRef = useRef(null); var prevHasPendingFilesRef = useRef(false); var hasPendingFilesInWrapper = pendingFiles.length > 0; useEffect(function () { if (hasPendingFilesInWrapper && !prevHasPendingFilesRef.current) { if (mentionedUsers.length > 0) { stashedMentionedUsersRef.current = mentionedUsers; } setMentionNickname(''); } else if (!hasPendingFilesInWrapper && prevHasPendingFilesRef.current) { if (stashedMentionedUsersRef.current) { setMentionedUsers(stashedMentionedUsersRef.current); stashedMentionedUsersRef.current = null; } } prevHasPendingFilesRef.current = hasPendingFilesInWrapper; }, [hasPendingFilesInWrapper]); // Reset when changing channel useEffect(function () { setShowVoiceMessageInput(false); clearPendingFiles(); stashedMentionedUsersRef.current = null; }, [currentChannel === null || currentChannel === void 0 ? void 0 : currentChannel.url]); var mentionNodes = useDirtyGetMentions({ ref: ref || messageInputRef }, { logger: logger }); var ableMention = (mentionNodes === null || mentionNodes === void 0 ? void 0 : mentionNodes.length) < (userMention === null || userMention === void 0 ? void 0 : userMention.maxMentionCount); useEffect(function () { setMentionedUsers(mentionedUsers.filter(function (_a) { var userId = _a.userId; var i = mentionedUserIds.indexOf(userId); if (i < 0) { return false; } else { mentionedUserIds.splice(i, 1); return true; } })); }, [mentionedUserIds]); if ((currentChannel === null || currentChannel === void 0 ? void 0 : currentChannel.isBroadcast) && (currentChannel === null || currentChannel === void 0 ? void 0 : currentChannel.myRole) !== Role.OPERATOR) { return React__default.createElement(React__default.Fragment, null); } return (React__default.createElement("div", { className: classnames(showVoiceMessageInput ? 'sendbird-thread-message-input--voice-message' : 'sendbird-thread-message-input', className) }, displaySuggestedMentionList && (React__default.createElement(SuggestedMentionList, { targetNickname: mentionNickname, inputEvent: messageInputEvent !== null && messageInputEvent !== void 0 ? messageInputEvent : undefined, // renderUserMentionItem={renderUserMentionItem} onUserItemClick: function (user) { if (user) { setMentionedUsers(__spreadArray(__spreadArray([], mentionedUsers, true), [user], false)); } setMentionNickname(''); setSelectedUser(user); setMessageInputEvent(null); }, onFocusItemChange: function () { setMessageInputEvent(null); }, onFetchUsers: function (users) { setMentionSuggestedUsers(users); }, ableAddMention: ableMention, maxMentionCount: userMention === null || userMention === void 0 ? void 0 : userMention.maxMentionCount, maxSuggestionCount: userMention === null || userMention === void 0 ? void 0 : userMention.maxSuggestionCount })), showVoiceMessageInput ? (React__default.createElement(VoiceMessageInputWrapper, { channel: currentChannel, onSubmitClick: function (recordedFile, duration) { sendVoiceMessage(recordedFile, duration, parentMessage); setShowVoiceMessageInput(false); }, onCancelClick: function () { setShowVoiceMessageInput(false); } })) : (React__default.createElement(MessageInput, { className: "sendbird-thread-message-input__message-input", messageFieldId: "sendbird-message-input-text-field--thread", channel: currentChannel, channelUrl: currentChannel === null || currentChannel === void 0 ? void 0 : currentChannel.url, isMobile: isMobile, disabled: threadInputDisabled, acceptableMimeTypes: acceptableMimeTypes, setMentionedUsers: setMentionedUsers, mentionSelectedUser: selectedUser, isMentionEnabled: isMentionEnabled, isVoiceMessageEnabled: isVoiceMessageEnabled, isSelectingMultipleFilesEnabled: isMultipleFilesMessageEnabled, onVoiceMessageIconClick: function () { setShowVoiceMessageInput(true); }, renderFileUploadIcon: renderFileUploadIcon, renderVoiceMessageIcon: renderVoiceMessageIcon, renderSendMessageIcon: renderSendMessageIcon, ref: ref || messageInputRef, placeholder: ((currentChannel === null || currentChannel === void 0 ? void 0 : currentChannel.isFrozen) && !((currentChannel === null || currentChannel === void 0 ? void 0 : currentChannel.myRole) === Role.OPERATOR) && stringSet.MESSAGE_INPUT__PLACE_HOLDER__DISABLED) || ((currentChannel === null || currentChannel === void 0 ? void 0 : currentChannel.myMutedState) === MutedState.MUTED && stringSet.MESSAGE_INPUT__PLACE_HOLDER__MUTED_SHORT) || (allThreadMessages.length > 0 ? stringSet.THREAD__INPUT__REPLY_TO_THREAD : stringSet.THREAD__INPUT__REPLY_IN_THREAD), onStartTyping: startTyping, onStopTyping: stopTyping, pendingFiles: pendingFiles, onAddFiles: addFiles, onRemoveFile: removeFile, onSubmit: handleSubmit, onUserMentioned: function (user) { if ((selectedUser === null || selectedUser === void 0 ? void 0 : selectedUser.userId) === (user === null || user === void 0 ? void 0 : user.userId)) { setSelectedUser(null); setMentionNickname(''); } }, onMentionStringChange: function (mentionText) { setMentionNickname(mentionText); }, onMentionedUserIdsUpdated: function (userIds) { setMentionedUserIds(userIds); }, onKeyDown: function (e) { if (displaySuggestedMentionList && (mentionSuggestedUsers === null || mentionSuggestedUsers === void 0 ? void 0 : mentionSuggestedUsers.length) > 0 && ((e.key === MessageInputKeys.Enter && ableMention) || e.key === MessageInputKeys.ArrowUp || e.key === MessageInputKeys.ArrowDown)) { setMessageInputEvent(e); return true; } return false; } })))); }; var ThreadMessageInput$1 = React__default.forwardRef(ThreadMessageInput); export { ThreadMessageInput$1 as default }; //# sourceMappingURL=ThreadMessageInput.js.map