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:
+1
-1
@@ -119,7 +119,7 @@ This document tracks identified bugs, edge cases, and architectural discrepancie
|
||||
| :---------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------- | :---------------------------------------------- | :----- |
|
||||
| State Sync | Fire-and-forget network call to set offline presence during `pagehide` event may not complete reliably, potentially causing UI drift in presence status. | `cinny/src/app/hooks/usePresenceUpdater.ts` | OPEN |
|
||||
| State Sync | Fire-and-forget network call `setPresence().catch(...)` suppresses errors, meaning the app may falsely assume presence update success. | `cinny/src/app/hooks/usePresenceUpdater.ts` | OPEN |
|
||||
| Memory Leak | Decrypted Media Memory Leak (Gallery & Lightbox) due to missing virtualization and blob revocation. | `cinny/src/app/features/room/MediaGallery.tsx` | OPEN |
|
||||
| Memory Leak | Decrypted Media Memory Leak (Gallery & Lightbox) due to missing virtualization and blob revocation. | `cinny/src/app/features/room/MediaGallery.tsx` | PARTIALLY FIXED ⚠️ UNTESTED — Blob revocation was already correct; added `enabled` param to `useDecryptedMediaUrl` and `useNearViewport(300px)` to each `GalleryTile` to gate decryption until near-viewport, reducing burst on pagination. True virtualization (windowing) deferred — requires significant refactor. |
|
||||
| Data Persistence | Scheduled Messages are ephemeral (lost on refresh) due to fragile `localStorage` parsing. | `cinny/src/app/state/scheduledMessages.ts` | FIXED — now uses `atomWithStorage` + `createJSONStorage` (Jotai's built-in persistence with error-safe JSON parsing) |
|
||||
| Memory Leak | Potential memory leak due to uncleaned `handleMouseMove` listener in `usePan`. | `cinny/src/app/hooks/usePan.ts` | FALSE POSITIVE — `usePan` already uses `attachedRef` to track listeners and cleans them up in an unmount `useEffect`. No change needed. |
|
||||
| Asset Optimization | Large unoptimized media asset (213KB) found in `public/res`. | `public/res/Lotus.png` | OPEN |
|
||||
|
||||
@@ -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}
|
||||
|
||||
Reference in New Issue
Block a user