Bluesky app fork with some witchin' additions 馃挮
1import {InteractionManager, View} from 'react-native'
2import {type AnimatedRef} from 'react-native-reanimated'
3import {Image} from 'expo-image'
4
5import {useLightboxControls} from '#/state/lightbox'
6import {useHighQualityImages} from '#/state/preferences/high-quality-images'
7import {
8 applyImageTransforms,
9 useImageCdnHost,
10} from '#/state/preferences/image-cdn-host'
11import {type Dimensions} from '#/view/com/lightbox/ImageViewing/@types'
12import {atoms as a, tokens} from '#/alf'
13import {AutoSizedImage} from '#/components/images/AutoSizedImage'
14import {Gallery} from '#/components/images/Gallery'
15import {ImageLayoutGrid} from '#/components/images/ImageLayoutGrid'
16import {PostEmbedViewContext} from '#/components/Post/Embed/types'
17import {useAnalytics} from '#/analytics'
18import {type EmbedType} from '#/types/bsky/post'
19import {type CommonProps} from './types'
20
21export function ImageEmbed({
22 embed,
23 ...rest
24}: CommonProps & {
25 embed: EmbedType<'images'>
26}) {
27 const ax = useAnalytics()
28 const {openLightbox} = useLightboxControls()
29 const highQualityImages = useHighQualityImages()
30 const imageCdnHost = useImageCdnHost()
31 const {images} = embed.view
32 const galleryEnabled = ax.features.enabled(ax.features.PostGalleryEmbedEnable)
33
34 if (images.length > 0) {
35 const items = images.map(img => ({
36 uri: applyImageTransforms(img.fullsize, {
37 imageCdnHost,
38 highQualityImages,
39 }),
40 thumbUri: applyImageTransforms(img.thumb, {
41 imageCdnHost,
42 highQualityImages,
43 }),
44 alt: img.alt,
45 dimensions: img.aspectRatio ?? null,
46 }))
47 const onPress = (
48 index: number,
49 refs: AnimatedRef<any>[],
50 fetchedDims: (Dimensions | null)[],
51 ) => {
52 openLightbox({
53 images: items.map((item, i) => ({
54 ...item,
55 thumbRect: null,
56 thumbRef: refs[i] ?? null,
57 thumbDimensions: fetchedDims[i] ?? null,
58 thumbBorderRadius: tokens.borderRadius.md,
59 type: 'image',
60 })),
61 index,
62 })
63 }
64 const onPressIn = (_: number) => {
65 InteractionManager.runAfterInteractions(() => {
66 Image.prefetch(
67 items.map(i => i.uri),
68 'memory',
69 )
70 })
71 }
72
73 if (images.length === 1) {
74 const image = images[0]
75 return (
76 <View style={[a.mt_sm, rest.style]}>
77 <AutoSizedImage
78 crop={
79 rest.viewContext === PostEmbedViewContext.ThreadHighlighted
80 ? 'none'
81 : rest.viewContext ===
82 PostEmbedViewContext.FeedEmbedRecordWithMedia
83 ? 'square'
84 : 'constrained'
85 }
86 image={image}
87 onPress={(containerRef, dims) => onPress(0, [containerRef], [dims])}
88 onPressIn={() => onPressIn(0)}
89 hideBadge={
90 rest.viewContext === PostEmbedViewContext.FeedEmbedRecordWithMedia
91 }
92 />
93 </View>
94 )
95 }
96
97 if (galleryEnabled) {
98 return (
99 <View style={[a.mt_sm, rest.style]}>
100 <Gallery
101 images={images}
102 onPress={onPress}
103 onPressIn={onPressIn}
104 viewContext={rest.viewContext}
105 />
106 </View>
107 )
108 }
109
110 return (
111 <View style={[a.mt_sm, rest.style]}>
112 <ImageLayoutGrid
113 images={images}
114 onPress={onPress}
115 onPressIn={onPressIn}
116 viewContext={rest.viewContext}
117 />
118 </View>
119 )
120 }
121}