diff --git a/package/expo-package/src/optionalDependencies/takePhoto.ts b/package/expo-package/src/optionalDependencies/takePhoto.ts index 2fed2c85fa..c6e59ab193 100644 --- a/package/expo-package/src/optionalDependencies/takePhoto.ts +++ b/package/expo-package/src/optionalDependencies/takePhoto.ts @@ -74,6 +74,7 @@ export const takePhoto = ImagePicker return { cancelled: false, + size: photo.fileSize, source: 'camera', uri: photo.uri, ...size, diff --git a/package/native-package/src/optionalDependencies/pickImage.ts b/package/native-package/src/optionalDependencies/pickImage.ts index 4d4cb29c3e..574c1538e9 100644 --- a/package/native-package/src/optionalDependencies/pickImage.ts +++ b/package/native-package/src/optionalDependencies/pickImage.ts @@ -10,7 +10,9 @@ try { export const pickImage = ImagePicker ? async () => { try { - const result = await ImagePicker.launchImageLibrary({ mediaType: 'mixed' }); + const result = await ImagePicker.launchImageLibrary({ + mediaType: 'mixed', + }); const canceled = result.didCancel; const errorCode = result.errorCode; @@ -20,7 +22,7 @@ export const pickImage = ImagePicker if (!canceled) { const assets = result.assets.map((asset) => ({ ...asset, - duration: asset.duration * 1000, // in milliseconds + duration: asset.duration ? asset.duration * 1000 : undefined, // in milliseconds name: asset.fileName, size: asset.fileSize, source: 'picker', diff --git a/package/native-package/src/optionalDependencies/takePhoto.ts b/package/native-package/src/optionalDependencies/takePhoto.ts index 33b2e694b8..3d39911028 100644 --- a/package/native-package/src/optionalDependencies/takePhoto.ts +++ b/package/native-package/src/optionalDependencies/takePhoto.ts @@ -60,6 +60,7 @@ export const takePhoto = ImagePicker } return { cancelled: false, + size: photo.size, source: 'camera', uri: photo.path, ...size, diff --git a/package/src/components/AttachmentPicker/components/AttachmentPickerItem.tsx b/package/src/components/AttachmentPicker/components/AttachmentPickerItem.tsx index 00f794466a..35859cb91d 100644 --- a/package/src/components/AttachmentPicker/components/AttachmentPickerItem.tsx +++ b/package/src/components/AttachmentPicker/components/AttachmentPickerItem.tsx @@ -5,7 +5,7 @@ import { Alert, ImageBackground, Platform, StyleSheet, Text, View } from 'react- import { TouchableOpacity } from '@gorhom/bottom-sheet'; import { lookup } from 'mime-types'; -import type { AttachmentPickerContextValue } from '../../../contexts/attachmentPickerContext/AttachmentPickerContext'; +import { AttachmentPickerContextValue } from '../../../contexts/attachmentPickerContext/AttachmentPickerContext'; import { useTheme } from '../../../contexts/themeContext/ThemeContext'; import { useViewport } from '../../../hooks/useViewport'; import { Recorder } from '../../../icons'; @@ -23,7 +23,6 @@ type AttachmentPickerItemType = Pick< selected: boolean; numberOfAttachmentPickerImageColumns?: number; }; - type AttachmentImageProps = Omit; type AttachmentVideoProps = Omit; diff --git a/package/src/components/MessageInput/FileUploadPreview.tsx b/package/src/components/MessageInput/FileUploadPreview.tsx index 54dbdb8c39..a1d9c6f677 100644 --- a/package/src/components/MessageInput/FileUploadPreview.tsx +++ b/package/src/components/MessageInput/FileUploadPreview.tsx @@ -34,6 +34,7 @@ const styles = StyleSheet.create({ dismiss: { borderRadius: 24, height: 24, + marginRight: 4, position: 'absolute', right: 8, top: 8, diff --git a/package/src/components/MessageInput/MessageInput.tsx b/package/src/components/MessageInput/MessageInput.tsx index a77c623c8b..b14cd74d2e 100644 --- a/package/src/components/MessageInput/MessageInput.tsx +++ b/package/src/components/MessageInput/MessageInput.tsx @@ -49,7 +49,7 @@ import { useTranslationContext, } from '../../contexts/translationContext/TranslationContext'; -import { triggerHaptic } from '../../native'; +import { isImageMediaLibraryAvailable, triggerHaptic } from '../../native'; import type { Asset, DefaultStreamChatGenerics } from '../../types/types'; import { AutoCompleteInput } from '../AutoCompleteInput/AutoCompleteInput'; @@ -118,7 +118,6 @@ type MessageInputPropsWithContext< | 'FileUploadPreview' | 'fileUploads' | 'giphyActive' - | 'hasImagePicker' | 'ImageUploadPreview' | 'imageUploads' | 'Input' @@ -185,7 +184,6 @@ const MessageInputWithContext = < FileUploadPreview, fileUploads, giphyActive, - hasImagePicker, ImageUploadPreview, imageUploads, Input, @@ -349,46 +347,71 @@ const MessageInputWithContext = < imagesToRemove.forEach((image) => removeImage(image.id)); }; + const uploadFilesHandler = async () => { + const fileToUpload = selectedFiles.find((selectedFile) => { + const uploadedFile = fileUploads.find( + (fileUpload) => + fileUpload.file.uri === selectedFile.uri || fileUpload.url === selectedFile.uri, + ); + return !uploadedFile; + }); + if (fileToUpload) await uploadNewFile(fileToUpload); + }; + + const removeFilesHandler = () => { + const filesToRemove = fileUploads.filter( + (fileUpload) => + !selectedFiles.find( + (selectedFile) => + selectedFile.uri === fileUpload.file.uri || selectedFile.uri === fileUpload.url, + ), + ); + filesToRemove.forEach((file) => removeFile(file.id)); + }; + + /** + * When a user selects or deselects an image in the image picker using media library. + */ useEffect(() => { - if (imagesForInput) { - if (selectedImagesLength > imageUploadsLength) { - /** User selected an image in bottom sheet attachment picker */ - uploadImagesHandler(); - } else { - /** User de-selected an image in bottom sheet attachment picker */ - removeImagesHandler(); + const uploadOrRemoveImage = async () => { + if (imagesForInput) { + if (selectedImagesLength > imageUploadsLength) { + /** User selected an image in bottom sheet attachment picker */ + await uploadImagesHandler(); + } else { + /** User de-selected an image in bottom sheet attachment picker */ + removeImagesHandler(); + } } - } + }; + // If image picker is not available, don't do anything + if (!isImageMediaLibraryAvailable()) return; + uploadOrRemoveImage(); // eslint-disable-next-line react-hooks/exhaustive-deps }, [selectedImagesLength]); + /** + * When a user selects or deselects a video in the image picker using media library. + */ useEffect(() => { - if (selectedFilesLength > fileUploadsLength) { - /** User selected a video in bottom sheet attachment picker */ - const fileToUpload = selectedFiles.find((selectedFile) => { - const uploadedFile = fileUploads.find( - (fileUpload) => - fileUpload.file.uri === selectedFile.uri || fileUpload.url === selectedFile.uri, - ); - return !uploadedFile; - }); - if (fileToUpload) uploadNewFile(fileToUpload); - } else { - /** User de-selected a video in bottom sheet attachment picker */ - const filesToRemove = fileUploads.filter( - (fileUpload) => - !selectedFiles.find( - (selectedFile) => - selectedFile.uri === fileUpload.file.uri || selectedFile.uri === fileUpload.url, - ), - ); - filesToRemove.forEach((file) => removeFile(file.id)); - } + const uploadOrRemoveFile = async () => { + if (selectedFilesLength > fileUploadsLength) { + /** User selected a video in bottom sheet attachment picker */ + await uploadFilesHandler(); + } else { + /** User de-selected a video in bottom sheet attachment picker */ + removeFilesHandler(); + } + }; + uploadOrRemoveFile(); // eslint-disable-next-line react-hooks/exhaustive-deps }, [selectedFilesLength]); + /** + * This is for image attachments selected from attachment picker. + */ useEffect(() => { - if (imagesForInput && hasImagePicker) { + if (imagesForInput && isImageMediaLibraryAvailable()) { if (imageUploadsLength < selectedImagesLength) { // /** User removed some image from seleted images within ImageUploadPreview. */ const updatedSelectedImages = selectedImages.filter((selectedImage) => { @@ -401,9 +424,7 @@ const MessageInputWithContext = < setSelectedImages(updatedSelectedImages); } else if (imageUploadsLength > selectedImagesLength) { /** - * User is editing some message which contains image attachments OR - * image attachment is added from custom image picker (other than the default bottomsheet image picker) - * using `uploadNewImage` function from `MessageInputContext`. + * User is editing some message which contains image attachments. **/ setSelectedImages( imageUploads @@ -418,10 +439,13 @@ const MessageInputWithContext = < } } // eslint-disable-next-line react-hooks/exhaustive-deps - }, [imageUploadsLength, hasImagePicker]); + }, [imageUploadsLength]); + /** + * This is for video attachments selected from attachment picker. + */ useEffect(() => { - if (hasImagePicker) { + if (isImageMediaLibraryAvailable()) { if (fileUploadsLength < selectedFilesLength) { /** User removed some video from seleted files within ImageUploadPreview. */ const updatedSelectedFiles = selectedFiles.filter((selectedFile) => { @@ -434,9 +458,7 @@ const MessageInputWithContext = < setSelectedFiles(updatedSelectedFiles); } else if (fileUploadsLength > selectedFilesLength) { /** - * User is editing some message which contains video attachments OR - * video attachment is added from custom image picker (other than the default bottom-sheet image picker) - * using `uploadNewFile` function from `MessageInputContext`. + * User is editing some message which contains video attachments. **/ setSelectedFiles( fileUploads.map((fileUpload) => ({ @@ -450,9 +472,10 @@ const MessageInputWithContext = < } } // eslint-disable-next-line react-hooks/exhaustive-deps - }, [fileUploadsLength, hasImagePicker]); + }, [fileUploadsLength]); const editingExists = !!editing; + useEffect(() => { if (editing && inputBoxRef.current) { inputBoxRef.current.focus(); @@ -1036,7 +1059,6 @@ export const MessageInput = < FileUploadPreview, fileUploads, giphyActive, - hasImagePicker, ImageUploadPreview, imageUploads, Input, @@ -1117,7 +1139,6 @@ export const MessageInput = < FileUploadPreview, fileUploads, giphyActive, - hasImagePicker, ImageUploadPreview, imageUploads, Input, diff --git a/package/src/contexts/messageInputContext/MessageInputContext.tsx b/package/src/contexts/messageInputContext/MessageInputContext.tsx index e3bbc60c98..412e0c7564 100644 --- a/package/src/contexts/messageInputContext/MessageInputContext.tsx +++ b/package/src/contexts/messageInputContext/MessageInputContext.tsx @@ -1,5 +1,12 @@ -import type { LegacyRef } from 'react'; -import React, { PropsWithChildren, useContext, useEffect, useRef, useState } from 'react'; +import React, { + LegacyRef, + PropsWithChildren, + useCallback, + useContext, + useEffect, + useRef, + useState, +} from 'react'; import { Alert, Keyboard, Linking, TextInput, TextInputProps } from 'react-native'; import uniq from 'lodash/uniq'; @@ -46,7 +53,7 @@ import type { SendButtonProps } from '../../components/MessageInput/SendButton'; import type { UploadProgressIndicatorProps } from '../../components/MessageInput/UploadProgressIndicator'; import type { MessageType } from '../../components/MessageList/hooks/useMessageList'; import type { Emoji } from '../../emoji-data'; -import { pickDocument, pickImage, takePhoto } from '../../native'; +import { isImageMediaLibraryAvailable, pickDocument, pickImage, takePhoto } from '../../native'; import { Asset, DefaultStreamChatGenerics, @@ -657,7 +664,7 @@ export const MessageInputProvider = < ); } if (!photo.cancelled) { - setSelectedImages((images) => [...images, photo]); + await uploadNewImage(photo); } }; @@ -677,14 +684,11 @@ export const MessageInputProvider = < ); } if (result.assets && result.assets.length > 0) { - result.assets.forEach((asset) => { + result.assets.forEach(async (asset) => { if (asset.type.includes('image')) { - setSelectedImages((prevImages) => [...prevImages, asset]); + await uploadNewImage(asset); } else { - setSelectedFiles((prevFiles) => [ - ...prevFiles, - { ...asset, mimeType: asset.type, type: FileTypes.Video }, - ]); + await uploadNewFile({ ...asset, mimeType: asset.type, type: FileTypes.Video }); } }); } @@ -693,30 +697,30 @@ export const MessageInputProvider = < /** * Function to open the attachment picker if the MediaLibary is installed. */ - const openAttachmentPicker = () => { + const openAttachmentPicker = useCallback(() => { Keyboard.dismiss(); setSelectedPicker('images'); openPicker(); - }; + }, [openPicker, setSelectedPicker]); /** * Function to close the attachment picker if the MediaLibrary is installed. */ - const closeAttachmentPicker = () => { + const closeAttachmentPicker = useCallback(() => { setSelectedPicker(undefined); closePicker(); - }; + }, [closePicker, setSelectedPicker]); /** * Function to toggle the attachment picker if the MediaLibrary is installed. */ - const toggleAttachmentPicker = () => { + const toggleAttachmentPicker = useCallback(() => { if (selectedPicker) { closeAttachmentPicker(); } else { openAttachmentPicker(); } - }; + }, [closeAttachmentPicker, openAttachmentPicker, selectedPicker]); const onSelectItem = (item: UserResponse) => { setMentionedUsers((prevMentionedUsers) => [...prevMentionedUsers, item.id]); @@ -740,7 +744,7 @@ export const MessageInputProvider = < }); if (!result.cancelled && result.assets) { - result.assets.forEach((asset) => { + result.assets.forEach(async (asset) => { /** * TODO: The current tight coupling of images to the image * picker does not allow images picked from the file picker @@ -748,26 +752,40 @@ export const MessageInputProvider = < * This should be updated alongside allowing image a file * uploads together. */ - uploadNewFile(asset); + await uploadNewFile(asset); }); } }; - const removeFile = (id: string) => { - if (fileUploads.some((file) => file.id === id)) { - setFileUploads((prevFileUploads) => prevFileUploads.filter((file) => file.id !== id)); - setNumberOfUploads((prevNumberOfUploads) => prevNumberOfUploads - 1); - } - }; + const removeFile = useCallback( + (id: string) => { + if (fileUploads.some((file) => file.id === id)) { + setFileUploads((prevFileUploads) => prevFileUploads.filter((file) => file.id !== id)); + setNumberOfUploads((prevNumberOfUploads) => prevNumberOfUploads - 1); + } + }, + [fileUploads, setFileUploads, setNumberOfUploads], + ); - const removeImage = (id: string) => { - if (imageUploads.some((image) => image.id === id)) { - setImageUploads((prevImageUploads) => prevImageUploads.filter((image) => image.id !== id)); - setNumberOfUploads((prevNumberOfUploads) => prevNumberOfUploads - 1); - } - }; + const removeImage = useCallback( + (id: string) => { + if (imageUploads.some((image) => image.id === id)) { + setImageUploads((prevImageUploads) => prevImageUploads.filter((image) => image.id !== id)); + setNumberOfUploads((prevNumberOfUploads) => prevNumberOfUploads - 1); + } + }, + [imageUploads, setImageUploads, setNumberOfUploads], + ); const resetInput = (pendingAttachments: Attachment[] = []) => { + /** + * If the MediaLibrary is available, reset the selected files and images + */ + if (isImageMediaLibraryAvailable()) { + setSelectedFiles([]); + setSelectedImages([]); + } + setFileUploads([]); setGiphyActive(false); setShowMoreOptions(true); @@ -1253,87 +1271,98 @@ export const MessageInputProvider = < }; const uploadNewFile = async (file: File) => { - const id: string = generateRandomId(); - const fileConfig = getFileUploadConfig(); - const { size_limit } = fileConfig; + try { + const id: string = generateRandomId(); + const fileConfig = getFileUploadConfig(); + const { size_limit } = fileConfig; - const isAllowed = isUploadAllowed({ config: fileConfig, file }); + const isAllowed = isUploadAllowed({ config: fileConfig, file }); - const sizeLimit = size_limit || MAX_FILE_SIZE_TO_UPLOAD; + const sizeLimit = size_limit || MAX_FILE_SIZE_TO_UPLOAD; - if (file.size && file.size > sizeLimit) { - Alert.alert( - t('File is too large: {{ size }}, maximum upload size is {{ limit }}', { - limit: prettifyFileSize(sizeLimit), - size: prettifyFileSize(file.size), - }), - ); - setSelectedFiles(selectedFiles.filter((selectedFile) => selectedFile.uri !== file.uri)); - return; - } + if (file.size && file.size > sizeLimit) { + Alert.alert( + t('File is too large: {{ size }}, maximum upload size is {{ limit }}', { + limit: prettifyFileSize(sizeLimit), + size: prettifyFileSize(file.size), + }), + ); + setSelectedFiles(selectedFiles.filter((selectedFile) => selectedFile.uri !== file.uri)); + return; + } - const fileState = isAllowed ? FileState.UPLOADING : FileState.NOT_SUPPORTED; + const fileState = isAllowed ? FileState.UPLOADING : FileState.NOT_SUPPORTED; - // If file type is explicitly provided while upload we use it, else we derive the file type. - const fileType = file.type || file.mimeType?.split('/')[0]; + // If file type is explicitly provided while upload we use it, else we derive the file type. + const fileType = file.type || file.mimeType?.split('/')[0]; - const newFile: FileUpload = { - duration: file.duration || 0, - file, - id: file.id || id, - state: fileState, - type: fileType, - }; + const newFile: FileUpload = { + duration: file.duration || 0, + file, + id: file.id || id, + state: fileState, + type: fileType, + url: file.uri, + }; - await Promise.all([ - setFileUploads((prevFileUploads) => prevFileUploads.concat([newFile])), - setNumberOfUploads((prevNumberOfUploads) => prevNumberOfUploads + 1), - ]); + await Promise.all([ + setFileUploads((prevFileUploads) => prevFileUploads.concat([newFile])), + setNumberOfUploads((prevNumberOfUploads) => prevNumberOfUploads + 1), + ]); - if (isAllowed) { - uploadFile({ newFile }); + if (isAllowed) { + await uploadFile({ newFile }); + } + } catch (error) { + console.log('Error uploading file', error); } }; const uploadNewImage = async (image: Partial) => { - const id = generateRandomId(); - const imageUploadConfig = getImageUploadConfig(); + try { + const id = generateRandomId(); + const imageUploadConfig = getImageUploadConfig(); - const { size_limit } = imageUploadConfig; + const { size_limit } = imageUploadConfig; - const isAllowed = isUploadAllowed({ config: imageUploadConfig, file: image }); + const isAllowed = isUploadAllowed({ config: imageUploadConfig, file: image }); - const sizeLimit = size_limit || MAX_FILE_SIZE_TO_UPLOAD; + const sizeLimit = size_limit || MAX_FILE_SIZE_TO_UPLOAD; - if (image.size && image?.size > sizeLimit) { - Alert.alert( - t('File is too large: {{ size }}, maximum upload size is {{ limit }}', { - limit: prettifyFileSize(sizeLimit), - size: prettifyFileSize(image.size), - }), - ); - setSelectedImages(selectedImages.filter((selectedImage) => selectedImage.uri !== image.uri)); - return; - } + if (image.size && image?.size > sizeLimit) { + Alert.alert( + t('File is too large: {{ size }}, maximum upload size is {{ limit }}', { + limit: prettifyFileSize(sizeLimit), + size: prettifyFileSize(image.size), + }), + ); + setSelectedImages( + selectedImages.filter((selectedImage) => selectedImage.uri !== image.uri), + ); + return; + } - const imageState = isAllowed ? FileState.UPLOADING : FileState.NOT_SUPPORTED; + const imageState = isAllowed ? FileState.UPLOADING : FileState.NOT_SUPPORTED; - const newImage: ImageUpload = { - file: image, - height: image.height, - id, - state: imageState, - url: image.uri, - width: image.width, - }; + const newImage: ImageUpload = { + file: image, + height: image.height, + id, + state: imageState, + url: image.uri, + width: image.width, + }; - await Promise.all([ - setImageUploads((prevImageUploads) => prevImageUploads.concat([newImage])), - setNumberOfUploads((prevNumberOfUploads) => prevNumberOfUploads + 1), - ]); + await Promise.all([ + setImageUploads((prevImageUploads) => prevImageUploads.concat([newImage])), + setNumberOfUploads((prevNumberOfUploads) => prevNumberOfUploads + 1), + ]); - if (isAllowed) { - uploadImage({ newImage }); + if (isAllowed) { + await uploadImage({ newImage }); + } + } catch (error) { + console.log('Error uploading image', error); } }; diff --git a/package/src/contexts/messageInputContext/__tests__/sendMessage.test.tsx b/package/src/contexts/messageInputContext/__tests__/sendMessage.test.tsx index e9bec02066..b091ab12da 100644 --- a/package/src/contexts/messageInputContext/__tests__/sendMessage.test.tsx +++ b/package/src/contexts/messageInputContext/__tests__/sendMessage.test.tsx @@ -12,6 +12,7 @@ import { generateMessage } from '../../../mock-builders/generator/message'; import { generateUser } from '../../../mock-builders/generator/user'; import type { DefaultStreamChatGenerics } from '../../../types/types'; import { FileState } from '../../../utils/utils'; +import * as AttachmentPickerContext from '../../attachmentPickerContext/AttachmentPickerContext'; import { InputMessageInputContextValue, MessageInputContextValue, @@ -39,6 +40,10 @@ const Wrapper = { + jest.spyOn(AttachmentPickerContext, 'useAttachmentPickerContext').mockImplementation(() => ({ + setSelectedFiles: jest.fn(), + setSelectedImages: jest.fn(), + })); const message: boolean | MessageType = generateMessage({ created_at: 'Sat Jul 02 2022 23:55:13 GMT+0530 (India Standard Time)', id: '7a85f744-cc89-4f82-a1d4-5456432cc8bf', diff --git a/package/src/contexts/messageInputContext/__tests__/updateMessage.test.tsx b/package/src/contexts/messageInputContext/__tests__/updateMessage.test.tsx index 8c702f3e14..39f5e131ff 100644 --- a/package/src/contexts/messageInputContext/__tests__/updateMessage.test.tsx +++ b/package/src/contexts/messageInputContext/__tests__/updateMessage.test.tsx @@ -11,6 +11,7 @@ import { ChatContextValue, ChatProvider } from '../../../contexts/chatContext/Ch import { generateMessage } from '../../../mock-builders/generator/message'; import { generateUser } from '../../../mock-builders/generator/user'; import type { DefaultStreamChatGenerics } from '../../../types/types'; +import * as AttachmentPickerContext from '../../attachmentPickerContext/AttachmentPickerContext'; import { InputMessageInputContextValue, MessageInputContextValue, @@ -49,6 +50,10 @@ const Wrapper = { + jest.spyOn(AttachmentPickerContext, 'useAttachmentPickerContext').mockImplementation(() => ({ + setSelectedFiles: jest.fn(), + setSelectedImages: jest.fn(), + })); const clearEditingStateMock = jest.fn(); const generatedMessage: boolean | MessageType = generateMessage({ created_at: 'Sat Jul 02 2022 23:55:13 GMT+0530 (India Standard Time)', diff --git a/package/src/contexts/messageInputContext/hooks/useMessageDetailsForState.ts b/package/src/contexts/messageInputContext/hooks/useMessageDetailsForState.ts index f58c4f206c..8708b6e4e1 100644 --- a/package/src/contexts/messageInputContext/hooks/useMessageDetailsForState.ts +++ b/package/src/contexts/messageInputContext/hooks/useMessageDetailsForState.ts @@ -8,7 +8,7 @@ import { FileUpload, ImageUpload, } from '../../../types/types'; -import { generateRandomId } from '../../../utils/utils'; +import { generateRandomId, stringifyMessage } from '../../../utils/utils'; import type { MessageInputContextValue } from '../MessageInputContext'; @@ -37,8 +37,7 @@ export const useMessageDetailsForState = < // eslint-disable-next-line react-hooks/exhaustive-deps }, [text, imageUploads.length, fileUploads.length]); - const messageValue = - message === undefined ? '' : `${message.id}${message.text}${message.updated_at}`; + const messageValue = message ? stringifyMessage(message) : ''; useEffect(() => { if (message && Array.isArray(message?.mentioned_users)) { @@ -67,9 +66,11 @@ export const useMessageDetailsForState = < } else if (attachment.type === FileTypes.Video) { return { file: { + duration: attachment.duration, mimeType: attachment.mime_type, name: attachment.title || '', size: attachment.file_size, + uri: attachment.asset_url, }, id, state: 'finished', @@ -96,6 +97,7 @@ export const useMessageDetailsForState = < mimeType: attachment.mime_type, name: attachment.title || '', size: attachment.file_size, + uri: attachment.asset_url, }, id, state: 'finished', @@ -107,6 +109,7 @@ export const useMessageDetailsForState = < mimeType: attachment.mime_type, name: attachment.title || '', size: attachment.file_size, + uri: attachment.asset_url, }, id, state: 'finished', @@ -128,9 +131,11 @@ export const useMessageDetailsForState = < const id = generateRandomId(); newImageUploads.push({ file: { + height: attachment.original_height, name: attachment.fallback, size: attachment.file_size, type: attachment.type, + width: attachment.original_width, }, id, state: 'finished',