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