diff --git a/.changeset/new-balloons-speak.md b/.changeset/new-balloons-speak.md new file mode 100644 index 000000000000..cecaabe353ba --- /dev/null +++ b/.changeset/new-balloons-speak.md @@ -0,0 +1,7 @@ +--- +'@rocket.chat/meteor': patch +--- + +Fixed a crash on web client due to service workers not being available, this can happen in multiple scenarios like on Firefox's private window or if the connection is not secure (non-HTTPS), [see more details](https://developer.mozilla.org/en-US/docs/Web/Security/Secure_Contexts). + +Rocket.Chat needs service workers to process E2EE encrypted files on rooms. These types of files won't be available inside private windows, but the rest of E2EE encrypted features should work normally diff --git a/apps/meteor/client/components/message/content/attachments/structure/AttachmentDownloadBase.tsx b/apps/meteor/client/components/message/content/attachments/structure/AttachmentDownloadBase.tsx index 48c078b9146c..c9adc4533a97 100644 --- a/apps/meteor/client/components/message/content/attachments/structure/AttachmentDownloadBase.tsx +++ b/apps/meteor/client/components/message/content/attachments/structure/AttachmentDownloadBase.tsx @@ -6,18 +6,19 @@ import Action from '../../Action'; type AttachmentDownloadBaseProps = Omit, 'icon'> & { title?: string | undefined; href: string }; -const AttachmentDownloadBase: FC = ({ title, href, ...props }) => { +const AttachmentDownloadBase: FC = ({ title, href, disabled, ...props }) => { const t = useTranslation(); return ( ); diff --git a/apps/meteor/client/hooks/useDownloadFromServiceWorker.ts b/apps/meteor/client/hooks/useDownloadFromServiceWorker.ts index 5ab7f804fec7..199d1507e284 100644 --- a/apps/meteor/client/hooks/useDownloadFromServiceWorker.ts +++ b/apps/meteor/client/hooks/useDownloadFromServiceWorker.ts @@ -7,13 +7,15 @@ import { downloadAs } from '../lib/download'; const ee = new Emitter>(); -navigator.serviceWorker.addEventListener('message', (event) => { - if (event.data.type === 'attachment-download-result') { - const { result } = event.data as { result: ArrayBuffer; id: string }; +if ('serviceWorker' in navigator) { + navigator.serviceWorker.addEventListener('message', (event) => { + if (event.data.type === 'attachment-download-result') { + const { result } = event.data as { result: ArrayBuffer; id: string }; - ee.emit(event.data.id, { result, id: event.data.id }); - } -}); + ee.emit(event.data.id, { result, id: event.data.id }); + } + }); +} export const registerDownloadForUid = (uid: string, t: ReturnType['t'], title?: string) => { ee.once(uid, ({ result }) => { @@ -23,8 +25,13 @@ export const registerDownloadForUid = (uid: string, t: ReturnType { if (!controller) { - controller = navigator.serviceWorker.controller; + controller = navigator?.serviceWorker?.controller; + } + + if (!controller) { + return; } + controller?.postMessage({ type: 'attachment-download', url: href, @@ -33,7 +40,7 @@ export const forAttachmentDownload = (uid: string, href: string, controller?: Se }; export const useDownloadFromServiceWorker = (href: string, title?: string) => { - const { controller } = navigator.serviceWorker; + const { controller } = navigator?.serviceWorker || {}; const uid = useUniqueId(); diff --git a/apps/meteor/client/views/room/contextualBar/RoomFiles/components/FileItemMenu.tsx b/apps/meteor/client/views/room/contextualBar/RoomFiles/components/FileItemMenu.tsx index 157df8d78027..4fbf2fc477f1 100644 --- a/apps/meteor/client/views/room/contextualBar/RoomFiles/components/FileItemMenu.tsx +++ b/apps/meteor/client/views/room/contextualBar/RoomFiles/components/FileItemMenu.tsx @@ -17,21 +17,24 @@ type FileItemMenuProps = { const ee = new Emitter>(); -navigator.serviceWorker.addEventListener('message', (event) => { - if (event.data.type === 'attachment-download-result') { - const { result } = event.data as { result: ArrayBuffer; id: string }; +if ('serviceWorker' in navigator) { + navigator.serviceWorker.addEventListener('message', (event) => { + if (event.data.type === 'attachment-download-result') { + const { result } = event.data as { result: ArrayBuffer; id: string }; - ee.emit(event.data.id, { result, id: event.data.id }); - } -}); + ee.emit(event.data.id, { result, id: event.data.id }); + } + }); +} const FileItemMenu = ({ fileData, onClickDelete }: FileItemMenuProps) => { const t = useTranslation(); const room = useRoom(); const userId = useUserId(); const isDeletionAllowed = useMessageDeletionIsAllowed(room._id, fileData, userId); + const canDownloadFile = !fileData.encryption || 'serviceWorker' in navigator; - const { controller } = navigator.serviceWorker; + const { controller } = navigator?.serviceWorker || {}; const uid = useUniqueId(); @@ -53,6 +56,10 @@ const FileItemMenu = ({ fileData, onClickDelete }: FileItemMenuProps) => { ), action: () => { if (fileData.path?.includes('/file-decrypt/')) { + if (!controller) { + return; + } + controller?.postMessage({ type: 'attachment-download', url: fileData.path, @@ -68,6 +75,7 @@ const FileItemMenu = ({ fileData, onClickDelete }: FileItemMenuProps) => { URL.revokeObjectURL(fileData.url); } }, + disabled: !canDownloadFile, }, ...(isDeletionAllowed && onClickDelete && { diff --git a/packages/i18n/src/locales/en.i18n.json b/packages/i18n/src/locales/en.i18n.json index f9f29f1fcc30..e4d1c611aef6 100644 --- a/packages/i18n/src/locales/en.i18n.json +++ b/packages/i18n/src/locales/en.i18n.json @@ -1756,6 +1756,7 @@ "Dont_ask_me_again_list": "Don't ask me again list", "Download": "Download", "Download_Destkop_App": "Download Desktop App", + "Download_Disabled": "Download disabled", "Download_Info": "Download info", "Download_My_Data": "Download My Data (HTML)", "Download_Pending_Avatars": "Download Pending Avatars",