perf(gallery): gate tile decryption until near-viewport

Adds enabled=true param to useDecryptedMediaUrl in MediaGallery.tsx.
GalleryTile now uses useNearViewport(300px) — decryption is deferred
until the tile approaches the viewport, preventing burst of 100
concurrent decrypt/fetch calls when a pagination batch loads.

Blob revocation was already correct (no actual leak); this fixes the
load-burst performance issue. Full windowing virtualization deferred.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-06-18 18:16:15 -04:00
parent 1b4c6cab6d
commit 8ac42cdbad
2 changed files with 9 additions and 3 deletions
+8 -2
View File
@@ -1,4 +1,5 @@
import React, { useCallback, useEffect, useRef, useState } from 'react';
import { useNearViewport } from '../../hooks/useNearViewport';
import {
Box,
Button,
@@ -45,11 +46,13 @@ function useDecryptedMediaUrl(
encInfo: IEncryptedFile | undefined,
useAuthentication: boolean,
mimeType?: string,
enabled = true,
): DecryptState {
const [state, setState] = useState<DecryptState>({ status: 'loading' });
const prevBlobUrl = useRef<string | null>(null);
useEffect(() => {
if (!enabled) return undefined;
if (!mxcUrl) {
setState({ status: 'error' });
return;
@@ -84,7 +87,7 @@ function useDecryptedMediaUrl(
return () => {
cancelled = true;
};
}, [mx, mxcUrl, encInfo, useAuthentication, mimeType]);
}, [mx, mxcUrl, encInfo, useAuthentication, mimeType, enabled]);
useEffect(
() => () => {
@@ -367,12 +370,15 @@ function GalleryTile({
onClick: () => void;
}) {
const mx = useMatrixClient();
const media = useDecryptedMediaUrl(mx, mxcUrl, encInfo, useAuthentication, mimeType);
const tileRef = useRef<HTMLButtonElement>(null);
const nearViewport = useNearViewport(tileRef, 300);
const media = useDecryptedMediaUrl(mx, mxcUrl, encInfo, useAuthentication, mimeType, nearViewport);
const [hovered, setHovered] = useState(false);
const relDate = formatRelativeDate(ts);
return (
<button
ref={tileRef}
type="button"
aria-label={body || (isVideo ? 'Video' : 'Image')}
onClick={onClick}