Skip to content

Commit 5be9ded

Browse files
committed
fix: improve upload preview components
1 parent d7206e8 commit 5be9ded

File tree

14 files changed

+314
-128
lines changed

14 files changed

+314
-128
lines changed

package/src/components/Channel/Channel.tsx

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,10 @@ import { StreamingMessageView as DefaultStreamingMessageView } from '../Message/
147147
import { AttachButton as AttachButtonDefault } from '../MessageInput/AttachButton';
148148
import { CommandsButton as CommandsButtonDefault } from '../MessageInput/CommandsButton';
149149
import { AttachmentUploadProgressIndicator as AttachmentUploadProgressIndicatorDefault } from '../MessageInput/components/AttachmentPreview/AttachmentUploadProgressIndicator';
150+
import { AudioAttachmentUploadPreview as AudioAttachmentUploadPreviewDefault } from '../MessageInput/components/AttachmentPreview/AudioAttachmentUploadPreview';
151+
import { FileAttachmentUploadPreview as FileAttachmentUploadPreviewDefault } from '../MessageInput/components/AttachmentPreview/FileAttachmentUploadPreview';
152+
import { ImageAttachmentUploadPreview as ImageAttachmentUploadPreviewDefault } from '../MessageInput/components/AttachmentPreview/ImageAttachmentUploadPreview';
153+
import { VideoAttachmentUploadPreview as VideoAttachmentUploadPreviewDefault } from '../MessageInput/components/AttachmentPreview/VideoAttachmentUploadPreview';
150154
import { AudioRecorder as AudioRecorderDefault } from '../MessageInput/components/AudioRecorder/AudioRecorder';
151155
import { AudioRecordingButton as AudioRecordingButtonDefault } from '../MessageInput/components/AudioRecorder/AudioRecordingButton';
152156
import { AudioRecordingInProgress as AudioRecordingInProgressDefault } from '../MessageInput/components/AudioRecorder/AudioRecordingInProgress';
@@ -474,7 +478,7 @@ const ChannelWithContext = (props: PropsWithChildren<ChannelPropsWithContext>) =
474478
Attachment = AttachmentDefault,
475479
AttachmentActions = AttachmentActionsDefault,
476480
AudioAttachment = AudioAttachmentDefault,
477-
AudioAttachmentUploadPreview = AudioAttachmentDefault,
481+
AudioAttachmentUploadPreview = AudioAttachmentUploadPreviewDefault,
478482
AudioRecorder = AudioRecorderDefault,
479483
audioRecordingEnabled = false,
480484
AudioRecordingInProgress = AudioRecordingInProgressDefault,
@@ -484,6 +488,7 @@ const ChannelWithContext = (props: PropsWithChildren<ChannelPropsWithContext>) =
484488
AutoCompleteSuggestionHeader = AutoCompleteSuggestionHeaderDefault,
485489
AutoCompleteSuggestionItem = AutoCompleteSuggestionItemDefault,
486490
AutoCompleteSuggestionList = AutoCompleteSuggestionListDefault,
491+
487492
autoCompleteSuggestionsLimit,
488493
Card = CardDefault,
489494
CardCover,
@@ -511,6 +516,7 @@ const ChannelWithContext = (props: PropsWithChildren<ChannelPropsWithContext>) =
511516
enableSwipeToReply = true,
512517
enforceUniqueReaction = false,
513518
FileAttachment = FileAttachmentDefault,
519+
FileAttachmentUploadPreview = FileAttachmentUploadPreviewDefault,
514520
FileAttachmentGroup = FileAttachmentGroupDefault,
515521
FileAttachmentIcon = FileIconDefault,
516522
FileUploadPreview = FileUploadPreviewDefault,
@@ -542,6 +548,7 @@ const ChannelWithContext = (props: PropsWithChildren<ChannelPropsWithContext>) =
542548
hasImagePicker = isImagePickerAvailable() || isImageMediaLibraryAvailable(),
543549
hideDateSeparators = false,
544550
hideStickyDateHeader = false,
551+
ImageAttachmentUploadPreview = ImageAttachmentUploadPreviewDefault,
545552
ImageLoadingFailedIndicator = ImageLoadingFailedIndicatorDefault,
546553
ImageLoadingIndicator = ImageLoadingIndicatorDefault,
547554
ImageReloadIndicator = ImageReloadIndicatorDefault,
@@ -649,6 +656,7 @@ const ChannelWithContext = (props: PropsWithChildren<ChannelPropsWithContext>) =
649656
UnreadMessagesNotification = UnreadMessagesNotificationDefault,
650657
UploadProgressIndicator = AttachmentUploadProgressIndicatorDefault,
651658
UrlPreview = CardDefault,
659+
VideoAttachmentUploadPreview = VideoAttachmentUploadPreviewDefault,
652660
VideoThumbnail = VideoThumbnailDefault,
653661
} = props;
654662

@@ -1701,12 +1709,14 @@ const ChannelWithContext = (props: PropsWithChildren<ChannelPropsWithContext>) =
17011709
doFileUploadRequest,
17021710
editing,
17031711
editMessage,
1712+
FileAttachmentUploadPreview,
17041713
FileUploadPreview,
17051714
handleAttachButtonPress,
17061715
hasCameraPicker,
17071716
hasCommands: hasCommands ?? (getChannelConfigSafely()?.commands ?? []).length > 0,
17081717
hasFilePicker,
17091718
hasImagePicker,
1719+
ImageAttachmentUploadPreview,
17101720
ImageUploadPreview,
17111721
initialValue,
17121722
Input,
@@ -1727,6 +1737,7 @@ const ChannelWithContext = (props: PropsWithChildren<ChannelPropsWithContext>) =
17271737
StartAudioRecordingButton,
17281738
StopMessageStreamingButton,
17291739
UploadProgressIndicator,
1740+
VideoAttachmentUploadPreview,
17301741
});
17311742

17321743
const messageListContext = useCreatePaginatedMessageListContext({

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,12 +30,14 @@ export const useCreateInputMessageInputContext = ({
3030
doFileUploadRequest,
3131
editing,
3232
editMessage,
33+
FileAttachmentUploadPreview,
3334
FileUploadPreview,
3435
handleAttachButtonPress,
3536
hasCameraPicker,
3637
hasCommands,
3738
hasFilePicker,
3839
hasImagePicker,
40+
ImageAttachmentUploadPreview,
3941
ImageUploadPreview,
4042
initialValue,
4143
Input,
@@ -57,6 +59,7 @@ export const useCreateInputMessageInputContext = ({
5759
StartAudioRecordingButton,
5860
StopMessageStreamingButton,
5961
UploadProgressIndicator,
62+
VideoAttachmentUploadPreview,
6063
}: InputMessageInputContextValue & {
6164
/**
6265
* To ensure we allow re-render, when channel is changed
@@ -93,12 +96,14 @@ export const useCreateInputMessageInputContext = ({
9396
doFileUploadRequest,
9497
editing,
9598
editMessage,
99+
FileAttachmentUploadPreview,
96100
FileUploadPreview,
97101
handleAttachButtonPress,
98102
hasCameraPicker,
99103
hasCommands,
100104
hasFilePicker,
101105
hasImagePicker,
106+
ImageAttachmentUploadPreview,
102107
ImageUploadPreview,
103108
initialValue,
104109
Input,
@@ -120,6 +125,7 @@ export const useCreateInputMessageInputContext = ({
120125
StartAudioRecordingButton,
121126
StopMessageStreamingButton,
122127
UploadProgressIndicator,
128+
VideoAttachmentUploadPreview,
123129
}),
124130
// eslint-disable-next-line react-hooks/exhaustive-deps
125131
[

package/src/components/MessageInput/FileUploadPreview.tsx

Lines changed: 35 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -11,40 +11,37 @@ import {
1111
isVoiceRecordingAttachment,
1212
LocalAudioAttachment,
1313
LocalFileAttachment,
14-
LocalVideoAttachment,
1514
LocalVoiceRecordingAttachment,
1615
} from 'stream-chat';
1716

18-
import { AudioAttachmentUploadPreview } from './components/AttachmentPreview/AudioAttachmentUploadPreview';
19-
import { FileAttachmentUploadPreview } from './components/AttachmentPreview/FileAttachmentUploadPreview';
20-
21-
import { ChatContextValue, useMessageComposer } from '../../contexts';
17+
import { useMessageComposer } from '../../contexts';
2218
import { useAttachmentManagerState } from '../../contexts/messageInputContext/hooks/useAttachmentManagerState';
23-
import { MessageInputContextValue } from '../../contexts/messageInputContext/MessageInputContext';
24-
import { MessagesContextValue } from '../../contexts/messagesContext/MessagesContext';
19+
import {
20+
MessageInputContextValue,
21+
useMessageInputContext,
22+
} from '../../contexts/messageInputContext/MessageInputContext';
2523
import { useTheme } from '../../contexts/themeContext/ThemeContext';
2624
import { isSoundPackageAvailable } from '../../native';
2725
import { AudioConfig } from '../../types/types';
2826

2927
const FILE_PREVIEW_HEIGHT = 60;
3028

31-
export type FileUploadPreviewProps = Partial<
32-
Pick<MessageInputContextValue, 'AudioAttachmentUploadPreview'>
33-
> &
34-
Partial<Pick<MessagesContextValue, 'FileAttachmentIcon'>> &
35-
Partial<Pick<ChatContextValue, 'enableOfflineSupport'>>;
29+
export type FileUploadPreviewPropsWithContext = Pick<
30+
MessageInputContextValue,
31+
'AudioAttachmentUploadPreview' | 'FileAttachmentUploadPreview'
32+
>;
3633

3734
type FileAttachmentType<CustomLocalMetadata = Record<string, unknown>> =
3835
| LocalFileAttachment<CustomLocalMetadata>
39-
| LocalVideoAttachment<CustomLocalMetadata>
4036
| LocalAudioAttachment<CustomLocalMetadata>
4137
| LocalVoiceRecordingAttachment<CustomLocalMetadata>;
4238

4339
/**
4440
* FileUploadPreview
4541
* UI Component to preview the files set for upload
4642
*/
47-
export const FileUploadPreview = () => {
43+
const UnMemoizedFileUploadPreview = (props: FileUploadPreviewPropsWithContext) => {
44+
const { AudioAttachmentUploadPreview, FileAttachmentUploadPreview } = props;
4845
const { attachmentManager } = useMessageComposer();
4946
const { attachments } = useAttachmentManagerState();
5047
const [audioAttachmentsStateMap, setAudioAttachmentsStateMap] = useState<
@@ -57,7 +54,6 @@ export const FileUploadPreview = () => {
5754
return attachments.filter(
5855
(attachment) =>
5956
isLocalFileAttachment(attachment) ||
60-
isLocalVideoAttachment(attachment) ||
6157
isAudioAttachment(attachment) ||
6258
isVoiceRecordingAttachment(attachment),
6359
);
@@ -147,7 +143,7 @@ export const FileUploadPreview = () => {
147143

148144
const renderItem = useCallback(
149145
({ item }: { item: FileAttachmentType }) => {
150-
if (isLocalImageAttachment(item)) {
146+
if (isLocalImageAttachment(item) || isLocalVideoAttachment(item)) {
151147
// This is already handled in the `ImageUploadPreview` component
152148
return null;
153149
} else if (isLocalVoiceRecordingAttachment(item)) {
@@ -185,15 +181,6 @@ export const FileUploadPreview = () => {
185181
/>
186182
);
187183
}
188-
} else if (isLocalVideoAttachment(item)) {
189-
return (
190-
<FileAttachmentUploadPreview
191-
attachment={item}
192-
flatListWidth={flatListWidth}
193-
handleRetry={attachmentManager.uploadAttachment}
194-
removeAttachments={attachmentManager.removeAttachments}
195-
/>
196-
);
197184
} else if (isLocalFileAttachment(item)) {
198185
return (
199186
<FileAttachmentUploadPreview
@@ -206,6 +193,8 @@ export const FileUploadPreview = () => {
206193
} else return null;
207194
},
208195
[
196+
AudioAttachmentUploadPreview,
197+
FileAttachmentUploadPreview,
209198
attachmentManager.removeAttachments,
210199
attachmentManager.uploadAttachment,
211200
audioAttachmentsStateMap,
@@ -252,6 +241,27 @@ export const FileUploadPreview = () => {
252241
);
253242
};
254243

244+
export type FileUploadPreviewProps = Partial<FileUploadPreviewPropsWithContext>;
245+
246+
const MemoizedFileUploadPreviewWithContext = React.memo(UnMemoizedFileUploadPreview);
247+
248+
/**
249+
* FileUploadPreview
250+
* UI Component to preview the files set for upload
251+
*/
252+
export const FileUploadPreview = (props: FileUploadPreviewProps) => {
253+
const { AudioAttachmentUploadPreview, FileAttachmentUploadPreview } = useMessageInputContext();
254+
return (
255+
<MemoizedFileUploadPreviewWithContext
256+
{...{
257+
AudioAttachmentUploadPreview,
258+
FileAttachmentUploadPreview,
259+
}}
260+
{...props}
261+
/>
262+
);
263+
};
264+
255265
const styles = StyleSheet.create({
256266
flatList: { marginBottom: 12, maxHeight: FILE_PREVIEW_HEIGHT * 2.5 + 16 },
257267
});

package/src/components/MessageInput/ImageUploadPreview.tsx

Lines changed: 64 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,45 @@
11
import React, { useCallback } from 'react';
22
import { FlatList, StyleSheet } from 'react-native';
33

4-
import { isLocalImageAttachment, LocalImageAttachment } from 'stream-chat';
4+
import {
5+
isLocalImageAttachment,
6+
isLocalVideoAttachment,
7+
LocalImageAttachment,
8+
LocalVideoAttachment,
9+
} from 'stream-chat';
510

6-
import { ImageAttachmentUploadPreview } from './components/AttachmentPreview/ImageAttachmentUploadPreview';
7-
8-
import { ChatContextValue, useMessageComposer } from '../../contexts';
11+
import {
12+
MessageInputContextValue,
13+
useMessageComposer,
14+
useMessageInputContext,
15+
} from '../../contexts';
916
import { useAttachmentManagerState } from '../../contexts/messageInputContext/hooks/useAttachmentManagerState';
1017
import { useTheme } from '../../contexts/themeContext/ThemeContext';
1118

1219
const IMAGE_PREVIEW_SIZE = 100;
1320

14-
export type ImageUploadPreviewProps = Partial<Pick<ChatContextValue, 'enableOfflineSupport'>>;
21+
export type ImageUploadPreviewPropsWithContext = Pick<
22+
MessageInputContextValue,
23+
'ImageAttachmentUploadPreview' | 'VideoAttachmentUploadPreview'
24+
>;
1525

1626
export type ImageAttachmentPreview<CustomLocalMetadata = Record<string, unknown>> =
17-
LocalImageAttachment<CustomLocalMetadata>;
27+
| LocalImageAttachment<CustomLocalMetadata>
28+
| LocalVideoAttachment<CustomLocalMetadata>;
1829

1930
type ImageUploadPreviewItem = { index: number; item: ImageAttachmentPreview };
2031

2132
/**
2233
* UI Component to preview the images set for upload
2334
*/
24-
export const ImageUploadPreview = () => {
35+
const UnmemoizedImageUploadPreview = (props: ImageUploadPreviewPropsWithContext) => {
36+
const { ImageAttachmentUploadPreview, VideoAttachmentUploadPreview } = props;
2537
const { attachmentManager } = useMessageComposer();
2638
const { attachments } = useAttachmentManagerState();
2739

28-
const imageUploads = attachments.filter((attachment) => isLocalImageAttachment(attachment));
40+
const imageOrVideoUploads = attachments.filter(
41+
(attachment) => isLocalImageAttachment(attachment) || isLocalVideoAttachment(attachment),
42+
);
2943

3044
const {
3145
theme: {
@@ -37,24 +51,39 @@ export const ImageUploadPreview = () => {
3751

3852
const renderItem = useCallback(
3953
({ item }: ImageUploadPreviewItem) => {
40-
return (
41-
<ImageAttachmentUploadPreview
42-
attachment={item}
43-
handleRetry={attachmentManager.uploadAttachment}
44-
removeAttachments={attachmentManager.removeAttachments}
45-
/>
46-
);
54+
if (isLocalImageAttachment(item)) {
55+
return (
56+
<ImageAttachmentUploadPreview
57+
attachment={item}
58+
handleRetry={attachmentManager.uploadAttachment}
59+
removeAttachments={attachmentManager.removeAttachments}
60+
/>
61+
);
62+
} else {
63+
return (
64+
<VideoAttachmentUploadPreview
65+
attachment={item}
66+
handleRetry={attachmentManager.uploadAttachment}
67+
removeAttachments={attachmentManager.removeAttachments}
68+
/>
69+
);
70+
}
4771
},
48-
[attachmentManager],
72+
[
73+
ImageAttachmentUploadPreview,
74+
VideoAttachmentUploadPreview,
75+
attachmentManager.removeAttachments,
76+
attachmentManager.uploadAttachment,
77+
],
4978
);
5079

51-
if (!imageUploads.length) {
80+
if (!imageOrVideoUploads.length) {
5281
return null;
5382
}
5483

5584
return (
5685
<FlatList
57-
data={imageUploads}
86+
data={imageOrVideoUploads}
5887
getItemLayout={(_, index) => ({
5988
index,
6089
length: IMAGE_PREVIEW_SIZE + 8,
@@ -68,6 +97,23 @@ export const ImageUploadPreview = () => {
6897
);
6998
};
7099

100+
const MemoizedImageUploadPreviewWithContext = React.memo(UnmemoizedImageUploadPreview);
101+
102+
export type ImageUploadPreviewProps = Partial<ImageUploadPreviewPropsWithContext>;
103+
104+
/**
105+
* UI Component to preview the images set for upload
106+
*/
107+
export const ImageUploadPreview = (props: ImageUploadPreviewProps) => {
108+
const { ImageAttachmentUploadPreview, VideoAttachmentUploadPreview } = useMessageInputContext();
109+
return (
110+
<MemoizedImageUploadPreviewWithContext
111+
{...{ ImageAttachmentUploadPreview, VideoAttachmentUploadPreview }}
112+
{...props}
113+
/>
114+
);
115+
};
116+
71117
const styles = StyleSheet.create({
72118
fileSizeText: {
73119
fontSize: 12,

0 commit comments

Comments
 (0)