import React, { useCallback, useRef, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { useDispatch, useSelector, useStore } from 'react-redux'; import { makeStyles } from 'tss-react/mui'; import { IReduxState } from '../../../app/types'; import Avatar from '../../../base/avatar/components/Avatar'; import Icon from '../../../base/icons/components/Icon'; import { IconCloudUpload, IconDownload, IconTrash } from '../../../base/icons/svg'; import BaseTheme from '../../../base/ui/components/BaseTheme.web'; import Button from '../../../base/ui/components/web/Button'; import { BUTTON_TYPES } from '../../../base/ui/constants.web'; import { downloadFile, removeFile } from '../../actions'; import { formatFileSize, formatTimestamp, getFileIcon, isFileUploadingEnabled, processFiles } from '../../functions.any'; const useStyles = makeStyles()(theme => { return { buttonContainer: { alignItems: 'center', bottom: 0, display: 'flex', justifyContent: 'end', gap: theme.spacing(2), position: 'absolute', right: theme.spacing(3), top: 0 }, container: { boxSizing: 'border-box', display: 'flex', flexDirection: 'column', height: '100%', margin: '0 auto', maxWidth: '600px', padding: theme.spacing(3), position: 'relative', width: '100%' }, dropZone: { backgroundColor: theme.palette.ui02, border: `2px dashed ${theme.palette.ui03}`, borderRadius: theme.shape.borderRadius, bottom: 0, left: 0, opacity: 0, position: 'absolute', right: 0, top: 0, zIndex: 0, '&.dragging': { backgroundColor: theme.palette.ui03, borderColor: theme.palette.action01, opacity: 0.8, zIndex: 2 } }, fileIconContainer: { display: 'flex', margin: 'auto' }, fileItem: { backgroundColor: theme.palette.ui02, borderRadius: theme.shape.borderRadius, display: 'flex', flexDirection: 'row', gap: theme.spacing(3), justifyContent: 'space-between', padding: theme.spacing(3), position: 'relative', '& .actionIconVisibility': { opacity: 0, transition: 'opacity 0.2s' }, '& .timestampVisibility': { opacity: 1 }, '&:hover': { backgroundColor: theme.palette.ui03, '& .actionIconVisibility': { opacity: 1 }, '& .timestampVisibility': { opacity: 0 } }, '&.focused .actionIconVisibility': { opacity: 1 }, '&.focused .timestampVisibility': { opacity: 0 } }, fileItemDetails: { display: 'flex', flexDirection: 'column', flexGrow: 2, gap: theme.spacing(1), justifyContent: 'center', minWidth: 0 }, fileList: { display: 'flex', flex: 1, flexDirection: 'column', gap: theme.spacing(2), gridTemplateColumns: 'repeat(auto-fill, minmax(200px, 1fr))', listStyleType: 'none', marginBottom: theme.spacing(8), marginTop: 0, overflowY: 'auto', padding: 0, zIndex: 1 }, fileName: { ...theme.typography.labelBold, gap: theme.spacing(1), overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }, fileAuthorParticipant: { alignItems: 'center', display: 'inline-flex', gap: theme.spacing(1) }, fileAuthorParticipantName: { ...theme.typography.labelBold, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }, fileSize: { ...theme.typography.labelRegular }, fileTimestamp: { ...theme.typography.labelRegular, display: 'flex', lineHeight: '1.2rem', marginTop: theme.spacing(1), textAlign: 'center', }, hiddenInput: { visibility: 'hidden' }, noFilesContainer: { display: 'flex', flexDirection: 'column', height: '88%', justifyContent: 'center', textAlign: 'center' }, noFilesText: { ...theme.typography.bodyLongBold, color: theme.palette.text02, padding: '0 24px', textAlign: 'center' }, progressBar: { backgroundColor: theme.palette.ui03, borderRadius: theme.shape.borderRadius, height: 4, overflow: 'hidden', width: '100%' }, progressFill: { backgroundColor: theme.palette.action01, height: '100%', transition: 'width 0.3s ease' }, uploadButton: { bottom: theme.spacing(4), cursor: 'pointer', left: '50%', position: 'absolute', transform: 'translateX(-50%)', width: '85%', zIndex: 1 }, uploadIcon: { margin: '0 auto' }, actionIcon: { background: 'transparent', border: 0, cursor: 'pointer', padding: theme.spacing(1), visibility: 'hidden', '&:focus': { outline: `2px solid ${theme.palette.action01}` } }, iconButton: { background: 'none', border: 'none', padding: 0, marginLeft: '8px', cursor: 'pointer', display: 'flex', alignItems: 'center', justifyContent: 'center', '&:focus-visible': { outline: `2px solid ${theme.palette.action01}`, borderRadius: '4px' } } }; }); const FileSharing = () => { const { classes } = useStyles(); const [ isDragging, setIsDragging ] = useState(false); const [ isFocused, setIsFocused ] = useState(false); const fileInputRef = useRef(null); const uploadButtonRef = useRef(null); const { t } = useTranslation(); const dispatch = useDispatch(); const store = useStore(); const { files } = useSelector((state: IReduxState) => state['features/file-sharing']); const sortedFiles = Array.from(files.values()).sort((a, b) => a.fileName.localeCompare(b.fileName)); const isUploadEnabled = useSelector(isFileUploadingEnabled); const handleDragEnter = useCallback((e: React.DragEvent) => { e.preventDefault(); e.stopPropagation(); setIsDragging(true); }, []); const handleDragLeave = useCallback((e: React.DragEvent) => { e.preventDefault(); e.stopPropagation(); setIsDragging(false); }, []); const handleDragOver = useCallback((e: React.DragEvent) => { e.preventDefault(); e.stopPropagation(); }, []); const handleFileSelect = useCallback((e: React.ChangeEvent) => { if (e.target.files) { processFiles(e.target.files as FileList, store); e.target.value = ''; // Reset the input value to allow re-uploading the same file uploadButtonRef.current?.focus(); } }, [ processFiles ]); const handleDrop = useCallback((e: React.DragEvent) => { e.preventDefault(); e.stopPropagation(); setIsDragging(false); if (e.dataTransfer.files?.length > 0) { processFiles(e.dataTransfer.files as FileList, store); } }, [ processFiles ]); const handleClick = useCallback(() => { fileInputRef.current?.click(); }, []); const handleKeyPress = useCallback((e: React.KeyboardEvent) => { e.preventDefault(); e.stopPropagation(); if (e.key === 'Enter' || e.key === ' ') { fileInputRef.current?.click(); } }, []); /* eslint-disable react/jsx-no-bind */ return (
{ isUploadEnabled && ( <>
{ sortedFiles.length === 0 && (
{ t('fileSharing.dragAndDrop') }
) } ) } { sortedFiles.length > 0 && (
    { sortedFiles.map(file => (
  • !e.currentTarget.contains(e.relatedTarget as Node) && setIsFocused(false) } onFocus = { () => setIsFocused(true) } tabIndex = { -1 } title = { file.fileName }> { (file.progress ?? 100) === 100 && ( <>
    { file.fileName }
    { formatFileSize(file.fileSize) }
    { file.authorParticipantName }
                                                            { formatTimestamp(file.timestamp) }
                                                        
    { isUploadEnabled && ( ) }
    ) } { (file.progress ?? 100) < 100 && ( <>
    ) }
  • )) }
) } { isUploadEnabled && (
); }; export default FileSharing;