UNPKG

@sendbird/uikit-react

Version:

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

382 lines (375 loc) 26.9 kB
import React__default, { useRef, useContext, useState, useCallback } from 'react'; import { m as isSendableMessage, z as getClassName, p as getUIKitMessageTypes, Q as isFormMessage, E as isTemplateMessage, R as isValidTemplateMessageType, N as isOGMessage, _ as isTextMessage, o as getUIKitMessageType, e as isMultipleFilesMessage, i as isVoiceMessage, r as isThumbnailMessage, h as getSenderName } from './bundle-vmZ9LoYK.js'; import { C as ContextMenu, b as MenuItems } from './bundle-Bch_Ry4S.js'; import { A as Avatar } from './bundle-CAEBoiEz.js'; import UserProfile from '../ui/UserProfile.js'; import { u as useUserProfileContext } from './bundle-B0s_McF0.js'; import { c as classnames } from './bundle-MlG9piGf.js'; import { a as __awaiter, b as __generator, _ as __assign } from './bundle-yl5d1NoZ.js'; import OGMessageItemBody from '../ui/OGMessageItemBody.js'; import TextMessageItemBody from '../ui/TextMessageItemBody.js'; import FileMessageItemBody from '../ui/FileMessageItemBody.js'; import { u as useThreadMessageKindKeySelector, a as useFileInfoListWithUploaded, M as MultipleFilesMessageItemBody } from './bundle-HwtLrcpn.js'; import { VoiceMessageItemBody } from '../ui/VoiceMessageItemBody.js'; import ThumbnailMessageItemBody from '../ui/ThumbnailMessageItemBody.js'; import UnknownMessageItemBody from '../ui/UnknownMessageItemBody.js'; import { K } from './bundle-DEuCwnTn.js'; import { TemplateMessageItemBody } from '../ui/TemplateMessageItemBody.js'; import Button from '../ui/Button.js'; import { L as Label, a as LabelTypography, b as LabelColors } from './bundle-viBng0Kh.js'; import MessageFeedbackFailedModal from '../ui/MessageFeedbackFailedModal.js'; import { L as LocalizationContext } from './bundle-Del33VzI.js'; import Icon, { IconTypes, IconColors } from '../ui/Icon.js'; import { T as TEXT_MESSAGE_BODY_CLASSNAME } from './bundle-B6udMinl.js'; import { f as isFormVersionCompatible } from './bundle-CzwYTfhQ.js'; import { M as MESSAGE_TEMPLATE_KEY } from './bundle-CqLLOVG5.js'; function MessageProfile(_a) { var // Internal props _b = _a.className, // Internal props className = _b === void 0 ? '' : _b, isByMe = _a.isByMe, displayThreadReplies = _a.displayThreadReplies, bottom = _a.bottom, // MessageContentProps message = _a.message, channel = _a.channel, userId = _a.userId, _c = _a.chainBottom, chainBottom = _c === void 0 ? false : _c; var avatarRef = useRef(null); var _d = useUserProfileContext(), disableUserProfile = _d.disableUserProfile, renderUserProfile = _d.renderUserProfile; if (isByMe || chainBottom || !isSendableMessage(message)) { return null; } return (React__default.createElement(ContextMenu, { menuTrigger: function (toggleDropdown) { var _a, _b; return (React__default.createElement(Avatar, { className: classnames(className, displayThreadReplies && 'use-thread-replies'), src: ((_b = (_a = channel === null || channel === void 0 ? void 0 : channel.members) === null || _a === void 0 ? void 0 : _a.find(function (member) { return (member === null || member === void 0 ? void 0 : member.userId) === message.sender.userId; })) === null || _b === void 0 ? void 0 : _b.profileUrl) || message.sender.profileUrl || '', // TODO: Divide getting profileUrl logic to utils ref: avatarRef, width: "28px", height: "28px", bottom: bottom, onClick: function () { if (!disableUserProfile) toggleDropdown(); } })); }, menuItems: function (closeDropdown) { return (renderUserProfile ? (renderUserProfile({ user: message.sender, close: closeDropdown, currentUserId: userId, avatarRef: avatarRef, })) : (React__default.createElement(MenuItems /** * parentRef: For catching location(x, y) of MenuItems * parentContainRef: For toggling more options(menus & reactions) */ , { /** * parentRef: For catching location(x, y) of MenuItems * parentContainRef: For toggling more options(menus & reactions) */ parentRef: avatarRef, parentContainRef: avatarRef, closeDropdown: closeDropdown, style: { paddingTop: '0px', paddingBottom: '0px' } }, React__default.createElement(UserProfile, { user: message.sender, onSuccess: closeDropdown })))); } })); } var InputLabel = function (_a) { var children = _a.children; return (React__default.createElement(Label, { className: 'sendbird-form-message__input__label', type: LabelTypography.CAPTION_2, color: LabelColors.ONBACKGROUND_2 }, children)); }; var FormInput = function (props) { var name = props.name, required = props.required, errorMessage = props.errorMessage, isValid = props.isValid, values = props.values, isInvalidated = props.isInvalidated, isSubmitTried = props.isSubmitTried, style = props.style, onFocused = props.onFocused, onChange = props.onChange, placeHolder = props.placeHolder, isSubmitted = props.isSubmitted; var layout = style.layout, _a = style.options, options = _a === void 0 ? [] : _a, resultCount = style.resultCount; var _b = resultCount !== null && resultCount !== void 0 ? resultCount : {}, _c = _b.min, min = _c === void 0 ? 1 : _c, _d = _b.max, max = _d === void 0 ? 1 : _d; var chipDataList = getInitialChipDataList(); var stringSet = useContext(LocalizationContext).stringSet; var handleFocus = function () { onFocused === null || onFocused === void 0 ? void 0 : onFocused(true); }; var handleBlur = function () { onFocused === null || onFocused === void 0 ? void 0 : onFocused(false); }; function getInitialChipDataList() { if (isSubmitted) { return options.map(function (option) { return ({ state: values.includes(option) ? 'submittedSelected' : 'submittedDefault', option: option, }); }); } else { return options.map(function (option) { return ({ state: values.includes(option) ? 'selected' : 'default', option: option, }); }); } } var onChipClick = function (index) { if (isSubmitted) return; var newDraftedValues; if (min === 1 && max === 1) { // Single select newDraftedValues = chipDataList[index].state === 'selected' ? [] : [chipDataList[index].option]; } else { newDraftedValues = chipDataList.reduce(function (acc, chipData, i) { if (i === index) { if (chipData.state === 'default' && values.length < max) { acc.push(chipData.option); } } else if (chipData.state === 'selected') { acc.push(chipData.option); } return acc; }, []); } onChange(newDraftedValues); }; return (React__default.createElement("div", { className: 'sendbird-form-message__input__root' }, React__default.createElement(InputLabel, null, React__default.createElement("div", { className: 'sendbird-form-message__input__title-container' }, name, " ", !required && React__default.createElement("span", { className: 'sendbird-form-message__input__title-optional' }, "(optional)"))), React__default.createElement("div", { className: 'sendbird-input_for_form' }, (function () { switch (layout) { case 'chip': { return (React__default.createElement("div", { className: 'sendbird-form-message__input__chip-container' }, chipDataList.map(function (chipData, index) { return (React__default.createElement("div", { className: "sendbird-form-message__input__chip ".concat(chipData.state), key: index, onClick: function () { return onChipClick(index); } }, React__default.createElement("div", { className: 'sendbird-form-message__input__chip-text' }, chipData.option), isSubmitted && chipData.state === 'submittedSelected' && (React__default.createElement(Icon, { className: 'sendbird-form-message__submitted-check-icon-chip', type: IconTypes.DONE, fillColor: IconColors.SECONDARY_2, width: '20px', height: '20px' })))); }))); } case 'textarea': { var currentValue = values.length > 0 ? values[0] : ''; return (React__default.createElement("div", { className: 'sendbird-form-message__input__container' }, isSubmitted ? (React__default.createElement("div", { className: 'sendbird-form-message__submitted-input-box textarea' }, React__default.createElement("div", { className: 'sendbird-form-message__submitted-input-box-text' }, currentValue), isValid && (React__default.createElement("div", { className: 'sendbird-form-message__submitted-check-icon-container' }, React__default.createElement(Icon, { type: IconTypes.DONE, fillColor: IconColors.SECONDARY_2, width: '20px', height: '20px' }))), !currentValue && (React__default.createElement(Label, { className: 'sendbird-input__placeholder', type: LabelTypography.BODY_1, color: LabelColors.ONBACKGROUND_3 }, stringSet.FORM_ITEM_OPTIONAL_EMPTY)))) : (React__default.createElement(React__default.Fragment, null, React__default.createElement("textarea", { className: classnames('sendbird-input__input', !!errorMessage && 'error', 'sendbird-form-message__input__textarea'), required: required, disabled: isSubmitted, value: currentValue, onFocus: handleFocus, onBlur: handleBlur, onChange: function (event) { var value = event.target.value; onChange(value ? [value] : []); } }), (placeHolder && !currentValue) && (React__default.createElement(Label, { className: 'sendbird-input__placeholder textarea', type: LabelTypography.BODY_1, color: LabelColors.ONBACKGROUND_3 }, placeHolder)))))); } case 'text': case 'number': case 'phone': case 'email': { var currentValue = values.length > 0 ? values[0] : ''; return (React__default.createElement("div", { className: "sendbird-form-message__input__container" }, isSubmitted ? (React__default.createElement("div", { className: "sendbird-form-message__submitted-input-box" }, React__default.createElement("div", { className: "sendbird-form-message__submitted-input-box-text" }, currentValue), isValid && (React__default.createElement("div", { className: "sendbird-form-message__submitted-check-icon-container" }, React__default.createElement(Icon, { type: IconTypes.DONE, fillColor: IconColors.SECONDARY_2, width: "20px", height: "20px" }))), !currentValue && (React__default.createElement(Label, { className: 'sendbird-input__placeholder', type: LabelTypography.BODY_1, color: LabelColors.ONBACKGROUND_3 }, stringSet.FORM_ITEM_OPTIONAL_EMPTY)))) : (React__default.createElement(React__default.Fragment, null, React__default.createElement("input", { type: layout === 'number' ? 'text' : layout, inputMode: layout === 'number' ? 'numeric' : 'text', className: "sendbird-input__input ".concat(errorMessage ? 'error' : ''), name: name, required: required, disabled: isSubmitted, value: currentValue, onFocus: handleFocus, onBlur: handleBlur, onChange: function (event) { var value = event.target.value; onChange(value ? [value] : []); } }), (placeHolder && !currentValue) && (React__default.createElement(Label, { className: 'sendbird-input__placeholder', type: LabelTypography.BODY_1, color: LabelColors.ONBACKGROUND_3 }, placeHolder)))))); } default: { return React__default.createElement(React__default.Fragment, null); } } })(), errorMessage && (isSubmitTried || isInvalidated) && (React__default.createElement(Label, { className: 'sendbird-form-message__error-label', type: LabelTypography.CAPTION_3 }, errorMessage))))); }; var FallbackUserMessage = function (_a) { var isByMe = _a.isByMe, text = _a.text; return (React__default.createElement("div", { className: getClassName([ 'sendbird-unknown-message-item-body', isByMe ? 'outgoing' : 'incoming', ]) }, React__default.createElement(Label, { className: "sendbird-unknown-message-item-body__description", type: LabelTypography.BODY_1, color: isByMe ? LabelColors.ONCONTENT_3 : LabelColors.ONBACKGROUND_3 }, text))); }; function FormMessageItemBody(props) { var _this = this; var message = props.message, form = props.form, isByMe = props.isByMe, logger = props.logger; var items = form.items, formId = form.id; var stringSet = useContext(LocalizationContext).stringSet; var _a = useState(false), submitFailed = _a[0], setSubmitFailed = _a[1]; var _b = useState(false), isSubmitTried = _b[0], setIsSubmitTried = _b[1]; var _c = useState(function () { var initialFormValues = []; items.forEach(function (_a) { var required = _a.required, _b = _a.style, style = _b === void 0 ? {} : _b; var layout = style.layout, _c = style.defaultOptions, defaultOptions = _c === void 0 ? [] : _c; initialFormValues.push({ draftValues: layout === 'chip' ? defaultOptions : [], required: required, errorMessage: null, isInvalidated: false, }); }); return initialFormValues; }), formValues = _c[0], setFormValues = _c[1]; var isSubmitted = form.isSubmitted; var hasError = formValues.some(function (_a) { var errorMessage = _a.errorMessage; return !!errorMessage; }); var hasInvalidated = formValues.some(function (_a) { var isInvalidated = _a.isInvalidated; return isInvalidated; }); var isButtonDisabled = (hasError && (isSubmitTried || hasInvalidated)) || isSubmitted; var handleSubmit = useCallback(function () { return __awaiter(_this, void 0, void 0, function () { var hasError_1, isMissingRequired, error_1; return __generator(this, function (_a) { switch (_a.label) { case 0: setIsSubmitTried(true); _a.label = 1; case 1: _a.trys.push([1, 3, , 4]); hasError_1 = formValues.some(function (_a) { var errorMessage = _a.errorMessage; return errorMessage; }); if (hasError_1) { return [2 /*return*/]; } isMissingRequired = formValues.some(function (formValue) { return formValue.required && (!formValue.draftValues || formValue.draftValues.length === 0); }); if (isMissingRequired) { setFormValues(function (oldFormValues) { return oldFormValues.map(function (formValue) { if (formValue.required && formValue.draftValues.length === 0) { return __assign(__assign({}, formValue), { errorMessage: stringSet.FORM_ITEM_REQUIRED }); } return formValue; }); }); return [2 /*return*/]; } formValues.forEach(function (formValue, index) { items[index].draftValues = formValue.draftValues; }); return [4 /*yield*/, message.submitMessageForm()]; case 2: _a.sent(); return [3 /*break*/, 4]; case 3: error_1 = _a.sent(); setSubmitFailed(true); logger === null || logger === void 0 ? void 0 : logger.error(error_1); return [3 /*break*/, 4]; case 4: return [2 /*return*/]; } }); }); }, [formValues, message.messageId, message.submitMessageForm, formId]); if (!isFormVersionCompatible(form.version)) { return React__default.createElement(FallbackUserMessage, { isByMe: isByMe, text: stringSet.FORM_VERSION_ERROR }); } return (React__default.createElement("div", { className: getClassName([ "".concat(TEXT_MESSAGE_BODY_CLASSNAME, " disable-hover"), 'sendbird-form-message__root', 'incoming', ]) }, items.map(function (item, index) { var _a; var name = item.name, placeholder = item.placeholder, id = item.id, required = item.required, style = item.style; var _b = formValues[index], _c = _b.draftValues, draftValues = _c === void 0 ? [] : _c, errorMessage = _b.errorMessage; return (React__default.createElement(FormInput, { key: id, style: style, placeHolder: placeholder, values: (_a = item.submittedValues) !== null && _a !== void 0 ? _a : draftValues, isInvalidated: formValues[index].isInvalidated, isSubmitTried: isSubmitTried, errorMessage: errorMessage, isValid: isSubmitted, isSubmitted: isSubmitted, name: name, required: required, onFocused: function (isFocus) { if (errorMessage && !isFocus && !formValues[index].isInvalidated) { setFormValues(function (_a) { var newInputs = _a.slice(0); newInputs[index] = __assign(__assign({}, newInputs[index]), { isInvalidated: true }); return newInputs; }); } else if (!errorMessage) { setFormValues(function (_a) { var newInputs = _a.slice(0); newInputs[index] = __assign(__assign({}, newInputs[index]), { isInvalidated: false }); return newInputs; }); } }, onChange: function (values) { setFormValues(function (_a) { var newInputs = _a.slice(0); newInputs[index] = __assign(__assign({}, newInputs[index]), { draftValues: values, errorMessage: (function () { if (!item.isValid(values)) { return stringSet.FORM_ITEM_INVALID; } if (required && values.length === 0) { return stringSet.FORM_ITEM_REQUIRED; } return null; })() }); return newInputs; // Return the new array }); } })); }), React__default.createElement(Button, { className: 'sendbird-form-message__submit-button', onClick: handleSubmit, disabled: isButtonDisabled, labelType: LabelTypography.BUTTON_2, labelColor: LabelColors.ONCONTENT_1 }, isSubmitted ? 'Submitted successfully' : 'Submit'), submitFailed && (React__default.createElement(MessageFeedbackFailedModal, { text: 'Submit failed.', onCancel: function () { setSubmitFailed(false); } })))); } var MESSAGE_ITEM_BODY_CLASSNAME = 'sendbird-message-content__middle__message-item-body'; var MessageBody = function (props) { var _a; var _b = props.className, className = _b === void 0 ? MESSAGE_ITEM_BODY_CLASSNAME : _b, message = props.message, channel = props.channel, showFileViewer = props.showFileViewer, onMessageHeightChange = props.onMessageHeightChange, onBeforeDownloadFileMessage = props.onBeforeDownloadFileMessage, mouseHover = props.mouseHover, isMobile = props.isMobile, config = props.config, isReactionEnabledInChannel = props.isReactionEnabledInChannel, isByMe = props.isByMe; // Private props for internal customization. var customSubcomponentsProps = (_a = props['customSubcomponentsProps']) !== null && _a !== void 0 ? _a : {}; var threadMessageKindKey = useThreadMessageKindKeySelector({ isMobile: isMobile, }); var statefulFileInfoList = useFileInfoListWithUploaded(message); // For MultipleFilesMessage. var messageTypes = getUIKitMessageTypes(); var isOgMessageEnabledInGroupChannel = (channel === null || channel === void 0 ? void 0 : channel.isGroupChannel()) && config.groupChannel.enableOgtag; var isFormMessageEnabledInGroupChannel = (channel === null || channel === void 0 ? void 0 : channel.isGroupChannel()) && config.groupChannel.enableFormTypeMessage; var renderUnknownMessageItemBody = function () { return React__default.createElement(UnknownMessageItemBody, { className: className, message: message, isByMe: isByMe, mouseHover: mouseHover, isReactionEnabled: isReactionEnabledInChannel }); }; return K(message) .when(function (message) { return isFormMessageEnabledInGroupChannel && isFormMessage(message); }, function () { return (React__default.createElement(FormMessageItemBody, { isByMe: isByMe, message: message, form: message.messageForm, logger: config.logger })); }) .when(isTemplateMessage, function () { var _a, _b; var templatePayload = message.extendedMessagePayload[MESSAGE_TEMPLATE_KEY]; if (!isValidTemplateMessageType(templatePayload)) { (_b = (_a = config.logger) === null || _a === void 0 ? void 0 : _a.error) === null || _b === void 0 ? void 0 : _b.call(_a, 'TemplateMessageItemBody: invalid type value in message.extendedMessagePayload.message_template.', templatePayload); return renderUnknownMessageItemBody(); } return React__default.createElement(TemplateMessageItemBody, { className: className, message: message, isByMe: isByMe, theme: config === null || config === void 0 ? void 0 : config.theme }); }) .when(function (message) { return isOgMessageEnabledInGroupChannel && isSendableMessage(message) && isOGMessage(message); }, function () { var _a; return (React__default.createElement(OGMessageItemBody, { className: className, message: message, isByMe: isByMe, mouseHover: mouseHover, isMentionEnabled: (_a = config.groupChannel.enableMention) !== null && _a !== void 0 ? _a : false, isReactionEnabled: isReactionEnabledInChannel, onMessageHeightChange: onMessageHeightChange, isMarkdownEnabled: config.groupChannel.enableMarkdownForUserMessage })); }) .when(isTextMessage, function () { var _a; return (React__default.createElement(TextMessageItemBody, { className: className, message: message, isByMe: isByMe, mouseHover: mouseHover, isMentionEnabled: (_a = config.groupChannel.enableMention) !== null && _a !== void 0 ? _a : false, isReactionEnabled: isReactionEnabledInChannel, isMarkdownEnabled: config.groupChannel.enableMarkdownForUserMessage })); }) .when(function (message) { return getUIKitMessageType(message) === messageTypes.FILE; }, function () { return (React__default.createElement(FileMessageItemBody, { className: className, message: message, isByMe: isByMe, mouseHover: mouseHover, isReactionEnabled: isReactionEnabledInChannel, onBeforeDownloadFileMessage: onBeforeDownloadFileMessage })); }) .when(isMultipleFilesMessage, function () { var _a; return (React__default.createElement(MultipleFilesMessageItemBody, __assign({ className: className, message: message, isByMe: isByMe, mouseHover: mouseHover, isReactionEnabled: isReactionEnabledInChannel, threadMessageKindKey: threadMessageKindKey, statefulFileInfoList: statefulFileInfoList, onBeforeDownloadFileMessage: onBeforeDownloadFileMessage }, (_a = customSubcomponentsProps['MultipleFilesMessageItemBody']) !== null && _a !== void 0 ? _a : {}))); }) .when(isVoiceMessage, function () { var _a; return (React__default.createElement(VoiceMessageItemBody, { className: className, message: message, channelUrl: (_a = channel === null || channel === void 0 ? void 0 : channel.url) !== null && _a !== void 0 ? _a : '', isByMe: isByMe, isReactionEnabled: isReactionEnabledInChannel })); }) .when(isThumbnailMessage, function () { var _a; return (React__default.createElement(ThumbnailMessageItemBody, __assign({ className: className, message: message, isByMe: isByMe, mouseHover: mouseHover, isReactionEnabled: isReactionEnabledInChannel, showFileViewer: showFileViewer, style: isMobile ? { width: '100%' } : {} }, (_a = customSubcomponentsProps['ThumbnailMessageItemBody']) !== null && _a !== void 0 ? _a : {}))); }) .otherwise(function () { return renderUnknownMessageItemBody(); }); }; var MessageHeader = function (props) { var _a, _b; var channel = props.channel, message = props.message; return (React__default.createElement(Label, { className: "sendbird-message-content__middle__sender-name", type: LabelTypography.CAPTION_2, color: LabelColors.ONBACKGROUND_2 }, /** * To use the latest member profile information, message.sender might be outdated */ ((_b = (_a = channel === null || channel === void 0 ? void 0 : channel.members) === null || _a === void 0 ? void 0 : _a.find(function (member) { var _a; // @ts-ignore return (member === null || member === void 0 ? void 0 : member.userId) === ((_a = message === null || message === void 0 ? void 0 : message.sender) === null || _a === void 0 ? void 0 : _a.userId); })) === null || _b === void 0 ? void 0 : _b.nickname) || getSenderName(message) // TODO: Divide getting profileUrl logic to utils )); }; export { MessageProfile as M, MessageBody as a, MessageHeader as b }; //# sourceMappingURL=bundle-BdQWhaHB.js.map