stream-chat-react-native-core 5.30.1-beta.1 → 5.31.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/Chat/hooks/useAppSettings.js +2 -4
- package/lib/commonjs/components/Chat/hooks/useAppSettings.js.map +1 -1
- package/lib/commonjs/components/MessageInput/FileUploadPreview.js +5 -6
- package/lib/commonjs/components/MessageInput/FileUploadPreview.js.map +1 -1
- package/lib/commonjs/components/MessageInput/MessageInput.js +4 -26
- package/lib/commonjs/components/MessageInput/MessageInput.js.map +1 -1
- package/lib/commonjs/contexts/messageInputContext/MessageInputContext.js +89 -76
- package/lib/commonjs/contexts/messageInputContext/MessageInputContext.js.map +1 -1
- package/lib/commonjs/contexts/messageInputContext/utils/utils.js +66 -0
- package/lib/commonjs/contexts/messageInputContext/utils/utils.js.map +1 -0
- package/lib/commonjs/i18n/en.json +1 -1
- package/lib/commonjs/i18n/es.json +21 -21
- package/lib/commonjs/i18n/fr.json +21 -21
- package/lib/commonjs/i18n/he.json +21 -21
- package/lib/commonjs/i18n/hi.json +21 -21
- package/lib/commonjs/i18n/it.json +21 -21
- package/lib/commonjs/i18n/ja.json +21 -21
- package/lib/commonjs/i18n/ko.json +21 -21
- package/lib/commonjs/i18n/nl.json +21 -21
- package/lib/commonjs/i18n/pt-BR.json +21 -21
- package/lib/commonjs/i18n/ru.json +21 -21
- package/lib/commonjs/i18n/tr.json +21 -21
- package/lib/commonjs/version.json +1 -1
- package/lib/module/components/Chat/hooks/useAppSettings.js +2 -4
- package/lib/module/components/Chat/hooks/useAppSettings.js.map +1 -1
- package/lib/module/components/MessageInput/FileUploadPreview.js +5 -6
- package/lib/module/components/MessageInput/FileUploadPreview.js.map +1 -1
- package/lib/module/components/MessageInput/MessageInput.js +4 -26
- package/lib/module/components/MessageInput/MessageInput.js.map +1 -1
- package/lib/module/contexts/messageInputContext/MessageInputContext.js +89 -76
- package/lib/module/contexts/messageInputContext/MessageInputContext.js.map +1 -1
- package/lib/module/contexts/messageInputContext/utils/utils.js +66 -0
- package/lib/module/contexts/messageInputContext/utils/utils.js.map +1 -0
- package/lib/module/i18n/en.json +1 -1
- package/lib/module/i18n/es.json +21 -21
- package/lib/module/i18n/fr.json +21 -21
- package/lib/module/i18n/he.json +21 -21
- package/lib/module/i18n/hi.json +21 -21
- package/lib/module/i18n/it.json +21 -21
- package/lib/module/i18n/ja.json +21 -21
- package/lib/module/i18n/ko.json +21 -21
- package/lib/module/i18n/nl.json +21 -21
- package/lib/module/i18n/pt-BR.json +21 -21
- package/lib/module/i18n/ru.json +21 -21
- package/lib/module/i18n/tr.json +21 -21
- package/lib/module/version.json +1 -1
- package/lib/typescript/components/Chat/hooks/useAppSettings.d.ts.map +1 -1
- package/lib/typescript/components/MessageInput/FileUploadPreview.d.ts.map +1 -1
- package/lib/typescript/components/MessageInput/MessageInput.d.ts.map +1 -1
- package/lib/typescript/contexts/messageInputContext/MessageInputContext.d.ts.map +1 -1
- package/lib/typescript/contexts/messageInputContext/utils/utils.d.ts +22 -0
- package/lib/typescript/contexts/messageInputContext/utils/utils.d.ts.map +1 -0
- package/lib/typescript/i18n/en.json +1 -1
- package/lib/typescript/i18n/es.json +21 -21
- package/lib/typescript/i18n/fr.json +21 -21
- package/lib/typescript/i18n/he.json +21 -21
- package/lib/typescript/i18n/hi.json +21 -21
- package/lib/typescript/i18n/it.json +21 -21
- package/lib/typescript/i18n/ja.json +21 -21
- package/lib/typescript/i18n/ko.json +21 -21
- package/lib/typescript/i18n/nl.json +21 -21
- package/lib/typescript/i18n/pt-BR.json +21 -21
- package/lib/typescript/i18n/ru.json +21 -21
- package/lib/typescript/i18n/tr.json +21 -21
- package/lib/typescript/utils/Streami18n.d.ts +1 -1
- package/package.json +1 -1
- package/src/components/Chat/hooks/useAppSettings.ts +4 -6
- package/src/components/MessageInput/FileUploadPreview.tsx +2 -3
- package/src/components/MessageInput/MessageInput.tsx +4 -45
- package/src/contexts/messageInputContext/MessageInputContext.tsx +60 -55
- package/src/contexts/messageInputContext/__tests__/MessageInputContext.test.tsx +9 -0
- package/src/contexts/messageInputContext/__tests__/pickFile.test.tsx +8 -3
- package/src/contexts/messageInputContext/utils/utils.ts +86 -0
- package/src/i18n/en.json +1 -1
- package/src/i18n/es.json +21 -21
- package/src/i18n/fr.json +21 -21
- package/src/i18n/he.json +21 -21
- package/src/i18n/hi.json +21 -21
- package/src/i18n/it.json +21 -21
- package/src/i18n/ja.json +21 -21
- package/src/i18n/ko.json +21 -21
- package/src/i18n/nl.json +21 -21
- package/src/i18n/pt-BR.json +21 -21
- package/src/i18n/ru.json +21 -21
- package/src/i18n/tr.json +21 -21
- package/src/version.json +1 -1
|
@@ -51,16 +51,15 @@ const styles = StyleSheet.create({
|
|
|
51
51
|
filenameText: {
|
|
52
52
|
fontSize: 14,
|
|
53
53
|
fontWeight: 'bold',
|
|
54
|
-
paddingHorizontal: 10,
|
|
55
54
|
},
|
|
56
55
|
fileSizeText: {
|
|
57
56
|
fontSize: 12,
|
|
58
57
|
marginTop: 10,
|
|
59
|
-
paddingHorizontal: 10,
|
|
60
58
|
},
|
|
61
59
|
fileTextContainer: {
|
|
62
60
|
justifyContent: 'space-around',
|
|
63
61
|
marginVertical: 10,
|
|
62
|
+
paddingHorizontal: 10,
|
|
64
63
|
},
|
|
65
64
|
flatList: { marginBottom: 12, maxHeight: FILE_PREVIEW_HEIGHT * 2.5 + 16 },
|
|
66
65
|
overlay: {
|
|
@@ -70,7 +69,7 @@ const styles = StyleSheet.create({
|
|
|
70
69
|
},
|
|
71
70
|
unsupportedFile: {
|
|
72
71
|
flexDirection: 'row',
|
|
73
|
-
|
|
72
|
+
paddingTop: 10,
|
|
74
73
|
},
|
|
75
74
|
unsupportedFileText: {
|
|
76
75
|
fontSize: 12,
|
|
@@ -1,11 +1,5 @@
|
|
|
1
1
|
import React, { useEffect, useMemo, useState } from 'react';
|
|
2
|
-
import {
|
|
3
|
-
Alert,
|
|
4
|
-
NativeSyntheticEvent,
|
|
5
|
-
StyleSheet,
|
|
6
|
-
TextInputFocusEventData,
|
|
7
|
-
View,
|
|
8
|
-
} from 'react-native';
|
|
2
|
+
import { NativeSyntheticEvent, StyleSheet, TextInputFocusEventData, View } from 'react-native';
|
|
9
3
|
|
|
10
4
|
import {
|
|
11
5
|
GestureEvent,
|
|
@@ -231,7 +225,6 @@ const MessageInputWithContext = <
|
|
|
231
225
|
} = props;
|
|
232
226
|
|
|
233
227
|
const [height, setHeight] = useState(0);
|
|
234
|
-
const { t } = useTranslationContext();
|
|
235
228
|
|
|
236
229
|
const {
|
|
237
230
|
theme: {
|
|
@@ -323,9 +316,6 @@ const MessageInputWithContext = <
|
|
|
323
316
|
}
|
|
324
317
|
}, [imagesForInput]);
|
|
325
318
|
|
|
326
|
-
const MEGA_BYTES_TO_BYTES = 1024 * 1024;
|
|
327
|
-
const MAX_FILE_SIZE_TO_UPLOAD_IN_MB = 100;
|
|
328
|
-
|
|
329
319
|
const uploadImagesHandler = () => {
|
|
330
320
|
const imageToUpload = selectedImages.find((selectedImage) => {
|
|
331
321
|
const uploadedImage = imageUploads.find(
|
|
@@ -334,23 +324,8 @@ const MessageInputWithContext = <
|
|
|
334
324
|
);
|
|
335
325
|
return !uploadedImage;
|
|
336
326
|
});
|
|
337
|
-
|
|
338
|
-
if (
|
|
339
|
-
imageToUpload &&
|
|
340
|
-
Number(imageToUpload.size) / MEGA_BYTES_TO_BYTES > MAX_FILE_SIZE_TO_UPLOAD_IN_MB
|
|
341
|
-
) {
|
|
342
|
-
Alert.alert(
|
|
343
|
-
t(
|
|
344
|
-
`Maximum file size upload limit reached. Please upload a file below {{MAX_FILE_SIZE_TO_UPLOAD_IN_MB}} MB.`,
|
|
345
|
-
{ MAX_FILE_SIZE_TO_UPLOAD_IN_MB },
|
|
346
|
-
),
|
|
347
|
-
);
|
|
348
|
-
setSelectedImages(
|
|
349
|
-
selectedImages.filter((selectedImage) => selectedImage.uri !== imageToUpload.uri),
|
|
350
|
-
);
|
|
351
|
-
} else {
|
|
352
|
-
if (imageToUpload) uploadNewImage(imageToUpload);
|
|
353
|
-
}
|
|
327
|
+
|
|
328
|
+
if (imageToUpload) uploadNewImage(imageToUpload);
|
|
354
329
|
};
|
|
355
330
|
|
|
356
331
|
const removeImagesHandler = () => {
|
|
@@ -386,23 +361,7 @@ const MessageInputWithContext = <
|
|
|
386
361
|
);
|
|
387
362
|
return !uploadedFile;
|
|
388
363
|
});
|
|
389
|
-
|
|
390
|
-
if (
|
|
391
|
-
fileToUpload &&
|
|
392
|
-
Number(fileToUpload.size) / MEGA_BYTES_TO_BYTES > MAX_FILE_SIZE_TO_UPLOAD_IN_MB
|
|
393
|
-
) {
|
|
394
|
-
Alert.alert(
|
|
395
|
-
t(
|
|
396
|
-
`Maximum file size upload limit reached. Please upload a file below {{MAX_FILE_SIZE_TO_UPLOAD_IN_MB}} MB.`,
|
|
397
|
-
{ MAX_FILE_SIZE_TO_UPLOAD_IN_MB },
|
|
398
|
-
),
|
|
399
|
-
);
|
|
400
|
-
setSelectedFiles(
|
|
401
|
-
selectedFiles.filter((selectedFile) => selectedFile.uri !== fileToUpload.uri),
|
|
402
|
-
);
|
|
403
|
-
} else {
|
|
404
|
-
if (fileToUpload) uploadNewFile(fileToUpload);
|
|
405
|
-
}
|
|
364
|
+
if (fileToUpload) uploadNewFile(fileToUpload);
|
|
406
365
|
} else {
|
|
407
366
|
/** User de-selected a video in bottom sheet attachment picker */
|
|
408
367
|
const filesToRemove = fileUploads.filter(
|
|
@@ -21,6 +21,8 @@ import {
|
|
|
21
21
|
import { useCreateMessageInputContext } from './hooks/useCreateMessageInputContext';
|
|
22
22
|
import { useMessageDetailsForState } from './hooks/useMessageDetailsForState';
|
|
23
23
|
|
|
24
|
+
import { isUploadAllowed, MAX_FILE_SIZE_TO_UPLOAD, prettifyFileSize } from './utils/utils';
|
|
25
|
+
|
|
24
26
|
import { AudioAttachmentProps } from '../../components/Attachment/AudioAttachment';
|
|
25
27
|
import { parseLinksFromText } from '../../components/Message/MessageSimple/utils/parseLinks';
|
|
26
28
|
import type { AttachButtonProps } from '../../components/MessageInput/AttachButton';
|
|
@@ -483,34 +485,36 @@ export const MessageInputProvider = <
|
|
|
483
485
|
}: PropsWithChildren<{
|
|
484
486
|
value: InputMessageInputContextValue<StreamChatGenerics>;
|
|
485
487
|
}>) => {
|
|
486
|
-
const {
|
|
487
|
-
|
|
488
|
+
const {
|
|
489
|
+
closePicker,
|
|
490
|
+
openPicker,
|
|
491
|
+
selectedFiles,
|
|
492
|
+
selectedImages,
|
|
493
|
+
selectedPicker,
|
|
494
|
+
setSelectedFiles,
|
|
495
|
+
setSelectedImages,
|
|
496
|
+
setSelectedPicker,
|
|
497
|
+
} = useAttachmentPickerContext();
|
|
488
498
|
const { appSettings, client, enableOfflineSupport } = useChatContext<StreamChatGenerics>();
|
|
489
499
|
const { removeMessage } = useMessagesContext();
|
|
490
500
|
|
|
491
501
|
const getFileUploadConfig = () => {
|
|
492
502
|
const fileConfig = appSettings?.app?.file_upload_config;
|
|
493
|
-
if (fileConfig !==
|
|
503
|
+
if (fileConfig !== undefined) {
|
|
494
504
|
return fileConfig;
|
|
495
505
|
} else {
|
|
496
506
|
return {};
|
|
497
507
|
}
|
|
498
508
|
};
|
|
499
509
|
|
|
500
|
-
const blockedFileExtensionTypes = getFileUploadConfig()?.blocked_file_extensions;
|
|
501
|
-
const blockedFileMimeTypes = getFileUploadConfig()?.blocked_mime_types;
|
|
502
|
-
|
|
503
510
|
const getImageUploadConfig = () => {
|
|
504
511
|
const imageConfig = appSettings?.app?.image_upload_config;
|
|
505
|
-
if (imageConfig !==
|
|
512
|
+
if (imageConfig !== undefined) {
|
|
506
513
|
return imageConfig;
|
|
507
514
|
}
|
|
508
515
|
return {};
|
|
509
516
|
};
|
|
510
517
|
|
|
511
|
-
const blockedImageExtensionTypes = getImageUploadConfig()?.blocked_file_extensions;
|
|
512
|
-
const blockedImageMimeTypes = getImageUploadConfig()?.blocked_mime_types;
|
|
513
|
-
|
|
514
518
|
const channelCapabities = useOwnCapabilitiesContext();
|
|
515
519
|
|
|
516
520
|
const { channel, giphyEnabled, uploadAbortControllerRef } =
|
|
@@ -663,30 +667,17 @@ export const MessageInputProvider = <
|
|
|
663
667
|
maxNumberOfFiles: value.maxNumberOfFiles - numberOfUploads,
|
|
664
668
|
});
|
|
665
669
|
|
|
666
|
-
const MEGA_BYTES_TO_BYTES = 1024 * 1024;
|
|
667
|
-
const MAX_FILE_SIZE_TO_UPLOAD_IN_MB = 100;
|
|
668
|
-
|
|
669
670
|
if (!result.cancelled && result.assets) {
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
/**
|
|
681
|
-
* TODO: The current tight coupling of images to the image
|
|
682
|
-
* picker does not allow images picked from the file picker
|
|
683
|
-
* to be rendered in a preview via the uploadNewImage call.
|
|
684
|
-
* This should be updated alongside allowing image a file
|
|
685
|
-
* uploads together.
|
|
686
|
-
*/
|
|
687
|
-
uploadNewFile(asset);
|
|
688
|
-
});
|
|
689
|
-
}
|
|
671
|
+
result.assets.forEach((asset) => {
|
|
672
|
+
/**
|
|
673
|
+
* TODO: The current tight coupling of images to the image
|
|
674
|
+
* picker does not allow images picked from the file picker
|
|
675
|
+
* to be rendered in a preview via the uploadNewImage call.
|
|
676
|
+
* This should be updated alongside allowing image a file
|
|
677
|
+
* uploads together.
|
|
678
|
+
*/
|
|
679
|
+
uploadNewFile(asset);
|
|
680
|
+
});
|
|
690
681
|
}
|
|
691
682
|
};
|
|
692
683
|
|
|
@@ -1191,18 +1182,25 @@ export const MessageInputProvider = <
|
|
|
1191
1182
|
|
|
1192
1183
|
const uploadNewFile = async (file: File) => {
|
|
1193
1184
|
const id: string = generateRandomId();
|
|
1185
|
+
const fileConfig = getFileUploadConfig();
|
|
1186
|
+
const { size_limit } = fileConfig;
|
|
1194
1187
|
|
|
1195
|
-
const
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1188
|
+
const isAllowed = isUploadAllowed({ config: fileConfig, file });
|
|
1189
|
+
|
|
1190
|
+
const sizeLimit = size_limit || MAX_FILE_SIZE_TO_UPLOAD;
|
|
1191
|
+
|
|
1192
|
+
if (file.size && file.size > sizeLimit) {
|
|
1193
|
+
Alert.alert(
|
|
1194
|
+
t('File is too large: {{ size }}, maximum upload size is {{ limit }}', {
|
|
1195
|
+
limit: prettifyFileSize(sizeLimit),
|
|
1196
|
+
size: prettifyFileSize(file.size),
|
|
1197
|
+
}),
|
|
1198
|
+
);
|
|
1199
|
+
setSelectedFiles(selectedFiles.filter((selectedFile) => selectedFile.uri !== file.uri));
|
|
1200
|
+
return;
|
|
1201
|
+
}
|
|
1201
1202
|
|
|
1202
|
-
const fileState =
|
|
1203
|
-
isBlockedFileExtension || isBlockedFileMimeType
|
|
1204
|
-
? FileState.NOT_SUPPORTED
|
|
1205
|
-
: FileState.UPLOADING;
|
|
1203
|
+
const fileState = isAllowed ? FileState.UPLOADING : FileState.NOT_SUPPORTED;
|
|
1206
1204
|
|
|
1207
1205
|
// If file type is explicitly provided while upload we use it, else we derive the file type.
|
|
1208
1206
|
const fileType = file.type || file.mimeType?.split('/')[0];
|
|
@@ -1220,26 +1218,33 @@ export const MessageInputProvider = <
|
|
|
1220
1218
|
setNumberOfUploads((prevNumberOfUploads) => prevNumberOfUploads + 1),
|
|
1221
1219
|
]);
|
|
1222
1220
|
|
|
1223
|
-
if (
|
|
1221
|
+
if (isAllowed) {
|
|
1224
1222
|
uploadFile({ newFile });
|
|
1225
1223
|
}
|
|
1226
1224
|
};
|
|
1227
1225
|
|
|
1228
1226
|
const uploadNewImage = async (image: Partial<Asset>) => {
|
|
1229
1227
|
const id = generateRandomId();
|
|
1228
|
+
const imageUploadConfig = getImageUploadConfig();
|
|
1230
1229
|
|
|
1231
|
-
const
|
|
1232
|
-
image.uri?.includes(mimeType),
|
|
1233
|
-
);
|
|
1230
|
+
const { size_limit } = imageUploadConfig;
|
|
1234
1231
|
|
|
1235
|
-
const
|
|
1236
|
-
|
|
1237
|
-
|
|
1232
|
+
const isAllowed = isUploadAllowed({ config: imageUploadConfig, file: image });
|
|
1233
|
+
|
|
1234
|
+
const sizeLimit = size_limit || MAX_FILE_SIZE_TO_UPLOAD;
|
|
1235
|
+
|
|
1236
|
+
if (image.size && image?.size > sizeLimit) {
|
|
1237
|
+
Alert.alert(
|
|
1238
|
+
t('File is too large: {{ size }}, maximum upload size is {{ limit }}', {
|
|
1239
|
+
limit: prettifyFileSize(sizeLimit),
|
|
1240
|
+
size: prettifyFileSize(image.size),
|
|
1241
|
+
}),
|
|
1242
|
+
);
|
|
1243
|
+
setSelectedImages(selectedImages.filter((selectedImage) => selectedImage.uri !== image.uri));
|
|
1244
|
+
return;
|
|
1245
|
+
}
|
|
1238
1246
|
|
|
1239
|
-
const imageState =
|
|
1240
|
-
isBlockedImageExtension || isBlockedImageMimeType
|
|
1241
|
-
? FileState.NOT_SUPPORTED
|
|
1242
|
-
: FileState.UPLOADING;
|
|
1247
|
+
const imageState = isAllowed ? FileState.UPLOADING : FileState.NOT_SUPPORTED;
|
|
1243
1248
|
|
|
1244
1249
|
const newImage: ImageUpload = {
|
|
1245
1250
|
file: image,
|
|
@@ -1255,7 +1260,7 @@ export const MessageInputProvider = <
|
|
|
1255
1260
|
setNumberOfUploads((prevNumberOfUploads) => prevNumberOfUploads + 1),
|
|
1256
1261
|
]);
|
|
1257
1262
|
|
|
1258
|
-
if (
|
|
1263
|
+
if (isAllowed) {
|
|
1259
1264
|
uploadImage({ newImage });
|
|
1260
1265
|
}
|
|
1261
1266
|
};
|
|
@@ -118,12 +118,21 @@ describe('MessageInputContext', () => {
|
|
|
118
118
|
act(() => {
|
|
119
119
|
result.current.uploadNewImage(
|
|
120
120
|
generateImageAttachment({
|
|
121
|
+
name: 'dummy.png',
|
|
121
122
|
uri: 'https://www.bastiaanmulder.nl/wp-content/uploads/2013/11/dummy-image-square.png',
|
|
122
123
|
}),
|
|
123
124
|
);
|
|
124
125
|
});
|
|
125
126
|
|
|
126
127
|
expect(result.current.imageUploads[0].state).toBe(FileState.NOT_SUPPORTED);
|
|
128
|
+
|
|
129
|
+
act(() => {
|
|
130
|
+
result.current.uploadNewFile({
|
|
131
|
+
name: 'dummy.mp3',
|
|
132
|
+
uri: 'https://www.bastiaanmulder.nl/wp-content/uploads/2013/11/dummy.mp3',
|
|
133
|
+
});
|
|
134
|
+
});
|
|
135
|
+
expect(result.current.imageUploads[0].state).toBe(FileState.NOT_SUPPORTED);
|
|
127
136
|
});
|
|
128
137
|
|
|
129
138
|
it('onSelectItem works', () => {
|
|
@@ -9,8 +9,9 @@ import { generateFileAttachment } from '../../../mock-builders/generator/attachm
|
|
|
9
9
|
import { generateMessage } from '../../../mock-builders/generator/message';
|
|
10
10
|
import { generateUser } from '../../../mock-builders/generator/user';
|
|
11
11
|
import * as NativeUtils from '../../../native';
|
|
12
|
-
|
|
13
12
|
import type { DefaultStreamChatGenerics } from '../../../types/types';
|
|
13
|
+
import * as AttachmentPickerContext from '../../attachmentPickerContext/AttachmentPickerContext';
|
|
14
|
+
|
|
14
15
|
import {
|
|
15
16
|
InputMessageInputContextValue,
|
|
16
17
|
MessageInputContextValue,
|
|
@@ -51,13 +52,17 @@ describe("MessageInputContext's pickFile", () => {
|
|
|
51
52
|
cancelled: false,
|
|
52
53
|
}),
|
|
53
54
|
);
|
|
55
|
+
jest.spyOn(AttachmentPickerContext, 'useAttachmentPickerContext').mockImplementation(() => ({
|
|
56
|
+
selectedFiles: [],
|
|
57
|
+
setSelectedFiles: jest.fn(),
|
|
58
|
+
}));
|
|
54
59
|
|
|
55
60
|
const initialProps = {
|
|
56
61
|
editing: message,
|
|
57
62
|
maxNumberOfFiles: 2,
|
|
58
63
|
};
|
|
59
64
|
|
|
60
|
-
it.each([[3,
|
|
65
|
+
it.each([[3, 2]])(
|
|
61
66
|
'run pickFile when numberOfUploads is %d and alert is triggered %d number of times',
|
|
62
67
|
async (numberOfUploads, numberOfTimesCalled) => {
|
|
63
68
|
const { rerender, result } = renderHook(() => useMessageInputContext(), {
|
|
@@ -103,6 +108,6 @@ describe("MessageInputContext's pickFile", () => {
|
|
|
103
108
|
result.current.pickFile();
|
|
104
109
|
});
|
|
105
110
|
|
|
106
|
-
expect(Alert.alert).toHaveBeenCalledTimes(
|
|
111
|
+
expect(Alert.alert).toHaveBeenCalledTimes(2);
|
|
107
112
|
});
|
|
108
113
|
});
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { lookup } from 'mime-types';
|
|
2
|
+
import type { FileUploadConfig } from 'stream-chat';
|
|
3
|
+
|
|
4
|
+
import { Asset, File } from '../../../types/types';
|
|
5
|
+
|
|
6
|
+
export const MAX_FILE_SIZE_TO_UPLOAD = 100 * 1024 * 1024; // 100 MB
|
|
7
|
+
|
|
8
|
+
type CheckUploadPermissionsParams = {
|
|
9
|
+
config: FileUploadConfig;
|
|
10
|
+
file: File | Partial<Asset>;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* This utility function checks if the file upload is allowed based on the file upload config.
|
|
15
|
+
* @param Object File upload config and file to check
|
|
16
|
+
* @returns
|
|
17
|
+
*/
|
|
18
|
+
export const isUploadAllowed = ({ config, file }: CheckUploadPermissionsParams) => {
|
|
19
|
+
const {
|
|
20
|
+
allowed_file_extensions,
|
|
21
|
+
allowed_mime_types,
|
|
22
|
+
blocked_file_extensions,
|
|
23
|
+
blocked_mime_types,
|
|
24
|
+
} = config;
|
|
25
|
+
|
|
26
|
+
if (allowed_file_extensions?.length) {
|
|
27
|
+
const allowed = allowed_file_extensions.some((fileExtension: string) =>
|
|
28
|
+
file.name?.toLowerCase().endsWith(fileExtension.toLowerCase()),
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
if (!allowed) {
|
|
32
|
+
return false;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (blocked_file_extensions?.length) {
|
|
37
|
+
const blocked = blocked_file_extensions.some((fileExtension: string) =>
|
|
38
|
+
file.name?.toLowerCase().endsWith(fileExtension.toLowerCase()),
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
if (blocked) {
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (allowed_mime_types?.length) {
|
|
47
|
+
if (file.name) {
|
|
48
|
+
const fileMimeType = lookup(file.name) as string;
|
|
49
|
+
const allowed = allowed_mime_types.some(
|
|
50
|
+
(mimeType: string) => fileMimeType.toLowerCase() === mimeType.toLowerCase(),
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
if (!allowed) {
|
|
54
|
+
return false;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (blocked_mime_types?.length) {
|
|
60
|
+
if (file.name) {
|
|
61
|
+
const fileMimeType = lookup(file.name) as string;
|
|
62
|
+
const blocked = blocked_mime_types.some(
|
|
63
|
+
(mimeType: string) => fileMimeType.toLowerCase() === mimeType.toLowerCase(),
|
|
64
|
+
);
|
|
65
|
+
|
|
66
|
+
if (blocked) {
|
|
67
|
+
return false;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return true;
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* This utility function prettifies the file size.
|
|
77
|
+
* @param bytes The bytes of the file
|
|
78
|
+
* @param precision The precision to which the file size should be rounded
|
|
79
|
+
* @returns
|
|
80
|
+
*/
|
|
81
|
+
export function prettifyFileSize(bytes: number, precision = 3) {
|
|
82
|
+
const units = ['B', 'kB', 'MB', 'GB'];
|
|
83
|
+
const exponent = Math.min(Math.floor(Math.log(bytes) / Math.log(1024)), units.length - 1);
|
|
84
|
+
const mantissa = bytes / 1024 ** exponent;
|
|
85
|
+
return `${mantissa.toPrecision(precision)} ${units[exponent]}`;
|
|
86
|
+
}
|
package/src/i18n/en.json
CHANGED
|
@@ -24,6 +24,7 @@
|
|
|
24
24
|
"Error loading channel list...": "Error loading channel list...",
|
|
25
25
|
"Error loading messages for this channel...": "Error loading messages for this channel...",
|
|
26
26
|
"Error while loading, please reload/refresh": "Error while loading, please reload/refresh",
|
|
27
|
+
"File is too large: {{ size }}, maximum upload size is {{ limit }}": "File is too large: {{ size }}, maximum upload size is {{ limit }}",
|
|
27
28
|
"File type not supported": "File type not supported",
|
|
28
29
|
"Flag": "Flag",
|
|
29
30
|
"Flag Message": "Flag Message",
|
|
@@ -36,7 +37,6 @@
|
|
|
36
37
|
"Loading channels...": "Loading channels...",
|
|
37
38
|
"Loading messages...": "Loading messages...",
|
|
38
39
|
"Loading...": "Loading...",
|
|
39
|
-
"Maximum file size upload limit reached. Please upload a file below {{MAX_FILE_SIZE_TO_UPLOAD_IN_MB}} MB.": "Maximum file size upload limit reached. Please upload a file below {{MAX_FILE_SIZE_TO_UPLOAD_IN_MB}} MB.",
|
|
40
40
|
"Message Reactions": "Message Reactions",
|
|
41
41
|
"Message deleted": "Message deleted",
|
|
42
42
|
"Message flagged": "Message flagged",
|
package/src/i18n/es.json
CHANGED
|
@@ -1,57 +1,57 @@
|
|
|
1
1
|
{
|
|
2
|
-
"1 Reply": "",
|
|
2
|
+
"1 Reply": "1 respuesta",
|
|
3
3
|
"1 Thread Reply": "",
|
|
4
4
|
"Allow access to your Gallery": "",
|
|
5
5
|
"Allow camera access in device settings": "",
|
|
6
6
|
"Also send to channel": "",
|
|
7
7
|
"Are you sure you want to permanently delete this message?": "",
|
|
8
|
-
"Are you sure?": "
|
|
8
|
+
"Are you sure?": "",
|
|
9
9
|
"Block User": "",
|
|
10
10
|
"Cancel": "",
|
|
11
11
|
"Cannot Flag Message": "",
|
|
12
|
-
"Consider how your comment might make others feel and be sure to follow our Community Guidelines": "
|
|
12
|
+
"Consider how your comment might make others feel and be sure to follow our Community Guidelines": "",
|
|
13
13
|
"Copy Message": "",
|
|
14
14
|
"Delete": "",
|
|
15
15
|
"Delete Message": "",
|
|
16
16
|
"Device camera is used to take photos or videos.": "",
|
|
17
17
|
"Do you want to send a copy of this message to a moderator for further investigation?": "",
|
|
18
18
|
"Edit Message": "",
|
|
19
|
-
"Edited": "",
|
|
19
|
+
"Edited": "Editado",
|
|
20
20
|
"Editing Message": "",
|
|
21
|
-
"Emoji matching": "",
|
|
22
|
-
"Empty message...": "",
|
|
21
|
+
"Emoji matching": "Coincidencia de emoji",
|
|
22
|
+
"Empty message...": "Mensaje vacío...",
|
|
23
23
|
"Error loading": "",
|
|
24
24
|
"Error loading channel list...": "",
|
|
25
25
|
"Error loading messages for this channel...": "",
|
|
26
|
-
"Error while loading, please reload/refresh": "",
|
|
26
|
+
"Error while loading, please reload/refresh": "Error al cargar, por favor recarga/actualiza",
|
|
27
|
+
"File is too large: {{ size }}, maximum upload size is {{ limit }}": "",
|
|
27
28
|
"File type not supported": "",
|
|
28
29
|
"Flag": "",
|
|
29
30
|
"Flag Message": "",
|
|
30
31
|
"Flag action failed either due to a network issue or the message is already flagged": "",
|
|
31
|
-
"Hold to start recording.": "
|
|
32
|
+
"Hold to start recording.": "",
|
|
32
33
|
"How about sending your first message to a friend?": "",
|
|
33
|
-
"Instant Commands": "",
|
|
34
|
+
"Instant Commands": "Comandos instantáneos",
|
|
34
35
|
"Let's start chatting!": "",
|
|
35
36
|
"Links are disabled": "",
|
|
36
|
-
"Loading channels...": "",
|
|
37
|
-
"Loading messages...": "",
|
|
38
|
-
"Loading...": "",
|
|
39
|
-
"Maximum file size upload limit reached. Please upload a file below {{MAX_FILE_SIZE_TO_UPLOAD_IN_MB}} MB.": "",
|
|
37
|
+
"Loading channels...": "Cargando canales...",
|
|
38
|
+
"Loading messages...": "Cargando mensajes...",
|
|
39
|
+
"Loading...": "Cargando...",
|
|
40
40
|
"Message Reactions": "",
|
|
41
41
|
"Message deleted": "Mensaje eliminado",
|
|
42
42
|
"Message flagged": "",
|
|
43
43
|
"Mute User": "",
|
|
44
44
|
"No chats here yet…": "",
|
|
45
|
-
"Not supported": "
|
|
46
|
-
"Nothing yet...": "",
|
|
45
|
+
"Not supported": "",
|
|
46
|
+
"Nothing yet...": "Aún no hay nada...",
|
|
47
47
|
"Ok": "",
|
|
48
|
-
"Only visible to you": "",
|
|
48
|
+
"Only visible to you": "Solo visible para ti",
|
|
49
49
|
"Open Settings": "",
|
|
50
50
|
"Photo": "",
|
|
51
51
|
"Photos and Videos": "",
|
|
52
52
|
"Pin to Conversation": "",
|
|
53
|
-
"Pinned by": "",
|
|
54
|
-
"Please allow Audio permissions in settings.": "
|
|
53
|
+
"Pinned by": "Fijado por",
|
|
54
|
+
"Please allow Audio permissions in settings.": "",
|
|
55
55
|
"Please enable access to your photos and videos so you can share them.": "",
|
|
56
56
|
"Please select a channel first": "",
|
|
57
57
|
"Reconnecting...": "Reconectando...",
|
|
@@ -60,7 +60,7 @@
|
|
|
60
60
|
"Resend": "",
|
|
61
61
|
"Search GIFs": "",
|
|
62
62
|
"Select More Photos": "",
|
|
63
|
-
"Send Anyway": "
|
|
63
|
+
"Send Anyway": "",
|
|
64
64
|
"Send a message": "",
|
|
65
65
|
"Sending links is not allowed in this conversation": "",
|
|
66
66
|
"Slow mode ON": "",
|
|
@@ -76,8 +76,8 @@
|
|
|
76
76
|
"You can't send messages in this channel": "",
|
|
77
77
|
"{{ firstUser }} and {{ nonSelfUserLength }} more are typing": "",
|
|
78
78
|
"{{ index }} of {{ photoLength }}": "",
|
|
79
|
-
"{{ replyCount }} Replies": "",
|
|
79
|
+
"{{ replyCount }} Replies": "{{ replyCount }} Respuestas",
|
|
80
80
|
"{{ replyCount }} Thread Replies": "",
|
|
81
81
|
"{{ user }} is typing": "",
|
|
82
|
-
"🏙 Attachment...": ""
|
|
82
|
+
"🏙 Attachment...": "🏙 Adjunto..."
|
|
83
83
|
}
|
package/src/i18n/fr.json
CHANGED
|
@@ -1,57 +1,57 @@
|
|
|
1
1
|
{
|
|
2
|
-
"1 Reply": "",
|
|
2
|
+
"1 Reply": "1 Réponse",
|
|
3
3
|
"1 Thread Reply": "",
|
|
4
4
|
"Allow access to your Gallery": "",
|
|
5
5
|
"Allow camera access in device settings": "",
|
|
6
6
|
"Also send to channel": "",
|
|
7
7
|
"Are you sure you want to permanently delete this message?": "",
|
|
8
|
-
"Are you sure?": "
|
|
8
|
+
"Are you sure?": "",
|
|
9
9
|
"Block User": "",
|
|
10
10
|
"Cancel": "",
|
|
11
11
|
"Cannot Flag Message": "",
|
|
12
|
-
"Consider how your comment might make others feel and be sure to follow our Community Guidelines": "
|
|
12
|
+
"Consider how your comment might make others feel and be sure to follow our Community Guidelines": "",
|
|
13
13
|
"Copy Message": "",
|
|
14
14
|
"Delete": "",
|
|
15
15
|
"Delete Message": "",
|
|
16
16
|
"Device camera is used to take photos or videos.": "",
|
|
17
17
|
"Do you want to send a copy of this message to a moderator for further investigation?": "",
|
|
18
18
|
"Edit Message": "",
|
|
19
|
-
"Edited": "",
|
|
19
|
+
"Edited": "Édité",
|
|
20
20
|
"Editing Message": "",
|
|
21
|
-
"Emoji matching": "",
|
|
22
|
-
"Empty message...": "",
|
|
21
|
+
"Emoji matching": "Correspondance Emoji",
|
|
22
|
+
"Empty message...": "Message vide...",
|
|
23
23
|
"Error loading": "",
|
|
24
24
|
"Error loading channel list...": "",
|
|
25
25
|
"Error loading messages for this channel...": "",
|
|
26
|
-
"Error while loading, please reload/refresh": "",
|
|
26
|
+
"Error while loading, please reload/refresh": "Erreur lors du chargement, veuillez recharger/rafraîchir",
|
|
27
|
+
"File is too large: {{ size }}, maximum upload size is {{ limit }}": "",
|
|
27
28
|
"File type not supported": "",
|
|
28
29
|
"Flag": "",
|
|
29
30
|
"Flag Message": "",
|
|
30
31
|
"Flag action failed either due to a network issue or the message is already flagged": "",
|
|
31
|
-
"Hold to start recording.": "
|
|
32
|
+
"Hold to start recording.": "",
|
|
32
33
|
"How about sending your first message to a friend?": "",
|
|
33
|
-
"Instant Commands": "",
|
|
34
|
+
"Instant Commands": "Commandes Instantanées",
|
|
34
35
|
"Let's start chatting!": "",
|
|
35
36
|
"Links are disabled": "",
|
|
36
|
-
"Loading channels...": "",
|
|
37
|
-
"Loading messages...": "",
|
|
38
|
-
"Loading...": "",
|
|
39
|
-
"Maximum file size upload limit reached. Please upload a file below {{MAX_FILE_SIZE_TO_UPLOAD_IN_MB}} MB.": "",
|
|
37
|
+
"Loading channels...": "Chargement des canaux...",
|
|
38
|
+
"Loading messages...": "Chargement des messages...",
|
|
39
|
+
"Loading...": "Chargement...",
|
|
40
40
|
"Message Reactions": "",
|
|
41
41
|
"Message deleted": "Message supprimé",
|
|
42
42
|
"Message flagged": "",
|
|
43
43
|
"Mute User": "",
|
|
44
44
|
"No chats here yet…": "",
|
|
45
|
-
"Not supported": "
|
|
46
|
-
"Nothing yet...": "",
|
|
45
|
+
"Not supported": "",
|
|
46
|
+
"Nothing yet...": "Aucun message...",
|
|
47
47
|
"Ok": "",
|
|
48
|
-
"Only visible to you": "",
|
|
48
|
+
"Only visible to you": "Seulement visible par vous",
|
|
49
49
|
"Open Settings": "",
|
|
50
50
|
"Photo": "",
|
|
51
51
|
"Photos and Videos": "",
|
|
52
52
|
"Pin to Conversation": "",
|
|
53
|
-
"Pinned by": "",
|
|
54
|
-
"Please allow Audio permissions in settings.": "
|
|
53
|
+
"Pinned by": "Épinglé par",
|
|
54
|
+
"Please allow Audio permissions in settings.": "",
|
|
55
55
|
"Please enable access to your photos and videos so you can share them.": "",
|
|
56
56
|
"Please select a channel first": "",
|
|
57
57
|
"Reconnecting...": "Se Reconnecter...",
|
|
@@ -60,7 +60,7 @@
|
|
|
60
60
|
"Resend": "",
|
|
61
61
|
"Search GIFs": "",
|
|
62
62
|
"Select More Photos": "",
|
|
63
|
-
"Send Anyway": "
|
|
63
|
+
"Send Anyway": "",
|
|
64
64
|
"Send a message": "",
|
|
65
65
|
"Sending links is not allowed in this conversation": "",
|
|
66
66
|
"Slow mode ON": "",
|
|
@@ -76,8 +76,8 @@
|
|
|
76
76
|
"You can't send messages in this channel": "",
|
|
77
77
|
"{{ firstUser }} and {{ nonSelfUserLength }} more are typing": "",
|
|
78
78
|
"{{ index }} of {{ photoLength }}": "",
|
|
79
|
-
"{{ replyCount }} Replies": "",
|
|
79
|
+
"{{ replyCount }} Replies": "{{ replyCount }} Réponses",
|
|
80
80
|
"{{ replyCount }} Thread Replies": "",
|
|
81
81
|
"{{ user }} is typing": "",
|
|
82
|
-
"🏙 Attachment...": ""
|
|
82
|
+
"🏙 Attachment...": "🏙 Pièce jointe..."
|
|
83
83
|
}
|