import { PermissionAction } from '@supabase/shared-types/out/constants' import { AlertCircle, ChevronDown, Copy, Download, LoaderCircle, Trash2, X } from 'lucide-react' import SVG from 'react-inlinesvg' import { Button, DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, } from 'ui' import { URL_EXPIRY_DURATION } from '../Storage.constants' import { StorageItem } from '../Storage.types' import { getPathAlongOpenedFolders } from './StorageExplorer.utils' import { useCopyUrl } from './useCopyUrl' import { useFetchFileUrlQuery } from './useFetchFileUrlQuery' import { ButtonTooltip } from '@/components/ui/ButtonTooltip' import { useAsyncCheckPermissions } from '@/hooks/misc/useCheckPermissions' import { BASE_PATH } from '@/lib/constants' import { formatBytes } from '@/lib/helpers' import { useStorageExplorerStateSnapshot } from '@/state/storage-explorer' const PREVIEW_SIZE_LIMIT = 10 * 1024 * 1024 // 10MB const PreviewFile = ({ item }: { item: StorageItem }) => { const { projectRef, selectedBucket, openedFolders } = useStorageExplorerStateSnapshot() const folderPath = getPathAlongOpenedFolders({ openedFolders, selectedBucket }, false) const path = [folderPath, item.name].filter(Boolean).join('/') const { data: previewUrl, isPending: isLoading } = useFetchFileUrlQuery({ path, projectRef: projectRef, bucket: selectedBucket, }) // if the size is not available, we set it to be greater than the max size const size = +(item.metadata?.size ?? PREVIEW_SIZE_LIMIT + 1) const mimeType = item.metadata?.mimetype const isSkipped = !!mimeType && !!size && size > PREVIEW_SIZE_LIMIT if (isLoading) { return (
) } if (isSkipped) { return (
code.replace(/svg/, 'svg class="mx-auto w-32 h-32 text-color-inherit opacity-75"') } />

File size is too large to preview in the explorer

) } if (!mimeType || !previewUrl) { return ( code.replace(/svg/, 'svg class="mx-auto w-32 h-32 text-color-inherit opacity-75"') } /> ) } if (mimeType.includes('image')) { return (
) } if (mimeType.includes('audio')) { return (
) } if (mimeType.includes('video')) { return (
) } return ( code.replace(/svg/, 'svg class="mx-auto w-32 h-32 text-color-inherit opacity-75"') } /> ) } export const PreviewPane = () => { const { selectedBucket, selectedFilePreview: file, setSelectedItemsToDelete, setSelectedFilePreview, setSelectedFileCustomExpiry, downloadFile, } = useStorageExplorerStateSnapshot() const { onCopyUrl } = useCopyUrl() const { can: canUpdateFiles } = useAsyncCheckPermissions(PermissionAction.STORAGE_WRITE, '*') if (!file) return null const width = 450 const size = file.metadata ? formatBytes(file.metadata.size) : null const mimeType = file.metadata ? file.metadata.mimetype : undefined const createdAt = file.created_at ? new Date(file.created_at).toLocaleString() : 'Unknown' const updatedAt = file.updated_at ? new Date(file.updated_at).toLocaleString() : 'Unknown' return (
{/* Preview Header */}
setSelectedFilePreview(undefined)} />
{/* Preview Thumbnail*/}
{/* Preview Information */}
{file.name}
{file.isCorrupted && (

File is corrupted, please delete and reupload this file again

)} {mimeType && (

{mimeType} {size && - {size}}

)}
{/* Preview Metadata */}

{createdAt}

{updatedAt}

{/* Actions */}
{selectedBucket.public ? ( ) : ( onCopyUrl(file.path!, URL_EXPIRY_DURATION.WEEK)} > Expire in 1 week onCopyUrl(file.path!, URL_EXPIRY_DURATION.MONTH)} > Expire in 1 month onCopyUrl(file.path!, URL_EXPIRY_DURATION.YEAR)} > Expire in 1 year setSelectedFileCustomExpiry(file)} > Custom expiry )}
} onClick={() => setSelectedItemsToDelete([file])} tooltip={{ content: { side: 'bottom', text: !canUpdateFiles ? 'You need additional permissions to delete this file' : undefined, }, }} > Delete file
) }