From a06392b6e6c0ad1aa9e674b82a068c71ef2fb3a3 Mon Sep 17 00:00:00 2001 From: Michal Klobukowski Date: Mon, 6 Aug 2018 18:45:44 +0200 Subject: [PATCH] Implement file remove dialogs Feature #13952 Arvados-DCO-1.1-Signed-off-by: Michal Klobukowski --- .../confirmation-dialog.tsx | 45 +++++++++++++++++++ src/store/dialog/dialog-reducer.ts | 4 +- src/store/dialog/with-dialog.ts | 4 +- .../collection-files-action-set.ts | 12 ++--- .../collection-files-item-action-set.ts | 8 ++-- .../file-remove-dialog/file-remove-dialog.ts | 38 ++++++++++++++++ .../multiple-files-remove-dialog.ts | 37 +++++++++++++++ src/views/workbench/workbench.tsx | 7 +-- 8 files changed, 138 insertions(+), 17 deletions(-) create mode 100644 src/components/confirmation-dialog/confirmation-dialog.tsx create mode 100644 src/views-components/file-remove-dialog/file-remove-dialog.ts create mode 100644 src/views-components/file-remove-dialog/multiple-files-remove-dialog.ts diff --git a/src/components/confirmation-dialog/confirmation-dialog.tsx b/src/components/confirmation-dialog/confirmation-dialog.tsx new file mode 100644 index 0000000000..6e87416781 --- /dev/null +++ b/src/components/confirmation-dialog/confirmation-dialog.tsx @@ -0,0 +1,45 @@ +// Copyright (C) The Arvados Authors. All rights reserved. +// +// SPDX-License-Identifier: AGPL-3.0 + +import * as React from "react"; +import { defaultTo, property } from 'lodash'; +import { Dialog, DialogTitle, DialogContent, DialogActions, Button, Typography, DialogContentText, CircularProgress } from "@material-ui/core"; +import { WithDialogProps } from "../../store/dialog/with-dialog"; +import { TextField } from "../text-field/text-field"; + +export interface ConfirmationDialogDataProps { + title: string; + text: string; + cancelButtonLabel?: string; + confirmButtonLabel?: string; +} + +export interface ConfirmationDialogProps { + onConfirm: () => void; +} + +export const ConfirmationDialog = (props: ConfirmationDialogProps & WithDialogProps) => + + {props.data.title} + + + {props.data.text} + + + + + + + ; diff --git a/src/store/dialog/dialog-reducer.ts b/src/store/dialog/dialog-reducer.ts index e49f65debc..34d38fdf4e 100644 --- a/src/store/dialog/dialog-reducer.ts +++ b/src/store/dialog/dialog-reducer.ts @@ -8,7 +8,7 @@ export type DialogState = Record; export interface Dialog { open: boolean; - data?: any; + data: any; } export const dialogReducer = (state: DialogState = {}, action: DialogAction) => @@ -16,7 +16,7 @@ export const dialogReducer = (state: DialogState = {}, action: DialogAction) => OPEN_DIALOG: ({ id, data }) => ({ ...state, [id]: { open: true, data } }), CLOSE_DIALOG: ({ id }) => ({ ...state, - [id]: state[id] ? { ...state[id], open: false } : { open: false } }), + [id]: state[id] ? { ...state[id], open: false } : { open: false, data: {} } }), default: () => state, }); diff --git a/src/store/dialog/with-dialog.ts b/src/store/dialog/with-dialog.ts index e42cd5b4c3..d53a79d6ef 100644 --- a/src/store/dialog/with-dialog.ts +++ b/src/store/dialog/with-dialog.ts @@ -10,7 +10,7 @@ import { dialogActions } from './dialog-actions'; export type WithDialogStateProps = { open: boolean; - data?: T; + data: T; }; export type WithDialogDispatchProps = { @@ -25,7 +25,7 @@ export const withDialog = (id: string) => export const mapStateToProps = (id: string) => (state: { dialog: DialogState }): WithDialogStateProps => { const dialog = state.dialog[id]; - return dialog ? dialog : { open: false }; + return dialog ? dialog : { open: false, data: {} }; }; export const mapDispatchToProps = (id: string) => (dispatch: Dispatch): WithDialogDispatchProps => ({ diff --git a/src/views-components/context-menu/action-sets/collection-files-action-set.ts b/src/views-components/context-menu/action-sets/collection-files-action-set.ts index 9396b9e472..9b7bddf7ef 100644 --- a/src/views-components/context-menu/action-sets/collection-files-action-set.ts +++ b/src/views-components/context-menu/action-sets/collection-files-action-set.ts @@ -4,7 +4,7 @@ import { ContextMenuActionSet } from "../context-menu-action-set"; import { collectionPanelFilesAction } from "../../../store/collection-panel/collection-panel-files/collection-panel-files-actions"; -import { openRemoveDialog } from "../../remove-dialog/remove-dialog"; +import { openMultipleFilesRemoveDialog } from "../../file-remove-dialog/multiple-files-remove-dialog"; export const collectionFilesActionSet: ContextMenuActionSet = [[{ @@ -12,22 +12,22 @@ export const collectionFilesActionSet: ContextMenuActionSet = [[{ execute: (dispatch) => { dispatch(collectionPanelFilesAction.SELECT_ALL_COLLECTION_FILES()); } -},{ +}, { name: "Unselect all", execute: (dispatch) => { dispatch(collectionPanelFilesAction.UNSELECT_ALL_COLLECTION_FILES()); } -},{ +}, { name: "Remove selected", execute: (dispatch, resource) => { - dispatch(openRemoveDialog('selected files')); + dispatch(openMultipleFilesRemoveDialog()); } -},{ +}, { name: "Download selected", execute: (dispatch, resource) => { return; } -},{ +}, { name: "Create a new collection with selected", execute: (dispatch, resource) => { return; diff --git a/src/views-components/context-menu/action-sets/collection-files-item-action-set.ts b/src/views-components/context-menu/action-sets/collection-files-item-action-set.ts index 430eba34f0..19c9f7cd53 100644 --- a/src/views-components/context-menu/action-sets/collection-files-item-action-set.ts +++ b/src/views-components/context-menu/action-sets/collection-files-item-action-set.ts @@ -4,8 +4,8 @@ import { ContextMenuActionSet } from "../context-menu-action-set"; import { RenameIcon, DownloadIcon, RemoveIcon } from "../../../components/icon/icon"; -import { openRemoveDialog } from "../../remove-dialog/remove-dialog"; import { openRenameFileDialog } from "../../rename-file-dialog/rename-file-dialog"; +import { openFileRemoveDialog } from "../../file-remove-dialog/file-remove-dialog"; export const collectionFilesItemActionSet: ContextMenuActionSet = [[{ @@ -14,16 +14,16 @@ export const collectionFilesItemActionSet: ContextMenuActionSet = [[{ execute: (dispatch, resource) => { dispatch(openRenameFileDialog(resource.name)); } -},{ +}, { name: "Download", icon: DownloadIcon, execute: (dispatch, resource) => { return; } -},{ +}, { name: "Remove", icon: RemoveIcon, execute: (dispatch, resource) => { - dispatch(openRemoveDialog('selected file')); + dispatch(openFileRemoveDialog(resource.uuid)); } }]]; diff --git a/src/views-components/file-remove-dialog/file-remove-dialog.ts b/src/views-components/file-remove-dialog/file-remove-dialog.ts new file mode 100644 index 0000000000..3678e53545 --- /dev/null +++ b/src/views-components/file-remove-dialog/file-remove-dialog.ts @@ -0,0 +1,38 @@ +// Copyright (C) The Arvados Authors. All rights reserved. +// +// SPDX-License-Identifier: AGPL-3.0 + +import { Dispatch } from "redux"; +import { connect } from "react-redux"; +import { ConfirmationDialog } from "../../components/confirmation-dialog/confirmation-dialog"; +import { withDialog } from "../../store/dialog/with-dialog"; +import { dialogActions } from "../../store/dialog/dialog-actions"; +import { snackbarActions } from "../../store/snackbar/snackbar-actions"; + +const FILE_REMOVE_DIALOG = 'fileRemoveDialog'; + +const mapDispatchToProps = (dispatch: Dispatch) => ({ + onConfirm: () => { + // TODO: dispatch action that removes single file + dispatch(dialogActions.CLOSE_DIALOG({ id: FILE_REMOVE_DIALOG })); + dispatch(snackbarActions.OPEN_SNACKBAR({ message: 'Removing file...', hideDuration: 2000 })); + setTimeout(() => { + dispatch(snackbarActions.OPEN_SNACKBAR({ message: 'File removed.', hideDuration: 2000 })); + }, 1000); + } +}); + +export const openFileRemoveDialog = (fileId: string) => + dialogActions.OPEN_DIALOG({ + id: FILE_REMOVE_DIALOG, + data: { + title: 'Removing file', + text: 'Are you sure you want to remove this file?', + confirmButtonLabel: 'Remove', + fileId + } + }); + +export const [FileRemoveDialog] = [ConfirmationDialog] + .map(withDialog(FILE_REMOVE_DIALOG)) + .map(connect(undefined, mapDispatchToProps)); \ No newline at end of file diff --git a/src/views-components/file-remove-dialog/multiple-files-remove-dialog.ts b/src/views-components/file-remove-dialog/multiple-files-remove-dialog.ts new file mode 100644 index 0000000000..8810e23a91 --- /dev/null +++ b/src/views-components/file-remove-dialog/multiple-files-remove-dialog.ts @@ -0,0 +1,37 @@ +// Copyright (C) The Arvados Authors. All rights reserved. +// +// SPDX-License-Identifier: AGPL-3.0 + +import { Dispatch } from "redux"; +import { connect } from "react-redux"; +import { ConfirmationDialog } from "../../components/confirmation-dialog/confirmation-dialog"; +import { withDialog } from "../../store/dialog/with-dialog"; +import { dialogActions } from "../../store/dialog/dialog-actions"; +import { snackbarActions } from "../../store/snackbar/snackbar-actions"; + +const MULTIPLE_FILES_REMOVE_DIALOG = 'multipleFilesRemoveDialog'; + +const mapDispatchToProps = (dispatch: Dispatch) => ({ + onConfirm: () => { + // TODO: dispatch action that removes multiple files + dispatch(dialogActions.CLOSE_DIALOG({ id: MULTIPLE_FILES_REMOVE_DIALOG })); + dispatch(snackbarActions.OPEN_SNACKBAR({message: 'Removing files...', hideDuration: 2000})); + setTimeout(() => { + dispatch(snackbarActions.OPEN_SNACKBAR({message: 'Files removed.', hideDuration: 2000})); + }, 1000); + } +}); + +export const openMultipleFilesRemoveDialog = () => + dialogActions.OPEN_DIALOG({ + id: MULTIPLE_FILES_REMOVE_DIALOG, + data: { + title: 'Removing files', + text: 'Are you sure you want to remove selected files?', + confirmButtonLabel: 'Remove' + } + }); + +export const [MultipleFilesRemoveDialog] = [ConfirmationDialog] + .map(withDialog(MULTIPLE_FILES_REMOVE_DIALOG)) + .map(connect(undefined, mapDispatchToProps)); \ No newline at end of file diff --git a/src/views/workbench/workbench.tsx b/src/views/workbench/workbench.tsx index 34a9fe571a..7f6a8c629d 100644 --- a/src/views/workbench/workbench.tsx +++ b/src/views/workbench/workbench.tsx @@ -34,17 +34,17 @@ import { ResourceKind } from '../../models/resource'; import { ContextMenu, ContextMenuKind } from "../../views-components/context-menu/context-menu"; import { FavoritePanel } from "../favorite-panel/favorite-panel"; import { CurrentTokenDialog } from '../../views-components/current-token-dialog/current-token-dialog'; -import { dataExplorerActions } from '../../store/data-explorer/data-explorer-action'; import { Snackbar } from '../../views-components/snackbar/snackbar'; import { favoritePanelActions } from '../../store/favorite-panel/favorite-panel-action'; import { CreateCollectionDialog } from '../../views-components/create-collection-dialog/create-collection-dialog'; import { CollectionPanel } from '../collection-panel/collection-panel'; import { loadCollection } from '../../store/collection-panel/collection-panel-action'; import { getCollectionUrl } from '../../models/collection'; -import { RemoveDialog } from '../../views-components/remove-dialog/remove-dialog'; import { UpdateCollectionDialog } from '../../views-components/update-collection-dialog/update-collection-dialog.'; import { AuthService } from "../../services/auth-service/auth-service"; import { RenameFileDialog } from '../../views-components/rename-file-dialog/rename-file-dialog'; +import { FileRemoveDialog } from '../../views-components/file-remove-dialog/file-remove-dialog'; +import { MultipleFilesRemoveDialog } from '../../views-components/file-remove-dialog/multiple-files-remove-dialog'; const DRAWER_WITDH = 240; const APP_BAR_HEIGHT = 100; @@ -234,8 +234,9 @@ export const Workbench = withStyles(styles)( - + +