import React from 'react' import {DimensionValue, Pressable, View} from 'react-native' import {Image} from 'expo-image' 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 {isNative} from '#/platform/detection' import {useLargeAltBadgeEnabled} from '#/state/preferences/large-alt-badge' import {atoms as a, useBreakpoints, useTheme} from '#/alf' import {ArrowsDiagonalOut_Stroke2_Corner0_Rounded as Fullscreen} from '#/components/icons/ArrowsDiagonal' 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, children, }: { aspectRatio: number fullBleed?: boolean children: React.ReactNode }) { const t = useTheme() const {gtMobile} = useBreakpoints() /** * Computed as a % value to apply as `paddingTop`, this basically controls * the height of the image. */ const outerAspectRatio = React.useMemo(() => { const ratio = isNative || !gtMobile ? Math.min(1 / aspectRatio, 16 / 9) // 9:16 bounding box : Math.min(1 / aspectRatio, 1) // 1:1 bounding box return `${ratio * 100}%` }, [aspectRatio, gtMobile]) return ( {children} ) } export function AutoSizedImage({ image, crop = 'constrained', hideBadge, onPress, onLongPress, onPressIn, }: { image: AppBskyEmbedImages.ViewImage crop?: 'none' | 'square' | 'constrained' hideBadge?: boolean onPress?: () => 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 cropDisabled = crop === 'none' const isCropped = rawIsCropped && !cropDisabled const hasAlt = !!image.alt const contents = ( <> {(hasAlt || isCropped) && !hideBadge ? ( {isCropped && ( )} {hasAlt && ( ALT )} ) : null} ) if (cropDisabled) { return ( {contents} ) } else { return ( {contents} ) } }