This commit is contained in:
Matthias Hochmeister
2026-03-13 13:51:08 +01:00
parent e26d77ef35
commit 3361f1e28d

View File

@@ -1,10 +1,13 @@
import React from 'react'; import React, { useState } from 'react';
import Box from '@mui/material/Box'; import Box from '@mui/material/Box';
import Typography from '@mui/material/Typography'; import Typography from '@mui/material/Typography';
import IconButton from '@mui/material/IconButton'; import IconButton from '@mui/material/IconButton';
import Tooltip from '@mui/material/Tooltip'; import Tooltip from '@mui/material/Tooltip';
import Dialog from '@mui/material/Dialog';
import DialogContent from '@mui/material/DialogContent';
import InsertDriveFileIcon from '@mui/icons-material/InsertDriveFile'; import InsertDriveFileIcon from '@mui/icons-material/InsertDriveFile';
import DownloadIcon from '@mui/icons-material/Download'; import DownloadIcon from '@mui/icons-material/Download';
import CloseIcon from '@mui/icons-material/Close';
interface FileParam { interface FileParam {
name: string; name: string;
@@ -40,7 +43,67 @@ function extractFileParams(messageParameters: Record<string, any>): FileParam[]
return files; return files;
} }
interface ImageLightboxProps {
open: boolean;
onClose: () => void;
previewUrl: string;
downloadUrl: string;
name: string;
}
const ImageLightbox: React.FC<ImageLightboxProps> = ({ open, onClose, previewUrl, downloadUrl, name }) => (
<Dialog
open={open}
onClose={onClose}
maxWidth="lg"
fullWidth
slotProps={{ paper: { sx: { bgcolor: 'black', m: 1 } } }}
>
<DialogContent sx={{ p: 0, position: 'relative', display: 'flex', justifyContent: 'center', alignItems: 'center', minHeight: 200 }}>
<Box
component="img"
src={previewUrl}
alt={name}
sx={{ maxWidth: '100%', maxHeight: '85vh', display: 'block', objectFit: 'contain' }}
/>
{/* Close button */}
<Tooltip title="Schließen">
<IconButton
onClick={onClose}
size="small"
sx={{ position: 'absolute', top: 8, right: 8, bgcolor: 'rgba(0,0,0,0.5)', color: 'white', '&:hover': { bgcolor: 'rgba(0,0,0,0.75)' } }}
>
<CloseIcon fontSize="small" />
</IconButton>
</Tooltip>
{/* Download button */}
<Tooltip title="Herunterladen">
<IconButton
component="a"
href={downloadUrl}
download={name}
target="_blank"
rel="noopener noreferrer"
size="small"
sx={{ position: 'absolute', top: 8, right: 44, bgcolor: 'rgba(0,0,0,0.5)', color: 'white', '&:hover': { bgcolor: 'rgba(0,0,0,0.75)' } }}
>
<DownloadIcon fontSize="small" />
</IconButton>
</Tooltip>
{/* Filename */}
<Typography
variant="caption"
sx={{ position: 'absolute', bottom: 8, left: 0, right: 0, textAlign: 'center', color: 'rgba(255,255,255,0.7)', px: 2 }}
noWrap
>
{name}
</Typography>
</DialogContent>
</Dialog>
);
const FileMessageContent: React.FC<FileMessageContentProps> = ({ messageParameters, isOwnMessage }) => { const FileMessageContent: React.FC<FileMessageContentProps> = ({ messageParameters, isOwnMessage }) => {
const [lightboxFile, setLightboxFile] = useState<FileParam | null>(null);
const files = extractFileParams(messageParameters); const files = extractFileParams(messageParameters);
if (files.length === 0) return null; if (files.length === 0) return null;
@@ -48,30 +111,45 @@ const FileMessageContent: React.FC<FileMessageContentProps> = ({ messageParamete
<> <>
{files.map((file, idx) => { {files.map((file, idx) => {
const downloadUrl = `/api/nextcloud/talk/files/${file.id}/download${file.path ? `?path=${encodeURIComponent(file.path)}` : ''}`; const downloadUrl = `/api/nextcloud/talk/files/${file.id}/download${file.path ? `?path=${encodeURIComponent(file.path)}` : ''}`;
const previewUrl = `/api/nextcloud/talk/files/${file.id}/preview?w=400&h=400`; const thumbUrl = `/api/nextcloud/talk/files/${file.id}/preview?w=400&h=400`;
const fullUrl = `/api/nextcloud/talk/files/${file.id}/preview?w=1200&h=1200`;
const isImage = file.mimetype?.startsWith('image/') && file.previewAvailable === 'yes'; const isImage = file.mimetype?.startsWith('image/') && file.previewAvailable === 'yes';
if (isImage) { if (isImage) {
return ( return (
<Box key={idx} sx={{ mt: 0.5 }}> <Box key={idx} sx={{ mt: 0.5 }}>
<a href={downloadUrl} target="_blank" rel="noopener noreferrer" download={file.name}> <Box
<Box component="img"
component="img" src={thumbUrl}
src={previewUrl} alt={file.name}
alt={file.name} onClick={() => setLightboxFile(file)}
sx={{ sx={{
maxWidth: '100%', maxWidth: '100%',
maxHeight: 200, maxHeight: 200,
borderRadius: 1, borderRadius: 1,
display: 'block', display: 'block',
cursor: 'pointer', cursor: 'zoom-in',
'&:hover': { opacity: 0.9 }, '&:hover': { opacity: 0.9 },
}} }}
/> />
</a> <Box sx={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', mt: 0.25 }}>
<Typography variant="caption" sx={{ opacity: 0.7, fontSize: '0.7rem' }}> <Typography variant="caption" sx={{ opacity: 0.7, fontSize: '0.7rem' }} noWrap>
{file.name} {file.name}
</Typography> </Typography>
<Tooltip title="Herunterladen">
<IconButton
size="small"
component="a"
href={downloadUrl}
download={file.name}
target="_blank"
rel="noopener noreferrer"
sx={{ color: 'inherit', p: 0.25, flexShrink: 0 }}
>
<DownloadIcon sx={{ fontSize: '0.9rem' }} />
</IconButton>
</Tooltip>
</Box>
</Box> </Box>
); );
} }
@@ -93,12 +171,7 @@ const FileMessageContent: React.FC<FileMessageContentProps> = ({ messageParamete
> >
<InsertDriveFileIcon fontSize="small" sx={{ opacity: 0.8, flexShrink: 0 }} /> <InsertDriveFileIcon fontSize="small" sx={{ opacity: 0.8, flexShrink: 0 }} />
<Box sx={{ flex: 1, minWidth: 0 }}> <Box sx={{ flex: 1, minWidth: 0 }}>
<Typography <Typography variant="body2" noWrap title={file.name} sx={{ fontWeight: 500, lineHeight: 1.2 }}>
variant="body2"
noWrap
title={file.name}
sx={{ fontWeight: 500, lineHeight: 1.2 }}
>
{file.name} {file.name}
</Typography> </Typography>
{file.size && ( {file.size && (
@@ -123,6 +196,16 @@ const FileMessageContent: React.FC<FileMessageContentProps> = ({ messageParamete
</Box> </Box>
); );
})} })}
{lightboxFile && (
<ImageLightbox
open
onClose={() => setLightboxFile(null)}
previewUrl={`/api/nextcloud/talk/files/${lightboxFile.id}/preview?w=1200&h=1200`}
downloadUrl={`/api/nextcloud/talk/files/${lightboxFile.id}/download${lightboxFile.path ? `?path=${encodeURIComponent(lightboxFile.path)}` : ''}`}
name={lightboxFile.name}
/>
)}
</> </>
); );
}; };