Feat: actions to cleanup all upload sessions

This commit is contained in:
HFO4 2022-02-27 14:39:53 +08:00
parent 00f6a6162a
commit a5de76bf64
5 changed files with 188 additions and 78 deletions

View File

@ -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>
);
}

View File

@ -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>
</>
);
}

View File

@ -87,6 +87,7 @@ export default function Uploader() {
openFileList={() => setTaskListOpen(true)}
/>
<TaskList
uploadManager={uploadManager}
taskList={uploaders}
open={taskListOpen}
onCancel={deleteTask}

View File

@ -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);
};
}

View File

@ -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;