11import React , { useCallback , useEffect , useMemo , useRef , useState } from 'react' ;
2- import { FlatList , LayoutChangeEvent , StyleSheet } from 'react-native' ;
2+ import { FlatList , LayoutChangeEvent , StyleSheet , View } from 'react-native' ;
33
44import {
55 isAudioAttachment ,
66 isLocalAudioAttachment ,
77 isLocalFileAttachment ,
88 isLocalImageAttachment ,
9- isLocalVideoAttachment ,
109 isLocalVoiceRecordingAttachment ,
1110 isVideoAttachment ,
1211 isVoiceRecordingAttachment ,
13- LocalAudioAttachment ,
14- LocalFileAttachment ,
15- LocalVideoAttachment ,
16- LocalVoiceRecordingAttachment ,
12+ LocalAttachment ,
13+ LocalImageAttachment ,
1714} from 'stream-chat' ;
1815
1916import { useMessageComposer } from '../../contexts' ;
@@ -26,45 +23,50 @@ import { useTheme } from '../../contexts/themeContext/ThemeContext';
2623import { isSoundPackageAvailable } from '../../native' ;
2724import { AudioConfig } from '../../types/types' ;
2825
26+ const IMAGE_PREVIEW_SIZE = 100 ;
2927const FILE_PREVIEW_HEIGHT = 60 ;
3028
31- export type FileUploadPreviewPropsWithContext = Pick <
29+ export type AttachmentUploadPreviewListPropsWithContext = Pick <
3230 MessageInputContextValue ,
33- 'AudioAttachmentUploadPreview' | 'FileAttachmentUploadPreview' | 'VideoAttachmentUploadPreview'
31+ | 'AudioAttachmentUploadPreview'
32+ | 'FileAttachmentUploadPreview'
33+ | 'ImageAttachmentUploadPreview'
34+ | 'VideoAttachmentUploadPreview'
3435> ;
3536
36- type FileAttachmentType < CustomLocalMetadata = Record < string , unknown > > =
37- | LocalFileAttachment < CustomLocalMetadata >
38- | LocalAudioAttachment < CustomLocalMetadata >
39- | LocalVoiceRecordingAttachment < CustomLocalMetadata >
40- | LocalVideoAttachment < CustomLocalMetadata > ;
41-
4237/**
43- * FileUploadPreview
38+ * AttachmentUploadPreviewList
4439 * UI Component to preview the files set for upload
4540 */
46- const UnMemoizedFileUploadPreview = ( props : FileUploadPreviewPropsWithContext ) => {
41+ const UnMemoizedAttachmentUploadListPreview = (
42+ props : AttachmentUploadPreviewListPropsWithContext ,
43+ ) => {
44+ const [ flatListWidth , setFlatListWidth ] = useState ( 0 ) ;
45+ const [ audioAttachmentsStateMap , setAudioAttachmentsStateMap ] = useState <
46+ Record < string , AudioConfig >
47+ > ( { } ) ;
48+ const flatListRef = useRef < FlatList < LocalAttachment > | null > ( null ) ;
4749 const {
4850 AudioAttachmentUploadPreview,
4951 FileAttachmentUploadPreview,
52+ ImageAttachmentUploadPreview,
5053 VideoAttachmentUploadPreview,
5154 } = props ;
5255 const { attachmentManager } = useMessageComposer ( ) ;
5356 const { attachments } = useAttachmentManagerState ( ) ;
54- const [ audioAttachmentsStateMap , setAudioAttachmentsStateMap ] = useState <
55- Record < string , AudioConfig >
56- > ( { } ) ;
57- const flatListRef = useRef < FlatList < FileAttachmentType > | null > ( null ) ;
58- const [ flatListWidth , setFlatListWidth ] = useState ( 0 ) ;
57+ const {
58+ theme : {
59+ colors : { grey_whisper } ,
60+ messageInput : {
61+ attachmentSeparator,
62+ attachmentUploadPreviewList : { filesFlatList, imagesFlatList, wrapper } ,
63+ } ,
64+ } ,
65+ } = useTheme ( ) ;
5966
67+ const imageUploads = attachments . filter ( ( attachment ) => isLocalImageAttachment ( attachment ) ) ;
6068 const fileUploads = useMemo ( ( ) => {
61- return attachments . filter (
62- ( attachment ) =>
63- isLocalFileAttachment ( attachment ) ||
64- isLocalAudioAttachment ( attachment ) ||
65- isLocalVoiceRecordingAttachment ( attachment ) ||
66- isLocalVideoAttachment ( attachment ) ,
67- ) ;
69+ return attachments . filter ( ( attachment ) => ! isLocalImageAttachment ( attachment ) ) ;
6870 } , [ attachments ] ) ;
6971
7072 useEffect ( ( ) => {
@@ -143,18 +145,27 @@ const UnMemoizedFileUploadPreview = (props: FileUploadPreviewPropsWithContext) =
143145 }
144146 } , [ ] ) ;
145147
146- const {
147- theme : {
148- messageInput : {
149- fileUploadPreview : { flatList } ,
150- } ,
148+ const renderImageItem = useCallback (
149+ ( { item } : { item : LocalImageAttachment } ) => {
150+ return (
151+ < ImageAttachmentUploadPreview
152+ attachment = { item }
153+ handleRetry = { attachmentManager . uploadAttachment }
154+ removeAttachments = { attachmentManager . removeAttachments }
155+ />
156+ ) ;
151157 } ,
152- } = useTheme ( ) ;
158+ [
159+ ImageAttachmentUploadPreview ,
160+ attachmentManager . removeAttachments ,
161+ attachmentManager . uploadAttachment ,
162+ ] ,
163+ ) ;
153164
154- const renderItem = useCallback (
155- ( { item } : { item : FileAttachmentType } ) => {
165+ const renderFileItem = useCallback (
166+ ( { item } : { item : LocalAttachment } ) => {
156167 if ( isLocalImageAttachment ( item ) ) {
157- // This is already handled in the `ImageUploadPreview` component
168+ // This is already handled in the `renderImageItem` above, so we return null here to avoid duplication.
158169 return null ;
159170 } else if ( isLocalVoiceRecordingAttachment ( item ) ) {
160171 return (
@@ -240,47 +251,80 @@ const UnMemoizedFileUploadPreview = (props: FileUploadPreviewPropsWithContext) =
240251 [ flatListRef ] ,
241252 ) ;
242253
243- if ( fileUploads . length === 0 ) {
254+ if ( ! attachments . length ) {
244255 return null ;
245256 }
246257
247258 return (
248- < FlatList
249- data = { fileUploads }
250- getItemLayout = { ( _ , index ) => ( {
251- index,
252- length : FILE_PREVIEW_HEIGHT + 8 ,
253- offset : ( FILE_PREVIEW_HEIGHT + 8 ) * index ,
254- } ) }
255- keyExtractor = { ( item ) => item . localMetadata . id }
256- onLayout = { onLayout }
257- ref = { flatListRef }
258- renderItem = { renderItem }
259- style = { [ styles . flatList , flatList ] }
260- testID = { 'file-upload-preview' }
261- />
259+ < View style = { [ wrapper ] } >
260+ { imageUploads . length ? (
261+ < FlatList
262+ data = { imageUploads }
263+ getItemLayout = { ( _ , index ) => ( {
264+ index,
265+ length : IMAGE_PREVIEW_SIZE + 8 ,
266+ offset : ( IMAGE_PREVIEW_SIZE + 8 ) * index ,
267+ } ) }
268+ horizontal
269+ keyExtractor = { ( item ) => item . localMetadata . id }
270+ renderItem = { renderImageItem }
271+ style = { [ styles . imagesFlatList , imagesFlatList ] }
272+ />
273+ ) : null }
274+ { imageUploads . length && fileUploads . length ? (
275+ < View
276+ style = { [
277+ styles . attachmentSeparator ,
278+ {
279+ borderBottomColor : grey_whisper ,
280+ } ,
281+ attachmentSeparator ,
282+ ] }
283+ />
284+ ) : null }
285+ { fileUploads . length ? (
286+ < FlatList
287+ data = { fileUploads }
288+ getItemLayout = { ( _ , index ) => ( {
289+ index,
290+ length : FILE_PREVIEW_HEIGHT + 8 ,
291+ offset : ( FILE_PREVIEW_HEIGHT + 8 ) * index ,
292+ } ) }
293+ keyExtractor = { ( item ) => item . localMetadata . id }
294+ onLayout = { onLayout }
295+ ref = { flatListRef }
296+ renderItem = { renderFileItem }
297+ style = { [ styles . filesFlatList , filesFlatList ] }
298+ testID = { 'file-upload-preview' }
299+ />
300+ ) : null }
301+ </ View >
262302 ) ;
263303} ;
264304
265- export type FileUploadPreviewProps = Partial < FileUploadPreviewPropsWithContext > ;
305+ export type AttachmentUploadPreviewListProps = Partial < AttachmentUploadPreviewListPropsWithContext > ;
266306
267- const MemoizedFileUploadPreviewWithContext = React . memo ( UnMemoizedFileUploadPreview ) ;
307+ const MemoizedAttachmentUploadPreviewListWithContext = React . memo (
308+ UnMemoizedAttachmentUploadListPreview ,
309+ ) ;
268310
269311/**
270- * FileUploadPreview
312+ * AttachmentUploadPreviewList
271313 * UI Component to preview the files set for upload
272314 */
273- export const FileUploadPreview = ( props : FileUploadPreviewProps ) => {
315+ export const AttachmentUploadPreviewList = ( props : AttachmentUploadPreviewListProps ) => {
274316 const {
275317 AudioAttachmentUploadPreview,
276318 FileAttachmentUploadPreview,
319+ ImageAttachmentUploadPreview,
277320 VideoAttachmentUploadPreview,
278321 } = useMessageInputContext ( ) ;
279322 return (
280- < MemoizedFileUploadPreviewWithContext
323+ < MemoizedAttachmentUploadPreviewListWithContext
281324 { ...{
282325 AudioAttachmentUploadPreview,
283326 FileAttachmentUploadPreview,
327+ ImageAttachmentUploadPreview,
284328 VideoAttachmentUploadPreview,
285329 } }
286330 { ...props }
@@ -289,7 +333,13 @@ export const FileUploadPreview = (props: FileUploadPreviewProps) => {
289333} ;
290334
291335const styles = StyleSheet . create ( {
292- flatList : { marginBottom : 12 , maxHeight : FILE_PREVIEW_HEIGHT * 2.5 + 16 } ,
336+ attachmentSeparator : {
337+ borderBottomWidth : 1 ,
338+ marginBottom : 10 ,
339+ } ,
340+ filesFlatList : { marginBottom : 12 , maxHeight : FILE_PREVIEW_HEIGHT * 2.5 + 16 } ,
341+ imagesFlatList : { paddingBottom : 12 } ,
293342} ) ;
294343
295- FileUploadPreview . displayName = 'FileUploadPreview{messageInput{fileUploadPreview}}' ;
344+ AttachmentUploadPreviewList . displayName =
345+ 'AttachmentUploadPreviewList{messageInput{attachmentUploadPreviewList}}' ;
0 commit comments