From f9dae011182afcf7a3727ff626760cedeb321041 Mon Sep 17 00:00:00 2001 From: Eric Bailey Date: Tue, 3 Sep 2024 18:19:19 -0500 Subject: [PATCH] Limit height of images within posts --- src/view/com/post-thread/PostThreadItem.tsx | 6 +- src/view/com/util/images/AutoSizedImage.tsx | 196 ++++++++++++-------- src/view/com/util/post-embeds/index.tsx | 14 +- 3 files changed, 133 insertions(+), 83 deletions(-) diff --git a/src/view/com/post-thread/PostThreadItem.tsx b/src/view/com/post-thread/PostThreadItem.tsx index 3b5ddb1dca..0472087f3f 100644 --- a/src/view/com/post-thread/PostThreadItem.tsx +++ b/src/view/com/post-thread/PostThreadItem.tsx @@ -363,7 +363,11 @@ let PostThreadItemLoaded = ({ ) : undefined} {post.embed && ( - + )} diff --git a/src/view/com/util/images/AutoSizedImage.tsx b/src/view/com/util/images/AutoSizedImage.tsx index 61cb6f69f3..9f19b7b32a 100644 --- a/src/view/com/util/images/AutoSizedImage.tsx +++ b/src/view/com/util/images/AutoSizedImage.tsx @@ -1,106 +1,152 @@ import React from 'react' -import {StyleProp, StyleSheet, Pressable, View, ViewStyle} from 'react-native' +import {Pressable, View} from 'react-native' import {Image} from 'expo-image' -import {clamp} from 'lib/numbers' -import {Dimensions} from 'lib/media/types' -import * as imageSizes from 'lib/media/image-sizes' +import {AppBskyEmbedImages} from '@atproto/api' import {msg} from '@lingui/macro' import {useLingui} from '@lingui/react' -const MIN_ASPECT_RATIO = 0.33 // 1/3 -const MAX_ASPECT_RATIO = 10 // 10/1 +import * as imageSizes from '#/lib/media/image-sizes' +import {Dimensions} from '#/lib/media/types' +import {atoms as a, useTheme} from '#/alf' -interface Props { - alt?: string - uri: string - dimensionsHint?: Dimensions - onPress?: () => void - onLongPress?: () => void - onPressIn?: () => void - style?: StyleProp - children?: React.ReactNode +export function useImageAspectRatio({ + src, + dimensions, +}: { + src: string + dimensions: Dimensions | undefined +}) { + const [aspectRatio, setAspectRatio] = React.useState( + dimensions ? calc(dimensions) : 1, + ) + + 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, + aspectRatio, + } +} + +export function SquareFramedImage({ + aspectRatio, + children, +}: { + aspectRatio: number + children: React.ReactNode +}) { + const t = useTheme() + const outerAspectRatio = React.useMemo(() => { + return Math.min(1 / aspectRatio, 1) + }, [aspectRatio]) + const innerAspectRatio = React.useMemo(() => { + return Math.max(aspectRatio, 0.75) + }, [aspectRatio]) + + return ( + + + + + {children} + + + + + ) } export function AutoSizedImage({ - alt, - uri, - dimensionsHint, + image, + disableCrop, onPress, onLongPress, onPressIn, - style, children = null, -}: Props) { +}: { + image: AppBskyEmbedImages.ViewImage + disableCrop?: boolean + children?: React.ReactNode + onPress?: () => void + onLongPress?: () => void + onPressIn?: () => void +}) { + const t = useTheme() const {_} = useLingui() - const [dim, setDim] = React.useState( - dimensionsHint || imageSizes.get(uri), - ) - const [aspectRatio, setAspectRatio] = React.useState( - dim ? calc(dim) : 1, - ) - React.useEffect(() => { - let aborted = false - if (dim) { - return - } - imageSizes.fetch(uri).then(newDim => { - if (aborted) { - return - } - setDim(newDim) - setAspectRatio(calc(newDim)) - }) - }, [dim, setDim, setAspectRatio, uri]) + const {aspectRatio} = useImageAspectRatio({ + src: image.thumb, + dimensions: image.aspectRatio, + }) - if (onPress || onLongPress || onPressIn) { + const contents = ( + + ) + + if (disableCrop) { return ( - // disable a11y rule because in this case we want the tags on the image (#1640) - // eslint-disable-next-line react-native-a11y/has-valid-accessibility-descriptors - + accessibilityLabel={image.alt} + accessibilityHint={_(msg`Tap to view fully`)} + style={[ + a.w_full, + a.rounded_sm, + a.overflow_hidden, + t.atoms.bg_contrast_25, + {aspectRatio}, + ]}> + {contents} {children} ) + } else { + return ( + + + {contents} + {children} + + + ) } - - return ( - - - {children} - - ) } function calc(dim: Dimensions) { if (dim.width === 0 || dim.height === 0) { return 1 } - return clamp(dim.width / dim.height, MIN_ASPECT_RATIO, MAX_ASPECT_RATIO) + return dim.width / dim.height } - -const styles = StyleSheet.create({ - container: { - overflow: 'hidden', - }, - image: { - width: '100%', - }, -}) diff --git a/src/view/com/util/post-embeds/index.tsx b/src/view/com/util/post-embeds/index.tsx index d9e075e772..d9932b48cf 100644 --- a/src/view/com/util/post-embeds/index.tsx +++ b/src/view/com/util/post-embeds/index.tsx @@ -50,12 +50,14 @@ export function PostEmbeds({ onOpen, style, allowNestedQuotes, + isHighlightedThreadItem, }: { embed?: Embed moderation?: ModerationDecision onOpen?: () => void style?: StyleProp allowNestedQuotes?: boolean + isHighlightedThreadItem?: boolean }) { const {openLightbox} = useLightboxControls() const largeAltBadge = useLargeAltBadgeEnabled() @@ -124,18 +126,16 @@ export function PostEmbeds({ } if (images.length === 1) { - const {alt, thumb, aspectRatio} = images[0] + const image = images[0] return ( _openLightbox(0)} - onPressIn={() => onPressIn(0)} - style={a.rounded_sm}> - {alt === '' ? null : ( + onPressIn={() => onPressIn(0)}> + {image.alt === '' ? null : (