diff options
Diffstat (limited to 'src/view/com/util')
-rw-r--r-- | src/view/com/util/images/AutoSizedImage.tsx | 67 | ||||
-rw-r--r-- | src/view/com/util/images/Gallery.tsx | 16 | ||||
-rw-r--r-- | src/view/com/util/images/ImageLayoutGrid.tsx | 13 | ||||
-rw-r--r-- | src/view/com/util/post-embeds/index.tsx | 10 |
4 files changed, 66 insertions, 40 deletions
diff --git a/src/view/com/util/images/AutoSizedImage.tsx b/src/view/com/util/images/AutoSizedImage.tsx index 21f6c529e..c26df8335 100644 --- a/src/view/com/util/images/AutoSizedImage.tsx +++ b/src/view/com/util/images/AutoSizedImage.tsx @@ -6,8 +6,7 @@ import {AppBskyEmbedImages} from '@atproto/api' import {msg} from '@lingui/macro' import {useLingui} from '@lingui/react' -import {useImageDimensions} from '#/lib/media/image-sizes' -import {Dimensions} from '#/lib/media/types' +import type {Dimensions} from '#/lib/media/types' import {isNative} from '#/platform/detection' import {useLargeAltBadgeEnabled} from '#/state/preferences/large-alt-badge' import {atoms as a, useBreakpoints, useTheme} from '#/alf' @@ -15,30 +14,6 @@ import {ArrowsDiagonalOut_Stroke2_Corner0_Rounded as Fullscreen} from '#/compone import {MediaInsetBorder} from '#/components/MediaInsetBorder' import {Text} from '#/components/Typography' -function useImageAspectRatio({ - src, - knownDimensions, -}: { - src: string - knownDimensions: Dimensions | null -}) { - const [raw] = useImageDimensions({src, knownDimensions}) - let constrained: number | undefined - let max: number | undefined - let isCropped: boolean | undefined - if (raw !== undefined) { - const ratio = 1 / 2 // max of 1:2 ratio in feeds - constrained = Math.max(raw, ratio) - max = Math.max(raw, 0.25) // max of 1:4 in thread - isCropped = raw < constrained - } - return { - constrained, - max, - isCropped, - } -} - export function ConstrainedImage({ aspectRatio, fullBleed, @@ -93,23 +68,38 @@ export function AutoSizedImage({ image: AppBskyEmbedImages.ViewImage crop?: 'none' | 'square' | 'constrained' hideBadge?: boolean - onPress?: (containerRef: AnimatedRef<React.Component<{}, {}, any>>) => void + onPress?: ( + containerRef: AnimatedRef<React.Component<{}, {}, any>>, + fetchedDims: Dimensions | null, + ) => void onLongPress?: () => void onPressIn?: () => void }) { const t = useTheme() const {_} = useLingui() const largeAlt = useLargeAltBadgeEnabled() - const { - constrained, - max, - isCropped: rawIsCropped, - } = useImageAspectRatio({ - src: image.thumb, - knownDimensions: image.aspectRatio ?? null, - }) const containerRef = useAnimatedRef() + const [fetchedDims, setFetchedDims] = React.useState<Dimensions | null>(null) + const dims = fetchedDims ?? image.aspectRatio + let aspectRatio: number | undefined + if (dims) { + aspectRatio = dims.width / dims.height + if (Number.isNaN(aspectRatio)) { + aspectRatio = undefined + } + } + + let constrained: number | undefined + let max: number | undefined + let rawIsCropped: boolean | undefined + if (aspectRatio !== undefined) { + const ratio = 1 / 2 // max of 1:2 ratio in feeds + constrained = Math.max(aspectRatio, ratio) + max = Math.max(aspectRatio, 0.25) // max of 1:4 in thread + rawIsCropped = aspectRatio < constrained + } + const cropDisabled = crop === 'none' const isCropped = rawIsCropped && !cropDisabled const hasAlt = !!image.alt @@ -123,6 +113,9 @@ export function AutoSizedImage({ accessibilityIgnoresInvertColors accessibilityLabel={image.alt} accessibilityHint="" + onLoad={e => { + setFetchedDims({width: e.source.width, height: e.source.height}) + }} /> <MediaInsetBorder /> @@ -194,7 +187,7 @@ export function AutoSizedImage({ if (cropDisabled) { return ( <Pressable - onPress={() => onPress?.(containerRef)} + onPress={() => onPress?.(containerRef, fetchedDims)} onLongPress={onLongPress} onPressIn={onPressIn} // alt here is what screen readers actually use @@ -216,7 +209,7 @@ export function AutoSizedImage({ fullBleed={crop === 'square'} aspectRatio={constrained ?? 1}> <Pressable - onPress={() => onPress?.(containerRef)} + onPress={() => onPress?.(containerRef, fetchedDims)} onLongPress={onLongPress} onPressIn={onPressIn} // alt here is what screen readers actually use diff --git a/src/view/com/util/images/Gallery.tsx b/src/view/com/util/images/Gallery.tsx index 0c691ec9a..9d0817bd2 100644 --- a/src/view/com/util/images/Gallery.tsx +++ b/src/view/com/util/images/Gallery.tsx @@ -6,6 +6,7 @@ import {AppBskyEmbedImages} from '@atproto/api' import {msg} from '@lingui/macro' import {useLingui} from '@lingui/react' +import {Dimensions} from '#/lib/media/types' import {useLargeAltBadgeEnabled} from '#/state/preferences/large-alt-badge' import {PostEmbedViewContext} from '#/view/com/util/post-embeds/types' import {atoms as a, useTheme} from '#/alf' @@ -20,6 +21,7 @@ interface Props { onPress?: ( index: number, containerRefs: AnimatedRef<React.Component<{}, {}, any>>[], + fetchedDims: (Dimensions | null)[], ) => void onLongPress?: EventFunction onPressIn?: EventFunction @@ -27,6 +29,7 @@ interface Props { viewContext?: PostEmbedViewContext insetBorderStyle?: StyleProp<ViewStyle> containerRefs: AnimatedRef<React.Component<{}, {}, any>>[] + thumbDimsRef: React.MutableRefObject<(Dimensions | null)[]> } export function GalleryItem({ @@ -39,6 +42,7 @@ export function GalleryItem({ viewContext, insetBorderStyle, containerRefs, + thumbDimsRef, }: Props) { const t = useTheme() const {_} = useLingui() @@ -53,7 +57,11 @@ export function GalleryItem({ ref={containerRefs[index]} collapsable={false}> <Pressable - onPress={onPress ? () => onPress(index, containerRefs) : undefined} + onPress={ + onPress + ? () => onPress(index, containerRefs, thumbDimsRef.current.slice()) + : undefined + } onPressIn={onPressIn ? () => onPressIn(index) : undefined} onLongPress={onLongPress ? () => onLongPress(index) : undefined} style={[ @@ -72,6 +80,12 @@ export function GalleryItem({ accessibilityLabel={image.alt} accessibilityHint="" accessibilityIgnoresInvertColors + onLoad={e => { + thumbDimsRef.current[index] = { + width: e.source.width, + height: e.source.height, + } + }} /> <MediaInsetBorder style={insetBorderStyle} /> </Pressable> diff --git a/src/view/com/util/images/ImageLayoutGrid.tsx b/src/view/com/util/images/ImageLayoutGrid.tsx index b9b966302..dcc330dac 100644 --- a/src/view/com/util/images/ImageLayoutGrid.tsx +++ b/src/view/com/util/images/ImageLayoutGrid.tsx @@ -5,6 +5,7 @@ import {AppBskyEmbedImages} from '@atproto/api' import {PostEmbedViewContext} from '#/view/com/util/post-embeds/types' import {atoms as a, useBreakpoints} from '#/alf' +import {Dimensions} from '../../lightbox/ImageViewing/@types' import {GalleryItem} from './Gallery' interface ImageLayoutGridProps { @@ -12,6 +13,7 @@ interface ImageLayoutGridProps { onPress?: ( index: number, containerRefs: AnimatedRef<React.Component<{}, {}, any>>[], + fetchedDims: (Dimensions | null)[], ) => void onLongPress?: (index: number) => void onPressIn?: (index: number) => void @@ -42,6 +44,7 @@ interface ImageLayoutGridInnerProps { onPress?: ( index: number, containerRefs: AnimatedRef<React.Component<{}, {}, any>>[], + fetchedDims: (Dimensions | null)[], ) => void onLongPress?: (index: number) => void onPressIn?: (index: number) => void @@ -57,6 +60,7 @@ function ImageLayoutGridInner(props: ImageLayoutGridInnerProps) { const containerRef2 = useAnimatedRef() const containerRef3 = useAnimatedRef() const containerRef4 = useAnimatedRef() + const thumbDimsRef = React.useRef<(Dimensions | null)[]>([]) switch (count) { case 2: { @@ -69,6 +73,7 @@ function ImageLayoutGridInner(props: ImageLayoutGridInnerProps) { index={0} insetBorderStyle={noCorners(['topRight', 'bottomRight'])} containerRefs={containerRefs} + thumbDimsRef={thumbDimsRef} /> </View> <View style={[a.flex_1, {aspectRatio: 1}]}> @@ -77,6 +82,7 @@ function ImageLayoutGridInner(props: ImageLayoutGridInnerProps) { index={1} insetBorderStyle={noCorners(['topLeft', 'bottomLeft'])} containerRefs={containerRefs} + thumbDimsRef={thumbDimsRef} /> </View> </View> @@ -93,6 +99,7 @@ function ImageLayoutGridInner(props: ImageLayoutGridInnerProps) { index={0} insetBorderStyle={noCorners(['topRight', 'bottomRight'])} containerRefs={containerRefs} + thumbDimsRef={thumbDimsRef} /> </View> <View style={[a.flex_1, {aspectRatio: 1}, gap]}> @@ -106,6 +113,7 @@ function ImageLayoutGridInner(props: ImageLayoutGridInnerProps) { 'bottomRight', ])} containerRefs={containerRefs} + thumbDimsRef={thumbDimsRef} /> </View> <View style={[a.flex_1]}> @@ -118,6 +126,7 @@ function ImageLayoutGridInner(props: ImageLayoutGridInnerProps) { 'topRight', ])} containerRefs={containerRefs} + thumbDimsRef={thumbDimsRef} /> </View> </View> @@ -145,6 +154,7 @@ function ImageLayoutGridInner(props: ImageLayoutGridInnerProps) { 'bottomRight', ])} containerRefs={containerRefs} + thumbDimsRef={thumbDimsRef} /> </View> <View style={[a.flex_1, {aspectRatio: 1.5}]}> @@ -157,6 +167,7 @@ function ImageLayoutGridInner(props: ImageLayoutGridInnerProps) { 'bottomRight', ])} containerRefs={containerRefs} + thumbDimsRef={thumbDimsRef} /> </View> </View> @@ -171,6 +182,7 @@ function ImageLayoutGridInner(props: ImageLayoutGridInnerProps) { 'bottomRight', ])} containerRefs={containerRefs} + thumbDimsRef={thumbDimsRef} /> </View> <View style={[a.flex_1, {aspectRatio: 1.5}]}> @@ -183,6 +195,7 @@ function ImageLayoutGridInner(props: ImageLayoutGridInnerProps) { 'topRight', ])} containerRefs={containerRefs} + thumbDimsRef={thumbDimsRef} /> </View> </View> diff --git a/src/view/com/util/post-embeds/index.tsx b/src/view/com/util/post-embeds/index.tsx index ab2471b33..1351a2cbc 100644 --- a/src/view/com/util/post-embeds/index.tsx +++ b/src/view/com/util/post-embeds/index.tsx @@ -35,6 +35,7 @@ import {atoms as a, useTheme} from '#/alf' import * as ListCard from '#/components/ListCard' import {Embed as StarterPackCard} from '#/components/StarterPack/StarterPackCard' import {ContentHider} from '../../../../components/moderation/ContentHider' +import {Dimensions} from '../../lightbox/ImageViewing/@types' import {AutoSizedImage} from '../images/AutoSizedImage' import {ImageLayoutGrid} from '../images/ImageLayoutGrid' import {ExternalLinkEmbed} from './ExternalLinkEmbed' @@ -148,11 +149,13 @@ export function PostEmbeds({ const _openLightbox = ( index: number, thumbRects: (MeasuredDimensions | null)[], + fetchedDims: (Dimensions | null)[], ) => { openLightbox({ images: items.map((item, i) => ({ ...item, thumbRect: thumbRects[i] ?? null, + thumbDimensions: fetchedDims[i] ?? null, type: 'image', })), index, @@ -161,11 +164,12 @@ export function PostEmbeds({ const onPress = ( index: number, refs: AnimatedRef<React.Component<{}, {}, any>>[], + fetchedDims: (Dimensions | null)[], ) => { runOnUI(() => { 'worklet' const rects = refs.map(ref => (ref ? measure(ref) : null)) - runOnJS(_openLightbox)(index, rects) + runOnJS(_openLightbox)(index, rects, fetchedDims) })() } const onPressIn = (_: number) => { @@ -189,7 +193,9 @@ export function PostEmbeds({ : 'constrained' } image={image} - onPress={containerRef => onPress(0, [containerRef])} + onPress={(containerRef, dims) => + onPress(0, [containerRef], [dims]) + } onPressIn={() => onPressIn(0)} hideBadge={ viewContext === PostEmbedViewContext.FeedEmbedRecordWithMedia |