Wire image context menu into grid and gallery embeds
Thread onPreviewPress(index) through ImageLayoutGrid / GalleryItem and the Gallery scroll item so multi-image embeds get the same iOS peek behaviour as single-image embeds. Each grid cell wraps its Pressable in ImageContextMenu with the image's true aspect ratio, so the preview lifts to the full uncropped image even when the cell is cropped to square. https://claude.ai/code/session_015REmux3R9uuEMMJUHxTyQT
This commit is contained in:
@@ -1,4 +1,5 @@
|
|||||||
import {type ReactNode} from 'react'
|
import {type ReactNode} from 'react'
|
||||||
|
import {type StyleProp, type ViewStyle} from 'react-native'
|
||||||
import {msg} from '@lingui/core/macro'
|
import {msg} from '@lingui/core/macro'
|
||||||
import {useLingui} from '@lingui/react'
|
import {useLingui} from '@lingui/react'
|
||||||
|
|
||||||
@@ -22,6 +23,7 @@ export function ImageContextMenu({
|
|||||||
aspectRatio,
|
aspectRatio,
|
||||||
borderRadius,
|
borderRadius,
|
||||||
onPreviewPress,
|
onPreviewPress,
|
||||||
|
style,
|
||||||
children,
|
children,
|
||||||
}: {
|
}: {
|
||||||
fullsizeUri: string
|
fullsizeUri: string
|
||||||
@@ -29,6 +31,7 @@ export function ImageContextMenu({
|
|||||||
aspectRatio: number | undefined
|
aspectRatio: number | undefined
|
||||||
borderRadius?: number
|
borderRadius?: number
|
||||||
onPreviewPress?: () => void
|
onPreviewPress?: () => void
|
||||||
|
style?: StyleProp<ViewStyle>
|
||||||
children: ReactNode
|
children: ReactNode
|
||||||
}) {
|
}) {
|
||||||
const {_} = useLingui()
|
const {_} = useLingui()
|
||||||
@@ -46,7 +49,7 @@ export function ImageContextMenu({
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ContextMenu.Root>
|
<ContextMenu.Root style={style}>
|
||||||
<ContextMenu.Trigger
|
<ContextMenu.Trigger
|
||||||
preview={{
|
preview={{
|
||||||
type: 'image',
|
type: 'image',
|
||||||
|
|||||||
@@ -57,6 +57,18 @@ export function ImageEmbed({
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
const onPreviewPress = (index: number) =>
|
||||||
|
openLightbox({
|
||||||
|
images: items.map(item => ({
|
||||||
|
...item,
|
||||||
|
thumbRect: null,
|
||||||
|
thumbRef: null,
|
||||||
|
thumbDimensions: null,
|
||||||
|
thumbBorderRadius: tokens.borderRadius.md,
|
||||||
|
type: 'image',
|
||||||
|
})),
|
||||||
|
index,
|
||||||
|
})
|
||||||
|
|
||||||
if (images.length === 1) {
|
if (images.length === 1) {
|
||||||
const image = images[0]
|
const image = images[0]
|
||||||
@@ -70,19 +82,7 @@ export function ImageEmbed({
|
|||||||
fullsizeUri={image.fullsize}
|
fullsizeUri={image.fullsize}
|
||||||
aspectRatio={aspect}
|
aspectRatio={aspect}
|
||||||
borderRadius={tokens.borderRadius.md}
|
borderRadius={tokens.borderRadius.md}
|
||||||
onPreviewPress={() =>
|
onPreviewPress={() => onPreviewPress(0)}>
|
||||||
openLightbox({
|
|
||||||
images: items.map(item => ({
|
|
||||||
...item,
|
|
||||||
thumbRect: null,
|
|
||||||
thumbRef: null,
|
|
||||||
thumbDimensions: null,
|
|
||||||
thumbBorderRadius: tokens.borderRadius.md,
|
|
||||||
type: 'image',
|
|
||||||
})),
|
|
||||||
index: 0,
|
|
||||||
})
|
|
||||||
}>
|
|
||||||
<AutoSizedImage
|
<AutoSizedImage
|
||||||
crop={
|
crop={
|
||||||
rest.viewContext === PostEmbedViewContext.ThreadHighlighted
|
rest.viewContext === PostEmbedViewContext.ThreadHighlighted
|
||||||
@@ -114,6 +114,7 @@ export function ImageEmbed({
|
|||||||
images={images}
|
images={images}
|
||||||
onPress={onPress}
|
onPress={onPress}
|
||||||
onPressIn={onPressIn}
|
onPressIn={onPressIn}
|
||||||
|
onPreviewPress={onPreviewPress}
|
||||||
viewContext={rest.viewContext}
|
viewContext={rest.viewContext}
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
@@ -126,6 +127,7 @@ export function ImageEmbed({
|
|||||||
images={images}
|
images={images}
|
||||||
onPress={onPress}
|
onPress={onPress}
|
||||||
onPressIn={onPressIn}
|
onPressIn={onPressIn}
|
||||||
|
onPreviewPress={onPreviewPress}
|
||||||
viewContext={rest.viewContext}
|
viewContext={rest.viewContext}
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ import {mergeRefs} from '#/lib/merge-refs'
|
|||||||
import {useA11y} from '#/state/a11y'
|
import {useA11y} from '#/state/a11y'
|
||||||
import {useLargeAltBadgeEnabled} from '#/state/preferences/large-alt-badge'
|
import {useLargeAltBadgeEnabled} from '#/state/preferences/large-alt-badge'
|
||||||
import {BlockDrawerGesture} from '#/view/shell/BlockDrawerGesture'
|
import {BlockDrawerGesture} from '#/view/shell/BlockDrawerGesture'
|
||||||
import {atoms as a, useBreakpoints, useTheme, web} from '#/alf'
|
import {atoms as a, tokens, useBreakpoints, useTheme, web} from '#/alf'
|
||||||
import {ArrowsDiagonalOut_Stroke2_Corner0_Rounded as Fullscreen} from '#/components/icons/ArrowsDiagonal'
|
import {ArrowsDiagonalOut_Stroke2_Corner0_Rounded as Fullscreen} from '#/components/icons/ArrowsDiagonal'
|
||||||
import {AutoSizedImage} from '#/components/images/AutoSizedImage'
|
import {AutoSizedImage} from '#/components/images/AutoSizedImage'
|
||||||
import {
|
import {
|
||||||
@@ -36,6 +36,7 @@ import {useKeyboardHandlers} from '#/components/images/Gallery/useKeyboardHandle
|
|||||||
import {usePointerHandlers} from '#/components/images/Gallery/usePointerHandlers'
|
import {usePointerHandlers} from '#/components/images/Gallery/usePointerHandlers'
|
||||||
import {getAspectRatio} from '#/components/images/Gallery/utils'
|
import {getAspectRatio} from '#/components/images/Gallery/utils'
|
||||||
import {MediaInsetBorder} from '#/components/MediaInsetBorder'
|
import {MediaInsetBorder} from '#/components/MediaInsetBorder'
|
||||||
|
import {ImageContextMenu} from '#/components/Post/Embed/ImageContextMenu'
|
||||||
import {PostEmbedViewContext} from '#/components/Post/Embed/types'
|
import {PostEmbedViewContext} from '#/components/Post/Embed/types'
|
||||||
import {Text} from '#/components/Typography'
|
import {Text} from '#/components/Typography'
|
||||||
import {useAnalytics} from '#/analytics'
|
import {useAnalytics} from '#/analytics'
|
||||||
@@ -52,6 +53,7 @@ interface GalleryProps {
|
|||||||
fetchedDims: (Dimensions | null)[],
|
fetchedDims: (Dimensions | null)[],
|
||||||
) => void
|
) => void
|
||||||
onPressIn?: (index: number) => void
|
onPressIn?: (index: number) => void
|
||||||
|
onPreviewPress?: (index: number) => void
|
||||||
viewContext?: PostEmbedViewContext
|
viewContext?: PostEmbedViewContext
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -95,6 +97,7 @@ export function Gallery({
|
|||||||
images,
|
images,
|
||||||
onPress,
|
onPress,
|
||||||
onPressIn,
|
onPressIn,
|
||||||
|
onPreviewPress,
|
||||||
viewContext,
|
viewContext,
|
||||||
}: GalleryProps) {
|
}: GalleryProps) {
|
||||||
const {t: l} = useLingui()
|
const {t: l} = useLingui()
|
||||||
@@ -306,6 +309,9 @@ export function Gallery({
|
|||||||
: undefined
|
: undefined
|
||||||
}
|
}
|
||||||
onPressIn={onPressIn ? () => onPressIn(index) : undefined}
|
onPressIn={onPressIn ? () => onPressIn(index) : undefined}
|
||||||
|
onPreviewPress={
|
||||||
|
onPreviewPress ? () => onPreviewPress(index) : undefined
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}}
|
}}
|
||||||
@@ -378,6 +384,7 @@ function GalleryImage({
|
|||||||
onThumbDims,
|
onThumbDims,
|
||||||
onPress,
|
onPress,
|
||||||
onPressIn,
|
onPressIn,
|
||||||
|
onPreviewPress,
|
||||||
}: {
|
}: {
|
||||||
contentHeight: number
|
contentHeight: number
|
||||||
image: AppBskyEmbedImages.ViewImage
|
image: AppBskyEmbedImages.ViewImage
|
||||||
@@ -391,6 +398,7 @@ function GalleryImage({
|
|||||||
onThumbDims: (index: number, dims: Dimensions) => void
|
onThumbDims: (index: number, dims: Dimensions) => void
|
||||||
onPress?: () => void
|
onPress?: () => void
|
||||||
onPressIn?: () => void
|
onPressIn?: () => void
|
||||||
|
onPreviewPress?: () => void
|
||||||
}) {
|
}) {
|
||||||
const t = useTheme()
|
const t = useTheme()
|
||||||
const {t: l} = useLingui()
|
const {t: l} = useLingui()
|
||||||
@@ -416,124 +424,130 @@ function GalleryImage({
|
|||||||
collapsable={false}
|
collapsable={false}
|
||||||
aria-roledescription={l`slide`}
|
aria-roledescription={l`slide`}
|
||||||
aria-label={image.alt || l`Image ${index + 1} of ${imageCount}`}>
|
aria-label={image.alt || l`Image ${index + 1} of ${imageCount}`}>
|
||||||
<Pressable
|
<ImageContextMenu
|
||||||
ref={itemRef}
|
fullsizeUri={image.fullsize}
|
||||||
tabIndex={index === 0 ? 0 : -1}
|
aspectRatio={aspectRatio}
|
||||||
onPress={onPress}
|
borderRadius={tokens.borderRadius.md}
|
||||||
onPressIn={onPressIn}
|
onPreviewPress={onPreviewPress}>
|
||||||
onFocus={() => setFocused(true)}
|
<Pressable
|
||||||
onBlur={() => setFocused(false)}
|
ref={itemRef}
|
||||||
accessibilityRole="button"
|
tabIndex={index === 0 ? 0 : -1}
|
||||||
accessibilityLabel={image.alt || l`Image ${index + 1}`}
|
onPress={onPress}
|
||||||
accessibilityHint={l`Opens full image`}
|
onPressIn={onPressIn}
|
||||||
android_ripple={{
|
onFocus={() => setFocused(true)}
|
||||||
color: utils.alpha(t.atoms.bg.backgroundColor, 0.2),
|
onBlur={() => setFocused(false)}
|
||||||
foreground: true,
|
accessibilityRole="button"
|
||||||
}}
|
accessibilityLabel={image.alt || l`Image ${index + 1}`}
|
||||||
style={({pressed}) => [
|
accessibilityHint={l`Opens full image`}
|
||||||
a.rounded_md,
|
android_ripple={{
|
||||||
a.overflow_hidden,
|
color: utils.alpha(t.atoms.bg.backgroundColor, 0.2),
|
||||||
t.atoms.bg_contrast_25,
|
foreground: true,
|
||||||
web([
|
|
||||||
{
|
|
||||||
cursor: 'inherit',
|
|
||||||
outline: 0,
|
|
||||||
border: 0,
|
|
||||||
},
|
|
||||||
a.transition_transform,
|
|
||||||
{transitionDuration: '200ms'},
|
|
||||||
pressed && {transform: [{scale: 0.99}]},
|
|
||||||
]),
|
|
||||||
]}>
|
|
||||||
<Image
|
|
||||||
source={{uri: image.thumb}}
|
|
||||||
contentFit="cover"
|
|
||||||
accessible={true}
|
|
||||||
accessibilityLabel={image.alt}
|
|
||||||
accessibilityHint=""
|
|
||||||
accessibilityIgnoresInvertColors
|
|
||||||
loading={index === 0 ? 'eager' : 'lazy'}
|
|
||||||
style={[dims]}
|
|
||||||
onLoad={e => {
|
|
||||||
const ar = getAspectRatio(e.source)
|
|
||||||
if (ar && ar !== aspectRatio) {
|
|
||||||
setAspectRatio(ar)
|
|
||||||
}
|
|
||||||
onThumbDims(index, {
|
|
||||||
width: e.source.width,
|
|
||||||
height: e.source.height,
|
|
||||||
})
|
|
||||||
}}
|
}}
|
||||||
/>
|
style={({pressed}) => [
|
||||||
|
a.rounded_md,
|
||||||
{(hasAlt || isCropped) && !hideBadges ? (
|
a.overflow_hidden,
|
||||||
<View
|
t.atoms.bg_contrast_25,
|
||||||
accessible={false}
|
web([
|
||||||
style={[
|
|
||||||
a.absolute,
|
|
||||||
a.flex_row,
|
|
||||||
{
|
{
|
||||||
bottom: a.p_xs.padding,
|
cursor: 'inherit',
|
||||||
right: a.p_xs.padding,
|
outline: 0,
|
||||||
gap: 3,
|
border: 0,
|
||||||
},
|
},
|
||||||
largeAltBadge && {
|
a.transition_transform,
|
||||||
gap: 4,
|
{transitionDuration: '200ms'},
|
||||||
},
|
pressed && {transform: [{scale: 0.99}]},
|
||||||
]}>
|
]),
|
||||||
{isCropped && (
|
]}>
|
||||||
<View
|
<Image
|
||||||
style={[
|
source={{uri: image.thumb}}
|
||||||
a.rounded_sm,
|
contentFit="cover"
|
||||||
a.p_xs,
|
accessible={true}
|
||||||
t.atoms.bg_contrast_25,
|
accessibilityLabel={image.alt}
|
||||||
{
|
accessibilityHint=""
|
||||||
opacity: 0.8,
|
accessibilityIgnoresInvertColors
|
||||||
},
|
loading={index === 0 ? 'eager' : 'lazy'}
|
||||||
largeAltBadge && {
|
style={[dims]}
|
||||||
padding: 6,
|
onLoad={e => {
|
||||||
},
|
const ar = getAspectRatio(e.source)
|
||||||
]}>
|
if (ar && ar !== aspectRatio) {
|
||||||
<Fullscreen
|
setAspectRatio(ar)
|
||||||
fill={t.atoms.text_contrast_high.color}
|
}
|
||||||
width={largeAltBadge ? 18 : 12}
|
onThumbDims(index, {
|
||||||
/>
|
width: e.source.width,
|
||||||
</View>
|
height: e.source.height,
|
||||||
)}
|
})
|
||||||
{hasAlt && (
|
}}
|
||||||
<View
|
/>
|
||||||
style={[
|
|
||||||
a.justify_center,
|
|
||||||
a.rounded_sm,
|
|
||||||
a.p_xs,
|
|
||||||
t.atoms.bg_contrast_25,
|
|
||||||
{
|
|
||||||
opacity: 0.8,
|
|
||||||
},
|
|
||||||
largeAltBadge && {
|
|
||||||
padding: 6,
|
|
||||||
},
|
|
||||||
]}>
|
|
||||||
<Text
|
|
||||||
style={[
|
|
||||||
a.font_bold,
|
|
||||||
largeAltBadge ? a.text_xs : {fontSize: 8},
|
|
||||||
]}>
|
|
||||||
<Trans>ALT</Trans>
|
|
||||||
</Text>
|
|
||||||
</View>
|
|
||||||
)}
|
|
||||||
</View>
|
|
||||||
) : null}
|
|
||||||
|
|
||||||
<MediaInsetBorder
|
{(hasAlt || isCropped) && !hideBadges ? (
|
||||||
style={
|
<View
|
||||||
focused && {
|
accessible={false}
|
||||||
borderWidth: 2,
|
style={[
|
||||||
|
a.absolute,
|
||||||
|
a.flex_row,
|
||||||
|
{
|
||||||
|
bottom: a.p_xs.padding,
|
||||||
|
right: a.p_xs.padding,
|
||||||
|
gap: 3,
|
||||||
|
},
|
||||||
|
largeAltBadge && {
|
||||||
|
gap: 4,
|
||||||
|
},
|
||||||
|
]}>
|
||||||
|
{isCropped && (
|
||||||
|
<View
|
||||||
|
style={[
|
||||||
|
a.rounded_sm,
|
||||||
|
a.p_xs,
|
||||||
|
t.atoms.bg_contrast_25,
|
||||||
|
{
|
||||||
|
opacity: 0.8,
|
||||||
|
},
|
||||||
|
largeAltBadge && {
|
||||||
|
padding: 6,
|
||||||
|
},
|
||||||
|
]}>
|
||||||
|
<Fullscreen
|
||||||
|
fill={t.atoms.text_contrast_high.color}
|
||||||
|
width={largeAltBadge ? 18 : 12}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
|
{hasAlt && (
|
||||||
|
<View
|
||||||
|
style={[
|
||||||
|
a.justify_center,
|
||||||
|
a.rounded_sm,
|
||||||
|
a.p_xs,
|
||||||
|
t.atoms.bg_contrast_25,
|
||||||
|
{
|
||||||
|
opacity: 0.8,
|
||||||
|
},
|
||||||
|
largeAltBadge && {
|
||||||
|
padding: 6,
|
||||||
|
},
|
||||||
|
]}>
|
||||||
|
<Text
|
||||||
|
style={[
|
||||||
|
a.font_bold,
|
||||||
|
largeAltBadge ? a.text_xs : {fontSize: 8},
|
||||||
|
]}>
|
||||||
|
<Trans>ALT</Trans>
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
|
</View>
|
||||||
|
) : null}
|
||||||
|
|
||||||
|
<MediaInsetBorder
|
||||||
|
style={
|
||||||
|
focused && {
|
||||||
|
borderWidth: 2,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
/>
|
||||||
/>
|
</Pressable>
|
||||||
</Pressable>
|
</ImageContextMenu>
|
||||||
</Animated.View>
|
</Animated.View>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ interface ImageLayoutGridProps {
|
|||||||
) => void
|
) => void
|
||||||
onLongPress?: (index: number) => void
|
onLongPress?: (index: number) => void
|
||||||
onPressIn?: (index: number) => void
|
onPressIn?: (index: number) => void
|
||||||
|
onPreviewPress?: (index: number) => void
|
||||||
style?: StyleProp<ViewStyle>
|
style?: StyleProp<ViewStyle>
|
||||||
viewContext?: PostEmbedViewContext
|
viewContext?: PostEmbedViewContext
|
||||||
}
|
}
|
||||||
@@ -48,6 +49,7 @@ interface ImageLayoutGridInnerProps {
|
|||||||
) => void
|
) => void
|
||||||
onLongPress?: (index: number) => void
|
onLongPress?: (index: number) => void
|
||||||
onPressIn?: (index: number) => void
|
onPressIn?: (index: number) => void
|
||||||
|
onPreviewPress?: (index: number) => void
|
||||||
viewContext?: PostEmbedViewContext
|
viewContext?: PostEmbedViewContext
|
||||||
gap: {gap: number}
|
gap: {gap: number}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import {type Dimensions} from '#/lib/media/types'
|
|||||||
import {useLargeAltBadgeEnabled} from '#/state/preferences/large-alt-badge'
|
import {useLargeAltBadgeEnabled} from '#/state/preferences/large-alt-badge'
|
||||||
import {atoms as a, useTheme} from '#/alf'
|
import {atoms as a, useTheme} from '#/alf'
|
||||||
import {MediaInsetBorder} from '#/components/MediaInsetBorder'
|
import {MediaInsetBorder} from '#/components/MediaInsetBorder'
|
||||||
|
import {ImageContextMenu} from '#/components/Post/Embed/ImageContextMenu'
|
||||||
import {PostEmbedViewContext} from '#/components/Post/Embed/types'
|
import {PostEmbedViewContext} from '#/components/Post/Embed/types'
|
||||||
import {Text} from '#/components/Typography'
|
import {Text} from '#/components/Typography'
|
||||||
|
|
||||||
@@ -26,6 +27,8 @@ interface Props {
|
|||||||
) => void
|
) => void
|
||||||
onLongPress?: EventFunction
|
onLongPress?: EventFunction
|
||||||
onPressIn?: EventFunction
|
onPressIn?: EventFunction
|
||||||
|
/** Fired from the native iOS peek preview tap. */
|
||||||
|
onPreviewPress?: EventFunction
|
||||||
imageStyle?: StyleProp<ImageStyle>
|
imageStyle?: StyleProp<ImageStyle>
|
||||||
viewContext?: PostEmbedViewContext
|
viewContext?: PostEmbedViewContext
|
||||||
insetBorderStyle?: StyleProp<ViewStyle>
|
insetBorderStyle?: StyleProp<ViewStyle>
|
||||||
@@ -40,6 +43,7 @@ export function GalleryItem({
|
|||||||
onPress,
|
onPress,
|
||||||
onPressIn,
|
onPressIn,
|
||||||
onLongPress,
|
onLongPress,
|
||||||
|
onPreviewPress,
|
||||||
viewContext,
|
viewContext,
|
||||||
insetBorderStyle,
|
insetBorderStyle,
|
||||||
containerRefs,
|
containerRefs,
|
||||||
@@ -52,46 +56,60 @@ export function GalleryItem({
|
|||||||
const hasAlt = !!image.alt
|
const hasAlt = !!image.alt
|
||||||
const hideBadges =
|
const hideBadges =
|
||||||
viewContext === PostEmbedViewContext.FeedEmbedRecordWithMedia
|
viewContext === PostEmbedViewContext.FeedEmbedRecordWithMedia
|
||||||
|
|
||||||
|
const aspect =
|
||||||
|
image.aspectRatio && image.aspectRatio.height > 0
|
||||||
|
? image.aspectRatio.width / image.aspectRatio.height
|
||||||
|
: undefined
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={a.flex_1} ref={containerRefs[index]} collapsable={false}>
|
<View style={a.flex_1} ref={containerRefs[index]} collapsable={false}>
|
||||||
<Pressable
|
<ImageContextMenu
|
||||||
onPress={
|
fullsizeUri={image.fullsize}
|
||||||
onPress
|
aspectRatio={aspect}
|
||||||
? () => onPress(index, containerRefs, thumbDimsRef.current.slice())
|
onPreviewPress={
|
||||||
: undefined
|
onPreviewPress ? () => onPreviewPress(index) : undefined
|
||||||
}
|
}
|
||||||
onPressIn={onPressIn ? () => onPressIn(index) : undefined}
|
style={a.flex_1}>
|
||||||
onLongPress={onLongPress ? () => onLongPress(index) : undefined}
|
<Pressable
|
||||||
android_ripple={{
|
onPress={
|
||||||
color: utils.alpha(t.atoms.bg.backgroundColor, 0.2),
|
onPress
|
||||||
foreground: true,
|
? () => onPress(index, containerRefs, thumbDimsRef.current.slice())
|
||||||
}}
|
: undefined
|
||||||
style={[
|
}
|
||||||
a.flex_1,
|
onPressIn={onPressIn ? () => onPressIn(index) : undefined}
|
||||||
a.overflow_hidden,
|
onLongPress={onLongPress ? () => onLongPress(index) : undefined}
|
||||||
t.atoms.bg_contrast_25,
|
android_ripple={{
|
||||||
imageStyle,
|
color: utils.alpha(t.atoms.bg.backgroundColor, 0.2),
|
||||||
]}
|
foreground: true,
|
||||||
accessibilityRole="button"
|
|
||||||
accessibilityLabel={image.alt || _(msg`Image`)}
|
|
||||||
accessibilityHint="">
|
|
||||||
<Image
|
|
||||||
source={{uri: image.thumb}}
|
|
||||||
style={[a.flex_1]}
|
|
||||||
accessible={true}
|
|
||||||
accessibilityLabel={image.alt}
|
|
||||||
accessibilityHint=""
|
|
||||||
accessibilityIgnoresInvertColors
|
|
||||||
onLoad={e => {
|
|
||||||
thumbDimsRef.current[index] = {
|
|
||||||
width: e.source.width,
|
|
||||||
height: e.source.height,
|
|
||||||
}
|
|
||||||
}}
|
}}
|
||||||
loading="lazy"
|
style={[
|
||||||
/>
|
a.flex_1,
|
||||||
<MediaInsetBorder style={insetBorderStyle} />
|
a.overflow_hidden,
|
||||||
</Pressable>
|
t.atoms.bg_contrast_25,
|
||||||
|
imageStyle,
|
||||||
|
]}
|
||||||
|
accessibilityRole="button"
|
||||||
|
accessibilityLabel={image.alt || _(msg`Image`)}
|
||||||
|
accessibilityHint="">
|
||||||
|
<Image
|
||||||
|
source={{uri: image.thumb}}
|
||||||
|
style={[a.flex_1]}
|
||||||
|
accessible={true}
|
||||||
|
accessibilityLabel={image.alt}
|
||||||
|
accessibilityHint=""
|
||||||
|
accessibilityIgnoresInvertColors
|
||||||
|
onLoad={e => {
|
||||||
|
thumbDimsRef.current[index] = {
|
||||||
|
width: e.source.width,
|
||||||
|
height: e.source.height,
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
loading="lazy"
|
||||||
|
/>
|
||||||
|
<MediaInsetBorder style={insetBorderStyle} />
|
||||||
|
</Pressable>
|
||||||
|
</ImageContextMenu>
|
||||||
{hasAlt && !hideBadges ? (
|
{hasAlt && !hideBadges ? (
|
||||||
<View
|
<View
|
||||||
accessible={false}
|
accessible={false}
|
||||||
|
|||||||
Reference in New Issue
Block a user