Can delete uploaded ISO files
This commit is contained in:
		@@ -46,4 +46,14 @@ export class IsoFilesApi {
 | 
			
		||||
      })
 | 
			
		||||
    ).data;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Delete iso file
 | 
			
		||||
   */
 | 
			
		||||
  static async Delete(file: IsoFile): Promise<void> {
 | 
			
		||||
    await APIClient.exec({
 | 
			
		||||
      method: "DELETE",
 | 
			
		||||
      uri: `/iso/${file.filename}`,
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,83 @@
 | 
			
		||||
import {
 | 
			
		||||
  Button,
 | 
			
		||||
  Dialog,
 | 
			
		||||
  DialogActions,
 | 
			
		||||
  DialogContent,
 | 
			
		||||
  DialogContentText,
 | 
			
		||||
  DialogTitle,
 | 
			
		||||
} from "@mui/material";
 | 
			
		||||
import React, { PropsWithChildren } from "react";
 | 
			
		||||
 | 
			
		||||
type ConfirmContext = (
 | 
			
		||||
  message: string,
 | 
			
		||||
  title?: string,
 | 
			
		||||
  confirmButton?: string
 | 
			
		||||
) => Promise<boolean>;
 | 
			
		||||
 | 
			
		||||
const ConfirmContextK = React.createContext<ConfirmContext | null>(null);
 | 
			
		||||
 | 
			
		||||
export function ConfirmDialogProvider(
 | 
			
		||||
  p: PropsWithChildren
 | 
			
		||||
): React.ReactElement {
 | 
			
		||||
  const [open, setOpen] = React.useState(false);
 | 
			
		||||
 | 
			
		||||
  const [title, setTitle] = React.useState<string | undefined>(undefined);
 | 
			
		||||
  const [message, setMessage] = React.useState("");
 | 
			
		||||
  const [confirmButton, setConfirmButton] = React.useState<string | undefined>(
 | 
			
		||||
    undefined
 | 
			
		||||
  );
 | 
			
		||||
 | 
			
		||||
  const cb = React.useRef<null | ((a: boolean) => void)>(null);
 | 
			
		||||
 | 
			
		||||
  const handleClose = (confirm: boolean) => {
 | 
			
		||||
    setOpen(false);
 | 
			
		||||
 | 
			
		||||
    if (cb.current !== null) cb.current(confirm);
 | 
			
		||||
    cb.current = null;
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  const hook: ConfirmContext = (message, title, confirmButton) => {
 | 
			
		||||
    setTitle(title);
 | 
			
		||||
    setMessage(message);
 | 
			
		||||
    setConfirmButton(confirmButton);
 | 
			
		||||
    setOpen(true);
 | 
			
		||||
 | 
			
		||||
    return new Promise((res) => {
 | 
			
		||||
      cb.current = res;
 | 
			
		||||
    });
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <>
 | 
			
		||||
      <ConfirmContextK.Provider value={hook}>
 | 
			
		||||
        {p.children}
 | 
			
		||||
      </ConfirmContextK.Provider>
 | 
			
		||||
 | 
			
		||||
      <Dialog
 | 
			
		||||
        open={open}
 | 
			
		||||
        onClose={() => handleClose(false)}
 | 
			
		||||
        aria-labelledby="alert-dialog-title"
 | 
			
		||||
        aria-describedby="alert-dialog-description"
 | 
			
		||||
      >
 | 
			
		||||
        {title && <DialogTitle id="alert-dialog-title">{title}</DialogTitle>}
 | 
			
		||||
        <DialogContent>
 | 
			
		||||
          <DialogContentText id="alert-dialog-description">
 | 
			
		||||
            {message}
 | 
			
		||||
          </DialogContentText>
 | 
			
		||||
        </DialogContent>
 | 
			
		||||
        <DialogActions>
 | 
			
		||||
          <Button onClick={() => handleClose(false)} autoFocus>
 | 
			
		||||
            Cancel
 | 
			
		||||
          </Button>
 | 
			
		||||
          <Button onClick={() => handleClose(true)} color="error">
 | 
			
		||||
            {confirmButton ?? "Confirm"}
 | 
			
		||||
          </Button>
 | 
			
		||||
        </DialogActions>
 | 
			
		||||
      </Dialog>
 | 
			
		||||
    </>
 | 
			
		||||
  );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function useConfirm(): ConfirmContext {
 | 
			
		||||
  return React.useContext(ConfirmContextK)!;
 | 
			
		||||
}
 | 
			
		||||
@@ -13,6 +13,7 @@ import { ThemeProvider, createTheme } from "@mui/material";
 | 
			
		||||
import { LoadingMessageProvider } from "./hooks/providers/LoadingMessageProvider";
 | 
			
		||||
import { AlertDialogProvider } from "./hooks/providers/AlertDialogProvider";
 | 
			
		||||
import { SnackbarProvider } from "./hooks/providers/SnackbarProvider";
 | 
			
		||||
import { ConfirmDialogProvider } from "./hooks/providers/ConfirmDialogProvider";
 | 
			
		||||
 | 
			
		||||
const darkTheme = createTheme({
 | 
			
		||||
  palette: {
 | 
			
		||||
@@ -26,15 +27,17 @@ const root = ReactDOM.createRoot(
 | 
			
		||||
root.render(
 | 
			
		||||
  <React.StrictMode>
 | 
			
		||||
    <ThemeProvider theme={darkTheme}>
 | 
			
		||||
      <AlertDialogProvider>
 | 
			
		||||
        <SnackbarProvider>
 | 
			
		||||
          <LoadingMessageProvider>
 | 
			
		||||
            <LoadServerConfig>
 | 
			
		||||
              <App />
 | 
			
		||||
            </LoadServerConfig>
 | 
			
		||||
          </LoadingMessageProvider>
 | 
			
		||||
        </SnackbarProvider>{" "}
 | 
			
		||||
      </AlertDialogProvider>
 | 
			
		||||
      <ConfirmDialogProvider>
 | 
			
		||||
        <AlertDialogProvider>
 | 
			
		||||
          <SnackbarProvider>
 | 
			
		||||
            <LoadingMessageProvider>
 | 
			
		||||
              <LoadServerConfig>
 | 
			
		||||
                <App />
 | 
			
		||||
              </LoadServerConfig>
 | 
			
		||||
            </LoadingMessageProvider>
 | 
			
		||||
          </SnackbarProvider>
 | 
			
		||||
        </AlertDialogProvider>
 | 
			
		||||
      </ConfirmDialogProvider>
 | 
			
		||||
    </ThemeProvider>
 | 
			
		||||
  </React.StrictMode>
 | 
			
		||||
);
 | 
			
		||||
 
 | 
			
		||||
@@ -1,16 +1,24 @@
 | 
			
		||||
import { Button, LinearProgress, TextField, Typography } from "@mui/material";
 | 
			
		||||
import DeleteIcon from "@mui/icons-material/Delete";
 | 
			
		||||
import {
 | 
			
		||||
  Button,
 | 
			
		||||
  IconButton,
 | 
			
		||||
  LinearProgress,
 | 
			
		||||
  TextField,
 | 
			
		||||
  Typography,
 | 
			
		||||
} from "@mui/material";
 | 
			
		||||
import { DataGrid, GridColDef } from "@mui/x-data-grid";
 | 
			
		||||
import { filesize } from "filesize";
 | 
			
		||||
import { MuiFileInput } from "mui-file-input";
 | 
			
		||||
import React from "react";
 | 
			
		||||
import { IsoFile, IsoFilesApi } from "../api/IsoFilesApi";
 | 
			
		||||
import { ServerApi } from "../api/ServerApi";
 | 
			
		||||
import { useAlert } from "../hooks/providers/AlertDialogProvider";
 | 
			
		||||
import { useLoadingMessage } from "../hooks/providers/LoadingMessageProvider";
 | 
			
		||||
import { useSnackbar } from "../hooks/providers/SnackbarProvider";
 | 
			
		||||
import { AsyncWidget } from "../widgets/AsyncWidget";
 | 
			
		||||
import { VirtWebPaper } from "../widgets/VirtWebPaper";
 | 
			
		||||
import { VirtWebRouteContainer } from "../widgets/VirtWebRouteContainer";
 | 
			
		||||
import { useLoadingMessage } from "../hooks/providers/LoadingMessageProvider";
 | 
			
		||||
import { AsyncWidget } from "../widgets/AsyncWidget";
 | 
			
		||||
import { DataGrid, GridColDef, GridRowsProp } from "@mui/x-data-grid";
 | 
			
		||||
import { useConfirm } from "../hooks/providers/ConfirmDialogProvider";
 | 
			
		||||
 | 
			
		||||
export function IsoFilesRoute(): React.ReactElement {
 | 
			
		||||
  const [list, setList] = React.useState<IsoFile[] | undefined>();
 | 
			
		||||
@@ -172,6 +180,33 @@ function IsoFilesList(p: {
 | 
			
		||||
  list: IsoFile[];
 | 
			
		||||
  onReload: () => void;
 | 
			
		||||
}): React.ReactElement {
 | 
			
		||||
  const confirm = useConfirm();
 | 
			
		||||
  const alert = useAlert();
 | 
			
		||||
  const loadingMessage = useLoadingMessage();
 | 
			
		||||
  const snackbar = useSnackbar();
 | 
			
		||||
 | 
			
		||||
  const deleteIso = async (entry: IsoFile) => {
 | 
			
		||||
    if (
 | 
			
		||||
      !(await confirm(
 | 
			
		||||
        `Do you really want to delete this file (${entry.filename}) ?`
 | 
			
		||||
      ))
 | 
			
		||||
    )
 | 
			
		||||
      return;
 | 
			
		||||
 | 
			
		||||
    loadingMessage.show("Deleting ISO file...");
 | 
			
		||||
 | 
			
		||||
    try {
 | 
			
		||||
      await IsoFilesApi.Delete(entry);
 | 
			
		||||
      snackbar("The file has been successfully deleted!");
 | 
			
		||||
      p.onReload();
 | 
			
		||||
    } catch (e) {
 | 
			
		||||
      console.error(e);
 | 
			
		||||
      alert("Failed to delete file!");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    loadingMessage.hide();
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  if (p.list.length === 0)
 | 
			
		||||
    return (
 | 
			
		||||
      <Typography variant="body1" style={{ textAlign: "center" }}>
 | 
			
		||||
@@ -189,6 +224,20 @@ function IsoFilesList(p: {
 | 
			
		||||
        return filesize(params.row.size);
 | 
			
		||||
      },
 | 
			
		||||
    },
 | 
			
		||||
    {
 | 
			
		||||
      field: "actions",
 | 
			
		||||
      headerName: "",
 | 
			
		||||
      width: 70,
 | 
			
		||||
      renderCell(params) {
 | 
			
		||||
        return (
 | 
			
		||||
          <>
 | 
			
		||||
            <IconButton onClick={() => deleteIso(params.row)}>
 | 
			
		||||
              <DeleteIcon />
 | 
			
		||||
            </IconButton>
 | 
			
		||||
          </>
 | 
			
		||||
        );
 | 
			
		||||
      },
 | 
			
		||||
    },
 | 
			
		||||
  ];
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user