UNPKG

communication-react-19

Version:

React library for building modern communication user experiences utilizing Azure Communication Services (React 19 compatible fork)

291 lines (287 loc) 16.8 kB
'use strict'; var react = require('@fluentui/react'); var reactChat = require('@fluentui-contrib/react-chat'); var reactComponents = require('@fluentui/react-components'); var index = require('./index-Bg4Qe5wP.js'); var React = require('react'); require('@azure/communication-common'); require('reselect'); require('@azure/communication-calling'); require('memoize-one'); require('@azure/logger'); require('events'); require('immer'); require('@fluentui/react-icons'); require('textarea-caret-ts'); require('use-debounce'); require('@fluentui/react-file-type-icons'); require('@griffel/react'); require('uuid'); require('roosterjs-content-model-core'); require('roosterjs-content-model-dom'); require('roosterjs-content-model-plugins'); require('roosterjs-content-model-api'); require('html-react-parser'); require('react-linkify'); require('dompurify'); require('@fluentui/react-hooks'); require('react-use-draggable-scroll'); require('copy-to-clipboard'); require('@azure/communication-chat'); require('nanoid'); require('@azure/communication-calling-effects'); // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. /** * @private */ const ChatMessageComponentAsRichTextEditBox = (props) => { var _a; const { onCancel, onSubmit, strings, message, /* @conditional-compile-remove(rich-text-editor-image-upload) */ onPaste, /* @conditional-compile-remove(rich-text-editor-image-upload) */ onInsertInlineImage, /* @conditional-compile-remove(rich-text-editor-image-upload) */ inlineImagesWithProgress, /* @conditional-compile-remove(rich-text-editor-image-upload) */ onRemoveInlineImage } = props; /* @conditional-compile-remove(rich-text-editor-image-upload) */ const [initialInlineImages, setInitialInlineImages] = React.useState([]); const initialContent = React.useMemo(() => { /* @conditional-compile-remove(rich-text-editor-image-upload) */ const content = message.content; /* @conditional-compile-remove(rich-text-editor-image-upload) */ setInitialInlineImages(index.getPreviousInlineImages(content)); /* @conditional-compile-remove(rich-text-editor-image-upload) */ const document = new DOMParser().parseFromString(content !== null && content !== void 0 ? content : '', 'text/html'); // The broken image element is a div element with all the attributes of the original image element. // We need to convert it to a img element so the Rooster knows how to render it. // And we need to copy over all the attributes such as id, width, etc. // which is needed for sending the message with the images correctly. /* @conditional-compile-remove(rich-text-editor-image-upload) */ document.querySelectorAll('.broken-image-wrapper').forEach((brokenImage) => { var _a; const imageElement = document.createElement('img'); const attributes = brokenImage.attributes; for (const attribute of attributes) { imageElement.setAttribute(attribute.name, attribute.value); } imageElement.src = index.BROKEN_IMAGE_SVG_DATA; imageElement.style.width = '3rem'; imageElement.style.height = '3rem'; (_a = brokenImage.parentElement) === null || _a === void 0 ? void 0 : _a.replaceChild(imageElement, brokenImage); }); /* @conditional-compile-remove(rich-text-editor-image-upload) */ return document.body.innerHTML; }, [message]); const [contentValue, setContentValue] = React.useState(initialContent || ''); /* @conditional-compile-remove(rich-text-editor-image-upload) */ const [contentValueWithInlineImagesOverflow, setContentValueWithInlineImagesOverflow] = React.useState(false); /* @conditional-compile-remove(file-sharing-acs) */ const [attachmentMetadata, handleAttachmentAction] = React.useReducer(index.attachmentMetadataReducer, (_a = index.getMessageWithAttachmentMetadata(message)) !== null && _a !== void 0 ? _a : []); /* @conditional-compile-remove(rich-text-editor-image-upload) */ const [attachmentUploadsPendingError, setAttachmentUploadsPendingError] = React.useState(undefined); const editTextFieldRef = React.useRef(null); const theme = index.useTheme(); const messageState = React.useMemo(() => { var _a, _b; // get plain text content from the editor to check if the message is empty // as the content may contain tags even when the content is empty const plainTextContent = (_b = (_a = editTextFieldRef.current) === null || _a === void 0 ? void 0 : _a.getPlainContent()) !== null && _b !== void 0 ? _b : contentValue; let messageLengthState = index.getMessageState(plainTextContent, /* @conditional-compile-remove(file-sharing-acs) */ attachmentMetadata !== null && attachmentMetadata !== void 0 ? attachmentMetadata : []); /* @conditional-compile-remove(rich-text-editor-image-upload) */ const imageIds = index.inlineImageIds(contentValue); /* @conditional-compile-remove(rich-text-editor-image-upload) */ if (messageLengthState !== 'OK' && imageIds.length > 0) { // Check if too long if (index.isMessageTooLong(contentValue.length)) { messageLengthState = 'too long'; } else { messageLengthState = 'OK'; } } return messageLengthState; }, [/* @conditional-compile-remove(file-sharing-acs) */ attachmentMetadata, contentValue]); /* @conditional-compile-remove(rich-text-editor-image-upload) */ const imageUploadErrorMessage = React.useMemo(() => { var _a, _b; return (_b = (_a = inlineImagesWithProgress === null || inlineImagesWithProgress === void 0 ? void 0 : inlineImagesWithProgress.filter((image) => image.error).pop()) === null || _a === void 0 ? void 0 : _a.error) === null || _b === void 0 ? void 0 : _b.message; }, [inlineImagesWithProgress]); const editContainerStyles = index.useChatMessageRichTextEditContainerStyles(); const chatMyMessageStyles = index.useChatMyMessageStyles(); const locale = index.useLocale().strings; const setContent = React.useCallback((newValue) => { setContentValue(newValue !== null && newValue !== void 0 ? newValue : ''); }, []); React.useEffect(() => { var _a; (_a = editTextFieldRef.current) === null || _a === void 0 ? void 0 : _a.focus(); }, []); const textValidationErrorMessage = React.useMemo(() => { return index.getTextValidationErrorMessage(messageState, strings.editBoxTextLimit, strings.editBoxEmptyWarningText, /* @conditional-compile-remove(rich-text-editor-image-upload) */ contentValueWithInlineImagesOverflow); }, [ messageState, strings.editBoxTextLimit, strings.editBoxEmptyWarningText, /* @conditional-compile-remove(rich-text-editor-image-upload) */ contentValueWithInlineImagesOverflow ]); const iconClassName = React.useCallback((isHover) => { const color = isHover ? theme.palette.accent : theme.palette.neutralSecondary; return react.mergeStyles(index.richTextEditBoxActionButtonIcon, { color }); }, [theme.palette.accent, theme.palette.neutralSecondary]); const onRenderThemedCancelIcon = React.useCallback((isHover) => { return index.onRenderCancelIcon(iconClassName(isHover)); }, [iconClassName]); const onRenderThemedSubmitIcon = React.useCallback((isHover) => { return index.onRenderSubmitIcon(iconClassName(isHover)); }, [iconClassName]); /* @conditional-compile-remove(file-sharing-acs) */ const hasMultipleAttachments = React.useMemo(() => { return index.doesMessageContainMultipleAttachments(message); }, [message]); const onSubmitHandler = React.useCallback(() => { if (messageState !== 'OK') { return; } /* @conditional-compile-remove(rich-text-editor-image-upload) */ if (inlineImagesWithProgress && inlineImagesWithProgress.length > 0) { const contentWithUpdatedInlineImagesInfo = index.getContentWithUpdatedInlineImagesInfo(contentValue, inlineImagesWithProgress); const messageTooLong = index.isMessageTooLong(contentWithUpdatedInlineImagesInfo.length); // Set contentValueWithInlineImagesOverflow state to display the error bar setContentValueWithInlineImagesOverflow(messageTooLong); // The change from the setContentValueOverflow in the previous line will not kick in yet. // We need to rely on the local value of messageTooLong to return early if the message is too long. if (messageTooLong) { return; } } // Don't send message until all attachments have been uploaded successfully /* @conditional-compile-remove(rich-text-editor-image-upload) */ setAttachmentUploadsPendingError(undefined); /* @conditional-compile-remove(rich-text-editor-image-upload) */ if (index.hasIncompleteAttachmentUploads(inlineImagesWithProgress)) { setAttachmentUploadsPendingError({ message: strings.imageUploadsPendingError, timestamp: Date.now(), errorBarType: index.SendBoxErrorBarType.info }); return; } let content = contentValue; /* @conditional-compile-remove(rich-text-editor-image-upload) */ content = index.removeBrokenImageContentAndClearImageSizeStyles(content); let initInlineImages = []; /* @conditional-compile-remove(rich-text-editor-image-upload) */ initInlineImages = initialInlineImages !== null && initialInlineImages !== void 0 ? initialInlineImages : []; index.modifyInlineImagesInContentString(content, initInlineImages, (content) => { // it's very important to pass an empty attachment here // so when user removes all attachments, UI can reflect it instantly // if you set it to undefined, the attachments pre-edited would still be there // until edit message event is received onSubmit(content, /* @conditional-compile-remove(file-sharing-acs) */ attachmentMetadata || []); }); }, [ messageState, /* @conditional-compile-remove(rich-text-editor-image-upload) */ inlineImagesWithProgress, /* @conditional-compile-remove(rich-text-editor-image-upload) */ initialInlineImages, contentValue, /* @conditional-compile-remove(rich-text-editor-image-upload) */ strings.imageUploadsPendingError, onSubmit, /* @conditional-compile-remove(file-sharing-acs) */ attachmentMetadata ]); const actionButtons = React.useMemo(() => { return (React.createElement(react.Stack, { horizontal: true }, React.createElement(index.InputBoxButton, { className: index.richTextActionButtonsStyle, ariaLabel: strings.editBoxCancelButton, tooltipContent: strings.editBoxCancelButton, onRenderIcon: onRenderThemedCancelIcon, onClick: () => { onCancel && onCancel(message.messageId); }, id: 'dismissIconWrapper', "data-testId": 'chat-message-rich-text-edit-box-cancel-button' }), React.createElement(index.InputBoxButton, { className: index.richTextActionButtonsStyle, ariaLabel: strings.editBoxSubmitButton, tooltipContent: strings.editBoxSubmitButton, onRenderIcon: onRenderThemedSubmitIcon, onClick: (e) => { onSubmitHandler(); e.stopPropagation(); }, id: 'submitIconWrapper', "data-testId": 'chat-message-rich-text-edit-box-submit-button' }))); }, [ message.messageId, onCancel, onRenderThemedCancelIcon, onRenderThemedSubmitIcon, strings.editBoxCancelButton, strings.editBoxSubmitButton, onSubmitHandler ]); const richTextLocaleStrings = React.useMemo(() => { /* @conditional-compile-remove(rich-text-editor) */ return Object.assign(Object.assign({}, locale.richTextSendBox), strings); }, [ /* @conditional-compile-remove(rich-text-editor) */ locale.richTextSendBox, /* @conditional-compile-remove(rich-text-editor) */ strings, locale.sendBox ]); /* @conditional-compile-remove(file-sharing-acs) */ const onCancelAttachmentUpload = React.useCallback((attachmentId) => { // edit box only capable of removing attachments // we need to expand attachment actions // if we want to support more actions e.g. add handleAttachmentAction({ type: 'remove', id: attachmentId }); }, []); /* @conditional-compile-remove(file-sharing-acs) */ const onRenderAttachmentUploads = React.useCallback(() => { return (React.createElement(react.Stack, { className: index.attachmentUploadCardsStyles }, React.createElement(index.FluentV9ThemeProvider, { v8Theme: theme }, React.createElement(index._AttachmentUploadCards, { attachments: attachmentMetadata, onCancelAttachmentUpload: onCancelAttachmentUpload })))); }, [attachmentMetadata, onCancelAttachmentUpload, theme]); const onChangeHandler = React.useCallback((content, /* @conditional-compile-remove(rich-text-editor-image-upload) */ removedInlineImages) => { /* @conditional-compile-remove(rich-text-editor-image-upload) */ removedInlineImages === null || removedInlineImages === void 0 ? void 0 : removedInlineImages.forEach((removedInlineImage) => { onRemoveInlineImage && onRemoveInlineImage(removedInlineImage, message.messageId); }); setContent(content); }, [ setContent, /* @conditional-compile-remove(rich-text-editor-image-upload) */ onRemoveInlineImage, /* @conditional-compile-remove(rich-text-editor-image-upload) */ message.messageId ]); const getContent = () => { return (React.createElement(react.Stack, { className: react.mergeStyles(index.editBoxWidthStyles) }, React.createElement(index.RichTextSendBoxErrors, { textValidationErrorMessage: textValidationErrorMessage, systemMessage: message.failureReason, /* @conditional-compile-remove(rich-text-editor-image-upload) */ attachmentUploadsPendingError: attachmentUploadsPendingError, /* @conditional-compile-remove(rich-text-editor-image-upload) */ attachmentProgressError: imageUploadErrorMessage ? { message: imageUploadErrorMessage, timestamp: Date.now(), errorBarType: index.SendBoxErrorBarType.error } : undefined }), React.createElement(index.RichTextInputBoxComponent, { placeholderText: strings.editBoxPlaceholderText, onChange: onChangeHandler, onEnterKeyDown: onSubmitHandler, editorComponentRef: editTextFieldRef, initialContent: initialContent, strings: richTextLocaleStrings, disabled: false, actionComponents: actionButtons, richTextEditorStyleProps: index.editBoxRichTextEditorStyle, isHorizontalLayoutDisabled: true, /* @conditional-compile-remove(file-sharing-acs) */ onRenderAttachmentUploads: onRenderAttachmentUploads, /* @conditional-compile-remove(rich-text-editor-image-upload) */ onPaste: onPaste, /* @conditional-compile-remove(rich-text-editor-image-upload) */ onInsertInlineImage: onInsertInlineImage ? (imageAttributes) => { onInsertInlineImage(imageAttributes, message.messageId); } : undefined }))); }; const attached = message.attached === true ? 'center' : message.attached === 'bottom' ? 'bottom' : 'top'; return (React.createElement(reactChat.ChatMyMessage, { attached: attached, root: { className: reactComponents.mergeClasses(chatMyMessageStyles.root, /* @conditional-compile-remove(file-sharing-acs) */ hasMultipleAttachments ? chatMyMessageStyles.multipleAttachmentsInEditing : undefined) }, body: { className: reactComponents.mergeClasses(editContainerStyles.body, attached !== 'top' ? editContainerStyles.bodyAttached : undefined) } }, getContent())); }; exports.ChatMessageComponentAsRichTextEditBox = ChatMessageComponentAsRichTextEditBox; exports.default = ChatMessageComponentAsRichTextEditBox; //# sourceMappingURL=ChatMessageComponentAsRichTextEditBox-BRrXFblQ.js.map