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
JavaScript
'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