mirror of
https://github.com/yusufipk/OpenFrame.git
synced 2026-09-11 17:46:06 +00:00
feat(video-assets): add full video asset system (uploads, downloads, @mentions, and cleanup/billing integration)
This commit is contained in:
@@ -9,71 +9,94 @@ import { Dialog, DialogContent, DialogTitle } from '@/components/ui/dialog';
|
||||
interface ImagePreviewDialogProps {
|
||||
previewImage: string | null;
|
||||
onClose: () => void;
|
||||
title?: string | null;
|
||||
downloadFileName?: string | null;
|
||||
canDownload?: boolean;
|
||||
}
|
||||
|
||||
export const ImagePreviewDialog = memo(function ImagePreviewDialog({
|
||||
previewImage,
|
||||
onClose,
|
||||
title,
|
||||
downloadFileName,
|
||||
canDownload = true,
|
||||
}: ImagePreviewDialogProps) {
|
||||
const resolvedDownloadName = downloadFileName || (previewImage ? previewImage.split('/').pop() || 'attachment.png' : 'attachment.png');
|
||||
|
||||
return (
|
||||
<Dialog open={!!previewImage} onOpenChange={(open) => !open && onClose()}>
|
||||
<DialogContent
|
||||
showCloseButton={false}
|
||||
className="max-w-none sm:max-w-none w-screen h-screen max-h-screen p-0 overflow-hidden bg-black/90 border-none shadow-none flex flex-col items-center justify-center rounded-none"
|
||||
className="max-w-none sm:max-w-none w-screen h-screen max-h-screen p-0 overflow-hidden bg-black/90 border-none shadow-none flex items-center justify-center rounded-none"
|
||||
onClick={onClose}
|
||||
onKeyDown={(event) => {
|
||||
event.stopPropagation();
|
||||
if (event.key === 'Escape') {
|
||||
event.preventDefault();
|
||||
onClose();
|
||||
}
|
||||
}}
|
||||
>
|
||||
<DialogTitle className="sr-only">Image Preview</DialogTitle>
|
||||
<div className="absolute top-4 right-4 flex gap-3 z-50">
|
||||
<Button
|
||||
variant="outline"
|
||||
size="icon"
|
||||
className="rounded-full bg-black/40 hover:bg-black/80 border-white/20 text-white h-10 w-10 backdrop-blur-md transition-all shrink-0"
|
||||
onClick={async (e) => {
|
||||
e.stopPropagation();
|
||||
try {
|
||||
if (!previewImage) return;
|
||||
const response = await fetch(previewImage);
|
||||
const blob = await response.blob();
|
||||
const url = window.URL.createObjectURL(blob);
|
||||
const a = document.createElement('a');
|
||||
a.href = url;
|
||||
a.download = previewImage.split('/').pop() || 'attachment.png';
|
||||
document.body.appendChild(a);
|
||||
a.click();
|
||||
document.body.removeChild(a);
|
||||
window.URL.revokeObjectURL(url);
|
||||
} catch (error) {
|
||||
console.error('Failed to download image:', error);
|
||||
toast.error('Failed to download image');
|
||||
}
|
||||
}}
|
||||
<DialogTitle className="sr-only">{title || 'Image Preview'}</DialogTitle>
|
||||
<div className="w-[min(96vw,1500px)] h-[min(94vh,1000px)] border border-border/60 bg-black/80 shadow-2xl flex flex-col overflow-hidden" onClick={(e) => e.stopPropagation()}>
|
||||
<div className="shrink-0 flex items-center gap-2 border-b border-border/60 bg-background/85 px-2 py-1.5 backdrop-blur-sm">
|
||||
<p className="flex-1 min-w-0 text-sm text-foreground truncate" title={title || undefined}>
|
||||
{title || 'Image Preview'}
|
||||
</p>
|
||||
{canDownload ? (
|
||||
<Button
|
||||
variant="outline"
|
||||
size="icon"
|
||||
className="h-8 w-8 shrink-0"
|
||||
onClick={async (e) => {
|
||||
e.stopPropagation();
|
||||
try {
|
||||
if (!previewImage) return;
|
||||
const response = await fetch(previewImage);
|
||||
const blob = await response.blob();
|
||||
const url = window.URL.createObjectURL(blob);
|
||||
const a = document.createElement('a');
|
||||
a.href = url;
|
||||
a.download = resolvedDownloadName;
|
||||
document.body.appendChild(a);
|
||||
a.click();
|
||||
document.body.removeChild(a);
|
||||
window.URL.revokeObjectURL(url);
|
||||
} catch (error) {
|
||||
console.error('Failed to download image:', error);
|
||||
toast.error('Failed to download image');
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Download className="h-4 w-4" />
|
||||
</Button>
|
||||
) : null}
|
||||
<Button
|
||||
variant="outline"
|
||||
size="icon"
|
||||
className="h-8 w-8 shrink-0"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
onClose();
|
||||
}}
|
||||
>
|
||||
<X className="h-4 w-4" />
|
||||
</Button>
|
||||
</div>
|
||||
<div
|
||||
className="relative flex-1 min-h-0 w-full flex items-center justify-center p-2 sm:p-4 cursor-zoom-out"
|
||||
onClick={onClose}
|
||||
>
|
||||
<Download className="h-5 w-5" />
|
||||
</Button>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="icon"
|
||||
className="rounded-full bg-black/40 hover:bg-black/80 border-white/20 text-white h-10 w-10 backdrop-blur-md transition-all shrink-0"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
onClose();
|
||||
}}
|
||||
>
|
||||
<X className="h-5 w-5" />
|
||||
</Button>
|
||||
</div>
|
||||
<div
|
||||
className="relative w-full h-full flex items-center justify-center p-4 cursor-zoom-out"
|
||||
onClick={onClose}
|
||||
>
|
||||
{previewImage && (
|
||||
// eslint-disable-next-line @next/next/no-img-element
|
||||
<img
|
||||
src={previewImage}
|
||||
alt="Preview"
|
||||
className="max-w-[95vw] max-h-[90vh] object-contain rounded-md select-none cursor-default"
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
/>
|
||||
)}
|
||||
{previewImage && (
|
||||
// eslint-disable-next-line @next/next/no-img-element
|
||||
<img
|
||||
src={previewImage}
|
||||
alt={title || 'Preview'}
|
||||
className="max-w-full max-h-full object-contain rounded-md select-none cursor-default"
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
|
||||
Reference in New Issue
Block a user