diff --git a/examples/ExpoMessaging/package.json b/examples/ExpoMessaging/package.json index d757a2846..54e49570f 100644 --- a/examples/ExpoMessaging/package.json +++ b/examples/ExpoMessaging/package.json @@ -15,7 +15,7 @@ "expo-av": "~13.2.1", "expo-clipboard": "^4.1.2", "expo-constants": "~14.2.1", - "expo-document-picker": "~11.2.2", + "expo-document-picker": "~11.5.4", "expo-file-system": "~15.2.2", "expo-haptics": "~12.2.1", "expo-image-manipulator": "~11.1.1", diff --git a/examples/ExpoMessaging/yarn.lock b/examples/ExpoMessaging/yarn.lock index 01736d907..f56fd5e39 100644 --- a/examples/ExpoMessaging/yarn.lock +++ b/examples/ExpoMessaging/yarn.lock @@ -3513,10 +3513,10 @@ expo-constants@~14.2.0, expo-constants@~14.2.1: "@expo/config" "~8.0.0" uuid "^3.3.2" -expo-document-picker@~11.2.2: - version "11.2.2" - resolved "https://registry.yarnpkg.com/expo-document-picker/-/expo-document-picker-11.2.2.tgz#707ff994a1c0b38505e12b2c9fe6b67715e6f41c" - integrity sha512-EeonRKxkK9E20LEAh93IvlwFjNkUCJKnhBEama9PmIDYWW7RyANZ8eP9C2PupThTDbivzRDNp7Ec7dIeyDAWjw== +expo-document-picker@~11.5.4: + version "11.5.4" + resolved "https://registry.yarnpkg.com/expo-document-picker/-/expo-document-picker-11.5.4.tgz#11c274d1a2f4aa1f0ca7c0403ca02017a20d832e" + integrity sha512-4lpRixi33kjoQQy9lfrGs48yFRMOgBgdnqjYWVFe85WFy/WkEoFC2E8usxcFdrIigtfy9Z+HY3/je4lHEYqyLg== expo-file-system@~15.2.0, expo-file-system@~15.2.2: version "15.2.2" diff --git a/examples/TypeScriptMessaging/ios/Podfile.lock b/examples/TypeScriptMessaging/ios/Podfile.lock index 1e3c8eb89..6a219a601 100644 --- a/examples/TypeScriptMessaging/ios/Podfile.lock +++ b/examples/TypeScriptMessaging/ios/Podfile.lock @@ -708,7 +708,7 @@ EXTERNAL SOURCES: :path: "../node_modules/react-native/ReactCommon/yoga" SPEC CHECKSUMS: - boost: a7c83b31436843459a1961bfd74b96033dc77234 + boost: 57d2868c099736d80fcd648bf211b4431e51a558 CocoaAsyncSocket: 065fd1e645c7abab64f7a6a2007a48038fdc6a99 DoubleConversion: 5189b271737e1565bdce30deb4a08d647e3f5f54 FBLazyVector: a89a0525bc7ca174675045c2b492b5280d5a2470 @@ -729,7 +729,7 @@ SPEC CHECKSUMS: OpenSSL-Universal: ebc357f1e6bc71fa463ccb2fe676756aff50e88c PromisesObjC: 09985d6d70fbe7878040aa746d78236e6946d2ef PromisesSwift: cf9eb58666a43bbe007302226e510b16c1e10959 - RCT-Folly: 0080d0a6ebf2577475bda044aa59e2ca1f909cda + RCT-Folly: 424b8c9a7a0b9ab2886ffe9c3b041ef628fd4fb1 RCTRequired: 5a4a30ac20c86eeadd6844a9328f78d4168cf9b2 RCTTypeSafety: 279fc5861a89f0f37db3a585f27f971485b4b734 React: 88307a9be3bd0e71a6822271cf28b84a587fb97f diff --git a/package/expo-package/src/optionalDependencies/pickDocument.ts b/package/expo-package/src/optionalDependencies/pickDocument.ts index 19571d471..6c12ebe74 100644 --- a/package/expo-package/src/optionalDependencies/pickDocument.ts +++ b/package/expo-package/src/optionalDependencies/pickDocument.ts @@ -15,15 +15,37 @@ if (!DocumentPicker) { export const pickDocument = DocumentPicker ? async () => { try { - const { type, ...rest } = await DocumentPicker.getDocumentAsync(); + const result = await DocumentPicker.getDocumentAsync(); + + // New data from latest version of expo-document-picker + const { assets, canceled } = result; + + // Old data from older version of expo-document-picker + const { type, ...rest } = result; + + // Applicable to latest version of expo-document-picker + if (canceled) { + return { + cancelled: true, + }; + } + // Applicable to older version of expo-document-picker if (type === 'cancel') { return { cancelled: true, }; } + // Applicable to latest version of expo-document-picker + if (assets) { + return { + assets, + cancelled: false, + }; + } + // Applicable to older version of expo-document-picker return { + assets: [rest], cancelled: false, - docs: [rest], }; } catch (err) { return { diff --git a/package/native-package/src/optionalDependencies/pickDocument.ts b/package/native-package/src/optionalDependencies/pickDocument.ts index 3e2b747b6..a62e5a5f4 100644 --- a/package/native-package/src/optionalDependencies/pickDocument.ts +++ b/package/native-package/src/optionalDependencies/pickDocument.ts @@ -37,13 +37,13 @@ export const pickDocument = DocumentPicker } return { - cancelled: false, - docs: res.map(({ name, size, type, uri }) => ({ + assets: res.map(({ name, size, type, uri }) => ({ + mimeType: type, name, size, - type, uri, })), + cancelled: false, }; } catch (err) { return { diff --git a/package/src/components/Attachment/FileAttachmentGroup.tsx b/package/src/components/Attachment/FileAttachmentGroup.tsx index 51ae70fb7..84f3c45fc 100644 --- a/package/src/components/Attachment/FileAttachmentGroup.tsx +++ b/package/src/components/Attachment/FileAttachmentGroup.tsx @@ -104,7 +104,7 @@ const FileAttachmentGroupWithContext = < setFilesToDisplay((prevFileUploads) => prevFileUploads.map((fileUpload, id) => ({ ...fileUpload, - paused: id.toString() === index ? false : true, + paused: id.toString() !== index, })), ); } else { diff --git a/package/src/components/Channel/Channel.tsx b/package/src/components/Channel/Channel.tsx index d82302a36..82a8bf65d 100644 --- a/package/src/components/Channel/Channel.tsx +++ b/package/src/components/Channel/Channel.tsx @@ -1324,7 +1324,7 @@ const ChannelWithContext = < ) { const response = doDocUploadRequest ? await doDocUploadRequest(file, channel) - : await channel.sendFile(file.uri, file.name, file.type); + : await channel.sendFile(file.uri, file.name, file.mimeType); attachment.asset_url = response.file; if (response.thumb_url) { attachment.thumb_url = response.thumb_url; diff --git a/package/src/components/MessageInput/FileUploadPreview.tsx b/package/src/components/MessageInput/FileUploadPreview.tsx index 3e1cf81cd..3d13227d2 100644 --- a/package/src/components/MessageInput/FileUploadPreview.tsx +++ b/package/src/components/MessageInput/FileUploadPreview.tsx @@ -183,7 +183,7 @@ const FileUploadPreviewWithContext = < setFileUploads((prevFileUploads) => prevFileUploads.map((fileUpload) => ({ ...fileUpload, - paused: fileUpload.id === index ? false : true, + paused: fileUpload.id !== index, })), ); } else { @@ -228,7 +228,7 @@ const FileUploadPreviewWithContext = < style={styles.overlay} type={indicatorType} > - {item.file.type?.startsWith('audio/') && isAudioPackageAvailable() ? ( + {item.file.mimeType?.startsWith('audio/') && isAudioPackageAvailable() ? ( - + selectedFilesLength) { /** * User is editing some message which contains video attachments OR - * video attachment is added from custom image picker (other than the default bottomsheet image picker) + * video attachment is added from custom image picker (other than the default bottom-sheet image picker) * using `uploadNewFile` function from `MessageInputContext`. **/ setSelectedFiles( fileUploads.map((fileUpload) => ({ duration: fileUpload.file.duration, + mimeType: fileUpload.file.mimeType, name: fileUpload.file.name, size: fileUpload.file.size, - type: fileUpload.file.type, uri: fileUpload.file.uri, })), ); diff --git a/package/src/components/MessageInput/__tests__/FileUploadPreview.test.js b/package/src/components/MessageInput/__tests__/FileUploadPreview.test.js index 929da04b3..f088bedaa 100644 --- a/package/src/components/MessageInput/__tests__/FileUploadPreview.test.js +++ b/package/src/components/MessageInput/__tests__/FileUploadPreview.test.js @@ -318,8 +318,8 @@ describe('FileUploadPreview', () => { const fileUploads = [ generateFileUploadPreview({ id: 'file-upload-id-1', + mimeType: 'audio/mp3', state: FileState.UPLOADED, - type: 'audio/mp3', }), ]; const removeFile = jest.fn(); diff --git a/package/src/contexts/messageInputContext/MessageInputContext.tsx b/package/src/contexts/messageInputContext/MessageInputContext.tsx index dee04612c..f1e768030 100644 --- a/package/src/contexts/messageInputContext/MessageInputContext.tsx +++ b/package/src/contexts/messageInputContext/MessageInputContext.tsx @@ -1,6 +1,5 @@ -import React, { PropsWithChildren, useContext, useEffect, useRef, useState } from 'react'; - import type { LegacyRef } from 'react'; +import React, { PropsWithChildren, useContext, useEffect, useRef, useState } from 'react'; import type { TextInput, TextInputProps } from 'react-native'; import { Alert, Keyboard, Platform } from 'react-native'; @@ -591,14 +590,14 @@ export const MessageInputProvider = < const MEGA_BYTES_TO_BYTES = 1024 * 1024; const MAX_FILE_SIZE_TO_UPLOAD_IN_MB = 100; - if (!result.cancelled && result.docs) { - const totalFileSize = result.docs.reduce((acc, doc) => acc + Number(doc.size), 0); + if (!result.cancelled && result.assets) { + const totalFileSize = result.assets.reduce((acc, asset) => acc + Number(asset.size), 0); if (totalFileSize / MEGA_BYTES_TO_BYTES > MAX_FILE_SIZE_TO_UPLOAD_IN_MB) { Alert.alert( `Maximum file size upload limit reached, please upload files below ${MAX_FILE_SIZE_TO_UPLOAD_IN_MB}MB.`, ); } else { - result.docs.forEach((doc) => { + result.assets.forEach((asset) => { /** * TODO: The current tight coupling of images to the image * picker does not allow images picked from the file picker @@ -606,7 +605,7 @@ export const MessageInputProvider = < * This should be updated alongside allowing image a file * uploads together. */ - uploadNewFile(doc); + uploadNewFile(asset); }); } } @@ -652,31 +651,30 @@ export const MessageInputProvider = < }; const mapFileUploadToAttachment = (file: FileUpload) => { - const mime_type: string | boolean = lookup(file.file.name as string); - if (file.file.type?.startsWith('image/')) { + if (file.file.mimeType?.startsWith('image/')) { return { fallback: file.file.name, image_url: file.url, - mime_type: mime_type ? mime_type : undefined, + mime_type: file.file.mimeType, originalFile: file.file, type: 'image', }; - } else if (file.file.type?.startsWith('audio/')) { + } else if (file.file.mimeType?.startsWith('audio/')) { return { asset_url: file.url || file.file.uri, duration: file.file.duration, file_size: file.file.size, - mime_type: file.file.type, + mime_type: file.file.mimeType, originalFile: file.file, title: file.file.name, type: 'audio', }; - } else if (file.file.type?.startsWith('video/')) { + } else if (file.file.mimeType?.startsWith('video/')) { return { asset_url: file.url || file.file.uri, duration: file.file.duration, file_size: file.file.size, - mime_type: file.file.type, + mime_type: file.file.mimeType, originalFile: file.file, thumb_url: file.thumb_url, title: file.file.name, @@ -686,7 +684,7 @@ export const MessageInputProvider = < return { asset_url: file.url || file.file.uri, file_size: file.file.size, - mime_type: file.file.type, + mime_type: file.file.mimeType, originalFile: file.file, title: file.file.name, type: 'file', @@ -915,14 +913,15 @@ export const MessageInputProvider = < } }; - const regExcondition = /File (extension \.\w{2,4}|type \S+) is not supported/; + const regexCondition = /File (extension \.\w{2,4}|type \S+) is not supported/; - const getUploadSetStateAction = ( - id: string, - fileState: FileStateValue, - extraData: Partial = {}, - ): React.SetStateAction => { - const uploads: (prevUploads: UploadType[]) => UploadType[] = (prevUploads: UploadType[]) => + const getUploadSetStateAction = + ( + id: string, + fileState: FileStateValue, + extraData: Partial = {}, + ): React.SetStateAction => + (prevUploads: UploadType[]) => prevUploads.map((prevUpload) => { if (prevUpload.id === id) { return { @@ -934,14 +933,11 @@ export const MessageInputProvider = < return prevUpload; }); - return uploads; - }; - const handleFileOrImageUploadError = (error: unknown, isImageError: boolean, id: string) => { if (isImageError) { setNumberOfUploads((prevNumberOfUploads) => prevNumberOfUploads - 1); if (error instanceof Error) { - if (regExcondition.test(error.message)) { + if (regexCondition.test(error.message)) { return setImageUploads(getUploadSetStateAction(id, FileState.NOT_SUPPORTED)); } @@ -951,7 +947,7 @@ export const MessageInputProvider = < setNumberOfUploads((prevNumberOfUploads) => prevNumberOfUploads - 1); if (error instanceof Error) { - if (regExcondition.test(error.message)) { + if (regexCondition.test(error.message)) { return setFileUploads(getUploadSetStateAction(id, FileState.NOT_SUPPORTED)); } return setFileUploads(getUploadSetStateAction(id, FileState.UPLOAD_FAILED)); @@ -969,9 +965,7 @@ export const MessageInputProvider = < if (value.doDocUploadRequest) { response = await value.doDocUploadRequest(file, channel); } else if (channel && file.uri) { - // For the case of Expo CLI where you need to fetch the file uri from file id. Here it is only done for iOS since for android the file.uri is fine. - const localAssetURI = Platform.OS === 'ios' && file.id && (await getLocalAssetUri(file.id)); - response = await channel.sendFile(localAssetURI || file.uri, file.name, file.type); + response = await channel.sendFile(file.uri, file.name, file.mimeType); } const extraData: Partial = { thumb_url: response.thumb_url, url: response.file }; setFileUploads(getUploadSetStateAction(id, FileState.UPLOADED, extraData)); @@ -996,7 +990,7 @@ export const MessageInputProvider = < /** * We skip compression if: * - the file is from the camera as that should already be compressed - * - the file has not height/width value to maintain for compression + * - the file has no height/width value to maintain for compression * - the compressImageQuality number is not present or is 1 (meaning no compression) */ const compressedUri = await (file.source === 'camera' || @@ -1055,7 +1049,6 @@ export const MessageInputProvider = < const uploadNewFile = async (file: File) => { const id: string = generateRandomId(); - const mimeType: string | boolean = lookup(file.name); const isBlockedFileExtension: boolean | undefined = blockedFileExtensionTypes?.some( (fileExtensionType: string) => file.name?.includes(fileExtensionType), @@ -1071,7 +1064,7 @@ export const MessageInputProvider = < const newFile: FileUpload = { duration: 0, - file: { ...file, type: mimeType || file?.type }, + file, id: file.id || id, paused: true, progress: 0, diff --git a/package/src/contexts/messageInputContext/__tests__/__snapshots__/sendMessage.test.tsx.snap b/package/src/contexts/messageInputContext/__tests__/__snapshots__/sendMessage.test.tsx.snap index 396aaf593..24e0bca9b 100644 --- a/package/src/contexts/messageInputContext/__tests__/__snapshots__/sendMessage.test.tsx.snap +++ b/package/src/contexts/messageInputContext/__tests__/__snapshots__/sendMessage.test.tsx.snap @@ -42,7 +42,7 @@ Object { Object { "asset_url": undefined, "file_size": undefined, - "mime_type": "file", + "mime_type": undefined, "originalFile": Object { "name": "dummy.pdf", "state": "uploaded", @@ -53,41 +53,39 @@ Object { }, Object { "asset_url": undefined, - "duration": undefined, "file_size": undefined, - "mime_type": "video/mp4", + "mime_type": undefined, "originalFile": Object { "name": "dummy.pdf", "state": "finished", "type": "video/mp4", }, - "thumb_url": undefined, "title": "dummy.pdf", - "type": "video", + "type": "file", }, Object { "asset_url": undefined, - "duration": undefined, "file_size": undefined, - "mime_type": "audio/mp3", + "mime_type": undefined, "originalFile": Object { "name": "dummy.pdf", "state": "uploaded", "type": "audio/mp3", }, "title": "dummy.pdf", - "type": "audio", + "type": "file", }, Object { - "fallback": "dummy.pdf", - "image_url": undefined, - "mime_type": "application/pdf", + "asset_url": undefined, + "file_size": undefined, + "mime_type": undefined, "originalFile": Object { "name": "dummy.pdf", "state": "finished", "type": "image/jpeg", }, - "type": "image", + "title": "dummy.pdf", + "type": "file", }, ], "mentioned_users": Array [ diff --git a/package/src/contexts/messageInputContext/__tests__/pickFile.test.tsx b/package/src/contexts/messageInputContext/__tests__/pickFile.test.tsx index b491a585e..293524128 100644 --- a/package/src/contexts/messageInputContext/__tests__/pickFile.test.tsx +++ b/package/src/contexts/messageInputContext/__tests__/pickFile.test.tsx @@ -40,11 +40,11 @@ describe("MessageInputContext's pickFile", () => { jest.spyOn(Alert, 'alert'); jest.spyOn(NativeUtils, 'pickDocument').mockImplementation( jest.fn().mockResolvedValue({ - cancelled: false, - docs: [ + assets: [ generateFileAttachment({ size: 500000000 }), generateFileAttachment({ size: 600000000 }), ], + cancelled: false, }), ); diff --git a/package/src/contexts/messageInputContext/hooks/useMessageDetailsForState.ts b/package/src/contexts/messageInputContext/hooks/useMessageDetailsForState.ts index b4a9b02fd..e4678b04f 100644 --- a/package/src/contexts/messageInputContext/hooks/useMessageDetailsForState.ts +++ b/package/src/contexts/messageInputContext/hooks/useMessageDetailsForState.ts @@ -54,9 +54,9 @@ export const useMessageDetailsForState = < const id = generateRandomId(); newFileUploads.push({ file: { + mimeType: attachment.mime_type, name: attachment.title || '', size: attachment.file_size, - type: attachment.mime_type, }, id, state: 'finished', @@ -78,9 +78,9 @@ export const useMessageDetailsForState = < const id = generateRandomId(); newFileUploads.push({ file: { + mimeType: attachment.mime_type, name: attachment.title || '', size: attachment.file_size, - type: attachment.mime_type, }, id, state: 'finished', diff --git a/package/src/native.ts b/package/src/native.ts index 2504036d4..22f092123 100644 --- a/package/src/native.ts +++ b/package/src/native.ts @@ -57,7 +57,7 @@ export let NetInfo: NetInfo = { type PickDocument = ({ maxNumberOfFiles }: { maxNumberOfFiles?: number }) => | Promise<{ cancelled: boolean; - docs?: File[]; + assets?: File[]; }> | never; export let pickDocument: PickDocument = fail; diff --git a/package/src/types/types.ts b/package/src/types/types.ts index 2902309a9..16cfeb964 100644 --- a/package/src/types/types.ts +++ b/package/src/types/types.ts @@ -13,15 +13,19 @@ export type Asset = { size?: number | string; }; -export type File = { +export type FileAssetType = { name: string; - duration?: string | null; - id?: string; + mimeType?: string; size?: number | string; - type?: string; + // The uri should be of type `string`. But is `string|undefined` because the same type is used for the response from Stream's Attachment. This shall be fixed. uri?: string; }; +export type File = FileAssetType & { + duration?: string | null; + id?: string; +}; + export type DefaultAttachmentType = UnknownType & { file_size?: number | string; mime_type?: string; @@ -34,6 +38,7 @@ interface DefaultUserType extends UnknownType { interface DefaultChannelType extends UnknownType { [key: string]: unknown; + image?: string; }