Skip to content

Commit 20713f0

Browse files
committed
feat: restructure images and files upload preview
1 parent 7b32f34 commit 20713f0

File tree

12 files changed

+350
-475
lines changed

12 files changed

+350
-475
lines changed

package/src/components/Channel/Channel.tsx

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,7 @@ import { ReactionListBottom as ReactionListBottomDefault } from '../Message/Mess
161161
import { ReactionListTop as ReactionListTopDefault } from '../Message/MessageSimple/ReactionList/ReactionListTop';
162162
import { StreamingMessageView as DefaultStreamingMessageView } from '../Message/MessageSimple/StreamingMessageView';
163163
import { AttachButton as AttachButtonDefault } from '../MessageInput/AttachButton';
164+
import { AttachmentUploadPreviewList as AttachmentUploadPreviewDefault } from '../MessageInput/AttachmentUploadPreviewList';
164165
import { CommandsButton as CommandsButtonDefault } from '../MessageInput/CommandsButton';
165166
import { AttachmentUploadProgressIndicator as AttachmentUploadProgressIndicatorDefault } from '../MessageInput/components/AttachmentPreview/AttachmentUploadProgressIndicator';
166167
import { AudioAttachmentUploadPreview as AudioAttachmentUploadPreviewDefault } from '../MessageInput/components/AttachmentPreview/AudioAttachmentUploadPreview';
@@ -176,8 +177,6 @@ import { CommandInput as CommandInputDefault } from '../MessageInput/components/
176177
import { InputEditingStateHeader as InputEditingStateHeaderDefault } from '../MessageInput/components/InputEditingStateHeader';
177178
import { InputReplyStateHeader as InputReplyStateHeaderDefault } from '../MessageInput/components/InputReplyStateHeader';
178179
import { CooldownTimer as CooldownTimerDefault } from '../MessageInput/CooldownTimer';
179-
import { FileUploadPreview as FileUploadPreviewDefault } from '../MessageInput/FileUploadPreview';
180-
import { ImageUploadPreview as ImageUploadPreviewDefault } from '../MessageInput/ImageUploadPreview';
181180
import { InputButtons as InputButtonsDefault } from '../MessageInput/InputButtons';
182181
import { MoreOptionsButton as MoreOptionsButtonDefault } from '../MessageInput/MoreOptionsButton';
183182
import { SendButton as SendButtonDefault } from '../MessageInput/SendButton';
@@ -526,6 +525,7 @@ const ChannelWithContext = (props: PropsWithChildren<ChannelPropsWithContext>) =
526525
AttachmentPickerError = DefaultAttachmentPickerError,
527526
AttachmentPickerErrorImage = DefaultAttachmentPickerErrorImage,
528527
AttachmentPickerIOSSelectMorePhotos = DefaultAttachmentPickerIOSSelectMorePhotos,
528+
AttachmentUploadPreviewList = AttachmentUploadPreviewDefault,
529529
ImageOverlaySelectedComponent = DefaultImageOverlaySelectedComponent,
530530
attachmentPickerErrorButtonText,
531531
attachmentPickerErrorText,
@@ -567,7 +567,6 @@ const ChannelWithContext = (props: PropsWithChildren<ChannelPropsWithContext>) =
567567
FileAttachmentUploadPreview = FileAttachmentUploadPreviewDefault,
568568
FileAttachmentGroup = FileAttachmentGroupDefault,
569569
FileAttachmentIcon = FileIconDefault,
570-
FileUploadPreview = FileUploadPreviewDefault,
571570
FlatList = NativeHandlers.FlatList,
572571
forceAlignMessages,
573572
Gallery = GalleryDefault,
@@ -600,7 +599,6 @@ const ChannelWithContext = (props: PropsWithChildren<ChannelPropsWithContext>) =
600599
ImageLoadingFailedIndicator = ImageLoadingFailedIndicatorDefault,
601600
ImageLoadingIndicator = ImageLoadingIndicatorDefault,
602601
ImageReloadIndicator = ImageReloadIndicatorDefault,
603-
ImageUploadPreview = ImageUploadPreviewDefault,
604602
initialScrollToFirstUnreadMessage = false,
605603
InlineDateSeparator = InlineDateSeparatorDefault,
606604
InlineUnreadIndicator = InlineUnreadIndicatorDefault,
@@ -1742,6 +1740,7 @@ const ChannelWithContext = (props: PropsWithChildren<ChannelPropsWithContext>) =
17421740
attachmentPickerBottomSheetHeight,
17431741
AttachmentPickerSelectionBar,
17441742
attachmentSelectionBarHeight,
1743+
AttachmentUploadPreviewList,
17451744
AttachmentUploadProgressIndicator,
17461745
AudioAttachmentUploadPreview,
17471746
AudioRecorder,
@@ -1765,15 +1764,13 @@ const ChannelWithContext = (props: PropsWithChildren<ChannelPropsWithContext>) =
17651764
editMessage,
17661765
FileAttachmentUploadPreview,
17671766
FileSelectorIcon,
1768-
FileUploadPreview,
17691767
handleAttachButtonPress,
17701768
hasCameraPicker,
17711769
hasCommands: hasCommands ?? !!clientChannelConfig?.commands?.length,
17721770
hasFilePicker,
17731771
hasImagePicker,
17741772
ImageAttachmentUploadPreview,
17751773
ImageSelectorIcon,
1776-
ImageUploadPreview,
17771774
Input,
17781775
InputButtons,
17791776
InputEditingStateHeader,

package/src/components/Channel/hooks/useCreateInputMessageInputContext.ts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ export const useCreateInputMessageInputContext = ({
1414
attachmentPickerBottomSheetHeight,
1515
AttachmentPickerSelectionBar,
1616
attachmentSelectionBarHeight,
17+
AttachmentUploadPreviewList,
1718
AttachmentUploadProgressIndicator,
1819
AudioAttachmentUploadPreview,
1920
AudioRecorder,
@@ -37,15 +38,13 @@ export const useCreateInputMessageInputContext = ({
3738
editMessage,
3839
FileAttachmentUploadPreview,
3940
FileSelectorIcon,
40-
FileUploadPreview,
4141
handleAttachButtonPress,
4242
hasCameraPicker,
4343
hasCommands,
4444
hasFilePicker,
4545
hasImagePicker,
4646
ImageAttachmentUploadPreview,
4747
ImageSelectorIcon,
48-
ImageUploadPreview,
4948
Input,
5049
InputButtons,
5150
InputEditingStateHeader,
@@ -82,6 +81,7 @@ export const useCreateInputMessageInputContext = ({
8281
attachmentPickerBottomSheetHeight,
8382
AttachmentPickerSelectionBar,
8483
attachmentSelectionBarHeight,
84+
AttachmentUploadPreviewList,
8585
AttachmentUploadProgressIndicator,
8686
AudioAttachmentUploadPreview,
8787
AudioRecorder,
@@ -104,15 +104,13 @@ export const useCreateInputMessageInputContext = ({
104104
editMessage,
105105
FileAttachmentUploadPreview,
106106
FileSelectorIcon,
107-
FileUploadPreview,
108107
handleAttachButtonPress,
109108
hasCameraPicker,
110109
hasCommands,
111110
hasFilePicker,
112111
hasImagePicker,
113112
ImageAttachmentUploadPreview,
114113
ImageSelectorIcon,
115-
ImageUploadPreview,
116114
Input,
117115
InputButtons,
118116
InputEditingStateHeader,

package/src/components/MessageInput/FileUploadPreview.tsx renamed to package/src/components/MessageInput/AttachmentUploadPreviewList.tsx

Lines changed: 109 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,16 @@
11
import 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

44
import {
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

1916
import { useMessageComposer } from '../../contexts';
@@ -26,45 +23,50 @@ import { useTheme } from '../../contexts/themeContext/ThemeContext';
2623
import { isSoundPackageAvailable } from '../../native';
2724
import { AudioConfig } from '../../types/types';
2825

26+
const IMAGE_PREVIEW_SIZE = 100;
2927
const 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

291335
const 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

Comments
 (0)