Files
leonpan-assets/src/component/FileManager/Dialogs/DeleteConfirmation.tsx
2025-10-19 13:31:11 +00:00

157 lines
6.2 KiB
TypeScript
Executable File

import { Alert, Checkbox, Collapse, DialogContent, FormGroup, Stack, Tooltip } from "@mui/material";
import dayjs from "dayjs";
import duration from "dayjs/plugin/duration";
import { useCallback, useMemo, useState } from "react";
import { Trans, useTranslation } from "react-i18next";
import { Metadata } from "../../../api/explorer.ts";
import { GroupPermission } from "../../../api/user.ts";
import { setFileDeleteModal } from "../../../redux/fileManagerSlice.ts";
import { useAppDispatch, useAppSelector } from "../../../redux/hooks.ts";
import { deleteDialogPromisePool } from "../../../redux/thunks/dialog.ts";
import SessionManager from "../../../session";
import { formatDuration } from "../../../util/datetime.ts";
import { SmallFormControlLabel } from "../../Common/StyledComponents.tsx";
import DialogAccordion from "../../Dialogs/DialogAccordion.tsx";
import DraggableDialog, { StyledDialogContentText } from "../../Dialogs/DraggableDialog.tsx";
dayjs.extend(duration);
export interface DeleteOption {
unlink?: boolean;
skip_soft_delete?: boolean;
}
const DeleteConfirmation = () => {
const { t } = useTranslation();
const dispatch = useAppDispatch();
const [unlink, setUnlink] = useState(false);
const [skipSoftDelete, setSkipSoftDelete] = useState(false);
const open = useAppSelector((state) => state.fileManager[0].deleteFileModalOpen);
const targets = useAppSelector((state) => state.fileManager[0].deleteFileModalSelected);
const promiseId = useAppSelector((state) => state.fileManager[0].deleteFileModalPromiseId);
const loading = useAppSelector((state) => state.fileManager[0].deleteFileModalLoading);
const hasTrashFiles = useMemo(() => {
if (targets) {
return targets.some((target) => target.metadata && target.metadata[Metadata.restore_uri]);
}
return false;
}, [targets]);
const onClose = useCallback(() => {
dispatch(
setFileDeleteModal({
index: 0,
value: [false, targets, undefined, false],
}),
);
if (promiseId) {
deleteDialogPromisePool[promiseId]?.reject("cancel");
}
}, [dispatch, targets, promiseId]);
const singleFileToTrash = targets && targets.length == 1 && !hasTrashFiles && !skipSoftDelete;
const multipleFilesToTrash = targets && targets.length > 1 && !hasTrashFiles && !skipSoftDelete;
const singleFilePermanently = targets && targets.length == 1 && (hasTrashFiles || skipSoftDelete);
const multipleFilesPermanently = targets && targets.length > 1 && (hasTrashFiles || skipSoftDelete);
const onAccept = useCallback(() => {
if (promiseId) {
deleteDialogPromisePool[promiseId]?.resolve({
unlink,
skip_soft_delete: singleFilePermanently || multipleFilesPermanently ? true : skipSoftDelete,
});
}
}, [promiseId, unlink, skipSoftDelete, singleFilePermanently, multipleFilesPermanently]);
const permission = SessionManager.currentUserGroupPermission();
const showSkipSoftDeleteOption = !hasTrashFiles;
const showUnlinkOption = (skipSoftDelete || hasTrashFiles) && permission.enabled(GroupPermission.advance_delete);
const showAdvanceOptions = showUnlinkOption || showSkipSoftDeleteOption;
const group = useMemo(() => SessionManager.currentUserGroup(), [open]);
return (
<DraggableDialog
title={t("application:modals.deleteTitle")}
showActions
loading={loading}
showCancel
onAccept={onAccept}
dialogProps={{
open: open ?? false,
onClose: onClose,
fullWidth: true,
maxWidth: "xs",
}}
>
<DialogContent>
<Stack spacing={2}>
<StyledDialogContentText>
{(singleFileToTrash || singleFilePermanently) && (
<Trans
i18nKey={singleFileToTrash ? "modals.deleteOneDescription" : "modals.deleteOneDescriptionHard"}
ns={"application"}
values={{
name: targets[0].name,
}}
components={[<strong key={0} />]}
/>
)}
{(multipleFilesToTrash || multipleFilesPermanently) &&
t(
multipleFilesToTrash
? "application:modals.deleteMultipleDescription"
: "application:modals.deleteMultipleDescriptionHard",
{
num: targets.length,
},
)}
<Collapse in={singleFileToTrash || multipleFilesToTrash}>
<Alert sx={{ mt: 1 }} severity="info">
<Trans
i18nKey="application:modals.trashRetention"
ns={"application"}
values={{ num: formatDuration(dayjs.duration((group?.trash_retention ?? 0) * 1000)) }}
components={[<strong key={0} />]}
/>
</Alert>
</Collapse>
</StyledDialogContentText>
{showAdvanceOptions && (
<DialogAccordion defaultExpanded={unlink || skipSoftDelete} title={t("application:modals.advanceOptions")}>
<FormGroup>
<Collapse in={showSkipSoftDeleteOption}>
<Tooltip title={t("application:modals.skipSoftDeleteDes")}>
<SmallFormControlLabel
control={
<Checkbox
size="small"
onChange={(e) => setSkipSoftDelete(e.target.checked)}
checked={skipSoftDelete}
/>
}
label={t("application:modals.skipSoftDelete")}
/>
</Tooltip>
</Collapse>
<Collapse in={showUnlinkOption}>
<Tooltip title={t("application:modals.unlinkOnlyDes")}>
<SmallFormControlLabel
control={<Checkbox size="small" onChange={(e) => setUnlink(e.target.checked)} checked={unlink} />}
label={t("application:modals.unlinkOnly")}
/>
</Tooltip>
</Collapse>
</FormGroup>
</DialogAccordion>
)}
</Stack>
</DialogContent>
</DraggableDialog>
);
};
export default DeleteConfirmation;