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