stream-chat-react-native-core 4.5.0-beta.1 → 4.6.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/commonjs/components/Attachment/FileIcon.js +9 -1
- package/lib/commonjs/components/Attachment/FileIcon.js.map +1 -1
- package/lib/commonjs/components/AttachmentPicker/AttachmentPicker.js +232 -48
- package/lib/commonjs/components/AttachmentPicker/AttachmentPicker.js.map +1 -1
- package/lib/commonjs/components/Message/MessageSimple/MessageContent.js +39 -21
- package/lib/commonjs/components/Message/MessageSimple/MessageContent.js.map +1 -1
- package/lib/commonjs/components/MessageInput/FileUploadPreview.js +1 -1
- package/lib/commonjs/components/MessageInput/FileUploadPreview.js.map +1 -1
- package/lib/commonjs/components/MessageInput/MessageInput.js +129 -44
- package/lib/commonjs/components/MessageInput/MessageInput.js.map +1 -1
- package/lib/commonjs/components/Reply/Reply.js +25 -9
- package/lib/commonjs/components/Reply/Reply.js.map +1 -1
- package/lib/commonjs/contexts/attachmentPickerContext/AttachmentPickerContext.js +15 -8
- package/lib/commonjs/contexts/attachmentPickerContext/AttachmentPickerContext.js.map +1 -1
- package/lib/commonjs/contexts/messageInputContext/MessageInputContext.js +10 -7
- package/lib/commonjs/contexts/messageInputContext/MessageInputContext.js.map +1 -1
- package/lib/commonjs/contexts/messageInputContext/hooks/useMessageDetailsForState.js +16 -1
- package/lib/commonjs/contexts/messageInputContext/hooks/useMessageDetailsForState.js.map +1 -1
- package/lib/commonjs/contexts/themeContext/utils/theme.js +1 -0
- package/lib/commonjs/contexts/themeContext/utils/theme.js.map +1 -1
- package/lib/commonjs/icons/Recorder.js +47 -0
- package/lib/commonjs/icons/Recorder.js.map +1 -0
- package/lib/commonjs/icons/Video.js +82 -0
- package/lib/commonjs/icons/Video.js.map +1 -0
- package/lib/commonjs/icons/index.js +26 -0
- package/lib/commonjs/icons/index.js.map +1 -1
- package/lib/commonjs/native.js.map +1 -1
- package/lib/commonjs/version.json +1 -1
- package/lib/module/components/Attachment/FileIcon.js +9 -1
- package/lib/module/components/Attachment/FileIcon.js.map +1 -1
- package/lib/module/components/AttachmentPicker/AttachmentPicker.js +232 -48
- package/lib/module/components/AttachmentPicker/AttachmentPicker.js.map +1 -1
- package/lib/module/components/Message/MessageSimple/MessageContent.js +39 -21
- package/lib/module/components/Message/MessageSimple/MessageContent.js.map +1 -1
- package/lib/module/components/MessageInput/FileUploadPreview.js +1 -1
- package/lib/module/components/MessageInput/FileUploadPreview.js.map +1 -1
- package/lib/module/components/MessageInput/MessageInput.js +129 -44
- package/lib/module/components/MessageInput/MessageInput.js.map +1 -1
- package/lib/module/components/Reply/Reply.js +25 -9
- package/lib/module/components/Reply/Reply.js.map +1 -1
- package/lib/module/contexts/attachmentPickerContext/AttachmentPickerContext.js +15 -8
- package/lib/module/contexts/attachmentPickerContext/AttachmentPickerContext.js.map +1 -1
- package/lib/module/contexts/messageInputContext/MessageInputContext.js +10 -7
- package/lib/module/contexts/messageInputContext/MessageInputContext.js.map +1 -1
- package/lib/module/contexts/messageInputContext/hooks/useMessageDetailsForState.js +16 -1
- package/lib/module/contexts/messageInputContext/hooks/useMessageDetailsForState.js.map +1 -1
- package/lib/module/contexts/themeContext/utils/theme.js +1 -0
- package/lib/module/contexts/themeContext/utils/theme.js.map +1 -1
- package/lib/module/icons/Recorder.js +47 -0
- package/lib/module/icons/Recorder.js.map +1 -0
- package/lib/module/icons/Video.js +82 -0
- package/lib/module/icons/Video.js.map +1 -0
- package/lib/module/icons/index.js +26 -0
- package/lib/module/icons/index.js.map +1 -1
- package/lib/module/native.js.map +1 -1
- package/lib/module/version.json +1 -1
- package/lib/typescript/components/MessageInput/MessageInput.d.ts +1 -1
- package/lib/typescript/contexts/attachmentPickerContext/AttachmentPickerContext.d.ts +3 -2
- package/lib/typescript/contexts/messageInputContext/MessageInputContext.d.ts +4 -15
- package/lib/typescript/contexts/themeContext/utils/theme.d.ts +1 -0
- package/lib/typescript/icons/Recorder.d.ts +3 -0
- package/lib/typescript/icons/Video.d.ts +3 -0
- package/lib/typescript/icons/index.d.ts +2 -0
- package/lib/typescript/native.d.ts +3 -14
- package/lib/typescript/types/types.d.ts +24 -0
- package/package.json +1 -1
- package/src/components/Attachment/FileIcon.tsx +45 -0
- package/src/components/AttachmentPicker/AttachmentPicker.tsx +204 -54
- package/src/components/Message/MessageSimple/MessageContent.tsx +23 -12
- package/src/components/MessageInput/FileUploadPreview.tsx +1 -1
- package/src/components/MessageInput/MessageInput.tsx +106 -19
- package/src/components/Reply/Reply.tsx +31 -14
- package/src/components/Thread/__tests__/__snapshots__/Thread.test.js.snap +12 -0
- package/src/contexts/attachmentPickerContext/AttachmentPickerContext.tsx +6 -2
- package/src/contexts/messageInputContext/MessageInputContext.tsx +17 -24
- package/src/contexts/messageInputContext/hooks/useMessageDetailsForState.ts +17 -1
- package/src/contexts/themeContext/utils/theme.ts +2 -0
- package/src/icons/Recorder.tsx +16 -0
- package/src/icons/Video.tsx +37 -0
- package/src/icons/index.ts +2 -0
- package/src/native.ts +4 -14
- package/src/types/types.ts +27 -0
- package/src/version.json +1 -1
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { FlatList as DefaultFlatList } from 'react-native';
|
|
2
2
|
import type { NetInfoSubscription } from '@react-native-community/netinfo';
|
|
3
|
+
import type { Asset, File } from './types/types';
|
|
3
4
|
declare type CompressImage = ({ compressImageQuality, height, uri, width, }: {
|
|
4
5
|
compressImageQuality: number;
|
|
5
6
|
height: number;
|
|
@@ -11,15 +12,8 @@ declare type DeleteFile = ({ uri }: {
|
|
|
11
12
|
uri: string;
|
|
12
13
|
}) => Promise<boolean> | never;
|
|
13
14
|
export declare let deleteFile: DeleteFile;
|
|
14
|
-
declare type GetLocalAssetUri = (uriOrAssetId: string) =>
|
|
15
|
+
declare type GetLocalAssetUri = (uriOrAssetId: string) => never;
|
|
15
16
|
export declare let getLocalAssetUri: GetLocalAssetUri;
|
|
16
|
-
export declare type Asset = {
|
|
17
|
-
height: number;
|
|
18
|
-
source: 'camera' | 'picker';
|
|
19
|
-
uri: string;
|
|
20
|
-
width: number;
|
|
21
|
-
id?: string;
|
|
22
|
-
};
|
|
23
17
|
declare type GetPhotos = ({ after, first }: {
|
|
24
18
|
first: number;
|
|
25
19
|
after?: string;
|
|
@@ -41,12 +35,7 @@ declare type PickDocument = ({ maxNumberOfFiles }: {
|
|
|
41
35
|
maxNumberOfFiles?: number;
|
|
42
36
|
}) => Promise<{
|
|
43
37
|
cancelled: boolean;
|
|
44
|
-
docs?:
|
|
45
|
-
name: string;
|
|
46
|
-
size?: number | string;
|
|
47
|
-
type?: string;
|
|
48
|
-
uri?: string;
|
|
49
|
-
}[];
|
|
38
|
+
docs?: File[];
|
|
50
39
|
}> | never;
|
|
51
40
|
export declare let pickDocument: PickDocument;
|
|
52
41
|
declare type SaveFileOptions = {
|
|
@@ -1,4 +1,28 @@
|
|
|
1
1
|
import type { ExtendableGenerics, LiteralStringForUnion } from 'stream-chat';
|
|
2
|
+
export declare type Asset = {
|
|
3
|
+
duration: number | null;
|
|
4
|
+
filename: string;
|
|
5
|
+
fileSize: string;
|
|
6
|
+
height: number;
|
|
7
|
+
playableDuration: number | null;
|
|
8
|
+
source: 'camera' | 'picker';
|
|
9
|
+
type: string;
|
|
10
|
+
uri: string;
|
|
11
|
+
width: number;
|
|
12
|
+
id?: string;
|
|
13
|
+
size?: number | string;
|
|
14
|
+
};
|
|
15
|
+
export declare type File = {
|
|
16
|
+
name: string;
|
|
17
|
+
duration?: string | null;
|
|
18
|
+
size?: number | string;
|
|
19
|
+
type?: string;
|
|
20
|
+
uri?: string;
|
|
21
|
+
};
|
|
22
|
+
export declare type DefaultAttachmentType = UnknownType & {
|
|
23
|
+
file_size?: number | string;
|
|
24
|
+
mime_type?: string;
|
|
25
|
+
};
|
|
2
26
|
interface DefaultUserType extends UnknownType {
|
|
3
27
|
image?: string;
|
|
4
28
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "stream-chat-react-native-core",
|
|
3
3
|
"description": "The official React Native and Expo components for Stream Chat, a service for building chat applications",
|
|
4
|
-
"version": "4.
|
|
4
|
+
"version": "4.6.0-beta.1",
|
|
5
5
|
"author": {
|
|
6
6
|
"company": "Stream.io Inc",
|
|
7
7
|
"name": "Stream.io Inc"
|
|
@@ -19,6 +19,7 @@ import { SEVEN_Z } from '../../icons/SEVEN_Z';
|
|
|
19
19
|
import { TAR } from '../../icons/TAR';
|
|
20
20
|
import { TXT } from '../../icons/TXT';
|
|
21
21
|
import type { IconProps } from '../../icons/utils/base';
|
|
22
|
+
import { Video } from '../../icons/Video';
|
|
22
23
|
import { XLS } from '../../icons/XLS';
|
|
23
24
|
import { XLSX } from '../../icons/XLSX';
|
|
24
25
|
import { ZIP } from '../../icons/ZIP';
|
|
@@ -320,6 +321,46 @@ const zipFileTypes = [
|
|
|
320
321
|
'application/zip',
|
|
321
322
|
];
|
|
322
323
|
|
|
324
|
+
const videoFileTypes = [
|
|
325
|
+
'video/3g2',
|
|
326
|
+
'video/3gp',
|
|
327
|
+
'video/aaf',
|
|
328
|
+
'video/asf',
|
|
329
|
+
'video/avchd',
|
|
330
|
+
'video/avi',
|
|
331
|
+
'video/drc',
|
|
332
|
+
'video/flv',
|
|
333
|
+
'video/m2v',
|
|
334
|
+
'video/m3u8',
|
|
335
|
+
'video/m4p',
|
|
336
|
+
'video/m4v',
|
|
337
|
+
'video/mkv',
|
|
338
|
+
'video/mng',
|
|
339
|
+
'video/mov',
|
|
340
|
+
'video/mp2',
|
|
341
|
+
'video/mp4',
|
|
342
|
+
'video/mpe',
|
|
343
|
+
'video/mpeg',
|
|
344
|
+
'video/mpg',
|
|
345
|
+
'video/mpv',
|
|
346
|
+
'video/mxf',
|
|
347
|
+
'video/nsv',
|
|
348
|
+
'video/ogg',
|
|
349
|
+
'video/ogv',
|
|
350
|
+
'video/qt',
|
|
351
|
+
'video/rm',
|
|
352
|
+
'video/rmvb',
|
|
353
|
+
'video/roq',
|
|
354
|
+
'video/svi',
|
|
355
|
+
'video/vob',
|
|
356
|
+
'video/webm',
|
|
357
|
+
'video/wmv',
|
|
358
|
+
'video/yuv',
|
|
359
|
+
'video/quicktime',
|
|
360
|
+
'video/webm',
|
|
361
|
+
'video/x-flv',
|
|
362
|
+
];
|
|
363
|
+
|
|
323
364
|
const mimeTypeToIconMap: Record<string, React.FC<IconProps>> = {
|
|
324
365
|
'application/pdf': PDF, // .pdf
|
|
325
366
|
'application/rtf': RTF, // .rtf
|
|
@@ -371,6 +412,10 @@ for (const type of zipFileTypes) {
|
|
|
371
412
|
mimeTypeToIconMap[type] = ZIP;
|
|
372
413
|
}
|
|
373
414
|
|
|
415
|
+
for (const type of videoFileTypes) {
|
|
416
|
+
mimeTypeToIconMap[type] = Video;
|
|
417
|
+
}
|
|
418
|
+
|
|
374
419
|
function mimeTypeToIcon(mimeType?: string): React.FC<IconProps> {
|
|
375
420
|
if (!mimeType) return GenericFile;
|
|
376
421
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React, { useEffect, useMemo, useState } from 'react';
|
|
1
|
+
import React, { useEffect, useMemo, useRef, useState } from 'react';
|
|
2
2
|
import {
|
|
3
3
|
BackHandler,
|
|
4
4
|
Dimensions,
|
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
Platform,
|
|
8
8
|
StatusBar,
|
|
9
9
|
StyleSheet,
|
|
10
|
+
Text,
|
|
10
11
|
View,
|
|
11
12
|
} from 'react-native';
|
|
12
13
|
|
|
@@ -15,42 +16,161 @@ import BottomSheet, {
|
|
|
15
16
|
BottomSheetHandleProps,
|
|
16
17
|
TouchableOpacity,
|
|
17
18
|
} from '@gorhom/bottom-sheet';
|
|
19
|
+
import dayjs from 'dayjs';
|
|
20
|
+
import duration from 'dayjs/plugin/duration';
|
|
21
|
+
import { lookup } from 'mime-types';
|
|
18
22
|
|
|
19
23
|
import type { AttachmentPickerErrorProps } from './components/AttachmentPickerError';
|
|
20
24
|
|
|
21
25
|
import { useAttachmentPickerContext } from '../../contexts/attachmentPickerContext/AttachmentPickerContext';
|
|
22
26
|
import { useTheme } from '../../contexts/themeContext/ThemeContext';
|
|
23
|
-
import {
|
|
27
|
+
import { Recorder } from '../../icons';
|
|
28
|
+
import { getPhotos } from '../../native';
|
|
29
|
+
import type { Asset, File } from '../../types/types';
|
|
24
30
|
import { vh, vw } from '../../utils/utils';
|
|
25
31
|
|
|
32
|
+
dayjs.extend(duration);
|
|
33
|
+
|
|
26
34
|
const styles = StyleSheet.create({
|
|
27
35
|
container: {
|
|
28
36
|
flexGrow: 1,
|
|
29
37
|
},
|
|
38
|
+
durationText: {
|
|
39
|
+
fontWeight: 'bold',
|
|
40
|
+
},
|
|
30
41
|
overlay: {
|
|
31
42
|
alignItems: 'flex-end',
|
|
32
43
|
flex: 1,
|
|
33
44
|
},
|
|
45
|
+
videoView: {
|
|
46
|
+
bottom: 5,
|
|
47
|
+
display: 'flex',
|
|
48
|
+
flexDirection: 'row',
|
|
49
|
+
justifyContent: 'space-between',
|
|
50
|
+
paddingHorizontal: 5,
|
|
51
|
+
position: 'absolute',
|
|
52
|
+
width: '100%',
|
|
53
|
+
},
|
|
34
54
|
});
|
|
35
55
|
|
|
36
56
|
const screenHeight = vh(100);
|
|
37
57
|
const fullScreenHeight = Dimensions.get('window').height;
|
|
38
58
|
|
|
39
59
|
type AttachmentImageProps = {
|
|
60
|
+
asset: Asset;
|
|
40
61
|
ImageOverlaySelectedComponent: React.ComponentType;
|
|
41
|
-
|
|
62
|
+
maxNumberOfFiles: number;
|
|
42
63
|
selected: boolean;
|
|
43
|
-
|
|
64
|
+
setSelectedImages: React.Dispatch<React.SetStateAction<Asset[]>>;
|
|
44
65
|
numberOfAttachmentPickerImageColumns?: number;
|
|
45
66
|
};
|
|
46
67
|
|
|
68
|
+
type AttachmentVideoProps = {
|
|
69
|
+
asset: Asset;
|
|
70
|
+
ImageOverlaySelectedComponent: React.ComponentType;
|
|
71
|
+
maxNumberOfFiles: number;
|
|
72
|
+
selected: boolean;
|
|
73
|
+
setSelectedFiles: React.Dispatch<React.SetStateAction<File[]>>;
|
|
74
|
+
numberOfAttachmentPickerImageColumns?: number;
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
const AttachmentVideo: React.FC<AttachmentVideoProps> = (props) => {
|
|
78
|
+
const {
|
|
79
|
+
asset,
|
|
80
|
+
ImageOverlaySelectedComponent,
|
|
81
|
+
maxNumberOfFiles,
|
|
82
|
+
numberOfAttachmentPickerImageColumns,
|
|
83
|
+
selected,
|
|
84
|
+
setSelectedFiles,
|
|
85
|
+
} = props;
|
|
86
|
+
|
|
87
|
+
const {
|
|
88
|
+
theme: {
|
|
89
|
+
attachmentPicker: { durationText, image, imageOverlay },
|
|
90
|
+
colors: { overlay, white },
|
|
91
|
+
},
|
|
92
|
+
} = useTheme();
|
|
93
|
+
|
|
94
|
+
const { duration, playableDuration, uri } = asset;
|
|
95
|
+
|
|
96
|
+
const videoDuration = duration ? duration : playableDuration;
|
|
97
|
+
|
|
98
|
+
const ONE_HOUR_IN_SECONDS = 3600;
|
|
99
|
+
|
|
100
|
+
let durationLabel = '00:00';
|
|
101
|
+
|
|
102
|
+
if (videoDuration) {
|
|
103
|
+
const isDurationLongerThanHour = videoDuration / ONE_HOUR_IN_SECONDS >= 1;
|
|
104
|
+
const formattedDurationParam = isDurationLongerThanHour ? 'HH:mm:ss' : 'mm:ss';
|
|
105
|
+
const formattedVideoDuration = dayjs
|
|
106
|
+
.duration(videoDuration, 'second')
|
|
107
|
+
.format(formattedDurationParam);
|
|
108
|
+
durationLabel = formattedVideoDuration;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const size = vw(100) / (numberOfAttachmentPickerImageColumns || 3) - 2;
|
|
112
|
+
|
|
113
|
+
const onPressVideo = () => {
|
|
114
|
+
if (selected) {
|
|
115
|
+
setSelectedFiles((files) => files.filter((file) => file.uri !== asset.uri));
|
|
116
|
+
} else {
|
|
117
|
+
setSelectedFiles((files) => {
|
|
118
|
+
if (files.length >= maxNumberOfFiles) {
|
|
119
|
+
return files;
|
|
120
|
+
}
|
|
121
|
+
return [
|
|
122
|
+
...files,
|
|
123
|
+
{
|
|
124
|
+
duration: durationLabel,
|
|
125
|
+
name: asset.filename,
|
|
126
|
+
size: asset.fileSize,
|
|
127
|
+
type: 'video',
|
|
128
|
+
uri: asset.uri,
|
|
129
|
+
},
|
|
130
|
+
];
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
return (
|
|
136
|
+
<TouchableOpacity onPress={onPressVideo}>
|
|
137
|
+
<ImageBackground
|
|
138
|
+
source={{ uri }}
|
|
139
|
+
style={[
|
|
140
|
+
{
|
|
141
|
+
height: size,
|
|
142
|
+
margin: 1,
|
|
143
|
+
width: size,
|
|
144
|
+
},
|
|
145
|
+
image,
|
|
146
|
+
]}
|
|
147
|
+
>
|
|
148
|
+
{selected && (
|
|
149
|
+
<View style={[styles.overlay, { backgroundColor: overlay }, imageOverlay]}>
|
|
150
|
+
<ImageOverlaySelectedComponent />
|
|
151
|
+
</View>
|
|
152
|
+
)}
|
|
153
|
+
<View style={styles.videoView}>
|
|
154
|
+
<Recorder height={20} pathFill={white} width={25} />
|
|
155
|
+
{videoDuration ? (
|
|
156
|
+
<Text style={[styles.durationText, durationText, { color: white }]}>
|
|
157
|
+
{durationLabel}
|
|
158
|
+
</Text>
|
|
159
|
+
) : null}
|
|
160
|
+
</View>
|
|
161
|
+
</ImageBackground>
|
|
162
|
+
</TouchableOpacity>
|
|
163
|
+
);
|
|
164
|
+
};
|
|
165
|
+
|
|
47
166
|
const AttachmentImage: React.FC<AttachmentImageProps> = (props) => {
|
|
48
167
|
const {
|
|
168
|
+
asset,
|
|
49
169
|
ImageOverlaySelectedComponent,
|
|
170
|
+
maxNumberOfFiles,
|
|
50
171
|
numberOfAttachmentPickerImageColumns,
|
|
51
|
-
onPress,
|
|
52
172
|
selected,
|
|
53
|
-
|
|
173
|
+
setSelectedImages,
|
|
54
174
|
} = props;
|
|
55
175
|
const {
|
|
56
176
|
theme: {
|
|
@@ -61,8 +181,23 @@ const AttachmentImage: React.FC<AttachmentImageProps> = (props) => {
|
|
|
61
181
|
|
|
62
182
|
const size = vw(100) / (numberOfAttachmentPickerImageColumns || 3) - 2;
|
|
63
183
|
|
|
184
|
+
const { uri } = asset;
|
|
185
|
+
|
|
186
|
+
const onPressImage = () => {
|
|
187
|
+
if (selected) {
|
|
188
|
+
setSelectedImages((images) => images.filter((image) => image.uri !== asset.uri));
|
|
189
|
+
} else {
|
|
190
|
+
setSelectedImages((images) => {
|
|
191
|
+
if (images.length >= maxNumberOfFiles) {
|
|
192
|
+
return images;
|
|
193
|
+
}
|
|
194
|
+
return [...images, asset];
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
};
|
|
198
|
+
|
|
64
199
|
return (
|
|
65
|
-
<TouchableOpacity onPress={
|
|
200
|
+
<TouchableOpacity onPress={onPressImage}>
|
|
66
201
|
<ImageBackground
|
|
67
202
|
source={{ uri }}
|
|
68
203
|
style={[
|
|
@@ -92,6 +227,7 @@ const renderImage = ({
|
|
|
92
227
|
ImageOverlaySelectedComponent: React.ComponentType;
|
|
93
228
|
maxNumberOfFiles: number;
|
|
94
229
|
selected: boolean;
|
|
230
|
+
setSelectedFiles: React.Dispatch<React.SetStateAction<File[]>>;
|
|
95
231
|
setSelectedImages: React.Dispatch<React.SetStateAction<Asset[]>>;
|
|
96
232
|
numberOfAttachmentPickerImageColumns?: number;
|
|
97
233
|
};
|
|
@@ -102,28 +238,37 @@ const renderImage = ({
|
|
|
102
238
|
maxNumberOfFiles,
|
|
103
239
|
numberOfAttachmentPickerImageColumns,
|
|
104
240
|
selected,
|
|
241
|
+
setSelectedFiles,
|
|
105
242
|
setSelectedImages,
|
|
106
243
|
} = item;
|
|
107
|
-
const onPress = () => {
|
|
108
|
-
if (selected) {
|
|
109
|
-
setSelectedImages((images) => images.filter((image) => image.uri !== asset.uri));
|
|
110
|
-
} else {
|
|
111
|
-
setSelectedImages((images) => {
|
|
112
|
-
if (images.length >= maxNumberOfFiles) {
|
|
113
|
-
return images;
|
|
114
|
-
}
|
|
115
|
-
return [...images, asset];
|
|
116
|
-
});
|
|
117
|
-
}
|
|
118
|
-
};
|
|
119
244
|
|
|
120
|
-
|
|
245
|
+
const contentType = lookup(asset.filename) || 'multipart/form-data';
|
|
246
|
+
|
|
247
|
+
const fileType = asset.filename
|
|
248
|
+
? contentType.startsWith('image/')
|
|
249
|
+
? 'image'
|
|
250
|
+
: 'video'
|
|
251
|
+
: asset.type === 'video'
|
|
252
|
+
? 'video'
|
|
253
|
+
: 'image';
|
|
254
|
+
|
|
255
|
+
return fileType === 'image' ? (
|
|
121
256
|
<AttachmentImage
|
|
257
|
+
asset={asset}
|
|
258
|
+
ImageOverlaySelectedComponent={ImageOverlaySelectedComponent}
|
|
259
|
+
maxNumberOfFiles={maxNumberOfFiles}
|
|
260
|
+
numberOfAttachmentPickerImageColumns={numberOfAttachmentPickerImageColumns}
|
|
261
|
+
selected={selected}
|
|
262
|
+
setSelectedImages={setSelectedImages}
|
|
263
|
+
/>
|
|
264
|
+
) : (
|
|
265
|
+
<AttachmentVideo
|
|
266
|
+
asset={asset}
|
|
122
267
|
ImageOverlaySelectedComponent={ImageOverlaySelectedComponent}
|
|
268
|
+
maxNumberOfFiles={maxNumberOfFiles}
|
|
123
269
|
numberOfAttachmentPickerImageColumns={numberOfAttachmentPickerImageColumns}
|
|
124
|
-
onPress={onPress}
|
|
125
270
|
selected={selected}
|
|
126
|
-
|
|
271
|
+
setSelectedFiles={setSelectedFiles}
|
|
127
272
|
/>
|
|
128
273
|
);
|
|
129
274
|
};
|
|
@@ -187,8 +332,10 @@ export const AttachmentPicker = React.forwardRef(
|
|
|
187
332
|
const {
|
|
188
333
|
closePicker,
|
|
189
334
|
maxNumberOfFiles,
|
|
335
|
+
selectedFiles,
|
|
190
336
|
selectedImages,
|
|
191
337
|
selectedPicker,
|
|
338
|
+
setSelectedFiles,
|
|
192
339
|
setSelectedImages,
|
|
193
340
|
setSelectedPicker,
|
|
194
341
|
topInset,
|
|
@@ -200,13 +347,7 @@ export const AttachmentPicker = React.forwardRef(
|
|
|
200
347
|
const [hasNextPage, setHasNextPage] = useState(true);
|
|
201
348
|
const [loadingPhotos, setLoadingPhotos] = useState(false);
|
|
202
349
|
const [photos, setPhotos] = useState<Asset[]>([]);
|
|
203
|
-
|
|
204
|
-
const hideAttachmentPicker = () => {
|
|
205
|
-
setSelectedPicker(undefined);
|
|
206
|
-
if ((ref as React.MutableRefObject<BottomSheet>)?.current) {
|
|
207
|
-
(ref as React.MutableRefObject<BottomSheet>).current.close();
|
|
208
|
-
}
|
|
209
|
-
};
|
|
350
|
+
const bottomSheetCloseOnKeyboardShowTimeout = useRef<NodeJS.Timeout>();
|
|
210
351
|
|
|
211
352
|
const getMorePhotos = async () => {
|
|
212
353
|
if (hasNextPage && !loadingPhotos && currentIndex > -1 && selectedPicker === 'images') {
|
|
@@ -248,6 +389,20 @@ export const AttachmentPicker = React.forwardRef(
|
|
|
248
389
|
}, [selectedPicker]);
|
|
249
390
|
|
|
250
391
|
useEffect(() => {
|
|
392
|
+
const hideAttachmentPicker = () => {
|
|
393
|
+
if (bottomSheetCloseOnKeyboardShowTimeout.current) {
|
|
394
|
+
clearTimeout(bottomSheetCloseOnKeyboardShowTimeout.current);
|
|
395
|
+
}
|
|
396
|
+
setSelectedPicker(undefined);
|
|
397
|
+
// This short timeout is to prevent a race condition
|
|
398
|
+
// where the close function is called during the point when a internal container layout happens within the bottomsheet due to keyboard affecting the layout
|
|
399
|
+
// If the container layout measures a shorter height than previous but if the close snapped to the previous height's position, the bottom sheet will show up
|
|
400
|
+
// this short delay ensures that close function is always called after a container layout due to keyboard change
|
|
401
|
+
bottomSheetCloseOnKeyboardShowTimeout.current = setTimeout(
|
|
402
|
+
() => (ref as React.MutableRefObject<BottomSheet | undefined>).current?.close(),
|
|
403
|
+
150,
|
|
404
|
+
);
|
|
405
|
+
};
|
|
251
406
|
const keyboardSubscription =
|
|
252
407
|
Platform.OS === 'ios'
|
|
253
408
|
? Keyboard.addListener('keyboardWillShow', hideAttachmentPicker)
|
|
@@ -265,6 +420,9 @@ export const AttachmentPicker = React.forwardRef(
|
|
|
265
420
|
} else {
|
|
266
421
|
Keyboard.removeListener('keyboardDidShow', hideAttachmentPicker);
|
|
267
422
|
}
|
|
423
|
+
if (bottomSheetCloseOnKeyboardShowTimeout.current) {
|
|
424
|
+
clearTimeout(bottomSheetCloseOnKeyboardShowTimeout.current);
|
|
425
|
+
}
|
|
268
426
|
};
|
|
269
427
|
}, []);
|
|
270
428
|
|
|
@@ -295,7 +453,10 @@ export const AttachmentPicker = React.forwardRef(
|
|
|
295
453
|
ImageOverlaySelectedComponent,
|
|
296
454
|
maxNumberOfFiles,
|
|
297
455
|
numberOfAttachmentPickerImageColumns,
|
|
298
|
-
selected:
|
|
456
|
+
selected:
|
|
457
|
+
selectedImages.some((image) => image.uri === asset.uri) ||
|
|
458
|
+
selectedFiles.some((file) => file.uri === asset.uri),
|
|
459
|
+
setSelectedFiles,
|
|
299
460
|
setSelectedImages,
|
|
300
461
|
}));
|
|
301
462
|
|
|
@@ -326,35 +487,24 @@ export const AttachmentPicker = React.forwardRef(
|
|
|
326
487
|
: statusBarHeight
|
|
327
488
|
: 0;
|
|
328
489
|
|
|
329
|
-
const initialSnapPoint =
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
(fullScreenHeight - screenHeight + androidBottomBarHeightAdjustment) -
|
|
334
|
-
handleHeight
|
|
335
|
-
: 308 + (fullScreenHeight - screenHeight + androidBottomBarHeightAdjustment),
|
|
336
|
-
[
|
|
337
|
-
attachmentPickerBottomSheetHeight,
|
|
338
|
-
androidBottomBarHeightAdjustment,
|
|
339
|
-
fullScreenHeight,
|
|
340
|
-
handleHeight,
|
|
341
|
-
screenHeight,
|
|
342
|
-
],
|
|
343
|
-
);
|
|
490
|
+
const initialSnapPoint =
|
|
491
|
+
attachmentPickerBottomSheetHeight ?? Platform.OS === 'android'
|
|
492
|
+
? 308 + (fullScreenHeight - screenHeight + androidBottomBarHeightAdjustment) - handleHeight
|
|
493
|
+
: 308 + (fullScreenHeight - screenHeight + androidBottomBarHeightAdjustment);
|
|
344
494
|
|
|
345
|
-
const finalSnapPoint =
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
: fullScreenHeight - topInset,
|
|
350
|
-
[fullScreenHeight, handleHeight, topInset],
|
|
351
|
-
);
|
|
495
|
+
const finalSnapPoint =
|
|
496
|
+
Platform.OS === 'android'
|
|
497
|
+
? fullScreenHeight - topInset - handleHeight
|
|
498
|
+
: fullScreenHeight - topInset;
|
|
352
499
|
|
|
353
500
|
/**
|
|
354
501
|
* Snap points changing cause a rerender of the position,
|
|
355
502
|
* this is an issue if you are calling close on the bottom sheet.
|
|
356
503
|
*/
|
|
357
|
-
const snapPoints =
|
|
504
|
+
const snapPoints = useMemo(
|
|
505
|
+
() => [initialSnapPoint, finalSnapPoint],
|
|
506
|
+
[initialSnapPoint, finalSnapPoint],
|
|
507
|
+
);
|
|
358
508
|
|
|
359
509
|
return (
|
|
360
510
|
<>
|
|
@@ -370,7 +520,7 @@ export const AttachmentPicker = React.forwardRef(
|
|
|
370
520
|
}
|
|
371
521
|
handleHeight={handleHeight}
|
|
372
522
|
index={-1}
|
|
373
|
-
onChange={
|
|
523
|
+
onChange={setCurrentIndex}
|
|
374
524
|
ref={ref}
|
|
375
525
|
snapPoints={snapPoints}
|
|
376
526
|
>
|
|
@@ -143,7 +143,7 @@ const MessageContentWithContext = <
|
|
|
143
143
|
colors: { accent_red, blue_alice, grey_gainsboro, grey_whisper, transparent, white },
|
|
144
144
|
messageSimple: {
|
|
145
145
|
content: {
|
|
146
|
-
container: { borderRadiusL, borderRadiusS, ...container },
|
|
146
|
+
container: { borderRadius, borderRadiusL, borderRadiusS, ...container },
|
|
147
147
|
containerInner,
|
|
148
148
|
errorContainer,
|
|
149
149
|
errorIcon,
|
|
@@ -223,6 +223,22 @@ const MessageContentWithContext = <
|
|
|
223
223
|
|
|
224
224
|
const repliesCurveColor = isMyMessage && !error ? backgroundColor : grey_whisper;
|
|
225
225
|
|
|
226
|
+
const isBorderColor = isMyMessage && !error;
|
|
227
|
+
|
|
228
|
+
const shouldApplyBorderRadius = (firstGroupStyle: string, secondGroupStyle: string): boolean =>
|
|
229
|
+
(groupStyle === firstGroupStyle || groupStyle === secondGroupStyle) &&
|
|
230
|
+
(!hasThreadReplies || threadList);
|
|
231
|
+
|
|
232
|
+
const applyBorderRadius = (
|
|
233
|
+
firstGroupStyle: string,
|
|
234
|
+
secondGroupStyle: string,
|
|
235
|
+
): number | undefined => {
|
|
236
|
+
if (shouldApplyBorderRadius(firstGroupStyle, secondGroupStyle)) {
|
|
237
|
+
return borderRadiusS;
|
|
238
|
+
}
|
|
239
|
+
return borderRadiusL;
|
|
240
|
+
};
|
|
241
|
+
|
|
226
242
|
return (
|
|
227
243
|
<TouchableOpacity
|
|
228
244
|
activeOpacity={0.7}
|
|
@@ -297,17 +313,12 @@ const MessageContentWithContext = <
|
|
|
297
313
|
styles.containerInner,
|
|
298
314
|
{
|
|
299
315
|
backgroundColor,
|
|
300
|
-
borderBottomLeftRadius:
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
(groupStyle === 'right_bottom' || groupStyle === 'right_single') &&
|
|
307
|
-
(!hasThreadReplies || threadList)
|
|
308
|
-
? borderRadiusS
|
|
309
|
-
: borderRadiusL,
|
|
310
|
-
borderColor: isMyMessage && !error ? backgroundColor : grey_whisper,
|
|
316
|
+
borderBottomLeftRadius: applyBorderRadius('left_bottom', 'left_single'),
|
|
317
|
+
borderBottomRightRadius: applyBorderRadius('right_bottom', 'right_single'),
|
|
318
|
+
borderColor: isBorderColor ? backgroundColor : grey_whisper,
|
|
319
|
+
borderRadius,
|
|
320
|
+
borderTopLeftRadius: applyBorderRadius('left_top', 'right_single'),
|
|
321
|
+
borderTopRightRadius: applyBorderRadius('right_top', 'right_single'),
|
|
311
322
|
},
|
|
312
323
|
noBorder ? { borderWidth: 0 } : {},
|
|
313
324
|
containerInner,
|
|
@@ -105,7 +105,7 @@ const UnsupportedFileTypeOrFileSizeIndicator = ({
|
|
|
105
105
|
</View>
|
|
106
106
|
) : (
|
|
107
107
|
<Text style={[styles.fileSizeText, { color: grey }, fileSizeText]}>
|
|
108
|
-
{getFileSizeDisplayText(item.file.size)}
|
|
108
|
+
{item.file.duration ? item.file.duration : getFileSizeDisplayText(item.file.size)}
|
|
109
109
|
</Text>
|
|
110
110
|
);
|
|
111
111
|
};
|