diff --git a/apps/meteor/client/components/message/content/attachments/file/GenericFileAttachment.tsx b/apps/meteor/client/components/message/content/attachments/file/GenericFileAttachment.tsx index 2e0fb7107d983..ee6d7978d3eff 100644 --- a/apps/meteor/client/components/message/content/attachments/file/GenericFileAttachment.tsx +++ b/apps/meteor/client/components/message/content/attachments/file/GenericFileAttachment.tsx @@ -15,6 +15,7 @@ import { getFileExtension } from '../../../../../../lib/utils/getFileExtension'; import { forAttachmentDownload, registerDownloadForUid } from '../../../../../hooks/useDownloadFromServiceWorker'; import MessageCollapsible from '../../../MessageCollapsible'; import AttachmentSize from '../structure/AttachmentSize'; +import { useOpenEncryptedPdf } from './hooks/useOpenEncryptedPdf'; const openDocumentViewer = window.RocketChatDesktop?.openDocumentViewer; @@ -31,24 +32,31 @@ const GenericFileAttachment = ({ const getURL = useMediaUrl(); const uid = useId(); const { t } = useTranslation(); + const openEncryptedPdf = useOpenEncryptedPdf(); - const handleTitleClick = (event: UIEvent): void => { + const handleTitleClick = async (event: UIEvent): Promise => { if (!link) { return; } - if (openDocumentViewer && format === 'PDF') { + const isEncrypted = link.includes('/file-decrypt/'); + + if (format === 'PDF' && openDocumentViewer) { event.preventDefault(); + if (isEncrypted) { + openEncryptedPdf(link, title, size, format, openDocumentViewer); + return; + } + const url = new URL(getURL(link), window.location.origin); url.searchParams.set('contentDisposition', 'inline'); openDocumentViewer(url.toString(), format, ''); return; } - if (link.includes('/file-decrypt/')) { + if (isEncrypted) { event.preventDefault(); - registerDownloadForUid(uid, t, title); forAttachmentDownload(uid, link); } diff --git a/apps/meteor/client/components/message/content/attachments/file/hooks/useOpenEncryptedPdf.tsx b/apps/meteor/client/components/message/content/attachments/file/hooks/useOpenEncryptedPdf.tsx new file mode 100644 index 0000000000000..45b5d567fc2c7 --- /dev/null +++ b/apps/meteor/client/components/message/content/attachments/file/hooks/useOpenEncryptedPdf.tsx @@ -0,0 +1,76 @@ +import { useMediaUrl } from '@rocket.chat/ui-contexts'; +import { useId, useEffect, useRef } from 'react'; +import { useTranslation } from 'react-i18next'; + +import { forAttachmentDownload, registerDownloadForUid } from '../../../../../../hooks/useDownloadFromServiceWorker'; + +export const useOpenEncryptedPdf = () => { + const getURL = useMediaUrl(); + const pdfPreviewSizeLimit = window.RocketChatDesktop?.getE2ePdfPreviewSizeLimit?.() ?? 10; + const pdfPreviewSizeLimitInBytes = pdfPreviewSizeLimit * 1024 * 1024; + const uid = useId(); + const { t } = useTranslation(); + + const blobUrlRef = useRef(undefined); + const abortControllerRef = useRef(null); + + useEffect(() => { + return () => { + if (abortControllerRef.current) { + abortControllerRef.current.abort(); + } + if (blobUrlRef.current) { + URL.revokeObjectURL(blobUrlRef.current); + blobUrlRef.current = undefined; + } + }; + }, []); + + const openEncryptedPdf = async ( + link: string, + title: string | undefined, + size: number | undefined, + format: string, + openDocumentViewer: (url: string, format: string, options: any) => void, + ) => { + if (size && size > pdfPreviewSizeLimitInBytes) { + registerDownloadForUid(uid, t, title); + forAttachmentDownload(uid, link); + return; + } + + if (blobUrlRef.current) { + URL.revokeObjectURL(blobUrlRef.current); + blobUrlRef.current = undefined; + } + + if (abortControllerRef.current) { + abortControllerRef.current.abort(); + } + + const abortController = new AbortController(); + abortControllerRef.current = abortController; + + try { + const response = await fetch(getURL(link), { + signal: abortController.signal, + }); + if (!response.ok) { + throw new Error(`Failed to fetch encrypted PDF: ${response.status}`); + } + const blob = await response.blob(); + if (abortController.signal.aborted || abortControllerRef.current !== abortController) { + return; + } + const blobUrl = URL.createObjectURL(blob); + blobUrlRef.current = blobUrl; + openDocumentViewer(blobUrl, format, title ?? ''); + } catch (error: any) { + if (error.name !== 'AbortError') { + console.error('Error opening preview of encrypted PDF', error); + } + } + }; + + return openEncryptedPdf; +}; diff --git a/packages/desktop-api/src/index.ts b/packages/desktop-api/src/index.ts index 5344ad5d18005..2d7b18c5cabb9 100644 --- a/packages/desktop-api/src/index.ts +++ b/packages/desktop-api/src/index.ts @@ -63,5 +63,6 @@ export interface IRocketChatDesktop { setUserToken: (token: string, userId: string) => void; openDocumentViewer: (url: string, format: string, options: any) => void; reloadServer: () => void; + getE2ePdfPreviewSizeLimit: () => number; openInBrowser: (url: string) => void; }