fix: media gallery thumbnails — skip auth URL, handle encrypted media
- Use useAuthentication=false for thumbnail requests: the v1 authenticated URL adds allow_redirect=true which Synapse rejects with 400 - Encrypted events (content.file set) show a lock+filename placeholder since server can't thumbnail encrypted blobs - Unencrypted thumbnails add onError handler to hide broken images gracefully Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -211,36 +211,65 @@ export function MediaGallery({ room, onClose }: MediaGalleryProps) {
|
|||||||
>
|
>
|
||||||
{events.map((mEvent) => {
|
{events.map((mEvent) => {
|
||||||
const content = mEvent.getContent();
|
const content = mEvent.getContent();
|
||||||
|
const isEncrypted = !!content.file;
|
||||||
const mxcUrl: string | undefined = content.url ?? content.file?.url;
|
const mxcUrl: string | undefined = content.url ?? content.file?.url;
|
||||||
if (!mxcUrl) return null;
|
if (!mxcUrl) return null;
|
||||||
const thumbUrl =
|
|
||||||
mxcUrlToHttp(mx, mxcUrl, useAuthentication, 120, 120, 'crop') ?? '';
|
|
||||||
const body: string = content.body ?? '';
|
const body: string = content.body ?? '';
|
||||||
|
// Use unauthenticated thumbnail URL — the v1 authenticated endpoint adds
|
||||||
|
// allow_redirect=true which Synapse rejects with 400.
|
||||||
|
const thumbUrl = isEncrypted
|
||||||
|
? null
|
||||||
|
: (mxcUrlToHttp(mx, mxcUrl, false, 120, 120, 'crop') ?? null);
|
||||||
|
const fullUrl = mxcUrlToHttp(mx, mxcUrl, useAuthentication) ?? '#';
|
||||||
return (
|
return (
|
||||||
<a
|
<a
|
||||||
key={mEvent.getId()}
|
key={mEvent.getId()}
|
||||||
href={mxcUrlToHttp(mx, mxcUrl, useAuthentication) ?? '#'}
|
href={isEncrypted ? '#' : fullUrl}
|
||||||
target="_blank"
|
target={isEncrypted ? undefined : '_blank'}
|
||||||
rel="noreferrer"
|
rel="noreferrer"
|
||||||
style={{
|
style={{
|
||||||
display: 'block',
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
aspectRatio: '1',
|
aspectRatio: '1',
|
||||||
overflow: 'hidden',
|
overflow: 'hidden',
|
||||||
borderRadius: config.radii.R300,
|
borderRadius: config.radii.R300,
|
||||||
background: 'var(--bg-surface)',
|
background: 'var(--bg-surface-low)',
|
||||||
|
cursor: isEncrypted ? 'default' : 'pointer',
|
||||||
}}
|
}}
|
||||||
title={body}
|
title={body}
|
||||||
>
|
>
|
||||||
<img
|
{thumbUrl ? (
|
||||||
src={thumbUrl}
|
<img
|
||||||
alt={body}
|
src={thumbUrl}
|
||||||
style={{
|
alt={body}
|
||||||
width: '100%',
|
onError={(e) => {
|
||||||
height: '100%',
|
(e.currentTarget as HTMLImageElement).style.display = 'none';
|
||||||
objectFit: 'cover',
|
}}
|
||||||
display: 'block',
|
style={{
|
||||||
}}
|
width: '100%',
|
||||||
/>
|
height: '100%',
|
||||||
|
objectFit: 'cover',
|
||||||
|
display: 'block',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<Box
|
||||||
|
direction="Column"
|
||||||
|
alignItems="Center"
|
||||||
|
gap="100"
|
||||||
|
style={{ padding: config.space.S100 }}
|
||||||
|
>
|
||||||
|
<Icon src={isEncrypted ? Icons.Lock : Icons.Photo} size="400" />
|
||||||
|
<Text
|
||||||
|
size="T200"
|
||||||
|
truncate
|
||||||
|
style={{ maxWidth: '100%', textAlign: 'center', opacity: 0.7 }}
|
||||||
|
>
|
||||||
|
{body || (isEncrypted ? 'Encrypted' : 'Image')}
|
||||||
|
</Text>
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
</a>
|
</a>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
|||||||
Reference in New Issue
Block a user