Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,14 @@ const ImageContainer = ({
const image = (
<Button onPress={onPress}>
<WidthAwareView>
<MessageImage uri={url} status={status} encrypted={isEncrypted} imagePreview={imagePreview} imageType={imageType} />
<MessageImage
uri={url}
status={status}
encrypted={isEncrypted}
imagePreview={imagePreview}
imageType={imageType}
imageDimensions={file.image_dimensions}
/>
</WidthAwareView>
</Button>
);
Expand Down
56 changes: 42 additions & 14 deletions app/containers/message/Components/Attachments/Image/Image.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,40 +13,63 @@ import { AUTOPLAY_GIFS_PREFERENCES_KEY } from '../../../../../lib/constants/keys
import ImageBadge from './ImageBadge';
import log from '../../../../../lib/methods/helpers/log';

export const MessageImage = React.memo(({ uri, status, encrypted = false, imagePreview, imageType }: IMessageImage) => {
export const MessageImage = React.memo(
({ uri, status, encrypted = false, imagePreview, imageType, imageDimensions: providedDimensions }: IMessageImage) => {
'use memo';

const { colors } = useTheme();
const [imageDimensions, setImageDimensions] = useState({ width: 0, height: 0 });
const [imageDimensions, setImageDimensions] = useState({
width: providedDimensions?.width ?? 0,
height: providedDimensions?.height ?? 0
});
const [autoplayGifs] = useUserPreferences<boolean>(AUTOPLAY_GIFS_PREFERENCES_KEY, true);
const maxSize = useContext(WidthAwareContext);
const showImage = isValidUrl(uri) && imageDimensions.width && status === 'downloaded';
const isGif = imageType === 'image/gif';
const isSvg = imageType?.includes('svg') ?? false;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Consider case-insensitive SVG detection.

The includes('svg') check is case-sensitive. While most servers return lowercase MIME types (e.g., 'image/svg+xml'), the HTTP specification doesn't mandate this. If a server returns 'image/SVG+xml', the SVG would not be detected and the sizing fix would fail.

Apply this diff for more robust detection:

-	const isSvg = imageType?.includes('svg') ?? false;
+	const isSvg = imageType?.toLowerCase().includes('svg') ?? false;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const isSvg = imageType?.includes('svg') ?? false;
const isSvg = imageType?.toLowerCase().includes('svg') ?? false;
🤖 Prompt for AI Agents
In app/containers/message/Components/Attachments/Image/Image.tsx around line 28,
the current SVG detection uses a case-sensitive includes('svg') which can miss
variants like 'image/SVG+xml'; update the check to be case-insensitive by first
confirming imageType exists and then calling
imageType.toLowerCase().includes('svg') (or use a safe conditional like
imageType ? imageType.toLowerCase().includes('svg') : false) so toLowerCase is
never called on undefined.


useEffect(() => {
if (providedDimensions?.width && providedDimensions?.height) {
setImageDimensions({ width: providedDimensions.width, height: providedDimensions.height });
}
}, [providedDimensions?.width, providedDimensions?.height]);

useEffect(() => {
// Preserve SVG sizing: stick to provided/fallback dimensions and skip intrinsic load that shrinks the view.
if (isSvg) {
return;
}

if (status === 'downloaded') {
Image.loadAsync(uri, {
onError: e => {
log(e);
}
}).then(image => {
setImageDimensions({ width: image.width, height: image.height });
});
})
.then(image => {
setImageDimensions({ width: image.width, height: image.height });
})
.catch(e => {
log(e);
});
}
}, [uri, status]);
}, [uri, status, isSvg]);

const width = Math.min(imageDimensions.width, maxSize) || 0;
const height = Math.min((imageDimensions.height * ((width * 100) / imageDimensions.width)) / 100, maxSize) || 0;
const hasDimensions = imageDimensions.width > 0 && imageDimensions.height > 0;
const fallbackSize = Math.min(maxSize, 200);
const ratio = hasDimensions ? imageDimensions.height / imageDimensions.width : 1;
const displayWidth = hasDimensions ? Math.min(imageDimensions.width, maxSize) : fallbackSize;
const displayHeight = hasDimensions ? Math.min(displayWidth * ratio, maxSize) : fallbackSize;
const showImage = isValidUrl(uri) && status === 'downloaded' && displayWidth > 0 && displayHeight > 0;
const imageStyle = {
width,
height
width: displayWidth,
height: displayHeight
};

const containerStyle: ViewStyle = {
alignItems: 'center',
justifyContent: 'center',
...(imageDimensions.width <= 64 && { width: 64 }),
...(imageDimensions.height <= 64 && { height: 64 })
...(displayWidth <= 64 && { width: 64 }),
...(displayHeight <= 64 && { height: 64 })
};

const borderStyle: ViewStyle = {
Expand All @@ -69,7 +92,12 @@ export const MessageImage = React.memo(({ uri, status, encrypted = false, imageP
<>
{showImage ? (
<View style={[containerStyle, borderStyle]}>
<Image autoplay={autoplayGifs} style={imageStyle} source={{ uri: encodeURI(uri) }} contentFit='cover' />
<Image
autoplay={autoplayGifs}
style={imageStyle}
source={{ uri: encodeURI(uri) }}
contentFit={isSvg ? 'contain' : 'cover'}
/>
</View>
) : null}
{['loading', 'to-download'].includes(status) || (status === 'downloaded' && !showImage) ? (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,8 @@ export interface IMessageImage {
encrypted: boolean;
imagePreview?: string;
imageType?: string;
imageDimensions?: {
width?: number;
height?: number;
};
}