diff --git a/src/app/PersistableValues.ts b/src/app/PersistableValues.ts index 1fe354ec3b..5ec892c130 100644 --- a/src/app/PersistableValues.ts +++ b/src/app/PersistableValues.ts @@ -1,5 +1,6 @@ import type { Certificate } from 'electron'; +import { DEFAULT_E2E_PDF_PREVIEW_SIZE_LIMIT_MB } from '../constants'; import type { Download } from '../downloads/common'; import type { Server } from '../servers/common'; import type { WindowState } from '../ui/common'; @@ -106,9 +107,13 @@ type PersistableValues_4_13_0 = PersistableValues_4_11_0 & { isDebugLoggingEnabled: boolean; }; +type PersistableValues_4_14_0 = PersistableValues_4_13_0 & { + e2ePdfPreviewSizeLimit: number; +}; + export type PersistableValues = Pick< - PersistableValues_4_13_0, - keyof PersistableValues_4_13_0 + PersistableValues_4_14_0, + keyof PersistableValues_4_14_0 >; export const migrations = { @@ -201,4 +206,8 @@ export const migrations = { ...before, isDebugLoggingEnabled: false, }), + '>=4.14.0': (before: PersistableValues_4_13_0): PersistableValues_4_14_0 => ({ + ...before, + e2ePdfPreviewSizeLimit: DEFAULT_E2E_PDF_PREVIEW_SIZE_LIMIT_MB, + }), }; diff --git a/src/app/selectors.ts b/src/app/selectors.ts index cc10bfd3a3..d2fc8dd681 100644 --- a/src/app/selectors.ts +++ b/src/app/selectors.ts @@ -83,4 +83,6 @@ export const selectPersistableValues = createStructuredSelector({ }: RootState) => isDetailedEventsLoggingEnabled, isDebugLoggingEnabled: ({ isDebugLoggingEnabled }: RootState) => isDebugLoggingEnabled, + e2ePdfPreviewSizeLimit: ({ e2ePdfPreviewSizeLimit }: RootState) => + e2ePdfPreviewSizeLimit, }); diff --git a/src/constants.ts b/src/constants.ts new file mode 100644 index 0000000000..d1fd2015f2 --- /dev/null +++ b/src/constants.ts @@ -0,0 +1 @@ +export const DEFAULT_E2E_PDF_PREVIEW_SIZE_LIMIT_MB = 10; diff --git a/src/documentViewer/ipc.ts b/src/documentViewer/ipc.ts index cd30b1377b..c612d2d6ae 100644 --- a/src/documentViewer/ipc.ts +++ b/src/documentViewer/ipc.ts @@ -11,14 +11,21 @@ export const startDocumentViewerHandler = (): void => { 'document-viewer/open-window', async (event, url, format, _options) => { const validUrl = new URL(url); - const allowedProtocols = ['http:', 'https:']; + const allowedProtocols = ['http:', 'https:', 'blob:']; if (!allowedProtocols.includes(validUrl.protocol)) { return; } + + const eventOrigin = new URL(event.getURL()).origin; + + if (validUrl.protocol === 'blob:') { + if (validUrl.origin === 'null' || validUrl.origin !== eventOrigin) { + return; + } + } + const server = select(({ servers }) => - servers.find( - (s) => new URL(s.url).origin === new URL(event.getURL()).origin - ) + servers.find((s) => new URL(s.url).origin === eventOrigin) ); if (!server) { return; diff --git a/src/i18n/en.i18n.json b/src/i18n/en.i18n.json index d4d3632a3f..4bef7dd526 100644 --- a/src/i18n/en.i18n.json +++ b/src/i18n/en.i18n.json @@ -335,6 +335,10 @@ "debugLogging": { "title": "Verbose Logging", "description": "Writes all console output to the log file. When disabled, only errors and important messages are saved, keeping logs smaller and focused." + }, + "e2ePdfPreviewSizeLimit": { + "title": "PDF preview size limit in encrypted rooms (MB)", + "description": "Encrypted files are loaded into memory for previews. Files larger than this limit will be downloaded directly." } } }, diff --git a/src/servers/preload/api.ts b/src/servers/preload/api.ts index 9ebc67567d..752f150975 100644 --- a/src/servers/preload/api.ts +++ b/src/servers/preload/api.ts @@ -18,6 +18,7 @@ import { setUserPresenceDetection } from '../../userPresence/preload'; import { setBadge } from './badge'; import { writeTextToClipboard } from './clipboard'; import { openDocumentViewer } from './documentViewer'; +import { getE2ePdfPreviewSizeLimit } from './e2ePdfPreviewSizeLimit'; import { setFavicon } from './favicon'; import { setGitCommitHash } from './gitCommitHash'; import { @@ -49,6 +50,7 @@ type ExtendedIRocketChatDesktop = IRocketChatDesktop & { ) => Promise; closeCustomNotification: (id: unknown) => void; openInBrowser: (url: string) => void; + getE2ePdfPreviewSizeLimit: () => number; }; declare global { @@ -95,4 +97,5 @@ export const RocketChatDesktop: Window['RocketChatDesktop'] = { openDocumentViewer, openInBrowser, reloadServer, + getE2ePdfPreviewSizeLimit, }; diff --git a/src/servers/preload/e2ePdfPreviewSizeLimit.ts b/src/servers/preload/e2ePdfPreviewSizeLimit.ts new file mode 100644 index 0000000000..e6d14443c2 --- /dev/null +++ b/src/servers/preload/e2ePdfPreviewSizeLimit.ts @@ -0,0 +1,6 @@ +import { DEFAULT_E2E_PDF_PREVIEW_SIZE_LIMIT_MB } from '../../constants'; +import { safeSelect } from '../../store'; + +export const getE2ePdfPreviewSizeLimit = (): number => + safeSelect(({ e2ePdfPreviewSizeLimit }) => e2ePdfPreviewSizeLimit) ?? + DEFAULT_E2E_PDF_PREVIEW_SIZE_LIMIT_MB; diff --git a/src/store/rootReducer.ts b/src/store/rootReducer.ts index a47f109ee6..20d0b6ac03 100644 --- a/src/store/rootReducer.ts +++ b/src/store/rootReducer.ts @@ -21,6 +21,7 @@ import { servers } from '../servers/reducers'; import { availableBrowsers } from '../ui/reducers/availableBrowsers'; import { currentView } from '../ui/reducers/currentView'; import { dialogs } from '../ui/reducers/dialogs'; +import { e2ePdfPreviewSizeLimit } from '../ui/reducers/e2ePdfPreviewSizeLimit'; import { hasHideOnTrayNotificationShown } from '../ui/reducers/hasHideOnTrayNotificationShown'; import { isAddNewServersEnabled } from '../ui/reducers/isAddNewServersEnabled'; import { isDebugLoggingEnabled } from '../ui/reducers/isDebugLoggingEnabled'; @@ -111,6 +112,7 @@ export const rootReducer = combineReducers({ isVideoCallWindowPersistenceEnabled, isDeveloperModeEnabled, isDebugLoggingEnabled, + e2ePdfPreviewSizeLimit, isDetailedEventsLoggingEnabled, isVerboseOutlookLoggingEnabled, updateChannel, diff --git a/src/ui/actions.ts b/src/ui/actions.ts index f12e243e82..5e34471dbe 100644 --- a/src/ui/actions.ts +++ b/src/ui/actions.ts @@ -128,6 +128,8 @@ export const SETTINGS_SET_DETAILED_EVENTS_LOGGING_CHANGED = 'settings/set-detailed-events-logging-changed'; export const SETTINGS_SET_DEBUG_LOGGING_CHANGED = 'settings/set-debug-logging-changed'; +export const SETTINGS_SET_E2E_PDF_PREVIEW_SIZE_LIMIT_CHANGED = + 'settings/set-e2e-pdf-preview-size-limit-changed'; export const SET_HAS_TRAY_MINIMIZE_NOTIFICATION_SHOWN = 'notifications/set-has-tray-minimize-notification-shown'; export const VIDEO_CALL_WINDOW_OPEN_URL = 'video-call-window/open-url'; @@ -266,6 +268,7 @@ export type UiActionTypeToPayloadMap = { [SETTINGS_SET_VERBOSE_OUTLOOK_LOGGING_CHANGED]: boolean; [SETTINGS_SET_DETAILED_EVENTS_LOGGING_CHANGED]: boolean; [SETTINGS_SET_DEBUG_LOGGING_CHANGED]: boolean; + [SETTINGS_SET_E2E_PDF_PREVIEW_SIZE_LIMIT_CHANGED]: number; [SET_HAS_TRAY_MINIMIZE_NOTIFICATION_SHOWN]: boolean; [VIDEO_CALL_WINDOW_OPEN_URL]: { url: string }; [DOWNLOADS_BACK_BUTTON_CLICKED]: string; diff --git a/src/ui/components/SettingsView/GeneralTab.tsx b/src/ui/components/SettingsView/GeneralTab.tsx index bab5933158..73242d820e 100644 --- a/src/ui/components/SettingsView/GeneralTab.tsx +++ b/src/ui/components/SettingsView/GeneralTab.tsx @@ -2,6 +2,7 @@ import { Box, FieldGroup } from '@rocket.chat/fuselage'; import { AvailableBrowsers } from './features/AvailableBrowsers'; import { ClearPermittedScreenCaptureServers } from './features/ClearPermittedScreenCaptureServers'; +import { E2ePdfPreviewSizeLimit } from './features/E2ePdfPreviewSizeLimit'; import { FlashFrame } from './features/FlashFrame'; import { HardwareAcceleration } from './features/HardwareAcceleration'; import { InternalVideoChatWindow } from './features/InternalVideoChatWindow'; @@ -35,6 +36,7 @@ export const GeneralTab = () => ( + {!process.mas && } diff --git a/src/ui/components/SettingsView/features/E2ePdfPreviewSizeLimit.tsx b/src/ui/components/SettingsView/features/E2ePdfPreviewSizeLimit.tsx new file mode 100644 index 0000000000..61ba96d711 --- /dev/null +++ b/src/ui/components/SettingsView/features/E2ePdfPreviewSizeLimit.tsx @@ -0,0 +1,74 @@ +import { + Field, + FieldLabel, + FieldHint, + InputBox, + Box, +} from '@rocket.chat/fuselage'; +import type { ChangeEvent } from 'react'; +import { useCallback, useId } from 'react'; +import { useTranslation } from 'react-i18next'; +import { useDispatch, useSelector } from 'react-redux'; +import type { Dispatch } from 'redux'; + +import type { RootAction } from '../../../../store/actions'; +import type { RootState } from '../../../../store/rootReducer'; +import { SETTINGS_SET_E2E_PDF_PREVIEW_SIZE_LIMIT_CHANGED } from '../../../actions'; + +type E2ePdfPreviewSizeLimitProps = { + className?: string; +}; + +export const E2ePdfPreviewSizeLimit = (props: E2ePdfPreviewSizeLimitProps) => { + const e2ePdfPreviewSizeLimit = useSelector( + ({ e2ePdfPreviewSizeLimit }: RootState) => e2ePdfPreviewSizeLimit + ); + const dispatch = useDispatch>(); + const { t } = useTranslation(); + + const handleChange = useCallback( + (event: ChangeEvent) => { + const value = parseInt(event.currentTarget.value, 10); + if (!isNaN(value) && value > 0) { + dispatch({ + type: SETTINGS_SET_E2E_PDF_PREVIEW_SIZE_LIMIT_CHANGED, + payload: value, + }); + } + }, + [dispatch] + ); + + const id = useId(); + + return ( + + + + + {t('settings.options.e2ePdfPreviewSizeLimit.title')} + + + {t('settings.options.e2ePdfPreviewSizeLimit.description')} + + + + + + + + ); +}; diff --git a/src/ui/reducers/e2ePdfPreviewSizeLimit.ts b/src/ui/reducers/e2ePdfPreviewSizeLimit.ts new file mode 100644 index 0000000000..06751519b4 --- /dev/null +++ b/src/ui/reducers/e2ePdfPreviewSizeLimit.ts @@ -0,0 +1,21 @@ +import type { Reducer } from 'redux'; + +import { APP_SETTINGS_LOADED } from '../../app/actions'; +import { DEFAULT_E2E_PDF_PREVIEW_SIZE_LIMIT_MB } from '../../constants'; +import type { ActionOf } from '../../store/actions'; +import { SETTINGS_SET_E2E_PDF_PREVIEW_SIZE_LIMIT_CHANGED } from '../actions'; + +export const e2ePdfPreviewSizeLimit: Reducer< + number, + | ActionOf + | ActionOf +> = (state = DEFAULT_E2E_PDF_PREVIEW_SIZE_LIMIT_MB, action) => { + switch (action.type) { + case APP_SETTINGS_LOADED: + return action.payload.e2ePdfPreviewSizeLimit ?? state; + case SETTINGS_SET_E2E_PDF_PREVIEW_SIZE_LIMIT_CHANGED: + return action.payload; + default: + return state; + } +};