@memori.ai/memori-react
Version:
[](https://www.npmjs.com/package/@memori.ai/memori-react)   • 12.4 kB
JavaScript
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
import { useState, useRef, useEffect } from 'react';
import { DocumentIcon } from '../icons/Document';
import { ImageIcon } from '../icons/Image';
import { UploadIcon } from '../icons/Upload';
import Spin from '../ui/Spin';
import Alert from '../ui/Alert';
import cx from 'classnames';
import UploadDocuments from './UploadDocuments/UploadDocuments';
import UploadImages from './UploadImages/UploadImages';
import { useTranslation } from 'react-i18next';
import { MAX_DOCUMENTS_PER_MESSAGE } from '../../helpers/constants';
const MAX_IMAGES = 5;
const MAX_DOCUMENTS = 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] = useState(false);
const [menuOpen, setMenuOpen] = useState(false);
const [errors, setErrors] = useState([]);
const { t, i18n } = useTranslation();
const menuRef = useRef(null);
const buttonRef = useRef(null);
const documentRef = useRef(null);
const imageRef = 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);
};
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, '&')
.replace(/"/g, '"')
.replace(/'/g, ''')
.replace(/</g, '<')
.replace(/>/g, '>');
};
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 (_jsxs("div", { className: "memori--unified-upload-wrapper", children: [_jsx("button", { ref: buttonRef, className: cx('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 ? (_jsx(Spin, { spinning: true, className: "memori--upload-icon" })) : (_jsx(UploadIcon, { className: "memori--upload-icon" })) }), currentImageCount > 0 && (_jsxs("div", { className: cx('memori--image-count', {
'memori--image-count-full': hasReachedImageLimit,
}), children: [currentImageCount, "/", MAX_IMAGES] })), currentDocumentCount > 0 && (_jsxs("div", { className: cx('memori--document-count', {
'memori--document-count-full': hasReachedDocumentLimit,
}), children: [currentDocumentCount, "/", MAX_DOCUMENTS] })), menuOpen && (_jsxs("div", { className: "memori--upload-menu", ref: menuRef, children: [_jsxs("div", { className: cx('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: [_jsx(DocumentIcon, { className: "memori--upload-menu-icon" }), _jsx("span", { children: (_e = t('upload.uploadDocument')) !== null && _e !== void 0 ? _e : 'Upload document' })] }), _jsxs("div", { className: cx('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: [_jsx(ImageIcon, { className: "memori--upload-menu-icon-image" }), _jsx("span", { children: (_k = t('upload.uploadImage')) !== null && _k !== void 0 ? _k : 'Upload image' })] })] })), _jsx("div", { className: "memori--hidden-uploader", ref: documentRef, children: _jsx(UploadDocuments, { setDocumentPreviewFiles: handleDocumentFiles, maxDocuments: MAX_DOCUMENTS, documentPreviewFiles: documentPreviewFiles, onLoadingChange: handleLoadingChange, onDocumentError: handleDocumentError, onValidateFile: validateDocumentFile, onValidatePayloadSize: validatePayloadSize }) }), _jsx("div", { className: "memori--hidden-uploader", ref: imageRef, children: _jsx(UploadImages, { authToken: authToken, client: client, setDocumentPreviewFiles: setDocumentPreviewFiles, sessionID: sessionID, documentPreviewFiles: documentPreviewFiles, isMediaAccepted: isMediaAccepted, onLoadingChange: handleLoadingChange, maxImages: MAX_IMAGES, memoriID: memoriID, onImageError: handleImageError, onValidateImageFile: validateImageFile }) }), _jsx("div", { className: "memori--error-message-container", children: errors.map((error, index) => (_jsx(Alert, { 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}`))) })] }));
};
export default UploadButton;
//# sourceMappingURL=UploadButton.js.map