import React , { useEffect , useRef , useState } from 'react' ; import { Dimensions , FlatList , Image , StyleSheet , Text , TouchableOpacity , View , ViewToken , } from 'react-native' ; import Dayjs from 'dayjs' ; import { SafeAreaView } from 'react-native-safe-area-context' ; import { DateHeader , Photo , useImageGalleryContext , useOverlayContext , useTheme , } from 'stream-chat-react-native' ; import { ScreenHeader } from '../components/ScreenHeader' ; import { usePaginatedAttachments } from '../hooks/usePaginatedAttachments' ; import { Picture } from '../icons/Picture' ; import type { RouteProp } from '@react-navigation/native' ; import type { Attachment } from 'stream-chat' ; import type { LocalAttachmentType , LocalChannelType , LocalCommandType , LocalEventType , LocalMessageType , LocalReactionType , LocalUserType , StackNavigatorParamList , } from '../types' ; const screen = Dimensions . get ( 'screen' ) . width ; const styles = StyleSheet . create ( { contentContainer : { flexGrow : 1 } , emptyContainer : { alignItems : 'center' , flex : 1 , justifyContent : 'center' , paddingHorizontal : 40 , } , flex : { flex : 1 } , noMedia : { fontSize : 16 , paddingBottom : 8 , } , noMediaDetails : { fontSize : 14 , textAlign : 'center' , } , stickyHeader : { left : 0 , position : 'absolute' , right : 0 , top : 8 , } , } ) ; type ChannelImagesScreenRouteProp = RouteProp < StackNavigatorParamList , 'ChannelImagesScreen' > ; export type ChannelImagesScreenProps = { route : ChannelImagesScreenRouteProp ; } ; export const ChannelImagesScreen = ( { route : { params : { channel } , } , } ) => { const { images , setImage , setImages } = useImageGalleryContext < LocalAttachmentType , LocalChannelType , LocalCommandType , LocalEventType , LocalMessageType , LocalReactionType , LocalUserType > ( ) ; const { setBlurType , setOverlay } = useOverlayContext ( ) ; const { loading , loadMore , messages } = usePaginatedAttachments ( channel , 'image' ) ; const { theme : { colors : { white } , } , } = useTheme ( ) ; const channelImages = useRef ( images ) ; const [ stickyHeaderDate , setStickyHeaderDate ] = useState ( Dayjs ( messages ?. [ 0 ] ?. created_at ) . format ( 'MMM YYYY' ) ) ; const stickyHeaderDateRef = useRef ( '' ) ; const updateStickyDate = useRef ( ( { viewableItems } ) => { if ( viewableItems ?. length ) { const lastItem = viewableItems [ 0 ] ; const created_at = lastItem ?. item ?. created_at ; if ( created_at && ! lastItem . item . deleted_at && Dayjs ( created_at ) . format ( 'MMM YYYY' ) !== stickyHeaderDateRef . current ) { stickyHeaderDateRef . current = Dayjs ( created_at ) . format ( 'MMM YYYY' ) ; const isCurrentYear = new Date ( created_at ) . getFullYear ( ) === new Date ( ) . getFullYear ( ) ; setStickyHeaderDate ( isCurrentYear ? Dayjs ( created_at ) . format ( 'MMM' ) : Dayjs ( created_at ) . format ( 'MMM YYYY' ) ) ; } } } ) ; /**
     * Photos array created from all currently available
     * photo attachments
     */ const photos = messages . reduce ( ( acc , cur ) => { const attachmentImages = ( cur . attachments as Attachment < LocalAttachmentType > [ ] ) ?. filter ( ( attachment ) => attachment . type === 'image' && ! attachment . title_link && ! attachment . og_scrape_url && ( attachment . image_url || attachment . thumb_url ) ) || [ ] ; const attachmentPhotos = attachmentImages . map ( ( attachmentImage ) => ( { created_at : cur . created_at , id : `photoId-${ cur . id }-${ attachmentImage . image_url || attachmentImage . thumb_url }` , messageId : cur . id , uri : attachmentImage . image_url || ( attachmentImage . thumb_url as string ) , } ) ) ; return [ ... acc , ... attachmentPhotos ] ; } , [ ] ) ; const messagesWithImages = messages . map ( ( message ) => ( { ... message , groupStyles : [ ] , readBy : false } ) ) . filter ( ( message ) => { if ( ! message . deleted_at && message . attachments ) { return message . attachments . some ( ( attachment ) => attachment . type === 'image' && ! attachment . title_link && ! attachment . og_scrape_url && ( attachment . image_url || attachment . thumb_url ) ) ; } return false ; } ) ; /**
     * This is for the useEffect to run again in the case that a message
     * gets edited with more or the same number of images
     */ const imageString = messagesWithImages . map ( ( message ) => ( message . attachments as Attachment < LocalAttachmentType > [ ] ) . map ( ( attachment ) => attachment . image_url || attachment . thumb_url || '' ) . join ( ) ) . join ( ) ; const numberOfMessagesWithImages = messagesWithImages . length ; useEffect ( ( ) => { setImages ( messagesWithImages ) ; return ( ) => setImages ( channelImages . current ) ; } , [ imageString , numberOfMessagesWithImages ] ) ; return ( < SafeAreaView style = { [ styles . flex , { backgroundColor : white } ] } > < ScreenHeader inSafeArea titleText = 'Photos and Videos' / > < View style = { styles . flex } > < FlatList contentContainerStyle = { styles . contentContainer } data = { photos } keyExtractor = { ( item , index ) => `${ item . id }-${ index }` } ListEmptyComponent = { EmptyListComponent } numColumns = { 3 } onEndReached = { loadMore } onViewableItemsChanged = { updateStickyDate . current } refreshing = { loading } renderItem = { ( { item } ) => ( < TouchableOpacity onPress = { ( ) => { setImage ( { messageId : item . messageId , url : item . uri , } ) ; setBlurType ( 'dark' ) ; setOverlay ( 'gallery' ) ; } } > < Image source = { { uri : item . uri } } style = { { height : screen / 3 , margin : 1 , width : screen / 3 - 2 , } } /> < /TouchableOpacity>)} style={styles.flex} viewabilityConfig={{ viewAreaCoveragePercentThreshold : 50 , } } /> { photos && photos . length ? ( < View style = { styles . stickyHeader } > < DateHeader dateString = { stickyHeaderDate } /> < /View>) : null} < /View> < /SafeAreaView>); } ; const EmptyListComponent = ( ) => { const { theme : { colors : { black , grey , grey_gainsboro } , } , } = useTheme ( ) ; return ( < View style = { styles . emptyContainer } > < Picture fill = { grey_gainsboro } scale = { 6 } / > < Text style = { [ styles . noMedia , { color : black } ] } > No media < /Text> < Text style = { [ styles . noMediaDetails , { color : grey } ] } > Photos or video sent in this chat will appear here < /Text> < /View>); } ;