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