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 * as imageSizes from '#/lib/media/image-sizes' import {Dimensions} from '#/lib/media/types' import {useLargeAltBadgeEnabled} from '#/state/preferences/large-alt-badge' import {atoms as a, useTheme} from '#/alf' import {Crop_Stroke2_Corner0_Rounded as Crop} from '#/components/icons/Crop' import {Text} from '#/components/Typography' export function useImageAspectRatio({ src, dimensions, }: { src: string dimensions: Dimensions | undefined }) { const [raw, setAspectRatio] = React.useState( dimensions ? calc(dimensions) : 1, ) const {isCropped, constrained, max} = React.useMemo(() => { const a34 = 0.75 // max of 3:4 ratio in feeds const constrained = Math.max(raw, a34) const max = Math.max(raw, 0.25) // max of 1:4 in thread const isCropped = raw < constrained return { isCropped, constrained, max, } }, [raw]) React.useEffect(() => { let aborted = false if (dimensions) return imageSizes.fetch(src).then(newDim => { if (aborted) return setAspectRatio(calc(newDim)) }) return () => { aborted = true } }, [dimensions, setAspectRatio, src]) return { dimensions, raw, constrained, max, isCropped, } } export function ConstrainedImage({ aspectRatio, fullBleed, children, }: { aspectRatio: number fullBleed?: boolean children: React.ReactNode }) { const t = useTheme() /** * Computed as a % value to apply as `paddingTop` */ const outerAspectRatio = React.useMemo(() => { // capped to square or shorter const ratio = Math.min(1 / aspectRatio, 1) return `${ratio * 100}%` }, [aspectRatio]) 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, dimensions: image.aspectRatio, }) 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} ) } } function calc(dim: Dimensions) { if (dim.width === 0 || dim.height === 0) { return 1 } return dim.width / dim.height }