diff --git a/src/components/BlobList/BlobList.tsx b/src/components/BlobList/BlobList.tsx index 97c924c..482b973 100644 --- a/src/components/BlobList/BlobList.tsx +++ b/src/components/BlobList/BlobList.tsx @@ -8,6 +8,7 @@ import { MusicalNoteIcon, PhotoIcon, TrashIcon, + XMarkIcon, } from '@heroicons/react/24/outline'; import { formatFileSize, formatDate } from '../../utils/utils'; import ImageBlobList from '../ImageBlobList/ImageBlobList'; @@ -23,7 +24,7 @@ import { useBlobSelection } from './useBlobSelection'; type BlobListProps = { blobs: BlobDescriptor[]; - onDelete?: (blob: BlobDescriptor) => void; + onDelete?: (blobs: BlobDescriptor[]) => void; title?: string; className?: string; }; @@ -32,7 +33,7 @@ const BlobList = ({ blobs, onDelete, title, className = '' }: BlobListProps) => const [mode, setMode] = useState('list'); const { distribution } = useServerInfo(); const fileMetaEventsByHash = useFileMetaEventsByHash(); - const { handleSelectBlob, selectedBlobs } = useBlobSelection(blobs); + const { handleSelectBlob, selectedBlobs, setSelectedBlobs } = useBlobSelection(blobs); const images = useMemo( () => blobs.filter(b => b.type?.startsWith('image/')).sort((a, b) => (a.uploaded > b.uploaded ? -1 : 1)), // descending [blobs] @@ -66,13 +67,6 @@ const BlobList = ({ blobs, onDelete, title, className = '' }: BlobListProps) => - {onDelete && ( - - onDelete(blob)} className="link link-primary tooltip" data-tip="Delete this blob"> - - - - )} ); @@ -100,7 +94,24 @@ const BlobList = ({ blobs, onDelete, title, className = '' }: BlobListProps) => {title &&

{title}

} {selectedCount > 0 && ( -
{selectedCount} blobs selected
+
+ {selectedCount} blobs selected + {onDelete && ( + + )} + +
)} {mode == 'gallery' && ( - + )} - {mode == 'video' && } + {mode == 'video' && } - {mode == 'audio' && } + {mode == 'audio' && } - {mode == 'docs' && } + {mode == 'docs' && } {mode == 'list' && (
@@ -147,15 +153,17 @@ const BlobList = ({ blobs, onDelete, title, className = '' }: BlobListProps) => key={blob.sha256} onClick={e => handleSelectBlob(blob.sha256, e)} > - + handleSelectBlob(blob.sha256, e)} onClick={e => e.stopPropagation()} /> {getMimeTypeIcon(blob.type)} + + {blob.sha256.slice(0, 15)} diff --git a/src/components/BlobList/useBlobSelection.ts b/src/components/BlobList/useBlobSelection.ts index b848bdb..0573bd9 100644 --- a/src/components/BlobList/useBlobSelection.ts +++ b/src/components/BlobList/useBlobSelection.ts @@ -1,49 +1,58 @@ import { BlobDescriptor } from 'blossom-client-sdk'; -import { useCallback, useState } from 'react'; +import { useState } from 'react'; export type HandleSelectBlobType = ( sha256: string, event: React.MouseEvent | React.ChangeEvent ) => void; export const useBlobSelection = (blobs: BlobDescriptor[]) => { - const [selectedBlobs, setSelectedBlobs] = useState<{ [key: string]: boolean }>({}); + const [selectedBlobs, setSelectedBlobs] = useState<{ [key: string]: boolean }>({}); - const handleSelectBlob: HandleSelectBlobType = useCallback( - (sha256: string, event: React.MouseEvent | React.ChangeEvent) => { - event.preventDefault(); + const handleSelectBlob: HandleSelectBlobType = ( + sha256: string, + event: React.MouseEvent | React.ChangeEvent + ) => { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const isMouseEvent = (event: any): event is React.MouseEvent => { + return event.ctrlKey !== undefined || event.metaKey !== undefined; + }; - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const isMouseEvent = (event: any): event is React.MouseEvent => { - return event.ctrlKey !== undefined || event.metaKey !== undefined; - }; - - if (isMouseEvent(event)) { - if (event.ctrlKey || event.metaKey) { - setSelectedBlobs(prev => ({ - ...prev, - [sha256]: !prev[sha256] - })); - } else if (event.shiftKey) { - const lastSelectedIndex = blobs.findIndex(blob => blob.sha256 === Object.keys(selectedBlobs).find(key => selectedBlobs[key])); - const currentIndex = blobs.findIndex(blob => blob.sha256 === sha256); - const [start, end] = [lastSelectedIndex, currentIndex].sort((a, b) => a - b); - const newSelection = blobs.slice(start, end + 1).reduce((acc, blob) => { + if (isMouseEvent(event)) { + if (event.ctrlKey || event.metaKey) { + setSelectedBlobs(prev => ({ + ...prev, + [sha256]: !prev[sha256], + })); + } else if (event.shiftKey) { + const lastSelectedIndex = blobs.findIndex( + blob => blob.sha256 === Object.keys(selectedBlobs).find(key => selectedBlobs[key]) + ); + const currentIndex = blobs.findIndex(blob => blob.sha256 === sha256); + const [start, end] = [lastSelectedIndex, currentIndex].sort((a, b) => a - b); + const newSelection = blobs.slice(start, end + 1).reduce( + (acc, blob) => { acc[blob.sha256] = true; return acc; - }, {} as { [key: string]: boolean }); - setSelectedBlobs(prev => ({ - ...prev, - ...newSelection - })); - } else { - setSelectedBlobs({ [sha256]: true }); - } + }, + {} as { [key: string]: boolean } + ); + setSelectedBlobs(prev => ({ + ...prev, + ...newSelection, + })); } else { - setSelectedBlobs({ [sha256]: true }); + setSelectedBlobs(prev => ({ + ...prev, + [sha256]: !prev[sha256], + })); } - }, - [blobs, selectedBlobs] - ); + } else { + setSelectedBlobs(prev => ({ + ...prev, + [sha256]: !prev[sha256], + })); + } + }; return { handleSelectBlob, selectedBlobs, setSelectedBlobs }; }; diff --git a/src/components/DocumentBlobList/DocumentBlobList.tsx b/src/components/DocumentBlobList/DocumentBlobList.tsx index 4a50bdb..b9d04d8 100644 --- a/src/components/DocumentBlobList/DocumentBlobList.tsx +++ b/src/components/DocumentBlobList/DocumentBlobList.tsx @@ -1,5 +1,5 @@ import { formatFileSize, formatDate } from '../../utils/utils'; -import { ClipboardDocumentIcon, TrashIcon } from '@heroicons/react/24/outline'; +import { ClipboardDocumentIcon } from '@heroicons/react/24/outline'; import { BlobDescriptor } from 'blossom-client-sdk'; import { Document, Page } from 'react-pdf'; @@ -8,7 +8,7 @@ type DocumentBlobListProps = { onDelete?: (blob: BlobDescriptor) => void; }; -const DocumentBlobList = ({ docs, onDelete }: DocumentBlobListProps) => ( +const DocumentBlobList = ({ docs }: DocumentBlobListProps) => (
{docs.map(blob => (
@@ -31,13 +31,6 @@ const DocumentBlobList = ({ docs, onDelete }: DocumentBlobListProps) => ( - {onDelete && ( - - onDelete(blob)} className="link link-primary tooltip" data-tip="Delete this blob"> - - - - )}
))} diff --git a/src/components/ImageBlobList/ImageBlobList.tsx b/src/components/ImageBlobList/ImageBlobList.tsx index 26983e5..0279d98 100644 --- a/src/components/ImageBlobList/ImageBlobList.tsx +++ b/src/components/ImageBlobList/ImageBlobList.tsx @@ -1,16 +1,15 @@ import { formatFileSize, formatDate } from '../../utils/utils'; -import { ClipboardDocumentIcon, TrashIcon } from '@heroicons/react/24/outline'; +import { ClipboardDocumentIcon } from '@heroicons/react/24/outline'; import { BlobDescriptor } from 'blossom-client-sdk'; import { HandleSelectBlobType } from '../BlobList/useBlobSelection'; type ImageBlobListProps = { images: BlobDescriptor[]; - onDelete?: (blob: BlobDescriptor) => void; handleSelectBlob: HandleSelectBlobType; selectedBlobs: { [key: string]: boolean }; }; -const ImageBlobList = ({ images, onDelete, handleSelectBlob, selectedBlobs }: ImageBlobListProps) => ( +const ImageBlobList = ({ images, handleSelectBlob, selectedBlobs }: ImageBlobListProps) => ( ))} diff --git a/src/components/VideoBlobList/VideoBlobList.tsx b/src/components/VideoBlobList/VideoBlobList.tsx index 6bb9f27..c6f000e 100644 --- a/src/components/VideoBlobList/VideoBlobList.tsx +++ b/src/components/VideoBlobList/VideoBlobList.tsx @@ -1,13 +1,12 @@ import { formatFileSize, formatDate } from '../../utils/utils'; -import { ClipboardDocumentIcon, TrashIcon } from '@heroicons/react/24/outline'; +import { ClipboardDocumentIcon } from '@heroicons/react/24/outline'; import { BlobDescriptor } from 'blossom-client-sdk'; type VideoBlobListProps = { videos: BlobDescriptor[]; - onDelete?: (blob: BlobDescriptor) => void; }; -const VideoBlobList = ({ videos, onDelete }: VideoBlobListProps) => ( +const VideoBlobList = ({ videos }: VideoBlobListProps) => (
{videos.map(blob => (
@@ -26,13 +25,6 @@ const VideoBlobList = ({ videos, onDelete }: VideoBlobListProps) => ( - {onDelete && ( - - onDelete(blob)} className="link link-primary tooltip" data-tip="Delete this blob"> - - - - )}
))} diff --git a/src/pages/Home.tsx b/src/pages/Home.tsx index fda8e65..5ac37c1 100644 --- a/src/pages/Home.tsx +++ b/src/pages/Home.tsx @@ -72,13 +72,15 @@ function Home() { className="mt-4" title={`Content on ${serverInfo[selectedServer].name}`} blobs={selectedServerBlobs} - onDelete={blob => - deleteBlob.mutate({ - serverName: serverInfo[selectedServer].name, - serverUrl: serverInfo[selectedServer].url, - hash: blob.sha256, - }) - } + onDelete={async blobs => { + for (const blob of blobs) { + await deleteBlob.mutateAsync({ + serverName: serverInfo[selectedServer].name, + serverUrl: serverInfo[selectedServer].url, + hash: blob.sha256, + }); + } + }} > )}