Bluesky app fork with some witchin' additions 馃挮
at readme-update 228 lines 6.7 kB view raw
1import {useRef} from 'react' 2import {type StyleProp, View, type ViewStyle} from 'react-native' 3import {type AnimatedRef, useAnimatedRef} from 'react-native-reanimated' 4import {type AppBskyEmbedImages} from '@atproto/api' 5 6import {type Dimensions} from '#/view/com/lightbox/ImageViewing/@types' 7import {atoms as a, useBreakpoints} from '#/alf' 8import {PostEmbedViewContext} from '#/components/Post/Embed/types' 9import {GalleryItem} from './Gallery' 10 11interface ImageLayoutGridProps { 12 images: AppBskyEmbedImages.ViewImage[] 13 onPress?: ( 14 index: number, 15 containerRefs: AnimatedRef<any>[], 16 fetchedDims: (Dimensions | null)[], 17 ) => void 18 onLongPress?: (index: number) => void 19 onPressIn?: (index: number) => void 20 style?: StyleProp<ViewStyle> 21 viewContext?: PostEmbedViewContext 22} 23 24export function ImageLayoutGrid({style, ...props}: ImageLayoutGridProps) { 25 const {gtMobile} = useBreakpoints() 26 const gap = 27 props.viewContext === PostEmbedViewContext.FeedEmbedRecordWithMedia 28 ? gtMobile 29 ? a.gap_xs 30 : a.gap_2xs 31 : a.gap_xs 32 33 return ( 34 <View style={style}> 35 <View style={[gap, a.rounded_md, a.overflow_hidden]}> 36 <ImageLayoutGridInner {...props} gap={gap} /> 37 </View> 38 </View> 39 ) 40} 41 42interface ImageLayoutGridInnerProps { 43 images: AppBskyEmbedImages.ViewImage[] 44 onPress?: ( 45 index: number, 46 containerRefs: AnimatedRef<any>[], 47 fetchedDims: (Dimensions | null)[], 48 ) => void 49 onLongPress?: (index: number) => void 50 onPressIn?: (index: number) => void 51 viewContext?: PostEmbedViewContext 52 gap: {gap: number} 53} 54 55function ImageLayoutGridInner(props: ImageLayoutGridInnerProps) { 56 const gap = props.gap 57 const count = props.images.length 58 59 const containerRef1 = useAnimatedRef() 60 const containerRef2 = useAnimatedRef() 61 const containerRef3 = useAnimatedRef() 62 const containerRef4 = useAnimatedRef() 63 const thumbDimsRef = useRef<(Dimensions | null)[]>([]) 64 65 switch (count) { 66 case 2: { 67 const containerRefs = [containerRef1, containerRef2] 68 return ( 69 <View style={[a.flex_1, a.flex_row, gap]}> 70 <View style={[a.flex_1, a.aspect_square]}> 71 <GalleryItem 72 {...props} 73 index={0} 74 insetBorderStyle={noCorners(['topRight', 'bottomRight'])} 75 containerRefs={containerRefs} 76 thumbDimsRef={thumbDimsRef} 77 /> 78 </View> 79 <View style={[a.flex_1, a.aspect_square]}> 80 <GalleryItem 81 {...props} 82 index={1} 83 insetBorderStyle={noCorners(['topLeft', 'bottomLeft'])} 84 containerRefs={containerRefs} 85 thumbDimsRef={thumbDimsRef} 86 /> 87 </View> 88 </View> 89 ) 90 } 91 92 case 3: { 93 const containerRefs = [containerRef1, containerRef2, containerRef3] 94 return ( 95 <View style={[a.flex_1, a.flex_row, gap]}> 96 <View style={[a.flex_1, a.aspect_square]}> 97 <GalleryItem 98 {...props} 99 index={0} 100 insetBorderStyle={noCorners(['topRight', 'bottomRight'])} 101 containerRefs={containerRefs} 102 thumbDimsRef={thumbDimsRef} 103 /> 104 </View> 105 <View style={[a.flex_1, a.aspect_square, gap]}> 106 <View style={[a.flex_1]}> 107 <GalleryItem 108 {...props} 109 index={1} 110 insetBorderStyle={noCorners([ 111 'topLeft', 112 'bottomLeft', 113 'bottomRight', 114 ])} 115 containerRefs={containerRefs} 116 thumbDimsRef={thumbDimsRef} 117 /> 118 </View> 119 <View style={[a.flex_1]}> 120 <GalleryItem 121 {...props} 122 index={2} 123 insetBorderStyle={noCorners([ 124 'topLeft', 125 'bottomLeft', 126 'topRight', 127 ])} 128 containerRefs={containerRefs} 129 thumbDimsRef={thumbDimsRef} 130 /> 131 </View> 132 </View> 133 </View> 134 ) 135 } 136 137 case 4: { 138 const containerRefs = [ 139 containerRef1, 140 containerRef2, 141 containerRef3, 142 containerRef4, 143 ] 144 return ( 145 <> 146 <View style={[a.flex_row, gap]}> 147 <View style={[a.flex_1, {aspectRatio: 1.5}]}> 148 <GalleryItem 149 {...props} 150 index={0} 151 insetBorderStyle={noCorners([ 152 'bottomLeft', 153 'topRight', 154 'bottomRight', 155 ])} 156 containerRefs={containerRefs} 157 thumbDimsRef={thumbDimsRef} 158 /> 159 </View> 160 <View style={[a.flex_1, {aspectRatio: 1.5}]}> 161 <GalleryItem 162 {...props} 163 index={1} 164 insetBorderStyle={noCorners([ 165 'topLeft', 166 'bottomLeft', 167 'bottomRight', 168 ])} 169 containerRefs={containerRefs} 170 thumbDimsRef={thumbDimsRef} 171 /> 172 </View> 173 </View> 174 <View style={[a.flex_row, gap]}> 175 <View style={[a.flex_1, {aspectRatio: 1.5}]}> 176 <GalleryItem 177 {...props} 178 index={2} 179 insetBorderStyle={noCorners([ 180 'topLeft', 181 'topRight', 182 'bottomRight', 183 ])} 184 containerRefs={containerRefs} 185 thumbDimsRef={thumbDimsRef} 186 /> 187 </View> 188 <View style={[a.flex_1, {aspectRatio: 1.5}]}> 189 <GalleryItem 190 {...props} 191 index={3} 192 insetBorderStyle={noCorners([ 193 'topLeft', 194 'bottomLeft', 195 'topRight', 196 ])} 197 containerRefs={containerRefs} 198 thumbDimsRef={thumbDimsRef} 199 /> 200 </View> 201 </View> 202 </> 203 ) 204 } 205 206 default: 207 return null 208 } 209} 210 211function noCorners( 212 corners: ('topLeft' | 'topRight' | 'bottomLeft' | 'bottomRight')[], 213) { 214 const styles: StyleProp<ViewStyle>[] = [] 215 if (corners.includes('topLeft')) { 216 styles.push({borderTopLeftRadius: 0}) 217 } 218 if (corners.includes('topRight')) { 219 styles.push({borderTopRightRadius: 0}) 220 } 221 if (corners.includes('bottomLeft')) { 222 styles.push({borderBottomLeftRadius: 0}) 223 } 224 if (corners.includes('bottomRight')) { 225 styles.push({borderBottomRightRadius: 0}) 226 } 227 return styles 228}