perf(media): defer image/video decryption until near-viewport (P5-5)
Creates useNearViewport hook (IntersectionObserver, 200px rootMargin, one-shot disconnect after first trigger). ImageContent and VideoContent now gate loadSrc() on nearViewport — when autoPlay is enabled, encrypted media is not decrypted until the element is within 200px of the visible area, reducing initial page load cost on long timelines. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import React, { ReactNode, useCallback, useEffect, useState } from 'react';
|
||||
import React, { ReactNode, useCallback, useEffect, useRef, useState } from 'react';
|
||||
import {
|
||||
Badge,
|
||||
Box,
|
||||
@@ -32,6 +32,7 @@ import {
|
||||
} from '../../../utils/matrix';
|
||||
import { useMediaAuthentication } from '../../../hooks/useMediaAuthentication';
|
||||
import { validBlurHash } from '../../../utils/blurHash';
|
||||
import { useNearViewport } from '../../../hooks/useNearViewport';
|
||||
|
||||
type RenderVideoProps = {
|
||||
title: string;
|
||||
@@ -79,6 +80,9 @@ export const VideoContent = as<'div', VideoContentProps>(
|
||||
const [error, setError] = useState(false);
|
||||
const [blurred, setBlurred] = useState(markedAsSpoiler ?? false);
|
||||
|
||||
const sentinelRef = useRef<HTMLDivElement>(null);
|
||||
const nearViewport = useNearViewport(sentinelRef);
|
||||
|
||||
const [srcState, loadSrc] = useAsyncCallback(
|
||||
useCallback(async () => {
|
||||
const mediaUrl = mxcUrlToHttp(mx, url, useAuthentication);
|
||||
@@ -106,11 +110,12 @@ export const VideoContent = as<'div', VideoContentProps>(
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (autoPlay) loadSrc().catch(() => undefined);
|
||||
}, [autoPlay, loadSrc]);
|
||||
if (autoPlay && nearViewport) loadSrc().catch(() => undefined);
|
||||
}, [autoPlay, nearViewport, loadSrc]);
|
||||
|
||||
return (
|
||||
<Box className={classNames(css.RelativeBase, className)} {...props} ref={ref}>
|
||||
<div ref={sentinelRef} style={{ position: 'absolute', inset: 0, pointerEvents: 'none' }} />
|
||||
{typeof blurHash === 'string' && !load && (
|
||||
<BlurhashCanvas
|
||||
style={{ width: '100%', height: '100%' }}
|
||||
|
||||
Reference in New Issue
Block a user