Merge branch '15256-removing-files-during-upload'
authorPeter Amstutz <peter.amstutz@curii.com>
Thu, 12 Dec 2019 15:57:44 +0000 (10:57 -0500)
committerPeter Amstutz <peter.amstutz@curii.com>
Thu, 12 Dec 2019 15:57:44 +0000 (10:57 -0500)
refs #15256

Arvados-DCO-1.1-Signed-off-by: Peter Amstutz <peter.amstutz@curii.com>

src/components/file-upload-dialog/file-upload-dialog.tsx
src/components/file-upload/file-upload.tsx
src/store/file-uploader/file-uploader-actions.ts
src/store/file-uploader/file-uploader-reducer.ts
src/views-components/file-uploader/file-uploader.tsx

index b178cdae6edd0d5af1759a1fe65311b80f047665..81558493e38af205c0793ffaf14cd85152472400 100644 (file)
@@ -14,6 +14,7 @@ export interface FilesUploadDialogProps {
     uploading: boolean;
     onSubmit: () => void;
     onChange: (files: File[]) => void;
+    onDelete: (files: File[]) => void; 
 }
 
 export const FilesUploadDialog = (props: FilesUploadDialogProps & WithDialogProps<{}>) =>
@@ -28,6 +29,7 @@ export const FilesUploadDialog = (props: FilesUploadDialogProps & WithDialogProp
                 files={props.files}
                 disabled={props.uploading}
                 onDrop={props.onChange}
+                onDelete={props.onDelete}
             />
         </DialogContent>
         <DialogActions>
index 64656e4865a920435883d1e7fa146a99890d1c90..23afc0bea3a2797da5b1cf68b4470e050dc31a87 100644 (file)
@@ -9,17 +9,18 @@ import {
     StyleRulesCallback,
     Table, TableBody, TableCell, TableHead, TableRow,
     Typography,
-    WithStyles
+    WithStyles,
+    IconButton
 } from '@material-ui/core';
 import { withStyles } from '@material-ui/core';
 import Dropzone from 'react-dropzone';
-import { CloudUploadIcon } from "../icon/icon";
+import { CloudUploadIcon, RemoveIcon } from "../icon/icon";
 import { formatFileSize, formatProgress, formatUploadSpeed } from "~/common/formatters";
 import { UploadFile } from '~/store/file-uploader/file-uploader-actions';
 
 type CssRules = "root" | "dropzone" | "dropzoneWrapper" | "container" | "uploadIcon"
     | "dropzoneBorder" | "dropzoneBorderLeft" | "dropzoneBorderRight" | "dropzoneBorderTop" | "dropzoneBorderBottom"
-    | "dropzoneBorderHorzActive" | "dropzoneBorderVertActive";
+    | "dropzoneBorderHorzActive" | "dropzoneBorderVertActive" | "deleteButton" | "deleteButtonDisabled" | "deleteIcon";
 
 const styles: StyleRulesCallback<CssRules> = theme => ({
     root: {
@@ -81,6 +82,15 @@ const styles: StyleRulesCallback<CssRules> = theme => ({
     },
     uploadIcon: {
         verticalAlign: "middle"
+    },
+    deleteButton: {
+        cursor: "pointer"
+    },
+    deleteButtonDisabled: {
+        cursor: "not-allowed"
+    },
+    deleteIcon: {
+        marginLeft: "-6px"
     }
 });
 
@@ -88,6 +98,7 @@ interface FileUploadPropsData {
     files: UploadFile[];
     disabled: boolean;
     onDrop: (files: File[]) => void;
+    onDelete: (files: File[]) => void;
 }
 
 interface FileUploadState {
@@ -104,6 +115,15 @@ export const FileUpload = withStyles(styles)(
                 focused: false
             };
         }
+        onDelete = (event: React.MouseEvent<HTMLTableCellElement>, file: any): void => {
+            const { onDelete, disabled } = this.props;
+
+            event.stopPropagation();
+
+            if (!disabled) {
+                onDelete([file]);
+            }
+        }
         render() {
             const { classes, onDrop, disabled, files } = this.props;
             return <div className={"file-upload-dropzone " + classes.dropzoneWrapper}>
@@ -149,6 +169,7 @@ export const FileUpload = withStyles(styles)(
                                     <TableCell>File size</TableCell>
                                     <TableCell>Upload speed</TableCell>
                                     <TableCell>Upload progress</TableCell>
+                                    <TableCell>Delete</TableCell>
                                 </TableRow>
                             </TableHead>
                             <TableBody>
@@ -158,7 +179,16 @@ export const FileUpload = withStyles(styles)(
                                         <TableCell>{formatFileSize(f.file.size)}</TableCell>
                                         <TableCell>{formatUploadSpeed(f.prevLoaded, f.loaded, f.prevTime, f.currentTime)}</TableCell>
                                         <TableCell>{formatProgress(f.loaded, f.total)}</TableCell>
-                                    </TableRow>
+                                        <TableCell>
+                                            <IconButton 
+                                                aria-label="Remove"
+                                                onClick={(event:    React.MouseEvent<HTMLTableCellElement>) => this.onDelete(event, f)}
+                                                className={disabled ? classnames(classes.deleteButtonDisabled, classes.deleteIcon) : classnames(classes.deleteButton, classes.deleteIcon)}
+                                            >
+                                                <RemoveIcon />
+                                            </IconButton>
+                                        </TableCell>
+                                    </TableRow> 
                                 )}
                             </TableBody>
                         </Table>
index f4a30a239ddc9e3b2eaf56608a80db362f1f2d7a..5a82958d4773a4e90df7ecdd6144b7f16fea8274 100644 (file)
@@ -17,12 +17,17 @@ export interface UploadFile {
     currentTime: number;
 }
 
+export interface FileWithId extends File {
+    id: number;
+}
+
 export const fileUploaderActions = unionize({
     CLEAR_UPLOAD: ofType(),
     SET_UPLOAD_FILES: ofType<File[]>(),
     UPDATE_UPLOAD_FILES: ofType<File[]>(),
     SET_UPLOAD_PROGRESS: ofType<{ fileId: number, loaded: number, total: number, currentTime: number }>(),
     START_UPLOAD: ofType(),
+    DELETE_UPLOAD_FILE: ofType<FileWithId[]>(),
 });
 
 export type FileUploaderAction = UnionOf<typeof fileUploaderActions>;
index 9ea6313131b1d0e598ad5771cf75b5d370b5b86b..bf24505b7f1077939147e25f90c9b35e50f10619 100644 (file)
@@ -37,6 +37,12 @@ export const fileUploaderReducer = (state: UploaderState = initialState, action:
 
             return uniqUpdatedState;
         },
+        DELETE_UPLOAD_FILE: files => {
+            const idToDelete: number = files[0].id;
+            const updatedState = state.filter( file => file.id !== idToDelete);
+
+            return updatedState;
+        },
         START_UPLOAD: () => {
             const startTime = Date.now();
             return state.map(f => ({ ...f, startTime, prevTime: startTime }));
index 83808016e60ee76d55430176bdb1705b8f1a9fc9..f14c54feae0f17269f72fd090c5590159594c83a 100644 (file)
@@ -8,7 +8,7 @@ import { connect } from 'react-redux';
 import { RootState } from '~/store/store';
 import { FileUploadProps } from '../../components/file-upload/file-upload';
 import { Dispatch } from 'redux';
-import { fileUploaderActions, getFileUploaderState } from '~/store/file-uploader/file-uploader-actions';
+import { fileUploaderActions, getFileUploaderState, FileWithId } from '~/store/file-uploader/file-uploader-actions';
 import { WrappedFieldProps } from 'redux-form';
 import { Typography } from '@material-ui/core';
 
@@ -19,7 +19,7 @@ const mapStateToProps = (state: RootState, { disabled }: FileUploaderProps): Pic
     files: state.fileUploader,
 });
 
-const mapDispatchToProps = (dispatch: Dispatch, { onDrop }: FileUploaderProps): Pick<FileUploadProps, 'onDrop'> => ({
+const mapDispatchToProps = (dispatch: Dispatch, { onDrop }: FileUploaderProps): Pick<FileUploadProps, 'onDrop' | 'onDelete'> => ({
     onDrop: files => {
         const state = dispatch<any>(getFileUploaderState());
         if (files.length > 0 && state.length === 0) {
@@ -30,6 +30,7 @@ const mapDispatchToProps = (dispatch: Dispatch, { onDrop }: FileUploaderProps):
             onDrop(files);
         }
     },
+    onDelete: files => dispatch(fileUploaderActions.DELETE_UPLOAD_FILE(files as FileWithId[])),
 });
 
 export const FileUploader = connect(mapStateToProps, mapDispatchToProps)(FileUpload);