mirror of
https://github.com/cloudreve/frontend.git
synced 2025-12-26 04:02:47 +00:00
Feat: actions to cleanup all upload sessions
This commit is contained in:
parent
00f6a6162a
commit
a5de76bf64
|
|
@ -0,0 +1,62 @@
|
|||
import {
|
||||
ListItemIcon,
|
||||
makeStyles,
|
||||
Menu,
|
||||
MenuItem,
|
||||
Tooltip,
|
||||
} from "@material-ui/core";
|
||||
import React, { useCallback } from "react";
|
||||
import { DeleteSweep } from "@material-ui/icons";
|
||||
import { useDispatch } from "react-redux";
|
||||
import { toggleSnackbar } from "../../../actions";
|
||||
import API from "../../../middleware/Api";
|
||||
|
||||
const useStyles = makeStyles((theme) => ({}));
|
||||
|
||||
const menuItems = [
|
||||
{
|
||||
item: [],
|
||||
},
|
||||
];
|
||||
|
||||
export default function MoreActions({ anchorEl, onClose, uploadManager }) {
|
||||
const classes = useStyles();
|
||||
const dispatch = useDispatch();
|
||||
const ToggleSnackbar = useCallback(
|
||||
(vertical, horizontal, msg, color) =>
|
||||
dispatch(toggleSnackbar(vertical, horizontal, msg, color)),
|
||||
[dispatch]
|
||||
);
|
||||
|
||||
const actionClicked = (next) => () => {
|
||||
onClose();
|
||||
next();
|
||||
};
|
||||
|
||||
const cleanupSessions = () => {
|
||||
uploadManager.cleanupSessions();
|
||||
API.delete("/file/upload")
|
||||
.then((response) => {
|
||||
ToggleSnackbar("top", "right", "上传会话已清除", "success");
|
||||
})
|
||||
.catch((error) => {
|
||||
ToggleSnackbar("top", "right", error.message, "error");
|
||||
});
|
||||
};
|
||||
|
||||
const open = Boolean(anchorEl);
|
||||
const id = open ? "uploader-action-popover" : undefined;
|
||||
|
||||
return (
|
||||
<Menu id={id} open={open} anchorEl={anchorEl} onClose={onClose}>
|
||||
<Tooltip title={"清空服务端所有未完成的上传会话"}>
|
||||
<MenuItem onClick={actionClicked(() => cleanupSessions())}>
|
||||
<ListItemIcon>
|
||||
<DeleteSweep fontSize="small" />
|
||||
</ListItemIcon>
|
||||
清空所有上传会话
|
||||
</MenuItem>
|
||||
</Tooltip>
|
||||
</Menu>
|
||||
);
|
||||
}
|
||||
|
|
@ -21,6 +21,7 @@ import AddIcon from "@material-ui/icons/Add";
|
|||
import classnames from "classnames";
|
||||
import UploadTask from "./UploadTask";
|
||||
import { MoreHoriz } from "@material-ui/icons";
|
||||
import MoreActions from "./MoreActions";
|
||||
|
||||
const Transition = React.forwardRef(function Transition(props, ref) {
|
||||
return <Slide direction="up" ref={ref} {...props} />;
|
||||
|
|
@ -81,12 +82,22 @@ export default function TaskList({
|
|||
selectFile,
|
||||
taskList,
|
||||
onCancel,
|
||||
uploadManager,
|
||||
}) {
|
||||
const classes = useStyles();
|
||||
const theme = useTheme();
|
||||
const fullScreen = useMediaQuery(theme.breakpoints.down("sm"));
|
||||
const [expanded, setExpanded] = useState(true);
|
||||
const [useAvgSpeed, setUseAvgSpeed] = useState(true);
|
||||
const [anchorEl, setAnchorEl] = React.useState(null);
|
||||
|
||||
const handleActionClick = (event) => {
|
||||
setAnchorEl(event.currentTarget);
|
||||
};
|
||||
|
||||
const handleActionClose = () => {
|
||||
setAnchorEl(null);
|
||||
};
|
||||
|
||||
const close = (e, reason) => {
|
||||
if (reason !== "backdropClick") {
|
||||
|
|
@ -100,89 +111,102 @@ export default function TaskList({
|
|||
};
|
||||
|
||||
return (
|
||||
<Dialog
|
||||
classes={{
|
||||
container: classes.popup, // class name, e.g. `classes-nesting-root-x`
|
||||
root: classnames({
|
||||
[classes.rootOverwrite]: !fullScreen,
|
||||
}),
|
||||
}}
|
||||
className={classnames({
|
||||
[classes.dialog]: !fullScreen,
|
||||
})}
|
||||
fullScreen={fullScreen}
|
||||
open={open}
|
||||
onClose={close}
|
||||
TransitionComponent={Transition}
|
||||
disableEnforceFocus={!expanded}
|
||||
hideBackdrop={!expanded}
|
||||
disableBackdropClick={!expanded}
|
||||
disableScrollLock={!expanded}
|
||||
>
|
||||
<Accordion
|
||||
expanded={expanded || fullScreen}
|
||||
onChange={handlePanelChange}
|
||||
<>
|
||||
<MoreActions
|
||||
onClose={handleActionClose}
|
||||
uploadManager={uploadManager}
|
||||
anchorEl={anchorEl}
|
||||
/>
|
||||
<Dialog
|
||||
classes={{
|
||||
container: classes.popup, // class name, e.g. `classes-nesting-root-x`
|
||||
root: classnames({
|
||||
[classes.rootOverwrite]: !fullScreen,
|
||||
}),
|
||||
}}
|
||||
className={classnames({
|
||||
[classes.dialog]: !fullScreen,
|
||||
})}
|
||||
fullScreen={fullScreen}
|
||||
open={open}
|
||||
onClose={close}
|
||||
TransitionComponent={Transition}
|
||||
disableEnforceFocus={!expanded}
|
||||
hideBackdrop={!expanded}
|
||||
disableBackdropClick={!expanded}
|
||||
disableScrollLock={!expanded}
|
||||
>
|
||||
<AppBar className={classes.appBar}>
|
||||
<Toolbar disableGutters className={classes.toolbar}>
|
||||
<Tooltip title={"隐藏队列"}>
|
||||
<IconButton
|
||||
color="inherit"
|
||||
onClick={close}
|
||||
aria-label="Close"
|
||||
>
|
||||
<CloseIcon />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
<Typography
|
||||
variant="h6"
|
||||
color="inherit"
|
||||
className={classes.flex}
|
||||
>
|
||||
上传队列
|
||||
</Typography>
|
||||
<Tooltip title={"更多操作"}>
|
||||
<IconButton color="inherit" onClick={selectFile}>
|
||||
<MoreHoriz />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
<Tooltip title={"添加新文件"}>
|
||||
<IconButton color="inherit" onClick={selectFile}>
|
||||
<AddIcon />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
{!fullScreen && (
|
||||
<Tooltip title={"展开/折叠队列"}>
|
||||
<Accordion
|
||||
expanded={expanded || fullScreen}
|
||||
onChange={handlePanelChange}
|
||||
>
|
||||
<AppBar className={classes.appBar}>
|
||||
<Toolbar disableGutters className={classes.toolbar}>
|
||||
<Tooltip title={"隐藏队列"}>
|
||||
<IconButton
|
||||
color="inherit"
|
||||
onClick={() => setExpanded(!expanded)}
|
||||
onClick={close}
|
||||
aria-label="Close"
|
||||
>
|
||||
<ExpandMoreIcon
|
||||
className={classnames({
|
||||
[classes.expandIconExpanded]: expanded,
|
||||
[classes.expandIcon]: true,
|
||||
})}
|
||||
/>
|
||||
<CloseIcon />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
)}
|
||||
</Toolbar>
|
||||
</AppBar>
|
||||
<AccordionDetails className={classes.paddingZero}>
|
||||
<DialogContent className={classes.dialogContent}>
|
||||
<List className={classes.paddingZero}>
|
||||
{taskList.map((uploader) => (
|
||||
<UploadTask
|
||||
onCancel={onCancel}
|
||||
key={uploader.id}
|
||||
useAvgSpeed={useAvgSpeed}
|
||||
uploader={uploader}
|
||||
/>
|
||||
))}
|
||||
</List>
|
||||
</DialogContent>
|
||||
</AccordionDetails>
|
||||
</Accordion>
|
||||
</Dialog>
|
||||
<Typography
|
||||
variant="h6"
|
||||
color="inherit"
|
||||
className={classes.flex}
|
||||
>
|
||||
上传队列
|
||||
</Typography>
|
||||
<Tooltip title={"更多操作"}>
|
||||
<IconButton
|
||||
color="inherit"
|
||||
onClick={handleActionClick}
|
||||
>
|
||||
<MoreHoriz />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
<Tooltip title={"添加新文件"}>
|
||||
<IconButton
|
||||
color="inherit"
|
||||
onClick={selectFile}
|
||||
>
|
||||
<AddIcon />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
{!fullScreen && (
|
||||
<Tooltip title={"展开/折叠队列"}>
|
||||
<IconButton
|
||||
color="inherit"
|
||||
onClick={() => setExpanded(!expanded)}
|
||||
>
|
||||
<ExpandMoreIcon
|
||||
className={classnames({
|
||||
[classes.expandIconExpanded]: expanded,
|
||||
[classes.expandIcon]: true,
|
||||
})}
|
||||
/>
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
)}
|
||||
</Toolbar>
|
||||
</AppBar>
|
||||
<AccordionDetails className={classes.paddingZero}>
|
||||
<DialogContent className={classes.dialogContent}>
|
||||
<List className={classes.paddingZero}>
|
||||
{taskList.map((uploader) => (
|
||||
<UploadTask
|
||||
onCancel={onCancel}
|
||||
key={uploader.id}
|
||||
useAvgSpeed={useAvgSpeed}
|
||||
uploader={uploader}
|
||||
/>
|
||||
))}
|
||||
</List>
|
||||
</DialogContent>
|
||||
</AccordionDetails>
|
||||
</Accordion>
|
||||
</Dialog>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -87,6 +87,7 @@ export default function Uploader() {
|
|||
openFileList={() => setTaskListOpen(true)}
|
||||
/>
|
||||
<TaskList
|
||||
uploadManager={uploadManager}
|
||||
taskList={uploaders}
|
||||
open={taskListOpen}
|
||||
onCancel={deleteTask}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import { UnknownPolicyError, UploaderError, UploaderErrorName } from "./errors";
|
|||
import Base from "./uploader/base";
|
||||
import Local from "./uploader/local";
|
||||
import { Pool } from "./utils/pool";
|
||||
import { cleanupResumeCtx } from "./utils";
|
||||
|
||||
export interface Option {
|
||||
logLevel: LogLevel;
|
||||
|
|
@ -115,4 +116,8 @@ export default class UploadManager {
|
|||
this.input.click();
|
||||
});
|
||||
};
|
||||
|
||||
public cleanupSessions = () => {
|
||||
cleanupResumeCtx(this.logger);
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -75,6 +75,24 @@ export function removeResumeCtx(task: Task, logger: Logger) {
|
|||
}
|
||||
}
|
||||
|
||||
export function cleanupResumeCtx(logger: Logger) {
|
||||
for (let i = 0; i < localStorage.length; i++) {
|
||||
const key = localStorage.key(i);
|
||||
if (key && key.startsWith(resumeKeyPrefix)) {
|
||||
try {
|
||||
localStorage.removeItem(key);
|
||||
} catch (err) {
|
||||
logger.warn(
|
||||
new UploaderError(
|
||||
UploaderErrorName.RemoveCtxFailed,
|
||||
`removeResumeCtx failed. key: ${key}`
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function getResumeCtx(task: Task, logger: Logger): Task | null {
|
||||
const ctxKey = getResumeCtxKey(task);
|
||||
let localInfoString: string | null = null;
|
||||
|
|
|
|||
Loading…
Reference in New Issue