import { memo, useEffect, useRef, useState, } from '../../lib/teact/teact'; import { getActions } from '../../global'; import type { ApiDocument, ApiMessage } from '../../api/types'; import type { ObserveFn } from '../../hooks/useIntersectionObserver'; import { getDocumentMediaHash, getMediaFormat, getMediaThumbUri, getMediaTransferState, isDocumentVideo, } from '../../global/helpers'; import { isIpRevealingMedia } from '../../util/media/ipRevealingMedia'; import { getDocumentExtension, getDocumentHasPreview } from './helpers/documentInfo'; import { preloadDocumentMedia } from './helpers/preloadDocumentMedia'; import useFlag from '../../hooks/useFlag'; import { useIsIntersecting } from '../../hooks/useIntersectionObserver'; import useLastCallback from '../../hooks/useLastCallback'; import useMedia from '../../hooks/useMedia'; import useMediaWithLoadProgress from '../../hooks/useMediaWithLoadProgress'; import useOldLang from '../../hooks/useOldLang'; import Checkbox from '../ui/Checkbox'; import ConfirmDialog from '../ui/ConfirmDialog'; import File from './File'; type OwnProps = { document: ApiDocument; observeIntersection?: ObserveFn; smaller?: boolean; isSelected?: boolean; isSelectable?: boolean; canAutoLoad?: boolean; uploadProgress?: number; datetime?: number; className?: string; sender?: string; autoLoadFileMaxSizeMb?: number; isDownloading?: boolean; shouldWarnAboutFiles?: boolean; onCancelUpload?: () => void; onMediaClick?: () => void; } & ({ message: ApiMessage; onDateClick: (arg: ApiMessage) => void; } | { message?: ApiMessage; onDateClick?: never; }); const BYTES_PER_MB = 1024 * 1024; const Document = ({ document, observeIntersection, smaller, canAutoLoad, autoLoadFileMaxSizeMb, uploadProgress, datetime, className, sender, isSelected, isSelectable, shouldWarnAboutFiles, isDownloading, message, onCancelUpload, onMediaClick, onDateClick, }: OwnProps) => { const { cancelMediaDownload, downloadMedia, setSharedSettingOption } = getActions(); const ref = useRef(); const lang = useOldLang(); const [isFileIpDialogOpen, openFileIpDialog, closeFileIpDialog] = useFlag(); const [shouldNotWarnAboutFiles, setShouldNotWarnAboutFiles] = useState(false); const { fileName, size, mimeType } = document; const extension = getDocumentExtension(document) || ''; const isIntersecting = useIsIntersecting(ref, observeIntersection); const [wasIntersected, markIntersected] = useFlag(); useEffect(() => { if (isIntersecting) { markIntersected(); } }, [isIntersecting, markIntersected]); // Auto-loading does not use global download manager because requires additional click to save files locally const [isLoadAllowed, setIsLoadAllowed] = useState( canAutoLoad && (!autoLoadFileMaxSizeMb || size <= autoLoadFileMaxSizeMb * BYTES_PER_MB), ); const shouldDownload = Boolean(isDownloading || (isLoadAllowed && wasIntersected)); const documentHash = getDocumentMediaHash(document, 'download'); const { loadProgress: downloadProgress, mediaData } = useMediaWithLoadProgress( documentHash, !shouldDownload, getMediaFormat(document, 'download'), undefined, true, ); const isLoaded = Boolean(mediaData); const { isUploading, isTransferring, transferProgress, } = getMediaTransferState( uploadProgress || downloadProgress, shouldDownload && !isLoaded, uploadProgress !== undefined, ); const hasPreview = getDocumentHasPreview(document); const thumbDataUri = hasPreview ? getMediaThumbUri(document) : undefined; const localBlobUrl = hasPreview ? document.previewBlobUrl : undefined; const previewData = useMedia(getDocumentMediaHash(document, 'pictogram'), !isIntersecting); const shouldForceDownload = document.innerMediaType === 'photo' && document.mediaSize && !document.mediaSize.fromDocumentAttribute && !document.mediaSize.fromPreload; const withMediaViewer = onMediaClick && document.innerMediaType && !shouldForceDownload; useEffect(() => { const fileEl = ref.current; if (!withMediaViewer || !fileEl || !message) return; const onHover = () => { preloadDocumentMedia(message); }; fileEl.addEventListener('mouseenter', onHover); return () => { fileEl.removeEventListener('mouseenter', onHover); }; }, [withMediaViewer, message]); const handleDownload = useLastCallback(() => { downloadMedia({ media: document, originMessage: message }); }); const handleClick = useLastCallback(() => { if (isUploading) { if (onCancelUpload) { onCancelUpload(); } return; } if (isDownloading) { cancelMediaDownload({ media: document }); return; } if (isTransferring) { setIsLoadAllowed(false); return; } if (withMediaViewer) { onMediaClick(); return; } if (isIpRevealingMedia({ mimeType, extension }) && shouldWarnAboutFiles) { openFileIpDialog(); return; } handleDownload(); }); const handleFileIpConfirm = useLastCallback(() => { setSharedSettingOption({ shouldWarnAboutFiles: !shouldNotWarnAboutFiles }); closeFileIpDialog(); handleDownload(); }); const handleDateClick = useLastCallback(() => { onDateClick?.(message); }); return ( <> {lang('lng_launch_svg_warning')} ); }; export default memo(Document);