UNPKG

@memori.ai/memori-react

Version:

[![npm version](https://img.shields.io/github/package-json/v/memori-ai/memori-react)](https://www.npmjs.com/package/@memori.ai/memori-react) ![Tests](https://github.com/memori-ai/memori-react/workflows/CI/badge.svg?branch=main) ![TypeScript Support](https

221 lines (219 loc) 13.3 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const tslib_1 = require("tslib"); const jsx_runtime_1 = require("react/jsx-runtime"); const react_1 = require("react"); const Document_1 = require("../icons/Document"); const Image_1 = require("../icons/Image"); const Upload_1 = require("../icons/Upload"); const Spin_1 = tslib_1.__importDefault(require("../ui/Spin")); const Alert_1 = tslib_1.__importDefault(require("../ui/Alert")); const classnames_1 = tslib_1.__importDefault(require("classnames")); const UploadDocuments_1 = tslib_1.__importDefault(require("./UploadDocuments/UploadDocuments")); const UploadImages_1 = tslib_1.__importDefault(require("./UploadImages/UploadImages")); const react_i18next_1 = require("react-i18next"); const constants_1 = require("../../helpers/constants"); const MAX_IMAGES = 5; const MAX_DOCUMENTS = constants_1.MAX_DOCUMENTS_PER_MESSAGE; const UploadButton = ({ authToken = '', client, sessionID = '', isMediaAccepted = false, setDocumentPreviewFiles, documentPreviewFiles, memoriID = '', }) => { var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k; const [isLoading, setIsLoading] = (0, react_1.useState)(false); const [menuOpen, setMenuOpen] = (0, react_1.useState)(false); const [errors, setErrors] = (0, react_1.useState)([]); const { t, i18n } = (0, react_i18next_1.useTranslation)(); const menuRef = (0, react_1.useRef)(null); const buttonRef = (0, react_1.useRef)(null); const documentRef = (0, react_1.useRef)(null); const imageRef = (0, react_1.useRef)(null); const currentImageCount = documentPreviewFiles.filter(file => file.type === 'image').length; const remainingSlots = MAX_IMAGES - currentImageCount; const currentDocumentCount = documentPreviewFiles.filter(file => file.type === 'document').length; const remainingDocumentSlots = MAX_DOCUMENTS - currentDocumentCount; const hasReachedImageLimit = remainingSlots <= 0; const hasReachedDocumentLimit = remainingDocumentSlots <= 0; const removeError = (errorMessage) => { setErrors(prev => prev.filter(e => e.message !== errorMessage)); }; const addError = (error) => { setErrors(prev => [...prev, error]); setTimeout(() => removeError(error.message), 5000); }; const toggleMenu = () => { setMenuOpen(prev => !prev); }; const closeMenu = () => { setMenuOpen(false); }; (0, react_1.useEffect)(() => { const handleClickOutside = (event) => { if (menuRef.current && buttonRef.current && !menuRef.current.contains(event.target) && !buttonRef.current.contains(event.target)) { closeMenu(); } }; document.addEventListener('mousedown', handleClickOutside); return () => { document.removeEventListener('mousedown', handleClickOutside); }; }, []); const handleDocumentFiles = (files) => { if (files.length === 0) return; const escapeAttributeValue = (text) => { return text .replace(/&/g, '&amp;') .replace(/"/g, '&quot;') .replace(/'/g, '&#39;') .replace(/</g, '&lt;') .replace(/>/g, '&gt;'); }; const processedDocuments = files.map(file => { const escapedFileName = escapeAttributeValue(file.name); const formattedContent = `<document_attachment filename="${escapedFileName}" type="${file.mimeType}"> ${file.content} </document_attachment>`; return { name: file.name, id: file.id, content: formattedContent, type: 'document', mimeType: file.mimeType, }; }); const imageFiles = documentPreviewFiles.filter((file) => file.type === 'image'); setDocumentPreviewFiles([ ...processedDocuments, ...imageFiles, ]); setIsLoading(false); }; const validateDocumentFile = (file) => { var _a; const fileExt = `.${(_a = file.name.split('.').pop()) === null || _a === void 0 ? void 0 : _a.toLowerCase()}`; const ALLOWED_FILE_TYPES = ['.pdf', '.txt', '.json', '.xlsx', '.csv', '.md']; const MAX_FILE_SIZE = 10 * 1024 * 1024; if (!ALLOWED_FILE_TYPES.includes(fileExt)) { addError({ message: `File type "${fileExt}" is not supported. Please use: ${ALLOWED_FILE_TYPES.join(', ')}`, severity: 'error', }); return false; } if (file.size > MAX_FILE_SIZE) { addError({ message: `File "${file.name}" exceeds ${MAX_FILE_SIZE / 1024 / 1024}MB limit`, severity: 'error', }); return false; } return true; }; const validatePayloadSize = (newDocuments) => { const { MAX_TOTAL_MESSAGE_PAYLOAD } = require('../../helpers/constants'); const existingDocuments = documentPreviewFiles.filter((file) => file.type === 'document'); const allDocuments = [...existingDocuments, ...newDocuments]; const totalPayloadSize = allDocuments.reduce((total, doc) => total + doc.content.length, 0); if (totalPayloadSize > MAX_TOTAL_MESSAGE_PAYLOAD) { addError({ message: `Total document content exceeds ${MAX_TOTAL_MESSAGE_PAYLOAD} characters limit. Please remove some documents.`, severity: 'error', }); return false; } return true; }; const handleDocumentError = (error) => { addError(error); }; const validateImageFile = (file) => { var _a; const fileExt = `.${(_a = file.name.split('.').pop()) === null || _a === void 0 ? void 0 : _a.toLowerCase()}`; const ALLOWED_FILE_TYPES = ['.jpg', '.jpeg', '.png']; const MAX_FILE_SIZE = 10 * 1024 * 1024; if (!ALLOWED_FILE_TYPES.includes(fileExt) && !file.type.startsWith('image/')) { addError({ message: `File type "${fileExt}" is not supported. Please use: ${ALLOWED_FILE_TYPES.join(', ')}`, severity: 'error', }); return false; } if (file.size > MAX_FILE_SIZE) { addError({ message: `File "${file.name}" exceeds ${MAX_FILE_SIZE / 1024 / 1024}MB limit`, severity: 'error', }); return false; } return true; }; const handleImageError = (error) => { addError(error); }; const handleDocumentClick = () => { var _a; if (hasReachedDocumentLimit) { addError({ message: `Maximum ${MAX_DOCUMENTS} documents allowed.`, severity: 'error', }); closeMenu(); return; } const documentButtonElement = (_a = documentRef.current) === null || _a === void 0 ? void 0 : _a.querySelector('button'); if (documentButtonElement) { documentButtonElement.click(); } closeMenu(); }; const handleImageClick = () => { var _a, _b, _c; if (!isMediaAccepted) { addError({ message: (_a = t('upload.mediaNotAccepted')) !== null && _a !== void 0 ? _a : 'Media uploads are not accepted', severity: 'info', }); closeMenu(); return; } if (hasReachedImageLimit) { addError({ message: (_b = t('upload.maxImagesReached', { max: MAX_IMAGES })) !== null && _b !== void 0 ? _b : `Maximum ${MAX_IMAGES} images already uploaded`, severity: 'warning', }); closeMenu(); return; } const imageButtonElement = (_c = imageRef.current) === null || _c === void 0 ? void 0 : _c.querySelector('button'); if (imageButtonElement) { imageButtonElement.click(); } closeMenu(); }; const handleLoadingChange = (loading) => { setIsLoading(loading); }; return ((0, jsx_runtime_1.jsxs)("div", { className: "memori--unified-upload-wrapper", children: [(0, jsx_runtime_1.jsx)("button", { ref: buttonRef, className: (0, classnames_1.default)('memori-button', 'memori-button--circle', 'memori-button--icon-only', 'memori-share-button--button', 'memori--conversation-button', 'memori--unified-upload-button', { 'memori--error': errors.length > 0 }), onClick: toggleMenu, disabled: isLoading, title: (_a = t('upload.uploadFiles')) !== null && _a !== void 0 ? _a : 'Upload files', children: isLoading ? ((0, jsx_runtime_1.jsx)(Spin_1.default, { spinning: true, className: "memori--upload-icon" })) : ((0, jsx_runtime_1.jsx)(Upload_1.UploadIcon, { className: "memori--upload-icon" })) }), currentImageCount > 0 && ((0, jsx_runtime_1.jsxs)("div", { className: (0, classnames_1.default)('memori--image-count', { 'memori--image-count-full': hasReachedImageLimit, }), children: [currentImageCount, "/", MAX_IMAGES] })), currentDocumentCount > 0 && ((0, jsx_runtime_1.jsxs)("div", { className: (0, classnames_1.default)('memori--document-count', { 'memori--document-count-full': hasReachedDocumentLimit, }), children: [currentDocumentCount, "/", MAX_DOCUMENTS] })), menuOpen && ((0, jsx_runtime_1.jsxs)("div", { className: "memori--upload-menu", ref: menuRef, children: [(0, jsx_runtime_1.jsxs)("div", { className: (0, classnames_1.default)('memori--upload-menu-item', { 'memori--upload-menu-item--disabled': hasReachedDocumentLimit, }), onClick: handleDocumentClick, title: hasReachedDocumentLimit ? (_b = t('upload.maxDocumentsReached', { max: MAX_DOCUMENTS })) !== null && _b !== void 0 ? _b : `Maximum ${MAX_DOCUMENTS} documents already uploaded` : remainingDocumentSlots === 1 ? (_c = t('upload.lastDocumentSlot')) !== null && _c !== void 0 ? _c : 'Upload last document' : (_d = t('upload.uploadDocument', { remaining: remainingDocumentSlots })) !== null && _d !== void 0 ? _d : `Upload document (${remainingDocumentSlots} remaining)`, children: [(0, jsx_runtime_1.jsx)(Document_1.DocumentIcon, { className: "memori--upload-menu-icon" }), (0, jsx_runtime_1.jsx)("span", { children: (_e = t('upload.uploadDocument')) !== null && _e !== void 0 ? _e : 'Upload document' })] }), (0, jsx_runtime_1.jsxs)("div", { className: (0, classnames_1.default)('memori--upload-menu-item', { 'memori--upload-menu-item--disabled': !isMediaAccepted || hasReachedImageLimit, }), onClick: handleImageClick, title: !isMediaAccepted ? (_f = t('upload.mediaNotAccepted')) !== null && _f !== void 0 ? _f : 'Media uploads not accepted' : hasReachedImageLimit ? (_g = t('upload.maxImagesReached', { max: MAX_IMAGES })) !== null && _g !== void 0 ? _g : `Maximum ${MAX_IMAGES} images already uploaded` : remainingSlots === 1 ? (_h = t('upload.lastImageSlot')) !== null && _h !== void 0 ? _h : 'Upload last image' : (_j = t('upload.uploadImage', { remaining: remainingSlots })) !== null && _j !== void 0 ? _j : `Upload image (${remainingSlots} remaining)`, children: [(0, jsx_runtime_1.jsx)(Image_1.ImageIcon, { className: "memori--upload-menu-icon-image" }), (0, jsx_runtime_1.jsx)("span", { children: (_k = t('upload.uploadImage')) !== null && _k !== void 0 ? _k : 'Upload image' })] })] })), (0, jsx_runtime_1.jsx)("div", { className: "memori--hidden-uploader", ref: documentRef, children: (0, jsx_runtime_1.jsx)(UploadDocuments_1.default, { setDocumentPreviewFiles: handleDocumentFiles, maxDocuments: MAX_DOCUMENTS, documentPreviewFiles: documentPreviewFiles, onLoadingChange: handleLoadingChange, onDocumentError: handleDocumentError, onValidateFile: validateDocumentFile, onValidatePayloadSize: validatePayloadSize }) }), (0, jsx_runtime_1.jsx)("div", { className: "memori--hidden-uploader", ref: imageRef, children: (0, jsx_runtime_1.jsx)(UploadImages_1.default, { authToken: authToken, client: client, setDocumentPreviewFiles: setDocumentPreviewFiles, sessionID: sessionID, documentPreviewFiles: documentPreviewFiles, isMediaAccepted: isMediaAccepted, onLoadingChange: handleLoadingChange, maxImages: MAX_IMAGES, memoriID: memoriID, onImageError: handleImageError, onValidateImageFile: validateImageFile }) }), (0, jsx_runtime_1.jsx)("div", { className: "memori--error-message-container", children: errors.map((error, index) => ((0, jsx_runtime_1.jsx)(Alert_1.default, { className: 'memori--error-message-alert', open: true, type: error.severity, title: 'Upload notification', description: error.message, onClose: () => removeError(error.message), width: "350px" }, `${error.message}-${index}`))) })] })); }; exports.default = UploadButton; //# sourceMappingURL=UploadButton.js.map