From df9a0a075fcfbd592fd41fc5a87fb598866ff177 Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Sun, 24 Nov 2024 21:52:58 +0000 Subject: [PATCH] Add to defer expensive views --- src/components/DeferReveal.tsx | 23 +++ src/view/com/util/images/AutoSizedImage.tsx | 142 +++++++++--------- src/view/com/util/images/Gallery.tsx | 32 ++-- .../util/post-embeds/ExternalLinkEmbed.tsx | 18 ++- .../util/post-embeds/ExternalPlayerEmbed.tsx | 63 ++++---- src/view/com/util/post-embeds/GifEmbed.tsx | 83 +++++----- 6 files changed, 202 insertions(+), 159 deletions(-) create mode 100644 src/components/DeferReveal.tsx diff --git a/src/components/DeferReveal.tsx b/src/components/DeferReveal.tsx new file mode 100644 index 0000000000..98c677ebda --- /dev/null +++ b/src/components/DeferReveal.tsx @@ -0,0 +1,23 @@ +import {ReactNode, useEffect, useState} from 'react' + +export function DeferReveal({ + children, + defer, + // To verify, toggle `forceReveal` between `true` and `false`. + iVerifiedThereAreNoLayoutJumps: _unused, + forceReveal, +}: { + children: ReactNode + defer: boolean + iVerifiedThereAreNoLayoutJumps: true + forceReveal?: never // DEV-only +}) { + const [isReady, setIsReady] = useState(!defer) + useEffect(() => { + if (!isReady) { + setIsReady(true) + } + }, [isReady]) + const finalIsReady = forceReveal ?? isReady + return finalIsReady ? children : null +} diff --git a/src/view/com/util/images/AutoSizedImage.tsx b/src/view/com/util/images/AutoSizedImage.tsx index 617b9bec4b..a89d85ce98 100644 --- a/src/view/com/util/images/AutoSizedImage.tsx +++ b/src/view/com/util/images/AutoSizedImage.tsx @@ -8,8 +8,10 @@ import {useLingui} from '@lingui/react' import {HandleRef, useHandleRef} from '#/lib/hooks/useHandleRef' import type {Dimensions} from '#/lib/media/types' import {isNative} from '#/platform/detection' +import {isAndroid} from '#/platform/detection' import {useLargeAltBadgeEnabled} from '#/state/preferences/large-alt-badge' import {atoms as a, useBreakpoints, useTheme} from '#/alf' +import {DeferReveal} from '#/components/DeferReveal' import {ArrowsDiagonalOut_Stroke2_Corner0_Rounded as Fullscreen} from '#/components/icons/ArrowsDiagonal' import {MediaInsetBorder} from '#/components/MediaInsetBorder' import {Text} from '#/components/Typography' @@ -107,84 +109,86 @@ export function AutoSizedImage({ const contents = ( - { - fetchedDimsRef.current = { - width: e.source.width, - height: e.source.height, - } - }} - /> - + + { + fetchedDimsRef.current = { + width: e.source.width, + height: e.source.height, + } + }} + /> + - {(hasAlt || isCropped) && !hideBadge ? ( - - {isCropped && ( - + {isCropped && ( + - - - )} - {hasAlt && ( - + + + )} + {hasAlt && ( + - - ALT - - - )} - - ) : null} + largeAlt && [ + { + padding: 5, + }, + ], + ]}> + + ALT + + + )} + + ) : null} + ) diff --git a/src/view/com/util/images/Gallery.tsx b/src/view/com/util/images/Gallery.tsx index cc3eda68d2..c0840d45e1 100644 --- a/src/view/com/util/images/Gallery.tsx +++ b/src/view/com/util/images/Gallery.tsx @@ -7,9 +7,11 @@ import {useLingui} from '@lingui/react' import {HandleRef} from '#/lib/hooks/useHandleRef' import {Dimensions} from '#/lib/media/types' +import {isAndroid} from '#/platform/detection' import {useLargeAltBadgeEnabled} from '#/state/preferences/large-alt-badge' import {PostEmbedViewContext} from '#/view/com/util/post-embeds/types' import {atoms as a, useTheme} from '#/alf' +import {DeferReveal} from '#/components/DeferReveal' import {MediaInsetBorder} from '#/components/MediaInsetBorder' import {Text} from '#/components/Typography' @@ -70,20 +72,22 @@ export function GalleryItem({ accessibilityRole="button" accessibilityLabel={image.alt || _(msg`Image`)} accessibilityHint=""> - { - thumbDimsRef.current[index] = { - width: e.source.width, - height: e.source.height, - } - }} - /> + + { + thumbDimsRef.current[index] = { + width: e.source.width, + height: e.source.height, + } + }} + /> + {hasAlt && !hideBadges ? ( diff --git a/src/view/com/util/post-embeds/ExternalLinkEmbed.tsx b/src/view/com/util/post-embeds/ExternalLinkEmbed.tsx index 64e869c94a..c85d219b63 100644 --- a/src/view/com/util/post-embeds/ExternalLinkEmbed.tsx +++ b/src/view/com/util/post-embeds/ExternalLinkEmbed.tsx @@ -9,12 +9,13 @@ import {parseAltFromGIFDescription} from '#/lib/gif-alt-text' import {shareUrl} from '#/lib/sharing' import {parseEmbedPlayerFromUrl} from '#/lib/strings/embed-player' import {toNiceDomain} from '#/lib/strings/url-helpers' -import {isNative} from '#/platform/detection' +import {isAndroid, isNative} from '#/platform/detection' import {useExternalEmbedsPrefs} from '#/state/preferences' import {ExternalGifEmbed} from '#/view/com/util/post-embeds/ExternalGifEmbed' import {ExternalPlayer} from '#/view/com/util/post-embeds/ExternalPlayerEmbed' import {GifEmbed} from '#/view/com/util/post-embeds/GifEmbed' import {atoms as a, useTheme} from '#/alf' +import {DeferReveal} from '#/components/DeferReveal' import {Divider} from '#/components/Divider' import {Earth_Stroke2_Corner0_Rounded as Globe} from '#/components/icons/Globe' import {Link} from '#/components/Link' @@ -87,13 +88,18 @@ export const ExternalLinkEmbed = ({ : t.atoms.border_contrast_low, ]}> {imageUri && !embedPlayerParams ? ( - + }}> + + + + ) : undefined} {embedPlayerParams?.isGif ? ( diff --git a/src/view/com/util/post-embeds/ExternalPlayerEmbed.tsx b/src/view/com/util/post-embeds/ExternalPlayerEmbed.tsx index 8ac7ee499d..172bc1cfcf 100644 --- a/src/view/com/util/post-embeds/ExternalPlayerEmbed.tsx +++ b/src/view/com/util/post-embeds/ExternalPlayerEmbed.tsx @@ -23,9 +23,10 @@ import {useNavigation} from '@react-navigation/native' import {NavigationProp} from '#/lib/routes/types' import {EmbedPlayerParams, getPlayerAspect} from '#/lib/strings/embed-player' -import {isNative} from '#/platform/detection' +import {isAndroid, isNative} from '#/platform/detection' import {useExternalEmbedsPrefs} from '#/state/preferences' import {atoms as a, useTheme} from '#/alf' +import {DeferReveal} from '#/components/DeferReveal' import {useDialogControl} from '#/components/Dialog' import {EmbedConsentDialog} from '#/components/dialogs/EmbedConsent' import {Fill} from '#/components/Fill' @@ -217,43 +218,45 @@ export function ExternalPlayer({ ref={viewRef} collapsable={false} style={[aspect, a.overflow_hidden]}> - {link.thumb && (!isPlayerActive || isLoading) ? ( - <> - + + {link.thumb && (!isPlayerActive || isLoading) ? ( + <> + + + + ) : ( - - ) : ( - - )} - - + + ) diff --git a/src/view/com/util/post-embeds/GifEmbed.tsx b/src/view/com/util/post-embeds/GifEmbed.tsx index 4dbc7d588f..6b0366bc20 100644 --- a/src/view/com/util/post-embeds/GifEmbed.tsx +++ b/src/view/com/util/post-embeds/GifEmbed.tsx @@ -12,10 +12,11 @@ import {useLingui} from '@lingui/react' import {HITSLOP_20} from '#/lib/constants' import {EmbedPlayerParams} from '#/lib/strings/embed-player' -import {isWeb} from '#/platform/detection' +import {isAndroid, isWeb} from '#/platform/detection' import {useAutoplayDisabled} from '#/state/preferences' import {useLargeAltBadgeEnabled} from '#/state/preferences/large-alt-badge' import {atoms as a, useTheme} from '#/alf' +import {DeferReveal} from '#/components/DeferReveal' import {Fill} from '#/components/Fill' import {Loader} from '#/components/Loader' import * as Prompt from '#/components/Prompt' @@ -121,46 +122,48 @@ export function GifEmbed({ {aspectRatio: params.dimensions!.width / params.dimensions!.height}, style, ]}> - - - - {!playerState.isPlaying && ( - + + - )} - {!hideAlt && isPreferredAltText && } - + + {!playerState.isPlaying && ( + + )} + {!hideAlt && isPreferredAltText && } + + ) }