forked from
jollywhoppers.com/witchsky.app
Bluesky app fork with some witchin' additions 馃挮
1import {Pressable, type StyleProp, View, type ViewStyle} from 'react-native'
2import {type AnimatedRef} from 'react-native-reanimated'
3import {Image, type ImageStyle} from 'expo-image'
4import {type AppBskyEmbedImages} from '@atproto/api'
5import {utils} from '@bsky.app/alf'
6import {msg} from '@lingui/macro'
7import {useLingui} from '@lingui/react'
8
9import {type Dimensions} from '#/lib/media/types'
10import {
11 maybeModifyHighQualityImage,
12 useHighQualityImages,
13} from '#/state/preferences/high-quality-images'
14import {useLargeAltBadgeEnabled} from '#/state/preferences/large-alt-badge'
15import {atoms as a, useTheme} from '#/alf'
16import {MediaInsetBorder} from '#/components/MediaInsetBorder'
17import {PostEmbedViewContext} from '#/components/Post/Embed/types'
18import {Text} from '#/components/Typography'
19
20type EventFunction = (index: number) => void
21
22interface Props {
23 images: AppBskyEmbedImages.ViewImage[]
24 index: number
25 onPress?: (
26 index: number,
27 containerRefs: AnimatedRef<any>[],
28 fetchedDims: (Dimensions | null)[],
29 ) => void
30 onLongPress?: EventFunction
31 onPressIn?: EventFunction
32 imageStyle?: StyleProp<ImageStyle>
33 viewContext?: PostEmbedViewContext
34 insetBorderStyle?: StyleProp<ViewStyle>
35 containerRefs: AnimatedRef<any>[]
36 thumbDimsRef: React.RefObject<(Dimensions | null)[]>
37}
38
39export function GalleryItem({
40 images,
41 index,
42 imageStyle,
43 onPress,
44 onPressIn,
45 onLongPress,
46 viewContext,
47 insetBorderStyle,
48 containerRefs,
49 thumbDimsRef,
50}: Props) {
51 const t = useTheme()
52 const {_} = useLingui()
53 const largeAltBadge = useLargeAltBadgeEnabled()
54 const highQualityImages = useHighQualityImages()
55 const image = images[index]
56 const hasAlt = !!image.alt
57 const hideBadges =
58 viewContext === PostEmbedViewContext.FeedEmbedRecordWithMedia
59 return (
60 <View style={a.flex_1} ref={containerRefs[index]} collapsable={false}>
61 <Pressable
62 onPress={
63 onPress
64 ? () => onPress(index, containerRefs, thumbDimsRef.current.slice())
65 : undefined
66 }
67 onPressIn={onPressIn ? () => onPressIn(index) : undefined}
68 onLongPress={onLongPress ? () => onLongPress(index) : undefined}
69 android_ripple={{
70 color: utils.alpha(t.atoms.bg.backgroundColor, 0.2),
71 foreground: true,
72 }}
73 style={[
74 a.flex_1,
75 a.overflow_hidden,
76 t.atoms.bg_contrast_25,
77 imageStyle,
78 ]}
79 accessibilityRole="button"
80 accessibilityLabel={image.alt || _(msg`Image`)}
81 accessibilityHint="">
82 <Image
83 source={{
84 uri: maybeModifyHighQualityImage(image.thumb, highQualityImages),
85 }}
86 style={[a.flex_1]}
87 accessible={true}
88 accessibilityLabel={image.alt}
89 accessibilityHint=""
90 accessibilityIgnoresInvertColors
91 onLoad={e => {
92 thumbDimsRef.current[index] = {
93 width: e.source.width,
94 height: e.source.height,
95 }
96 }}
97 loading="lazy"
98 />
99 <MediaInsetBorder style={insetBorderStyle} />
100 </Pressable>
101 {hasAlt && !hideBadges ? (
102 <View
103 accessible={false}
104 style={[
105 a.absolute,
106 a.flex_row,
107 a.align_center,
108 a.rounded_xs,
109 t.atoms.bg_contrast_25,
110 {
111 gap: 3,
112 padding: 3,
113 bottom: a.p_xs.padding,
114 right: a.p_xs.padding,
115 opacity: 0.8,
116 },
117 largeAltBadge && [
118 {
119 gap: 4,
120 padding: 5,
121 },
122 ],
123 ]}>
124 <Text
125 style={[a.font_bold, largeAltBadge ? a.text_xs : {fontSize: 8}]}>
126 ALT
127 </Text>
128 </View>
129 ) : null}
130 </View>
131 )
132}