From ca3155fabcfe6dd61b2151c52861b2786e9bec40 Mon Sep 17 00:00:00 2001 From: Lisa Knox Date: Mon, 28 Aug 2023 11:25:24 -0400 Subject: [PATCH] 15768: project multimove works Arvados-DCO-1.1-Signed-off-by: Lisa Knox --- .../multiselectToolbar/MultiselectToolbar.tsx | 63 +++++++------ src/store/projects/project-move-actions.ts | 37 ++++---- src/store/workbench/workbench-actions.ts | 53 ++--------- .../action-sets/project-action-set.ts | 89 ++++++++----------- .../context-menu/context-menu-action-set.ts | 7 +- 5 files changed, 97 insertions(+), 152 deletions(-) diff --git a/src/components/multiselectToolbar/MultiselectToolbar.tsx b/src/components/multiselectToolbar/MultiselectToolbar.tsx index 99d15e07d5..880d0f49c2 100644 --- a/src/components/multiselectToolbar/MultiselectToolbar.tsx +++ b/src/components/multiselectToolbar/MultiselectToolbar.tsx @@ -2,39 +2,38 @@ // // SPDX-License-Identifier: AGPL-3.0 -import React from 'react'; -import { connect } from 'react-redux'; -import { StyleRulesCallback, withStyles, WithStyles, Toolbar, Tooltip, IconButton } from '@material-ui/core'; -import { ArvadosTheme } from 'common/custom-theme'; -import { RootState } from 'store/store'; -import { Dispatch } from 'redux'; -import { TCheckedList } from 'components/data-table/data-table'; -import { ContextMenuResource } from 'store/context-menu/context-menu-actions'; -import { Resource, extractUuidKind } from 'models/resource'; -import { getResource } from 'store/resources/resources'; -import { ResourcesState } from 'store/resources/resources'; -import { ContextMenuAction, ContextMenuActionSet } from 'views-components/context-menu/context-menu-action-set'; -import { RestoreFromTrashIcon, TrashIcon } from 'components/icon/icon'; -import { multiselectActionsFilters, TMultiselectActionsFilters } from './ms-toolbar-action-filters'; -import { kindToActionSet, findActionByName } from './ms-kind-action-differentiator'; -import { toggleTrashAction } from 'views-components/context-menu/action-sets/project-action-set'; - -type CssRules = 'root' | 'button'; +import React from "react"; +import { connect } from "react-redux"; +import { StyleRulesCallback, withStyles, WithStyles, Toolbar, Tooltip, IconButton } from "@material-ui/core"; +import { ArvadosTheme } from "common/custom-theme"; +import { RootState } from "store/store"; +import { Dispatch } from "redux"; +import { TCheckedList } from "components/data-table/data-table"; +import { ContextMenuResource } from "store/context-menu/context-menu-actions"; +import { Resource, extractUuidKind } from "models/resource"; +import { getResource } from "store/resources/resources"; +import { ResourcesState } from "store/resources/resources"; +import { ContextMenuAction, ContextMenuActionSet } from "views-components/context-menu/context-menu-action-set"; +import { RestoreFromTrashIcon, TrashIcon } from "components/icon/icon"; +import { multiselectActionsFilters, TMultiselectActionsFilters } from "./ms-toolbar-action-filters"; +import { kindToActionSet, findActionByName } from "./ms-kind-action-differentiator"; +import { toggleTrashAction } from "views-components/context-menu/action-sets/project-action-set"; + +type CssRules = "root" | "button"; const styles: StyleRulesCallback = (theme: ArvadosTheme) => ({ root: { - display: 'flex', - flexDirection: 'row', + display: "flex", + flexDirection: "row", width: 0, padding: 0, - margin: '1rem auto auto 0.5rem', - overflow: 'hidden', - transition: 'width 150ms', - // borderBottom: '1px solid gray', + margin: "1rem auto auto 0.5rem", + overflow: "hidden", + transition: "width 150ms", }, button: { - width: '1rem', - margin: 'auto 5px', + width: "1rem", + margin: "auto 5px", }, }); @@ -53,7 +52,7 @@ export const MultiselectToolbar = connect( const { classes, checkedList } = props; const currentResourceKinds = Array.from(selectedToKindSet(checkedList)); - const currentPathIsTrash = window.location.pathname === '/trash'; + const currentPathIsTrash = window.location.pathname === "/trash"; const buttons = currentPathIsTrash && selectedToKindSet(checkedList).size ? [toggleTrashAction] @@ -65,10 +64,10 @@ export const MultiselectToolbar = connect( style={{ width: `${buttons.length * 2.12}rem` }}> {buttons.length ? ( buttons.map((btn, i) => - btn.name === 'ToggleTrashAction' ? ( + btn.name === "ToggleTrashAction" ? ( props.executeMulti(btn, checkedList, props.resources)}> @@ -138,7 +137,7 @@ function selectActionsByKind(currentResourceKinds: Array, filterSet: TMu const filteredNameSet = allFiltersArray.map(filterArray => { const resultSet = new Set(); - filterArray.forEach(action => resultSet.add(action.name || '')); + filterArray.forEach(action => resultSet.add(action.name || "")); return resultSet; }); @@ -150,8 +149,8 @@ function selectActionsByKind(currentResourceKinds: Array, filterSet: TMu }); return filteredResult.sort((a, b) => { - const nameA = a.name || ''; - const nameB = b.name || ''; + const nameA = a.name || ""; + const nameB = b.name || ""; if (nameA < nameB) { return -1; } diff --git a/src/store/projects/project-move-actions.ts b/src/store/projects/project-move-actions.ts index ab46e4b59a..618ce21e2d 100644 --- a/src/store/projects/project-move-actions.ts +++ b/src/store/projects/project-move-actions.ts @@ -2,34 +2,31 @@ // // SPDX-License-Identifier: AGPL-3.0 -import { Dispatch } from 'redux'; -import { dialogActions } from 'store/dialog/dialog-actions'; -import { startSubmit, stopSubmit, initialize, FormErrors } from 'redux-form'; -import { ServiceRepository } from 'services/services'; -import { RootState } from 'store/store'; -import { getUserUuid } from 'common/getuser'; -import { getCommonResourceServiceError, CommonResourceServiceError } from 'services/common-service/common-resource-service'; -import { MoveToFormDialogData } from 'store/move-to-dialog/move-to-dialog'; -import { resetPickerProjectTree } from 'store/project-tree-picker/project-tree-picker-actions'; -import { initProjectsTreePicker } from 'store/tree-picker/tree-picker-actions'; -import { projectPanelActions } from 'store/project-panel/project-panel-action'; -import { loadSidePanelTreeProjects } from '../side-panel-tree/side-panel-tree-actions'; +import { Dispatch } from "redux"; +import { dialogActions } from "store/dialog/dialog-actions"; +import { startSubmit, stopSubmit, initialize, FormErrors } from "redux-form"; +import { ServiceRepository } from "services/services"; +import { RootState } from "store/store"; +import { getUserUuid } from "common/getuser"; +import { getCommonResourceServiceError, CommonResourceServiceError } from "services/common-service/common-resource-service"; +import { MoveToFormDialogData } from "store/move-to-dialog/move-to-dialog"; +import { resetPickerProjectTree } from "store/project-tree-picker/project-tree-picker-actions"; +import { initProjectsTreePicker } from "store/tree-picker/tree-picker-actions"; +import { projectPanelActions } from "store/project-panel/project-panel-action"; +import { loadSidePanelTreeProjects } from "../side-panel-tree/side-panel-tree-actions"; -export const PROJECT_MOVE_FORM_NAME = 'projectMoveFormName'; +export const PROJECT_MOVE_FORM_NAME = "projectMoveFormName"; -// export const openMoveProjectDialog = (resource: { name: string; uuid: string }) => { -// console.log(resource); export const openMoveProjectDialog = (resources: Array) => { return (dispatch: Dispatch) => { dispatch(resetPickerProjectTree()); dispatch(initProjectsTreePicker(PROJECT_MOVE_FORM_NAME)); - dispatch(initialize(PROJECT_MOVE_FORM_NAME, resources[0], undefined, resources)); //here + dispatch(initialize(PROJECT_MOVE_FORM_NAME, resources[0])); dispatch(dialogActions.OPEN_DIALOG({ id: PROJECT_MOVE_FORM_NAME, data: {} })); }; }; export const moveProject = (resource: MoveToFormDialogData) => async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => { - console.log('SUCCESS?', resource); const userUuid = getUserUuid(getState()); if (!userUuid) { return; @@ -45,13 +42,13 @@ export const moveProject = (resource: MoveToFormDialogData) => async (dispatch: const error = getCommonResourceServiceError(e); if (error === CommonResourceServiceError.UNIQUE_NAME_VIOLATION) { dispatch( - stopSubmit(PROJECT_MOVE_FORM_NAME, { ownerUuid: 'A project with the same name already exists in the target project.' } as FormErrors) + stopSubmit(PROJECT_MOVE_FORM_NAME, { ownerUuid: "A project with the same name already exists in the target project." } as FormErrors) ); } else if (error === CommonResourceServiceError.OWNERSHIP_CYCLE) { - dispatch(stopSubmit(PROJECT_MOVE_FORM_NAME, { ownerUuid: 'Cannot move a project into itself.' } as FormErrors)); + dispatch(stopSubmit(PROJECT_MOVE_FORM_NAME, { ownerUuid: "Cannot move a project into itself." } as FormErrors)); } else { dispatch(dialogActions.CLOSE_DIALOG({ id: PROJECT_MOVE_FORM_NAME })); - throw new Error('Could not move the project.'); + throw new Error("Could not move the project."); } return; } diff --git a/src/store/workbench/workbench-actions.ts b/src/store/workbench/workbench-actions.ts index ab3041ad2a..6182a314d8 100644 --- a/src/store/workbench/workbench-actions.ts +++ b/src/store/workbench/workbench-actions.ts @@ -279,31 +279,23 @@ export const createProject = (data: projectCreateActions.ProjectCreateFormDialog }; export const moveProject = (data: MoveToFormDialogData) => async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => { - console.log("moveProject---"); - console.log("moveProject:", data); - // const currentState = getState(); - // console.log('current', currentState.multiselect.checkedList); - const projectsToMove = selectedToArray(getState().multiselect.checkedList); - // console.log("resource?", getResource(projectsToMove[0])(getState().resources)); - const sourceUuid = getResource(projectsToMove[0])(getState().resources)?.ownerUuid; + const projectsToMove: string[] = selectedToArray(getState().multiselect.checkedList); + //if no items in checkedlist, default to normal context menu behavior + if (!projectsToMove.length) projectsToMove.push(data.uuid); + const sourceUuid = getResource(data.uuid)(getState().resources)?.ownerUuid; const destinationUuid = data.ownerUuid; - for (const project of projectsToMove) { - await moveSingleProject(project); + for (const projectUuid of projectsToMove) { + await moveSingleProject(projectUuid); } async function moveSingleProject(projectUuid) { try { - // const oldProject = getResource(projectUuid)(getState().resources); - const originalProject = getResource(projectUuid)(getState().resources) as any; + const originalProject = getResource(projectUuid)(getState().resources); const oldProject = { ...originalProject, ownerUuid: data.ownerUuid } as any; - // const { name, uuid, ownerUuid } = getResource(projectUuid)(getState().resources) as any; - // const oldProject: MoveToFormDialogData = { name, uuid, ownerUuid }; - console.log("oldProject", oldProject, data); const oldOwnerUuid = oldProject ? oldProject.ownerUuid : ""; const movedProject = await dispatch(projectMoveActions.moveProject(oldProject)); if (movedProject) { - console.log("movedProject", movedProject); dispatch( snackbarActions.OPEN_SNACKBAR({ message: "Project has been moved", @@ -311,10 +303,6 @@ export const moveProject = (data: MoveToFormDialogData) => async (dispatch: Disp kind: SnackbarKind.SUCCESS, }) ); - // if (oldProject) { - // await dispatch(loadSidePanelTreeProjects(oldProject.ownerUuid)); - // await dispatch(loadSidePanelTreeProjects(originalProject.ownerUuid)); - // } await dispatch(reloadProjectMatchingUuid([oldOwnerUuid, movedProject.ownerUuid, movedProject.uuid])); } } catch (e) { @@ -329,33 +317,6 @@ export const moveProject = (data: MoveToFormDialogData) => async (dispatch: Disp } if (sourceUuid) await dispatch(loadSidePanelTreeProjects(sourceUuid)); await dispatch(loadSidePanelTreeProjects(destinationUuid)); - - // try { - // const oldProject = getResource(data.uuid)(getState().resources); - // const oldOwnerUuid = oldProject ? oldProject.ownerUuid : ''; - // const movedProject = await dispatch(projectMoveActions.moveProject(data)); - // if (movedProject) { - // dispatch( - // snackbarActions.OPEN_SNACKBAR({ - // message: 'Project has been moved', - // hideDuration: 2000, - // kind: SnackbarKind.SUCCESS, - // }) - // ); - // if (oldProject) { - // await dispatch(loadSidePanelTreeProjects(oldProject.ownerUuid)); - // } - // dispatch(reloadProjectMatchingUuid([oldOwnerUuid, movedProject.ownerUuid, movedProject.uuid])); - // } - // } catch (e) { - // dispatch( - // snackbarActions.OPEN_SNACKBAR({ - // message: e.message, - // hideDuration: 2000, - // kind: SnackbarKind.ERROR, - // }) - // ); - // } }; export const updateProject = (data: projectUpdateActions.ProjectUpdateFormDialogData) => async (dispatch: Dispatch) => { diff --git a/src/views-components/context-menu/action-sets/project-action-set.ts b/src/views-components/context-menu/action-sets/project-action-set.ts index 21019ce8b1..877c34b935 100644 --- a/src/views-components/context-menu/action-sets/project-action-set.ts +++ b/src/views-components/context-menu/action-sets/project-action-set.ts @@ -2,30 +2,30 @@ // // SPDX-License-Identifier: AGPL-3.0 -import { ContextMenuActionSet } from '../context-menu-action-set'; -import { NewProjectIcon, RenameIcon, MoveToIcon, DetailsIcon, AdvancedIcon, OpenIcon, Link, FolderSharedIcon } from 'components/icon/icon'; -import { ToggleFavoriteAction } from '../actions/favorite-action'; -import { toggleFavorite } from 'store/favorites/favorites-actions'; -import { favoritePanelActions } from 'store/favorite-panel/favorite-panel-action'; -import { openMoveProjectDialog } from 'store/projects/project-move-actions'; -import { openProjectCreateDialog } from 'store/projects/project-create-actions'; -import { openProjectUpdateDialog } from 'store/projects/project-update-actions'; -import { ToggleTrashAction } from 'views-components/context-menu/actions/trash-action'; -import { toggleProjectTrashed } from 'store/trash/trash-actions'; -import { ShareIcon } from 'components/icon/icon'; -import { openSharingDialog } from 'store/sharing-dialog/sharing-dialog-actions'; -import { openAdvancedTabDialog } from 'store/advanced-tab/advanced-tab'; -import { toggleDetailsPanel } from 'store/details-panel/details-panel-action'; -import { copyToClipboardAction, openInNewTabAction } from 'store/open-in-new-tab/open-in-new-tab.actions'; -import { openWebDavS3InfoDialog } from 'store/collections/collection-info-actions'; -import { ToggleLockAction } from '../actions/lock-action'; -import { freezeProject, unfreezeProject } from 'store/projects/project-lock-actions'; +import { ContextMenuActionSet } from "../context-menu-action-set"; +import { NewProjectIcon, RenameIcon, MoveToIcon, DetailsIcon, AdvancedIcon, OpenIcon, Link, FolderSharedIcon } from "components/icon/icon"; +import { ToggleFavoriteAction } from "../actions/favorite-action"; +import { toggleFavorite } from "store/favorites/favorites-actions"; +import { favoritePanelActions } from "store/favorite-panel/favorite-panel-action"; +import { openMoveProjectDialog } from "store/projects/project-move-actions"; +import { openProjectCreateDialog } from "store/projects/project-create-actions"; +import { openProjectUpdateDialog } from "store/projects/project-update-actions"; +import { ToggleTrashAction } from "views-components/context-menu/actions/trash-action"; +import { toggleProjectTrashed } from "store/trash/trash-actions"; +import { ShareIcon } from "components/icon/icon"; +import { openSharingDialog } from "store/sharing-dialog/sharing-dialog-actions"; +import { openAdvancedTabDialog } from "store/advanced-tab/advanced-tab"; +import { toggleDetailsPanel } from "store/details-panel/details-panel-action"; +import { copyToClipboardAction, openInNewTabAction } from "store/open-in-new-tab/open-in-new-tab.actions"; +import { openWebDavS3InfoDialog } from "store/collections/collection-info-actions"; +import { ToggleLockAction } from "../actions/lock-action"; +import { freezeProject, unfreezeProject } from "store/projects/project-lock-actions"; export const toggleFavoriteAction = { component: ToggleFavoriteAction, - name: 'ToggleFavoriteAction', + name: "ToggleFavoriteAction", execute: (dispatch, resources) => { - resources.forEach((resource) => + resources.forEach(resource => dispatch(toggleFavorite(resource)).then(() => { dispatch(favoritePanelActions.REQUEST_ITEMS()); }) @@ -35,15 +35,15 @@ export const toggleFavoriteAction = { export const openInNewTabMenuAction = { icon: OpenIcon, - name: 'Open in new tab', + name: "Open in new tab", execute: (dispatch, resources) => { - resources.forEach((resource) => dispatch(openInNewTabAction(resource))); + resources.forEach(resource => dispatch(openInNewTabAction(resource))); }, }; export const copyToClipboardMenuAction = { icon: Link, - name: 'Copy to clipboard', + name: "Copy to clipboard", execute: (dispatch, resources) => { dispatch(copyToClipboardAction(resources)); }, @@ -51,76 +51,65 @@ export const copyToClipboardMenuAction = { export const viewDetailsAction = { icon: DetailsIcon, - name: 'View details', - execute: (dispatch) => { + name: "View details", + execute: dispatch => { dispatch(toggleDetailsPanel()); }, }; export const advancedAction = { icon: AdvancedIcon, - name: 'API Details', + name: "API Details", execute: (dispatch, resources) => { - resources.forEach((resource) => dispatch(openAdvancedTabDialog(resource.uuid))); + resources.forEach(resource => dispatch(openAdvancedTabDialog(resource.uuid))); }, }; export const openWith3rdPartyClientAction = { icon: FolderSharedIcon, - name: 'Open with 3rd party client', + name: "Open with 3rd party client", execute: (dispatch, resources) => { - resources.forEach((resource) => dispatch(openWebDavS3InfoDialog(resource.uuid))); + resources.forEach(resource => dispatch(openWebDavS3InfoDialog(resource.uuid))); }, }; export const editProjectAction = { icon: RenameIcon, - name: 'Edit project', + name: "Edit project", execute: (dispatch, resources) => { - resources.forEach((resource) => dispatch(openProjectUpdateDialog(resource))); + resources.forEach(resource => dispatch(openProjectUpdateDialog(resource))); }, }; export const shareAction = { icon: ShareIcon, - name: 'Share', + name: "Share", execute: (dispatch, processes) => { - processes.forEach((process) => dispatch(openSharingDialog(process.uuid))); + processes.forEach(process => dispatch(openSharingDialog(process.uuid))); }, }; export const moveToAction = { icon: MoveToIcon, - name: 'Move to', + name: "Move to", execute: (dispatch, resources) => { - // resources.length === 1 - // ? dispatch(openMoveProjectDialog(resources[0])) - // : resources.forEach((resource) => { dispatch(openMoveProjectDialog(resources)); - // }) }, - // execute: (dispatch, resources) => { - // resources.forEach((resource) => { - // dispatch(openMoveProjectDialog(resource)); - // }); - // }, }; export const toggleTrashAction = { component: ToggleTrashAction, - name: 'ToggleTrashAction', + name: "ToggleTrashAction", execute: (dispatch, resources) => { - resources.forEach((resource) => - dispatch(toggleProjectTrashed(resource.uuid, resource.ownerUuid, resource.isTrashed!!, resources.length > 1)) - ); + resources.forEach(resource => dispatch(toggleProjectTrashed(resource.uuid, resource.ownerUuid, resource.isTrashed!!, resources.length > 1))); }, }; export const freezeProjectAction = { component: ToggleLockAction, - name: 'ToggleLockAction', + name: "ToggleLockAction", execute: (dispatch, resources) => { - resources.forEach((resource) => { + resources.forEach(resource => { if (resource.isFrozen) { dispatch(unfreezeProject(resource.uuid)); } else { @@ -132,7 +121,7 @@ export const freezeProjectAction = { export const newProjectAction: any = { icon: NewProjectIcon, - name: 'New project', + name: "New project", execute: (dispatch, resource): void => { dispatch(openProjectCreateDialog(resource.uuid)); }, diff --git a/src/views-components/context-menu/context-menu-action-set.ts b/src/views-components/context-menu/context-menu-action-set.ts index 9ee4106eb6..a953500b3a 100644 --- a/src/views-components/context-menu/context-menu-action-set.ts +++ b/src/views-components/context-menu/context-menu-action-set.ts @@ -2,10 +2,9 @@ // // SPDX-License-Identifier: AGPL-3.0 -import { Dispatch } from 'redux'; -import { ContextMenuItem } from 'components/context-menu/context-menu'; -import { ContextMenuResource } from 'store/context-menu/context-menu-actions'; -import { RootState } from 'store/store'; +import { Dispatch } from "redux"; +import { ContextMenuItem } from "components/context-menu/context-menu"; +import { ContextMenuResource } from "store/context-menu/context-menu-actions"; export interface ContextMenuAction extends ContextMenuItem { execute(dispatch: Dispatch, resources: ContextMenuResource[], state?: any): void; -- 2.30.2