diff --git a/lib/types/media-types.js b/lib/types/media-types.js --- a/lib/types/media-types.js +++ b/lib/types/media-types.js @@ -470,6 +470,13 @@ | EncryptFileMediaMissionStep | GetOrientationMediaMissionStep | GenerateThumbhashMediaMissionStep + | { + +step: 'preload_resource', + +success: boolean, + +exceptionMessage: ?string, + +time: number, // ms + +uri: string, + } | { +step: 'preload_image', +success: boolean, diff --git a/web/input/input-state-container.react.js b/web/input/input-state-container.react.js --- a/web/input/input-state-container.react.js +++ b/web/input/input-state-container.react.js @@ -105,7 +105,11 @@ } from './input-state.js'; import { encryptFile } from '../media/encryption-utils.js'; import { generateThumbHash } from '../media/image-utils.js'; -import { validateFile, preloadImage } from '../media/media-utils.js'; +import { + preloadMediaResource, + validateFile, + preloadImage, +} from '../media/media-utils.js'; import InvalidUploadModal from '../modals/chat/invalid-upload.react.js'; import { updateNavInfoActionType } from '../redux/action-types.js'; import { useSelector } from '../redux/redux-utils.js'; @@ -976,8 +980,10 @@ }; }); - // we cannot preload encrypted media this way, we don't have cache - if (!encryptionKey) { + if (encryptionKey) { + const { steps: preloadSteps } = await preloadMediaResource(result.uri); + steps.push(...preloadSteps); + } else { const { steps: preloadSteps } = await preloadImage(result.uri); steps.push(...preloadSteps); } diff --git a/web/media/media-utils.js b/web/media/media-utils.js --- a/web/media/media-utils.js +++ b/web/media/media-utils.js @@ -50,6 +50,44 @@ return { steps: [step], result: image }; } +/** + * Preloads a media resource (image or video) from a URI. This sends a HTTP GET + * request to the URI to let the browser download it and cache it, + * so further requests will be loaded from the cache. + * + * For raw images, use {@link preloadImage} instead. + * + * @param uri The URI of the media resource. + * @returns Steps and the result of the preload. The preload is successful + * if the HTTP response is OK (20x). + */ +async function preloadMediaResource(uri: string): Promise<{ + steps: $ReadOnlyArray, + result: { +success: boolean }, +}> { + const start = Date.now(); + let success, exceptionMessage; + try { + const response = await fetch(uri); + // we need to read the blob to make sure the browser caches it + await response.blob(); + success = response.ok; + } catch (e) { + success = false; + exceptionMessage = getMessageForException(e); + } + + const step = { + step: 'preload_resource', + success, + exceptionMessage, + time: Date.now() - start, + uri, + }; + + return { steps: [step], result: { success } }; +} + type ProcessFileSuccess = { success: true, uri: string, @@ -230,4 +268,4 @@ return placeholder; } -export { preloadImage, validateFile, usePlaceholder }; +export { preloadImage, preloadMediaResource, validateFile, usePlaceholder };