diff --git a/src/renderer/src/i18n/locales/en-us.json b/src/renderer/src/i18n/locales/en-us.json index 4b3dbfd2b..b87aa8f68 100644 --- a/src/renderer/src/i18n/locales/en-us.json +++ b/src/renderer/src/i18n/locales/en-us.json @@ -868,6 +868,8 @@ "files": { "actions": "Actions", "all": "All Files", + "batch_delete": "Batch delete", + "batch_operation": "Select All", "count": "files", "created_at": "Created At", "delete": { diff --git a/src/renderer/src/i18n/locales/ja-jp.json b/src/renderer/src/i18n/locales/ja-jp.json index 4d00c647b..d8c33f98a 100644 --- a/src/renderer/src/i18n/locales/ja-jp.json +++ b/src/renderer/src/i18n/locales/ja-jp.json @@ -868,6 +868,8 @@ "files": { "actions": "操作", "all": "すべてのファイル", + "batch_delete": "一括削除", + "batch_operation": "すべて選択", "count": "ファイル", "created_at": "作成日", "delete": { diff --git a/src/renderer/src/i18n/locales/ru-ru.json b/src/renderer/src/i18n/locales/ru-ru.json index cd8ebd26a..929c57490 100644 --- a/src/renderer/src/i18n/locales/ru-ru.json +++ b/src/renderer/src/i18n/locales/ru-ru.json @@ -868,6 +868,8 @@ "files": { "actions": "Действия", "all": "Все файлы", + "batch_delete": "массовое удаление", + "batch_operation": "Выделить всё", "count": "файлов", "created_at": "Дата создания", "delete": { diff --git a/src/renderer/src/i18n/locales/zh-cn.json b/src/renderer/src/i18n/locales/zh-cn.json index 000f4f9e2..c7f948ce7 100644 --- a/src/renderer/src/i18n/locales/zh-cn.json +++ b/src/renderer/src/i18n/locales/zh-cn.json @@ -868,6 +868,8 @@ "files": { "actions": "操作", "all": "所有文件", + "batch_delete": "批量删除", + "batch_operation": "全选", "count": "个文件", "created_at": "创建时间", "delete": { diff --git a/src/renderer/src/i18n/locales/zh-tw.json b/src/renderer/src/i18n/locales/zh-tw.json index a4cdafb53..50956a8a3 100644 --- a/src/renderer/src/i18n/locales/zh-tw.json +++ b/src/renderer/src/i18n/locales/zh-tw.json @@ -868,6 +868,8 @@ "files": { "actions": "操作", "all": "所有檔案", + "batch_delete": "批次刪除", + "batch_operation": "全選", "count": "個檔案", "created_at": "建立時間", "delete": { diff --git a/src/renderer/src/i18n/translate/el-gr.json b/src/renderer/src/i18n/translate/el-gr.json index 84d37cdb5..ff51752b1 100644 --- a/src/renderer/src/i18n/translate/el-gr.json +++ b/src/renderer/src/i18n/translate/el-gr.json @@ -868,6 +868,8 @@ "files": { "actions": "Ενέργειες", "all": "Όλα τα αρχεία", + "batch_delete": "μαζική διαγραφή", + "batch_operation": "Επιλογή όλων", "count": "Αριθμός αρχείων", "created_at": "Ημερομηνία δημιουργίας", "delete": { diff --git a/src/renderer/src/i18n/translate/es-es.json b/src/renderer/src/i18n/translate/es-es.json index e0f346ee1..679e62b39 100644 --- a/src/renderer/src/i18n/translate/es-es.json +++ b/src/renderer/src/i18n/translate/es-es.json @@ -868,6 +868,8 @@ "files": { "actions": "Acciones", "all": "Todos los archivos", + "batch_delete": "Eliminación masiva", + "batch_operation": "Seleccionar todo", "count": "Número de archivos", "created_at": "Fecha de creación", "delete": { diff --git a/src/renderer/src/i18n/translate/fr-fr.json b/src/renderer/src/i18n/translate/fr-fr.json index 8c26617eb..ba76018ae 100644 --- a/src/renderer/src/i18n/translate/fr-fr.json +++ b/src/renderer/src/i18n/translate/fr-fr.json @@ -868,6 +868,8 @@ "files": { "actions": "Actions", "all": "Tous les fichiers", + "batch_delete": "supprimer en masse", + "batch_operation": "Tout sélectionner", "count": "Nombre de fichiers", "created_at": "Date de création", "delete": { diff --git a/src/renderer/src/i18n/translate/pt-pt.json b/src/renderer/src/i18n/translate/pt-pt.json index d1f69edf5..777d51217 100644 --- a/src/renderer/src/i18n/translate/pt-pt.json +++ b/src/renderer/src/i18n/translate/pt-pt.json @@ -868,6 +868,8 @@ "files": { "actions": "Ações", "all": "Todos os Arquivos", + "batch_delete": "excluir em massa", + "batch_operation": "Selecionar tudo", "count": "Número de Arquivos", "created_at": "Data de Criação", "delete": { diff --git a/src/renderer/src/pages/files/FilesPage.tsx b/src/renderer/src/pages/files/FilesPage.tsx index e988efa6c..edf2b3827 100644 --- a/src/renderer/src/pages/files/FilesPage.tsx +++ b/src/renderer/src/pages/files/FilesPage.tsx @@ -6,9 +6,10 @@ import db from '@renderer/databases' import { getFileFieldLabel } from '@renderer/i18n/label' import { handleDelete, handleRename, sortFiles, tempFilesSort } from '@renderer/services/FileAction' import FileManager from '@renderer/services/FileManager' +import store from '@renderer/store' import { FileMetadata, FileTypes } from '@renderer/types' import { formatFileSize } from '@renderer/utils' -import { Button, Empty, Flex, Popconfirm } from 'antd' +import { Button, Checkbox, Dropdown, Empty, Flex, Popconfirm } from 'antd' import dayjs from 'dayjs' import { useLiveQuery } from 'dexie-react-hooks' import { @@ -19,7 +20,7 @@ import { FileText, FileType as FileTypeIcon } from 'lucide-react' -import { FC, useState } from 'react' +import { FC, useEffect, useState } from 'react' import { useTranslation } from 'react-i18next' import styled from 'styled-components' @@ -33,6 +34,11 @@ const FilesPage: FC = () => { const [fileType, setFileType] = useState('document') const [sortField, setSortField] = useState('created_at') const [sortOrder, setSortOrder] = useState('desc') + const [selectedFileIds, setSelectedFileIds] = useState([]) + + useEffect(() => { + setSelectedFileIds([]) + }, [fileType]) const files = useLiveQuery(() => { if (fileType === 'all') { @@ -43,6 +49,44 @@ const FilesPage: FC = () => { const sortedFiles = files ? sortFiles(files, sortField, sortOrder) : [] + const handleBatchDelete = async () => { + const selectedFiles = await Promise.all(selectedFileIds.map((id) => FileManager.getFile(id))) + const validFiles = selectedFiles.filter((file) => file !== null && file !== undefined) + + const paintings = store.getState().paintings.paintings + const paintingsFiles = paintings.flatMap((p) => p.files) + + const filesInPaintings = validFiles.filter((file) => paintingsFiles.some((p) => p.id === file.id)) + + if (filesInPaintings.length > 0) { + window.modal.warning({ + content: t('files.delete.paintings.warning'), + centered: true + }) + return + } + + await Promise.all(selectedFileIds.map((fileId) => handleDelete(fileId, t))) + + setSelectedFileIds([]) + } + + const handleSelectFile = (fileId: string, checked: boolean) => { + if (checked) { + setSelectedFileIds((prev) => [...prev, fileId]) + } else { + setSelectedFileIds((prev) => prev.filter((id) => id !== fileId)) + } + } + + const handleSelectAll = (checked: boolean) => { + if (checked) { + setSelectedFileIds(sortedFiles.map((file) => file.id)) + } else { + setSelectedFileIds([]) + } + } + const dataSource = sortedFiles?.map((file) => { return { key: file.id, @@ -71,6 +115,13 @@ const FilesPage: FC = () => { icon={}>