Skip to content

Commit 6cb45ca

Browse files
committed
feat: move attachment picker context to Channel wrapper and handle only bottom sheet stuff in it
1 parent d7206e8 commit 6cb45ca

File tree

13 files changed

+467
-614
lines changed

13 files changed

+467
-614
lines changed

package/src/components/AttachmentPicker/AttachmentPicker.tsx

Lines changed: 7 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,8 @@ import type { AttachmentPickerErrorProps } from './components/AttachmentPickerEr
1111

1212
import { renderAttachmentPickerItem } from './components/AttachmentPickerItem';
1313

14-
import {
15-
AttachmentPickerContextValue,
16-
useAttachmentPickerContext,
17-
} from '../../contexts/attachmentPickerContext/AttachmentPickerContext';
14+
import { useAttachmentPickerContext } from '../../contexts/attachmentPickerContext/AttachmentPickerContext';
15+
import { MessageInputContextValue } from '../../contexts/messageInputContext/MessageInputContext';
1816
import { useTheme } from '../../contexts/themeContext/ThemeContext';
1917
import { useScreenDimensions } from '../../hooks/useScreenDimensions';
2018
import { NativeHandlers } from '../../native';
@@ -31,7 +29,7 @@ const styles = StyleSheet.create({
3129
});
3230

3331
export type AttachmentPickerProps = Pick<
34-
AttachmentPickerContextValue,
32+
MessageInputContextValue,
3533
| 'AttachmentPickerBottomSheetHandle'
3634
| 'attachmentPickerBottomSheetHandleHeight'
3735
| 'attachmentSelectionBarHeight'
@@ -87,17 +85,8 @@ export const AttachmentPicker = React.forwardRef(
8785
colors: { white },
8886
},
8987
} = useTheme();
90-
const {
91-
closePicker,
92-
maxNumberOfFiles,
93-
selectedFiles,
94-
selectedImages,
95-
selectedPicker,
96-
setSelectedFiles,
97-
setSelectedImages,
98-
setSelectedPicker,
99-
topInset,
100-
} = useAttachmentPickerContext();
88+
const { closePicker, selectedPicker, setSelectedPicker, topInset } =
89+
useAttachmentPickerContext();
10190
const { vh: screenVh } = useScreenDimensions();
10291

10392
const fullScreenHeight = screenVh(100);
@@ -182,8 +171,7 @@ export const AttachmentPicker = React.forwardRef(
182171
const backHandler = BackHandler.addEventListener('hardwareBackPress', backAction);
183172

184173
return () => backHandler.remove();
185-
// eslint-disable-next-line react-hooks/exhaustive-deps
186-
}, [selectedPicker, closePicker]);
174+
}, [selectedPicker, closePicker, setSelectedPicker]);
187175

188176
useEffect(() => {
189177
const onKeyboardOpenHandler = () => {
@@ -207,8 +195,7 @@ export const AttachmentPicker = React.forwardRef(
207195
Keyboard.removeListener(keyboardShowEvent, onKeyboardOpenHandler);
208196
}
209197
};
210-
// eslint-disable-next-line react-hooks/exhaustive-deps
211-
}, [closePicker, selectedPicker]);
198+
}, [closePicker, selectedPicker, setSelectedPicker]);
212199

213200
useEffect(() => {
214201
if (currentIndex < 0) {
@@ -240,17 +227,7 @@ export const AttachmentPicker = React.forwardRef(
240227
const selectedPhotos = photos.map((asset) => ({
241228
asset,
242229
ImageOverlaySelectedComponent,
243-
maxNumberOfFiles,
244230
numberOfAttachmentPickerImageColumns,
245-
numberOfUploads: selectedFiles.length + selectedImages.length,
246-
// `id` is available for Expo MediaLibrary while Cameraroll doesn't share id therefore we use `uri`
247-
selected:
248-
selectedImages.some((image) => (image?.uri ? image.uri === asset.uri : false)) ||
249-
selectedFiles.some((file) => (file?.uri ? file.uri === asset.uri : false)),
250-
selectedFiles,
251-
selectedImages,
252-
setSelectedFiles,
253-
setSelectedImages,
254231
}));
255232

256233
const handleHeight = attachmentPickerBottomSheetHandleHeight;

package/src/components/AttachmentPicker/components/AttachmentPickerItem.tsx

Lines changed: 53 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -2,41 +2,38 @@ import React from 'react';
22

33
import { Alert, ImageBackground, StyleSheet, Text, View } from 'react-native';
44

5-
import { AttachmentPickerContextValue } from '../../../contexts/attachmentPickerContext/AttachmentPickerContext';
5+
import { FileReference, isLocalImageAttachment, isLocalVideoAttachment } from 'stream-chat';
6+
7+
import { useAttachmentManagerState } from '../../../contexts/messageInputContext/hooks/useAttachmentManagerState';
8+
import { useMessageComposer } from '../../../contexts/messageInputContext/hooks/useMessageComposer';
9+
import { useMessageInputContext } from '../../../contexts/messageInputContext/MessageInputContext';
610
import { useTheme } from '../../../contexts/themeContext/ThemeContext';
711
import { useTranslationContext } from '../../../contexts/translationContext/TranslationContext';
812
import { useViewport } from '../../../hooks/useViewport';
913
import { Recorder } from '../../../icons';
1014
import type { File } from '../../../types/types';
1115
import { getDurationLabelFromDuration } from '../../../utils/utils';
1216
import { BottomSheetTouchableOpacity } from '../../BottomSheetCompatibility/BottomSheetTouchableOpacity';
13-
type AttachmentPickerItemType = Pick<
14-
AttachmentPickerContextValue,
15-
'selectedFiles' | 'setSelectedFiles' | 'setSelectedImages' | 'selectedImages' | 'maxNumberOfFiles'
16-
> & {
17+
18+
type AttachmentPickerItemType = {
1719
asset: File;
1820
ImageOverlaySelectedComponent: React.ComponentType;
19-
numberOfUploads: number;
20-
selected: boolean;
2121
numberOfAttachmentPickerImageColumns?: number;
2222
};
23-
type AttachmentImageProps = Omit<AttachmentPickerItemType, 'setSelectedFiles' | 'selectedFiles'>;
2423

25-
type AttachmentVideoProps = Omit<AttachmentPickerItemType, 'setSelectedImages' | 'selectedImages'>;
26-
27-
const AttachmentVideo = (props: AttachmentVideoProps) => {
28-
const {
29-
asset,
30-
ImageOverlaySelectedComponent,
31-
maxNumberOfFiles,
32-
numberOfAttachmentPickerImageColumns,
33-
numberOfUploads,
34-
selected,
35-
selectedFiles,
36-
setSelectedFiles,
37-
} = props;
24+
const AttachmentVideo = (props: AttachmentPickerItemType) => {
25+
const { asset, ImageOverlaySelectedComponent, numberOfAttachmentPickerImageColumns } = props;
3826
const { vw } = useViewport();
3927
const { t } = useTranslationContext();
28+
const messageComposer = useMessageComposer();
29+
const { uploadNewFile } = useMessageInputContext();
30+
const { attachmentManager } = messageComposer;
31+
const { attachments, availableUploadSlots } = useAttachmentManagerState();
32+
const videoUploads = attachments.filter((attachment) => isLocalVideoAttachment(attachment));
33+
34+
const selected = videoUploads.some(
35+
(attachment) => (attachment.localMetadata.file as FileReference).uri === asset.uri,
36+
);
4037

4138
const {
4239
theme: {
@@ -51,22 +48,20 @@ const AttachmentVideo = (props: AttachmentVideoProps) => {
5148

5249
const size = vw(100) / (numberOfAttachmentPickerImageColumns || 3) - 2;
5350

54-
const updateSelectedFiles = () => {
55-
if (numberOfUploads >= maxNumberOfFiles) {
56-
Alert.alert(t('Maximum number of files reached'));
57-
return;
58-
}
59-
setSelectedFiles([...selectedFiles, asset]);
60-
};
61-
62-
const onPressVideo = () => {
51+
const onPressVideo = async () => {
6352
if (selected) {
64-
setSelectedFiles((files) =>
65-
// `id` is available for Expo MediaLibrary while Cameraroll doesn't share id therefore we use `uri`
66-
files.filter((file) => file.uri !== uri),
53+
const attachment = videoUploads.find(
54+
(attachment) => (attachment.localMetadata.file as FileReference).uri === uri,
6755
);
56+
if (attachment) {
57+
attachmentManager.removeAttachments([attachment.localMetadata.id]);
58+
}
6859
} else {
69-
updateSelectedFiles();
60+
if (!availableUploadSlots) {
61+
Alert.alert(t('Maximum number of files reached'));
62+
return;
63+
}
64+
await uploadNewFile(asset);
7065
}
7166
};
7267

@@ -101,44 +96,43 @@ const AttachmentVideo = (props: AttachmentVideoProps) => {
10196
);
10297
};
10398

104-
const AttachmentImage = (props: AttachmentImageProps) => {
105-
const {
106-
asset,
107-
ImageOverlaySelectedComponent,
108-
maxNumberOfFiles,
109-
numberOfAttachmentPickerImageColumns,
110-
numberOfUploads,
111-
selected,
112-
selectedImages,
113-
setSelectedImages,
114-
} = props;
99+
const AttachmentImage = (props: AttachmentPickerItemType) => {
100+
const { asset, ImageOverlaySelectedComponent, numberOfAttachmentPickerImageColumns } = props;
115101
const {
116102
theme: {
117103
attachmentPicker: { image, imageOverlay },
118104
colors: { overlay },
119105
},
120106
} = useTheme();
121107
const { vw } = useViewport();
122-
const { t } = useTranslationContext();
108+
const { uploadNewImage } = useMessageInputContext();
109+
const messageComposer = useMessageComposer();
110+
const { attachmentManager } = messageComposer;
111+
const { attachments, availableUploadSlots } = useAttachmentManagerState();
112+
const imageUploads = attachments.filter((attachment) => isLocalImageAttachment(attachment));
113+
114+
const selected = imageUploads.some(
115+
(attachment) => attachment.localMetadata.previewUri === asset.uri,
116+
);
123117

124118
const size = vw(100) / (numberOfAttachmentPickerImageColumns || 3) - 2;
125119

126120
const { uri } = asset;
127121

128-
const updateSelectedImages = () => {
129-
if (numberOfUploads >= maxNumberOfFiles) {
130-
Alert.alert(t('Maximum number of files reached'));
131-
return;
132-
}
133-
setSelectedImages([...selectedImages, asset]);
134-
};
135-
136-
const onPressImage = () => {
122+
const onPressImage = async () => {
137123
if (selected) {
138-
// `id` is available for Expo MediaLibrary while Cameraroll doesn't share id therefore we use `uri`
139-
setSelectedImages((images) => images.filter((image) => image.uri !== uri));
124+
const attachment = imageUploads.find(
125+
(attachment) => attachment.localMetadata.previewUri === uri,
126+
);
127+
if (attachment) {
128+
await attachmentManager.removeAttachments([attachment.localMetadata.id]);
129+
}
140130
} else {
141-
updateSelectedImages();
131+
if (!availableUploadSlots) {
132+
Alert.alert('Maximum number of files reached');
133+
return;
134+
}
135+
await uploadNewImage(asset);
142136
}
143137
};
144138

@@ -166,18 +160,7 @@ const AttachmentImage = (props: AttachmentImageProps) => {
166160
};
167161

168162
export const renderAttachmentPickerItem = ({ item }: { item: AttachmentPickerItemType }) => {
169-
const {
170-
asset,
171-
ImageOverlaySelectedComponent,
172-
maxNumberOfFiles,
173-
numberOfAttachmentPickerImageColumns,
174-
numberOfUploads,
175-
selected,
176-
selectedFiles,
177-
selectedImages,
178-
setSelectedFiles,
179-
setSelectedImages,
180-
} = item;
163+
const { asset, ImageOverlaySelectedComponent, numberOfAttachmentPickerImageColumns } = item;
181164

182165
/**
183166
* Expo Media Library - Result of asset type
@@ -192,12 +175,7 @@ export const renderAttachmentPickerItem = ({ item }: { item: AttachmentPickerIte
192175
<AttachmentVideo
193176
asset={asset}
194177
ImageOverlaySelectedComponent={ImageOverlaySelectedComponent}
195-
maxNumberOfFiles={maxNumberOfFiles}
196178
numberOfAttachmentPickerImageColumns={numberOfAttachmentPickerImageColumns}
197-
numberOfUploads={numberOfUploads}
198-
selected={selected}
199-
selectedFiles={selectedFiles}
200-
setSelectedFiles={setSelectedFiles}
201179
/>
202180
);
203181
}
@@ -206,12 +184,7 @@ export const renderAttachmentPickerItem = ({ item }: { item: AttachmentPickerIte
206184
<AttachmentImage
207185
asset={asset}
208186
ImageOverlaySelectedComponent={ImageOverlaySelectedComponent}
209-
maxNumberOfFiles={maxNumberOfFiles}
210187
numberOfAttachmentPickerImageColumns={numberOfAttachmentPickerImageColumns}
211-
numberOfUploads={numberOfUploads}
212-
selected={selected}
213-
selectedImages={selectedImages}
214-
setSelectedImages={setSelectedImages}
215188
/>
216189
);
217190
};

package/src/components/AttachmentPicker/components/AttachmentPickerSelectionBar.tsx

Lines changed: 18 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -20,26 +20,22 @@ const styles = StyleSheet.create({
2020
});
2121

2222
export const AttachmentPickerSelectionBar = () => {
23+
const { closePicker, selectedPicker, setSelectedPicker } = useAttachmentPickerContext();
24+
2325
const {
2426
attachmentSelectionBarHeight,
2527
CameraSelectorIcon,
26-
closePicker,
2728
CreatePollIcon,
2829
FileSelectorIcon,
29-
ImageSelectorIcon,
30-
selectedPicker,
31-
setSelectedPicker,
32-
VideoRecorderSelectorIcon,
33-
} = useAttachmentPickerContext();
34-
35-
const {
3630
hasCameraPicker,
3731
hasFilePicker,
3832
hasImagePicker,
33+
ImageSelectorIcon,
3934
openPollCreationDialog,
4035
pickFile,
4136
sendMessage,
4237
takeAndUploadImage,
38+
VideoRecorderSelectorIcon,
4339
} = useMessageInputContext();
4440
const { threadList } = useChannelContext();
4541
const { hasCreatePoll } = useMessagesContext();
@@ -72,6 +68,18 @@ export const AttachmentPickerSelectionBar = () => {
7268
openPollCreationDialog?.({ sendMessage });
7369
};
7470

71+
const onCameraPickerPress = () => {
72+
setSelectedPicker(undefined);
73+
closePicker();
74+
takeAndUploadImage(Platform.OS === 'android' ? 'image' : 'mixed');
75+
};
76+
77+
const onVideoRecorderPickerPress = () => {
78+
setSelectedPicker(undefined);
79+
closePicker();
80+
takeAndUploadImage('video');
81+
};
82+
7583
return (
7684
<View style={[styles.container, container, { height: attachmentSelectionBarHeight }]}>
7785
{hasImagePicker ? (
@@ -99,9 +107,7 @@ export const AttachmentPickerSelectionBar = () => {
99107
{hasCameraPicker ? (
100108
<TouchableOpacity
101109
hitSlop={{ bottom: 15, top: 15 }}
102-
onPress={() => {
103-
takeAndUploadImage(Platform.OS === 'android' ? 'image' : 'mixed');
104-
}}
110+
onPress={onCameraPickerPress}
105111
testID='take-photo-touchable'
106112
>
107113
<View style={[styles.icon, icon]}>
@@ -112,9 +118,7 @@ export const AttachmentPickerSelectionBar = () => {
112118
{hasCameraPicker && Platform.OS === 'android' ? (
113119
<TouchableOpacity
114120
hitSlop={{ bottom: 15, top: 15 }}
115-
onPress={() => {
116-
takeAndUploadImage('video');
117-
}}
121+
onPress={onVideoRecorderPickerPress}
118122
testID='take-photo-touchable'
119123
>
120124
<View style={[styles.icon, { marginTop: 4 }, icon]}>

0 commit comments

Comments
 (0)