From 77b5b4656c25ab5c403c464d291e1da099149977 Mon Sep 17 00:00:00 2001 From: Nazareno Bucciarelli Date: Wed, 13 May 2026 15:29:08 -0300 Subject: [PATCH 01/13] add encrypted pdf file blob codification --- .../file/GenericFileAttachment.tsx | 40 ++++++++++++++++--- 1 file changed, 35 insertions(+), 5 deletions(-) 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..2920a460dd11c 100644 --- a/apps/meteor/client/components/message/content/attachments/file/GenericFileAttachment.tsx +++ b/apps/meteor/client/components/message/content/attachments/file/GenericFileAttachment.tsx @@ -7,12 +7,13 @@ import { MessageGenericPreviewDescription, } from '@rocket.chat/fuselage'; import { useMediaUrl } from '@rocket.chat/ui-contexts'; -import { useId } from 'react'; +import { useId, useEffect, useRef } from 'react'; import type { UIEvent } from 'react'; import { useTranslation } from 'react-i18next'; import { getFileExtension } from '../../../../../../lib/utils/getFileExtension'; import { forAttachmentDownload, registerDownloadForUid } from '../../../../../hooks/useDownloadFromServiceWorker'; +import { MAX_FILE_SIZE_PREVIEW } from '../../../../../lib/constants'; import MessageCollapsible from '../../../MessageCollapsible'; import AttachmentSize from '../structure/AttachmentSize'; @@ -32,23 +33,52 @@ const GenericFileAttachment = ({ const uid = useId(); const { t } = useTranslation(); - const handleTitleClick = (event: UIEvent): void => { + const blobUrlRef = useRef(undefined); + + useEffect(() => { + return () => { + if (blobUrlRef.current) { + URL.revokeObjectURL(blobUrlRef.current); + blobUrlRef.current = undefined; + } + }; + }, []); + + 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) { + if (size && size > MAX_FILE_SIZE_PREVIEW) { + registerDownloadForUid(uid, t, title); + forAttachmentDownload(uid, link); + return; + } + if (blobUrlRef.current) { + URL.revokeObjectURL(blobUrlRef.current); + } + const response = await fetch(getURL(link)); + const blob = await response.blob(); + const blobUrl = URL.createObjectURL(blob); + blobUrlRef.current = blobUrl; + openDocumentViewer(blobUrl, format, title ?? ''); + 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); } From 339c708dd3dc48f8ff377df42ff3792a895ada7a Mon Sep 17 00:00:00 2001 From: Nazareno Bucciarelli Date: Thu, 14 May 2026 19:13:36 -0300 Subject: [PATCH 02/13] add changeset --- .changeset/fine-jokes-trade.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/fine-jokes-trade.md diff --git a/.changeset/fine-jokes-trade.md b/.changeset/fine-jokes-trade.md new file mode 100644 index 0000000000000..42662692a39c5 --- /dev/null +++ b/.changeset/fine-jokes-trade.md @@ -0,0 +1,5 @@ +--- +'@rocket.chat/meteor': patch +--- + +Fixes inability to download encrypted PDFs on desktop From ab04cdf68d25765a514f7eaac1d808c2b590d1ed Mon Sep 17 00:00:00 2001 From: Nazareno Bucciarelli Date: Fri, 15 May 2026 11:19:09 -0300 Subject: [PATCH 03/13] add use of abort controller --- .../file/GenericFileAttachment.tsx | 33 ++++++++++++++++--- 1 file changed, 28 insertions(+), 5 deletions(-) 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 2920a460dd11c..dd9ca13613266 100644 --- a/apps/meteor/client/components/message/content/attachments/file/GenericFileAttachment.tsx +++ b/apps/meteor/client/components/message/content/attachments/file/GenericFileAttachment.tsx @@ -34,9 +34,13 @@ const GenericFileAttachment = ({ 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; @@ -60,14 +64,33 @@ const GenericFileAttachment = ({ forAttachmentDownload(uid, link); return; } + if (blobUrlRef.current) { URL.revokeObjectURL(blobUrlRef.current); + blobUrlRef.current = undefined; + } + + if (abortControllerRef.current) { + abortControllerRef.current.abort(); + } + abortControllerRef.current = new AbortController(); + + try { + const response = await fetch(getURL(link), { + signal: abortControllerRef.current.signal, + }); + const blob = await response.blob(); + if (abortControllerRef.current.signal.aborted) { + return; + } + const blobUrl = URL.createObjectURL(blob); + blobUrlRef.current = blobUrl; + openDocumentViewer(blobUrl, format, title ?? ''); + } catch (error: any) { + if (error.name !== 'AbortError') { + console.error('Error fetching encrypted PDF', error); + } } - const response = await fetch(getURL(link)); - const blob = await response.blob(); - const blobUrl = URL.createObjectURL(blob); - blobUrlRef.current = blobUrl; - openDocumentViewer(blobUrl, format, title ?? ''); return; } From 78225b7f1463044f554765a0a3b06cec30b2b94f Mon Sep 17 00:00:00 2001 From: Nazareno Bucciarelli Date: Fri, 15 May 2026 13:00:40 -0300 Subject: [PATCH 04/13] add fetch failure handling --- .../content/attachments/file/GenericFileAttachment.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) 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 dd9ca13613266..a447112c502b2 100644 --- a/apps/meteor/client/components/message/content/attachments/file/GenericFileAttachment.tsx +++ b/apps/meteor/client/components/message/content/attachments/file/GenericFileAttachment.tsx @@ -79,6 +79,9 @@ const GenericFileAttachment = ({ const response = await fetch(getURL(link), { signal: abortControllerRef.current.signal, }); + if (!response.ok) { + throw new Error(`Failed to fetch encrypted PDF: ${response.status}`); + } const blob = await response.blob(); if (abortControllerRef.current.signal.aborted) { return; @@ -88,7 +91,7 @@ const GenericFileAttachment = ({ openDocumentViewer(blobUrl, format, title ?? ''); } catch (error: any) { if (error.name !== 'AbortError') { - console.error('Error fetching encrypted PDF', error); + console.error('Error opening preview of encrypted PDF', error); } } return; From 98015cb011096d419568a9850a4e1e14d00af0c5 Mon Sep 17 00:00:00 2001 From: Nazareno Bucciarelli Date: Mon, 18 May 2026 19:01:11 -0300 Subject: [PATCH 05/13] add new setting --- .../content/attachments/file/GenericFileAttachment.tsx | 7 ++++--- apps/meteor/server/settings/e2e.ts | 7 +++++++ packages/i18n/src/locales/en.i18n.json | 2 ++ 3 files changed, 13 insertions(+), 3 deletions(-) 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 a447112c502b2..a0aa6fde03a16 100644 --- a/apps/meteor/client/components/message/content/attachments/file/GenericFileAttachment.tsx +++ b/apps/meteor/client/components/message/content/attachments/file/GenericFileAttachment.tsx @@ -6,14 +6,13 @@ import { MessageGenericPreviewTitle, MessageGenericPreviewDescription, } from '@rocket.chat/fuselage'; -import { useMediaUrl } from '@rocket.chat/ui-contexts'; +import { useMediaUrl, useSetting } from '@rocket.chat/ui-contexts'; import { useId, useEffect, useRef } from 'react'; import type { UIEvent } from 'react'; import { useTranslation } from 'react-i18next'; import { getFileExtension } from '../../../../../../lib/utils/getFileExtension'; import { forAttachmentDownload, registerDownloadForUid } from '../../../../../hooks/useDownloadFromServiceWorker'; -import { MAX_FILE_SIZE_PREVIEW } from '../../../../../lib/constants'; import MessageCollapsible from '../../../MessageCollapsible'; import AttachmentSize from '../structure/AttachmentSize'; @@ -30,6 +29,8 @@ const GenericFileAttachment = ({ collapsed, }: GenericFileAttachmentProps) => { const getURL = useMediaUrl(); + const pdfPreviewSizeLimitMb = useSetting('E2E_PDF_Preview_Size_Limit', 10); + const pdfPreviewSizeLimit = Number(pdfPreviewSizeLimitMb) * 1024 * 1024; const uid = useId(); const { t } = useTranslation(); @@ -59,7 +60,7 @@ const GenericFileAttachment = ({ event.preventDefault(); if (isEncrypted) { - if (size && size > MAX_FILE_SIZE_PREVIEW) { + if (size && size > pdfPreviewSizeLimit) { registerDownloadForUid(uid, t, title); forAttachmentDownload(uid, link); return; diff --git a/apps/meteor/server/settings/e2e.ts b/apps/meteor/server/settings/e2e.ts index b94bfc38d8c66..dd5da7a87a236 100644 --- a/apps/meteor/server/settings/e2e.ts +++ b/apps/meteor/server/settings/e2e.ts @@ -34,6 +34,13 @@ export const createE2ESettings = () => enableQuery: { _id: 'E2E_Enable', value: true }, }); + await this.add('E2E_PDF_Preview_Size_Limit', 10, { + type: 'int', + public: true, + i18nLabel: 'E2E_PDF_Preview_Size_Limit', + i18nDescription: 'E2E_PDF_Preview_Size_Limit_Description', + }); + await this.add('E2E_Enabled_Mentions', true, { type: 'boolean', public: true, diff --git a/packages/i18n/src/locales/en.i18n.json b/packages/i18n/src/locales/en.i18n.json index 4bf33527ac06a..04a18ec94f0cc 100644 --- a/packages/i18n/src/locales/en.i18n.json +++ b/packages/i18n/src/locales/en.i18n.json @@ -1921,6 +1921,8 @@ "E2E_Encryption_enabled_for_room": "End-to-end encryption enabled for #{{roomName}}", "E2E_Invalid_Key": "No E2E encryption key found for this room", "E2E_Key_Error": "This message is end-to-end encrypted and cannot be decrypted due to incorrect encryption key", + "E2E_PDF_Preview_Size_Limit": "PDF size limit for previews (MB)", + "E2E_PDF_Preview_Size_Limit_Description": "Encrypted files are loaded to memory for previews. Files larger than this limit will be downloaded directly.", "E2E_Reset_Email_Content": "You've been automatically logged out. When you log in again, a new key will be generated and access will be restored to any encrypted room with at least one member online. If no members are online, access will be restored as soon as a member comes online.", "E2E_Reset_Other_Key_Warning": "Resetting the E2EE key will log out the user. When the user logs in again, a new key will be generated and access will be restored to any encrypted room with at least one member online. If no members are online, access will be restored as soon as a member comes online.", "E2E_disable_encryption": "Disable encryption", From 4b6851cd409ca26650f54b32da162e03ef0d941e Mon Sep 17 00:00:00 2001 From: Nazareno Bucciarelli Date: Tue, 19 May 2026 10:52:29 -0300 Subject: [PATCH 06/13] use bytes rather than megabytes for setting --- .../message/content/attachments/file/GenericFileAttachment.tsx | 3 +-- apps/meteor/server/settings/e2e.ts | 2 +- packages/i18n/src/locales/en.i18n.json | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) 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 a0aa6fde03a16..bd96f7a2d2e3c 100644 --- a/apps/meteor/client/components/message/content/attachments/file/GenericFileAttachment.tsx +++ b/apps/meteor/client/components/message/content/attachments/file/GenericFileAttachment.tsx @@ -29,8 +29,7 @@ const GenericFileAttachment = ({ collapsed, }: GenericFileAttachmentProps) => { const getURL = useMediaUrl(); - const pdfPreviewSizeLimitMb = useSetting('E2E_PDF_Preview_Size_Limit', 10); - const pdfPreviewSizeLimit = Number(pdfPreviewSizeLimitMb) * 1024 * 1024; + const pdfPreviewSizeLimit = useSetting('E2E_PDF_Preview_Size_Limit', 10485760); const uid = useId(); const { t } = useTranslation(); diff --git a/apps/meteor/server/settings/e2e.ts b/apps/meteor/server/settings/e2e.ts index dd5da7a87a236..a58ed36093b50 100644 --- a/apps/meteor/server/settings/e2e.ts +++ b/apps/meteor/server/settings/e2e.ts @@ -34,7 +34,7 @@ export const createE2ESettings = () => enableQuery: { _id: 'E2E_Enable', value: true }, }); - await this.add('E2E_PDF_Preview_Size_Limit', 10, { + await this.add('E2E_PDF_Preview_Size_Limit', 10485760, { type: 'int', public: true, i18nLabel: 'E2E_PDF_Preview_Size_Limit', diff --git a/packages/i18n/src/locales/en.i18n.json b/packages/i18n/src/locales/en.i18n.json index 04a18ec94f0cc..6f66f3182a801 100644 --- a/packages/i18n/src/locales/en.i18n.json +++ b/packages/i18n/src/locales/en.i18n.json @@ -1921,7 +1921,7 @@ "E2E_Encryption_enabled_for_room": "End-to-end encryption enabled for #{{roomName}}", "E2E_Invalid_Key": "No E2E encryption key found for this room", "E2E_Key_Error": "This message is end-to-end encrypted and cannot be decrypted due to incorrect encryption key", - "E2E_PDF_Preview_Size_Limit": "PDF size limit for previews (MB)", + "E2E_PDF_Preview_Size_Limit": "PDF size limit for previews (in bytes)", "E2E_PDF_Preview_Size_Limit_Description": "Encrypted files are loaded to memory for previews. Files larger than this limit will be downloaded directly.", "E2E_Reset_Email_Content": "You've been automatically logged out. When you log in again, a new key will be generated and access will be restored to any encrypted room with at least one member online. If no members are online, access will be restored as soon as a member comes online.", "E2E_Reset_Other_Key_Warning": "Resetting the E2EE key will log out the user. When the user logs in again, a new key will be generated and access will be restored to any encrypted room with at least one member online. If no members are online, access will be restored as soon as a member comes online.", From a082845f4f864705a1ad8b60752a118a5e124533 Mon Sep 17 00:00:00 2001 From: Nazareno Bucciarelli Date: Tue, 19 May 2026 11:18:50 -0300 Subject: [PATCH 07/13] address abortController race condition --- .../content/attachments/file/GenericFileAttachment.tsx | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) 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 bd96f7a2d2e3c..44ed7a836e848 100644 --- a/apps/meteor/client/components/message/content/attachments/file/GenericFileAttachment.tsx +++ b/apps/meteor/client/components/message/content/attachments/file/GenericFileAttachment.tsx @@ -73,17 +73,18 @@ const GenericFileAttachment = ({ if (abortControllerRef.current) { abortControllerRef.current.abort(); } - abortControllerRef.current = new AbortController(); + const abortController = new AbortController(); + abortControllerRef.current = abortController; try { const response = await fetch(getURL(link), { - signal: abortControllerRef.current.signal, + signal: abortController.signal, }); if (!response.ok) { throw new Error(`Failed to fetch encrypted PDF: ${response.status}`); } const blob = await response.blob(); - if (abortControllerRef.current.signal.aborted) { + if (abortController.signal.aborted || abortControllerRef.current !== abortController) { return; } const blobUrl = URL.createObjectURL(blob); From 45fb0fec4ca6beea5d89c0265b696f7e11e6f405 Mon Sep 17 00:00:00 2001 From: Nazareno Bucciarelli Date: Tue, 19 May 2026 11:20:31 -0300 Subject: [PATCH 08/13] update changeset --- .changeset/fine-jokes-trade.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.changeset/fine-jokes-trade.md b/.changeset/fine-jokes-trade.md index 42662692a39c5..4cba21cf6c18b 100644 --- a/.changeset/fine-jokes-trade.md +++ b/.changeset/fine-jokes-trade.md @@ -1,5 +1,5 @@ --- -'@rocket.chat/meteor': patch +'@rocket.chat/meteor': minor --- -Fixes inability to download encrypted PDFs on desktop +Adds new `E2E_PDF_Preview_Size_Limit` setting to set the maximum file size for PDF preview in E2E encrypted rooms. The default value is 10MB. If a PDF file exceeds this limit, it will not be previewed and will be downloaded instead. From 6375d390c68c0148ab7859119837f633182f5cd9 Mon Sep 17 00:00:00 2001 From: Nazareno Bucciarelli Date: Tue, 19 May 2026 11:28:24 -0300 Subject: [PATCH 09/13] fix grammar in translation --- packages/i18n/src/locales/en.i18n.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/i18n/src/locales/en.i18n.json b/packages/i18n/src/locales/en.i18n.json index 6f66f3182a801..66db34b39cf38 100644 --- a/packages/i18n/src/locales/en.i18n.json +++ b/packages/i18n/src/locales/en.i18n.json @@ -1922,7 +1922,7 @@ "E2E_Invalid_Key": "No E2E encryption key found for this room", "E2E_Key_Error": "This message is end-to-end encrypted and cannot be decrypted due to incorrect encryption key", "E2E_PDF_Preview_Size_Limit": "PDF size limit for previews (in bytes)", - "E2E_PDF_Preview_Size_Limit_Description": "Encrypted files are loaded to memory for previews. Files larger than this limit will be downloaded directly.", + "E2E_PDF_Preview_Size_Limit_Description": "Encrypted files are loaded into memory for previews. Files larger than this limit will be downloaded directly.", "E2E_Reset_Email_Content": "You've been automatically logged out. When you log in again, a new key will be generated and access will be restored to any encrypted room with at least one member online. If no members are online, access will be restored as soon as a member comes online.", "E2E_Reset_Other_Key_Warning": "Resetting the E2EE key will log out the user. When the user logs in again, a new key will be generated and access will be restored to any encrypted room with at least one member online. If no members are online, access will be restored as soon as a member comes online.", "E2E_disable_encryption": "Disable encryption", From 7a51828f4cb3e2873ef910149cee1383f81bd1f3 Mon Sep 17 00:00:00 2001 From: Nazareno Bucciarelli Date: Tue, 19 May 2026 12:07:18 -0300 Subject: [PATCH 10/13] extract encrypted pdf opening logic to hook --- .../file/GenericFileAttachment.tsx | 59 ++------------- .../file/hooks/useOpenEncryptedPdf.tsx | 75 +++++++++++++++++++ 2 files changed, 80 insertions(+), 54 deletions(-) create mode 100644 apps/meteor/client/components/message/content/attachments/file/hooks/useOpenEncryptedPdf.tsx 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 44ed7a836e848..ee6d7978d3eff 100644 --- a/apps/meteor/client/components/message/content/attachments/file/GenericFileAttachment.tsx +++ b/apps/meteor/client/components/message/content/attachments/file/GenericFileAttachment.tsx @@ -6,8 +6,8 @@ import { MessageGenericPreviewTitle, MessageGenericPreviewDescription, } from '@rocket.chat/fuselage'; -import { useMediaUrl, useSetting } from '@rocket.chat/ui-contexts'; -import { useId, useEffect, useRef } from 'react'; +import { useMediaUrl } from '@rocket.chat/ui-contexts'; +import { useId } from 'react'; import type { UIEvent } from 'react'; import { useTranslation } from 'react-i18next'; @@ -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; @@ -29,24 +30,9 @@ const GenericFileAttachment = ({ collapsed, }: GenericFileAttachmentProps) => { const getURL = useMediaUrl(); - const pdfPreviewSizeLimit = useSetting('E2E_PDF_Preview_Size_Limit', 10485760); 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 = useOpenEncryptedPdf(); const handleTitleClick = async (event: UIEvent): Promise => { if (!link) { @@ -59,42 +45,7 @@ const GenericFileAttachment = ({ event.preventDefault(); if (isEncrypted) { - if (size && size > pdfPreviewSizeLimit) { - 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); - } - } + openEncryptedPdf(link, title, size, format, openDocumentViewer); return; } 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..7f32f110e1076 --- /dev/null +++ b/apps/meteor/client/components/message/content/attachments/file/hooks/useOpenEncryptedPdf.tsx @@ -0,0 +1,75 @@ +import { useMediaUrl, useSetting } 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 = useSetting('E2E_PDF_Preview_Size_Limit', 10485760); + 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 > pdfPreviewSizeLimit) { + 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; +}; From 68f5ae03a384fcc776f65b0bc71bccda1436022d Mon Sep 17 00:00:00 2001 From: Nazareno Bucciarelli Date: Tue, 19 May 2026 16:47:09 -0300 Subject: [PATCH 11/13] remove workspace setting to use desktop setting --- .changeset/fine-jokes-trade.md | 5 ----- .../content/attachments/file/hooks/useOpenEncryptedPdf.tsx | 4 ++-- apps/meteor/server/settings/e2e.ts | 7 ------- packages/i18n/src/locales/en.i18n.json | 2 -- 4 files changed, 2 insertions(+), 16 deletions(-) delete mode 100644 .changeset/fine-jokes-trade.md diff --git a/.changeset/fine-jokes-trade.md b/.changeset/fine-jokes-trade.md deleted file mode 100644 index 4cba21cf6c18b..0000000000000 --- a/.changeset/fine-jokes-trade.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@rocket.chat/meteor': minor ---- - -Adds new `E2E_PDF_Preview_Size_Limit` setting to set the maximum file size for PDF preview in E2E encrypted rooms. The default value is 10MB. If a PDF file exceeds this limit, it will not be previewed and will be downloaded instead. 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 index 7f32f110e1076..1f2df06fd0119 100644 --- a/apps/meteor/client/components/message/content/attachments/file/hooks/useOpenEncryptedPdf.tsx +++ b/apps/meteor/client/components/message/content/attachments/file/hooks/useOpenEncryptedPdf.tsx @@ -1,4 +1,4 @@ -import { useMediaUrl, useSetting } from '@rocket.chat/ui-contexts'; +import { useMediaUrl } from '@rocket.chat/ui-contexts'; import { useId, useEffect, useRef } from 'react'; import { useTranslation } from 'react-i18next'; @@ -6,7 +6,7 @@ import { forAttachmentDownload, registerDownloadForUid } from '../../../../../.. export const useOpenEncryptedPdf = () => { const getURL = useMediaUrl(); - const pdfPreviewSizeLimit = useSetting('E2E_PDF_Preview_Size_Limit', 10485760); + const pdfPreviewSizeLimit = window.RocketChatDesktop?.getE2ePdfPreviewSizeLimit?.() ?? 10; const uid = useId(); const { t } = useTranslation(); diff --git a/apps/meteor/server/settings/e2e.ts b/apps/meteor/server/settings/e2e.ts index a58ed36093b50..b94bfc38d8c66 100644 --- a/apps/meteor/server/settings/e2e.ts +++ b/apps/meteor/server/settings/e2e.ts @@ -34,13 +34,6 @@ export const createE2ESettings = () => enableQuery: { _id: 'E2E_Enable', value: true }, }); - await this.add('E2E_PDF_Preview_Size_Limit', 10485760, { - type: 'int', - public: true, - i18nLabel: 'E2E_PDF_Preview_Size_Limit', - i18nDescription: 'E2E_PDF_Preview_Size_Limit_Description', - }); - await this.add('E2E_Enabled_Mentions', true, { type: 'boolean', public: true, diff --git a/packages/i18n/src/locales/en.i18n.json b/packages/i18n/src/locales/en.i18n.json index 66db34b39cf38..4bf33527ac06a 100644 --- a/packages/i18n/src/locales/en.i18n.json +++ b/packages/i18n/src/locales/en.i18n.json @@ -1921,8 +1921,6 @@ "E2E_Encryption_enabled_for_room": "End-to-end encryption enabled for #{{roomName}}", "E2E_Invalid_Key": "No E2E encryption key found for this room", "E2E_Key_Error": "This message is end-to-end encrypted and cannot be decrypted due to incorrect encryption key", - "E2E_PDF_Preview_Size_Limit": "PDF size limit for previews (in bytes)", - "E2E_PDF_Preview_Size_Limit_Description": "Encrypted files are loaded into memory for previews. Files larger than this limit will be downloaded directly.", "E2E_Reset_Email_Content": "You've been automatically logged out. When you log in again, a new key will be generated and access will be restored to any encrypted room with at least one member online. If no members are online, access will be restored as soon as a member comes online.", "E2E_Reset_Other_Key_Warning": "Resetting the E2EE key will log out the user. When the user logs in again, a new key will be generated and access will be restored to any encrypted room with at least one member online. If no members are online, access will be restored as soon as a member comes online.", "E2E_disable_encryption": "Disable encryption", From a7323abac67eb2d1ae80ee3fcb2b8cd8eedde52a Mon Sep 17 00:00:00 2001 From: Nazareno Bucciarelli Date: Tue, 19 May 2026 16:55:50 -0300 Subject: [PATCH 12/13] convert setting value in bytes --- .../content/attachments/file/hooks/useOpenEncryptedPdf.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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 index 1f2df06fd0119..45b5d567fc2c7 100644 --- a/apps/meteor/client/components/message/content/attachments/file/hooks/useOpenEncryptedPdf.tsx +++ b/apps/meteor/client/components/message/content/attachments/file/hooks/useOpenEncryptedPdf.tsx @@ -7,6 +7,7 @@ import { forAttachmentDownload, registerDownloadForUid } from '../../../../../.. export const useOpenEncryptedPdf = () => { const getURL = useMediaUrl(); const pdfPreviewSizeLimit = window.RocketChatDesktop?.getE2ePdfPreviewSizeLimit?.() ?? 10; + const pdfPreviewSizeLimitInBytes = pdfPreviewSizeLimit * 1024 * 1024; const uid = useId(); const { t } = useTranslation(); @@ -32,7 +33,7 @@ export const useOpenEncryptedPdf = () => { format: string, openDocumentViewer: (url: string, format: string, options: any) => void, ) => { - if (size && size > pdfPreviewSizeLimit) { + if (size && size > pdfPreviewSizeLimitInBytes) { registerDownloadForUid(uid, t, title); forAttachmentDownload(uid, link); return; From ca906d3312eab55e5b483480541a50ea8047a588 Mon Sep 17 00:00:00 2001 From: Nazareno Bucciarelli Date: Wed, 20 May 2026 11:28:08 -0300 Subject: [PATCH 13/13] add missing property signatura to IRocketChatDesktop interface --- packages/desktop-api/src/index.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/desktop-api/src/index.ts b/packages/desktop-api/src/index.ts index 9d10c1f89d1c5..a8cbadd2aff47 100644 --- a/packages/desktop-api/src/index.ts +++ b/packages/desktop-api/src/index.ts @@ -63,4 +63,5 @@ export interface IRocketChatDesktop { setUserToken: (token: string, userId: string) => void; openDocumentViewer: (url: string, format: string, options: any) => void; reloadServer: () => void; + getE2ePdfPreviewSizeLimit: () => number; }