[🐛] Image attachment is not rendering properly inside message.
We are customising the AttachButton as per doc and uploading new images using uploadNewImage. This works fine. However, when the image is attached inside the message list, there is a weird grey view appearing bottom of the image.
Please note that when user goes back and come back to the chat screen again, the image is rendered fine. Only when the image is attached instantly in a message then the issue occurs. I believe this happens mainly when I am taking the picture in Potrait mode.
This is after I attach a photo from camera,
But the image renders fine when I come back to chat,
This is how my code looks,
// Custom attach button component
const CustomAttachButton = () => {
const {showActionSheetWithOptions} = useActionSheet();
const {uploadNewImage} = useMessageInputContext();
const compressAndUploadImage = async imageFile => {
try {
const compressedUri = await ImageCompressor.compress(imageFile.uri, {
compressionMethod: 'auto',
});
return uploadNewImage({
name: imageFile.fileName,
type: imageFile.type,
uri: compressedUri,
});
} catch (error) {
// Fallback to original image if compression fails
return uploadNewImage({
name: imageFile.fileName,
type: imageFile.type,
uri: imageFile.uri,
});
}
};
const pickImageFromGallery = async () => {
try {
const images = await launchImageLibrary({
selectionLimit: 1,
mediaType: 'photo',
});
if (images?.assets?.length > 0) {
const selectedImage = images.assets[0];
await compressAndUploadImage(selectedImage);
}
} catch (error) {
console.error('Error picking image from gallery:', error);
}
};
const pickImageFromCamera = async () => {
try {
if (Platform.OS === 'android') {
const granted = await PermissionsAndroid.request(
PermissionsAndroid.PERMISSIONS.CAMERA,
{
title: 'Camera Permission',
message: 'ShiftCare needs camera permission to take photos',
buttonNeutral: 'Ask Me Later',
buttonNegative: 'Cancel',
buttonPositive: 'OK',
},
);
if (granted !== PermissionsAndroid.RESULTS.GRANTED) {
console.log('Camera permission denied');
return;
}
}
const images = await launchCamera({
mediaType: 'photo',
});
if (images?.assets?.length > 0) {
const selectedImage = images.assets[0];
await compressAndUploadImage(selectedImage);
}
} catch (error) {
console.error('Error picking image from camera:', error);
}
};
const onPress = () => {
showActionSheetWithOptions(
{
cancelButtonIndex: 2,
destructiveButtonIndex: 2,
options: ['Photo Library', 'Camera', 'Cancel'],
},
buttonIndex => {
switch (buttonIndex) {
case 0:
pickImageFromGallery();
break;
case 1:
pickImageFromCamera();
break;
default:
break;
}
},
);
};
return <AttachButton handleOnPress={onPress} />;
};
// Chat component
return (
<SafeAreaView edges={['bottom']} className="flex-1">
<ActionSheetProvider>
<ChannelComponent
key={channel.cid}
supportedReactions={supportedReactions}
hasFilePicker={false}
MessageFooter={featureReadReceipts ? CustomMessageFooter : () => null}
CommandsButton={() => null}
AttachButton={CustomAttachButton}
channel={channel}>
<MessageList />
<MessageInput />
<BottomSpacing />
</ChannelComponent>
</ActionSheetProvider>
</SafeAreaView>
);
Hey @Saad-Bashar, I tried to triage the issue on my end by sending a similar photo on the Android emulator and capturing the image, but I couldn't reproduce it. I am attaching a video. Do you have any added customization?
https://github.com/user-attachments/assets/84110e82-d401-432a-944d-28e131c89430
Hey @Saad-Bashar, are you still facing this issue, or can we close it?
Yes, we are still having this issue, unfortunately. This one mostly started to happen when we started to use uploadNewFile instead of the built-in way. We are using react-native-image-picker. We don't have any other customizations. These are the versions,
"@stream-io/flat-list-mvcp": "^0.10.3",
"stream-chat": "^8.44.0",
"stream-chat-react-native": "^5.41.4"
same problem here
?
Same problem here when I uploaded the picture, this issue occurred. it didn't do it with that horizontal picture, only occurred in the case I uploaded a vertical picture. I am using iOS Emulator
I'm having the same issue and have done no UI customization.
@bridgetrosefitz figured out that if you adjust this styling property, it looks better:
theme.messageSimple.gallery = {
...theme.messageSimple.gallery,
galleryItemColumn: {
justifyContent: "center",
}
}
But using the sendMessage API still doesn't recognize vertical images, and makes every attached image horizontal:
https://getstream.io/chat/docs/react-native/send_message/#complex-example
"stream-chat-expo": "^6.7.4",
After applying the style fix:
Hello, @kevinmcalear, thanks for considering my problem
I did like this:
import {defaultTheme} from 'stream-chat-react-native';
const customTheme = {
...defaultTheme,
messageSimple: {
...defaultTheme.messageSimple,
gallery: {
...defaultTheme.messageSimple.gallery,
galleryItemColumn: {
justifyContent: 'center',
},
},
},
};
return channel ? (
<SafeAreaView edges={['top', 'bottom']} style={styles.container}>
<Header rightButtons={headerIcons} />
{!loading ? (
<View style={{flex: 1}}>
<TouchableOpacity
onPress={() =>
navigateToProfile(chatPartnerId || chatPartnerUserId)
}>
<View style={styles.partnerHeader}>
<PartnerHeader
chatPartnerId={chatPartnerId || chatPartnerUserId}
/>
<TDText variant="h3" paddingLeft={5}>
{chatPartnerName} {!clientIsReady ? ' (Pending)' : ''}
</TDText>
<OnlineStatus
status={chatPartnerOnline ? 'online' : 'offline'}
hasLabel={false}
/>
</View>
</TouchableOpacity>
<KeyboardAvoidingView
behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
style={{flex: 1}}
keyboardVerticalOffset={Platform.OS === 'ios' ? 135 : 120}>
<Channel
theme={customTheme} // Pass the custom theme here
EmptyStateIndicator={CustomEmptyStateIndicator}
MessageAvatar={() => (
<TruDateMessageAvatar
onPress={() =>
navigateToProfile(chatPartnerId || chatPartnerUserId)
}
userId={chatPartnerId || chatPartnerUserId}
/>
)}
channel={channel}
maxNumberOfFiles={1}
FileAttachment={ChatFileAttachment}
allowThreadMessagesInChannel={false}
reactionListPosition="top"
ReactionList={CustomReactionList}
supportedReactions={supportedReactions}
disableTypingIndicator
giphyEnabled={false}
hasCommands={false}
hasImagePicker={Platform.OS === 'ios' ? true : true}
AttachButton={CustomAttachButton}
hasFilePicker={false}
initialScrollToFirstUnreadMessage
keyboardVerticalOffset={Platform.OS === 'ios' ? 120 : 100}
messageId={messageId}
mentionAllAppUsersEnabled={false}
autoCompleteTriggerSettings={() => ({})}
messageActions={({
isMyMessage,
copyMessage,
flagMessage,
messageReactions,
retry,
}) =>
isMyMessage
? [copyMessage, retry]
: [flagMessage, messageReactions]
}
NetworkDownIndicator={() => null}>
<MessageList<StreamChatGenerics> />
<MessageInput />
</Channel>
</KeyboardAvoidingView>
</View>
) : (
''
)}
<ReportUserActionSheet
show={showReport}
userId={(chatPartnerId || chatPartnerUserId)?.replace('-', '|')}
reportUserName={chatPartnerName || 'user'}
setIsLoading={setLoading}
onHide={() => setShowReport(false)}
onPressSafetyCenter={() => navigation.navigate('SafetyCenter')}
/>
</SafeAreaView>
) : invitationSent ? (
<View>
<Text>Invitation to chat was sent and is awaiting acceptance</Text>
</View>
) : null;
};
but this code didn't fix the problem. Please kindly guide me. thanks
I ran into this problem today.
It's occurring because of the image dimensions are not being set to the image instance after the image is taken with the camera.
There is code to manage this in the takeAndUploadImage method which is used by the default AttachButton.
Specifically the setting of dimensions is in code for the takePhoto function here.
In order to solve this when using a custom AttachButton button I extracted the logic and used it to set the width and height to be passed into uploadNewImage.
type Dimenions = { width?: number; height?: number };
/**
* Gets image size no matter if it came from camera or gallery
* Copied from https://github.com/GetStream/stream-chat-react-native/blob/develop/package/native-package/src/optionalDependencies/takePhoto.ts
*/
async function getImageDimensions(image: ImagePickerAsset) {
let size: Dimenions = {};
if (Platform.OS === 'android') {
const getSize = (): Promise<Dimenions> =>
new Promise((resolve) => {
Image.getSize(image.uri, (width, height) => {
resolve({ height, width });
});
});
try {
const { height, width } = await getSize();
size.height = height;
size.width = width;
} catch (e) {
console.warn('Error get image size of picture caputred from camera ', e);
}
} else {
size = {
height: image.height,
width: image.width,
};
}
return size;
}
Usage:
const dimensions = await getImageDimensions(image);
const result = await uploadNewImage({
uri: image.uri,
name: image.fileName ?? image.uri.split('/').pop() ?? 'image.jpg',
size: image.fileSize!,
type: image.mimeType!,
...dimensions,
});
Thanks very much, @lukeggchapman Your code fixed my problem. 👍