--- /dev/null
- export const MakeACopyDialog = (props: WithDialogProps<string> & InjectedFormProps<{ name: string }>) =>
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+import * as React from "react";
+import { Field, InjectedFormProps, WrappedFieldProps } from "redux-form";
+import { Dialog, DialogTitle, DialogContent, DialogActions, Button, CircularProgress } from "@material-ui/core";
+import { WithDialogProps } from "~/store/dialog/with-dialog";
+import { ProjectTreePicker } from "~/views-components/project-tree-picker/project-tree-picker";
+import { MAKE_A_COPY_VALIDATION, COPY_NAME_VALIDATION } from "~/validators/validators";
+import { TextField } from '~/components/text-field/text-field';
- name="copyName"
++
++export interface CopyFormData {
++ name: string;
++ projectUuid: string;
++ uuid: string;
++}
++
++export const MakeACopyDialog = (props: WithDialogProps<string> & InjectedFormProps<CopyFormData>) =>
+ <form>
+ <Dialog open={props.open}
+ disableBackdropClick={true}
+ disableEscapeKeyDown={true}>
+ <DialogTitle>Make a copy</DialogTitle>
+ <DialogContent>
+ <Field
++ name="name"
+ component={TextField}
+ validate={COPY_NAME_VALIDATION}
+ label="Enter a new name for the copy" />
+ <Field
+ name="projectUuid"
+ component={Picker}
+ validate={MAKE_A_COPY_VALIDATION} />
+ </DialogContent>
+ <DialogActions>
+ <Button
+ variant='flat'
+ color='primary'
+ disabled={props.submitting}
+ onClick={props.closeDialog}>
+ Cancel
+ </Button>
+ <Button
+ variant='contained'
+ color='primary'
+ type='submit'
+ onClick={props.handleSubmit}
+ disabled={props.pristine || props.invalid || props.submitting}>
+ {props.submitting ? <CircularProgress size={20} /> : 'Copy'}
+ </Button>
+ </DialogActions>
+ </Dialog>
+ </form>;
+const Picker = (props: WrappedFieldProps) =>
+ <div style={{ width: '400px', height: '144px', display: 'flex', flexDirection: 'column' }}>
+ <ProjectTreePicker onChange={projectUuid => props.input.onChange(projectUuid)} />
+ </div>;
</List>;
}
- return status === TreeItemStatus.PENDING || (status === TreeItemStatus.LOADED && !items) ? <span /> : <SidePanelRightArrowIcon />;
+ getProperArrowAnimation = (status: string, items: Array<TreeItem<T>>) => {
++ return status === TreeItemStatus.PENDING || (status === TreeItemStatus.LOADED && !items) || (status === TreeItemStatus.LOADED && items && items.length === 0) ? <span /> : <SidePanelRightArrowIcon />;
+ }
+
getToggableIconClassNames = (isOpen?: boolean, isActive?: boolean) => {
const { iconOpen, iconClose, active, toggableIcon } = this.props.classes;
return classnames(toggableIcon, {
import { collectionFilesItemActionSet } from './views-components/context-menu/action-sets/collection-files-item-action-set';
import { collectionActionSet } from './views-components/context-menu/action-sets/collection-action-set';
import { collectionResourceActionSet } from './views-components/context-menu/action-sets/collection-resource-action-set';
-import { initPickerProjectTree } from './views-components/project-tree-picker/project-tree-picker';
++import { initPickerProjectTree } from './store/project-tree-picker/project-tree-picker-actions';
const getBuildNumber = () => "BN-" + (process.env.BUILD_NUMBER || "dev");
const getGitCommit = () => "GIT-" + (process.env.GIT_COMMIT || "latest").substr(0, 7);
--- /dev/null
--- /dev/null
++// Copyright (C) The Arvados Authors. All rights reserved.
++//
++// SPDX-License-Identifier: AGPL-3.0
++
++import { Dispatch } from "redux";
++import { RootState } from "~/store/store";
++import { ServiceRepository } from "~/services/services";
++import { TreePickerId, receiveTreePickerData } from "~/views-components/project-tree-picker/project-tree-picker";
++import { mockProjectResource } from "~/models/test-utils";
++import { treePickerActions } from "~/store/tree-picker/tree-picker-actions";
++
++export const resetPickerProjectTree = () => (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
++ dispatch<any>(treePickerActions.RESET_TREE_PICKER({pickerId: TreePickerId.PROJECTS}));
++ dispatch<any>(treePickerActions.RESET_TREE_PICKER({pickerId: TreePickerId.SHARED_WITH_ME}));
++ dispatch<any>(treePickerActions.RESET_TREE_PICKER({pickerId: TreePickerId.FAVORITES}));
++
++ dispatch<any>(initPickerProjectTree());
++};
++
++export const initPickerProjectTree = () => (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
++ const uuid = services.authService.getUuid();
++
++ dispatch<any>(getPickerTreeProjects(uuid));
++ dispatch<any>(getSharedWithMeProjectsPickerTree(uuid));
++ dispatch<any>(getFavoritesProjectsPickerTree(uuid));
++};
++
++const getPickerTreeProjects = (uuid: string = '') => {
++ return getProjectsPickerTree(uuid, TreePickerId.PROJECTS);
++};
++
++const getSharedWithMeProjectsPickerTree = (uuid: string = '') => {
++ return getProjectsPickerTree(uuid, TreePickerId.SHARED_WITH_ME);
++};
++
++const getFavoritesProjectsPickerTree = (uuid: string = '') => {
++ return getProjectsPickerTree(uuid, TreePickerId.FAVORITES);
++};
++
++const getProjectsPickerTree = (uuid: string, kind: string) => {
++ return receiveTreePickerData(
++ '',
++ [mockProjectResource({ uuid, name: kind })],
++ kind
++ );
++};
import { TreePickerNode } from "./tree-picker";
export const treePickerActions = unionize({
- LOAD_TREE_PICKER_NODE: ofType<{ id: string }>(),
- LOAD_TREE_PICKER_NODE_SUCCESS: ofType<{ id: string, nodes: Array<TreePickerNode> }>(),
- TOGGLE_TREE_PICKER_NODE_COLLAPSE: ofType<{ id: string }>(),
- TOGGLE_TREE_PICKER_NODE_SELECT: ofType<{ id: string }>()
+ LOAD_TREE_PICKER_NODE: ofType<{ nodeId: string, pickerId: string }>(),
+ LOAD_TREE_PICKER_NODE_SUCCESS: ofType<{ nodeId: string, nodes: Array<TreePickerNode>, pickerId: string }>(),
+ TOGGLE_TREE_PICKER_NODE_COLLAPSE: ofType<{ nodeId: string, pickerId: string }>(),
- TOGGLE_TREE_PICKER_NODE_SELECT: ofType<{ nodeId: string, pickerId: string }>()
++ TOGGLE_TREE_PICKER_NODE_SELECT: ofType<{ nodeId: string, pickerId: string }>(),
++ RESET_TREE_PICKER: ofType<{ pickerId: string }>()
}, {
tag: 'type',
value: 'payload'
import { TreePicker, TreePickerNode } from "./tree-picker";
import { treePickerActions, TreePickerAction } from "./tree-picker-actions";
import { TreeItemStatus } from "~/components/tree/tree";
+ import { compose } from "redux";
- export const treePickerReducer = (state: TreePicker = createTree(), action: TreePickerAction) =>
+ export const treePickerReducer = (state: TreePicker = {}, action: TreePickerAction) =>
treePickerActions.match(action, {
- LOAD_TREE_PICKER_NODE: ({ id }) =>
- setNodeValueWith(setPending)(id)(state),
- LOAD_TREE_PICKER_NODE_SUCCESS: ({ id, nodes }) => {
- const [newState] = [state]
- .map(receiveNodes(nodes)(id))
- .map(setNodeValueWith(setLoaded)(id));
- return newState;
- },
- TOGGLE_TREE_PICKER_NODE_COLLAPSE: ({ id }) =>
- setNodeValueWith(toggleCollapse)(id)(state),
- TOGGLE_TREE_PICKER_NODE_SELECT: ({ id }) =>
- mapTreeValues(toggleSelect(id))(state),
+ LOAD_TREE_PICKER_NODE: ({ nodeId, pickerId }) =>
+ updateOrCreatePicker(state, pickerId, setNodeValueWith(setPending)(nodeId)),
- LOAD_TREE_PICKER_NODE_SUCCESS: ({ nodeId, nodes, pickerId }) =>
- updateOrCreatePicker(state, pickerId, compose(receiveNodes(nodes)(nodeId),setNodeValueWith(setLoaded)(nodeId))),
- TOGGLE_TREE_PICKER_NODE_COLLAPSE: ({ nodeId, pickerId }) =>
++ LOAD_TREE_PICKER_NODE_SUCCESS: ({ nodeId, nodes, pickerId }) =>
++ updateOrCreatePicker(state, pickerId, compose(receiveNodes(nodes)(nodeId), setNodeValueWith(setLoaded)(nodeId))),
++ TOGGLE_TREE_PICKER_NODE_COLLAPSE: ({ nodeId, pickerId }) =>
+ updateOrCreatePicker(state, pickerId, setNodeValueWith(toggleCollapse)(nodeId)),
- TOGGLE_TREE_PICKER_NODE_SELECT: ({ nodeId, pickerId }) =>
++ TOGGLE_TREE_PICKER_NODE_SELECT: ({ nodeId, pickerId }) =>
+ updateOrCreatePicker(state, pickerId, mapTreeValues(toggleSelect(nodeId))),
++ RESET_TREE_PICKER: ({ pickerId }) =>
++ updateOrCreatePicker(state, pickerId, createTree),
default: () => state
});
export const COLLECTION_DESCRIPTION_VALIDATION = [maxLength(255)];
export const COLLECTION_PROJECT_VALIDATION = [require];
-export const MOVE_TO_VALIDATION = [require];
+export const COPY_NAME_VALIDATION = [require, maxLength(255)];
- export const MAKE_A_COPY_VALIDATION = [require, maxLength(255)];
++export const MAKE_A_COPY_VALIDATION = [require, maxLength(255)];
++
++export const MOVE_TO_VALIDATION = [require];
import { RenameIcon, ShareIcon, MoveToIcon, CopyIcon, DetailsIcon, ProvenanceGraphIcon, AdvancedIcon, RemoveIcon } from "~/components/icon/icon";
import { openUpdater } from "~/store/collections/updater/collection-updater-action";
import { favoritePanelActions } from "~/store/favorite-panel/favorite-panel-action";
- import { openMakeACopyDialog } from "~/views-components/make-a-copy-dialog/make-a-copy-dialog";
++import { openMakeACopyDialog, MAKE_A_COPY_DIALOG } from "~/views-components/make-a-copy-dialog/make-a-copy-dialog";
+ import { openMoveToDialog } from "../../move-to-dialog/move-to-dialog";
++import { reset } from 'redux-form';
export const collectionActionSet: ContextMenuActionSet = [[
{
{
icon: CopyIcon,
name: "Copy to project",
- execute: dispatch => dispatch<any>(openMakeACopyDialog())
+ execute: (dispatch, resource) => {
- // add code
++ dispatch(reset(MAKE_A_COPY_DIALOG));
++ dispatch<any>(openMakeACopyDialog({name: resource.name, projectUuid: resource.uuid}));
+ }
},
{
icon: DetailsIcon,
import { RenameIcon, ShareIcon, MoveToIcon, CopyIcon, DetailsIcon, RemoveIcon } from "~/components/icon/icon";
import { openUpdater } from "~/store/collections/updater/collection-updater-action";
import { favoritePanelActions } from "~/store/favorite-panel/favorite-panel-action";
- import { openMakeACopyDialog } from "~/views-components/make-a-copy-dialog/make-a-copy-dialog";
-import { openMoveToDialog } from "../../move-to-dialog/move-to-dialog";
++import { openMakeACopyDialog, MAKE_A_COPY_DIALOG } from "~/views-components/make-a-copy-dialog/make-a-copy-dialog";
++import { openMoveToDialog } from '../../move-to-dialog/move-to-dialog';
++import { reset } from 'redux-form';
export const collectionResourceActionSet: ContextMenuActionSet = [[
{
{
icon: CopyIcon,
name: "Copy to project",
- execute: dispatch => dispatch<any>(openMakeACopyDialog())
+ execute: (dispatch, resource) => {
- // add code
- }
++ dispatch(reset(MAKE_A_COPY_DIALOG));
++ dispatch<any>(openMakeACopyDialog({name: resource.name, projectUuid: resource.uuid}));
++ },
},
{
icon: DetailsIcon,
import { ContextMenuActionSet } from "../context-menu-action-set";
import { projectActions, PROJECT_FORM_NAME } from "~/store/project/project-action";
- import { NewProjectIcon, RenameIcon, CopyIcon } from "~/components/icon/icon";
-import { NewProjectIcon, MoveToIcon, RenameIcon } from "~/components/icon/icon";
++import { NewProjectIcon, RenameIcon, CopyIcon, MoveToIcon } 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 { openMoveToDialog } from "../../move-to-dialog/move-to-dialog";
import { PROJECT_CREATE_DIALOG } from "../../dialog-create/dialog-project-create";
- import { openMakeACopyDialog } from "~/views-components/make-a-copy-dialog/make-a-copy-dialog";
++import { openMakeACopyDialog, MAKE_A_COPY_DIALOG } from "~/views-components/make-a-copy-dialog/make-a-copy-dialog";
export const projectActionSet: ContextMenuActionSet = [[
{
});
}
},
- execute: dispatch => dispatch<any>(openMoveToDialog())
+ {
+ icon: MoveToIcon,
+ name: "Move to",
- execute: dispatch => dispatch<any>(openMakeACopyDialog())
- },
++ execute: dispatch => dispatch<any>(openMoveToDialog())
+ },
+ {
+ icon: CopyIcon,
+ name: "Copy to project",
++ execute: (dispatch, resource) => {
++ dispatch(reset(MAKE_A_COPY_DIALOG));
++ dispatch<any>(openMakeACopyDialog({name: resource.name, projectUuid: resource.uuid}));
++ }
++ }
]];
import { withDialog } from "~/store/dialog/with-dialog";
import { dialogActions } from "~/store/dialog/dialog-actions";
import { DialogCollectionCreateWithSelected } from "../dialog-create/dialog-collection-create-selected";
- import { loadProjectTreePickerProjects } from "../project-tree-picker/project-tree-picker";
++import { resetPickerProjectTree } from "~/store/project-tree-picker/project-tree-picker-actions";
export const DIALOG_COLLECTION_CREATE_WITH_SELECTED = 'dialogCollectionCreateWithSelected';
export const createCollectionWithSelected = () =>
(dispatch: Dispatch) => {
dispatch(reset(DIALOG_COLLECTION_CREATE_WITH_SELECTED));
- dispatch<any>(loadProjectTreePickerProjects(''));
++ dispatch<any>(resetPickerProjectTree());
dispatch(dialogActions.OPEN_DIALOG({ id: DIALOG_COLLECTION_CREATE_WITH_SELECTED, data: {} }));
};
--- /dev/null
- import { MakeACopyDialog } from "../../components/make-a-copy/make-a-copy";
- import { reduxForm, startSubmit, stopSubmit } from "redux-form";
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+import { Dispatch, compose } from "redux";
+import { withDialog } from "../../store/dialog/with-dialog";
+import { dialogActions } from "../../store/dialog/dialog-actions";
- export const MAKE_A_COPY_DIALOG = 'makeACopyDialog';
- export const openMakeACopyDialog = () =>
++import { MakeACopyDialog, CopyFormData } from "../../components/make-a-copy/make-a-copy";
++import { reduxForm, startSubmit, stopSubmit, initialize } from 'redux-form';
++import { resetPickerProjectTree } from "~/store/project-tree-picker/project-tree-picker-actions";
+
- export const MakeACopyToProjectDialog = compose(
++export const MAKE_A_COPY_DIALOG = 'makeACopyDialog';
++export const openMakeACopyDialog = (data: {projectUuid: string, name: string}) =>
+ (dispatch: Dispatch) => {
++ dispatch<any>(resetPickerProjectTree());
++ const initialData: CopyFormData = {name: "Copy of: " + data.name, projectUuid: '', uuid: data.projectUuid};
++ dispatch<any>(initialize(MAKE_A_COPY_DIALOG, initialData));
+ dispatch(dialogActions.OPEN_DIALOG({ id: MAKE_A_COPY_DIALOG, data: {} }));
+ };
++export const MakeACopyToProjectDialog = compose(
+ withDialog(MAKE_A_COPY_DIALOG),
+ reduxForm({
+ form: MAKE_A_COPY_DIALOG,
+ onSubmit: (data, dispatch) => {
+ dispatch(startSubmit(MAKE_A_COPY_DIALOG));
+ setTimeout(() => dispatch(stopSubmit(MAKE_A_COPY_DIALOG, { name: 'Invalid path' })), 2000);
+ }
+ })
+)(MakeACopyDialog);
--- /dev/null
+ // Copyright (C) The Arvados Authors. All rights reserved.
+ //
+ // SPDX-License-Identifier: AGPL-3.0
+
+ import { Dispatch, compose } from "redux";
+ import { withDialog } from "../../store/dialog/with-dialog";
+ import { dialogActions } from "../../store/dialog/dialog-actions";
+ import { MoveToDialog } from "../../components/move-to-dialog/move-to-dialog";
+ import { reduxForm, startSubmit, stopSubmit } from "redux-form";
++import { resetPickerProjectTree } from "~/store/project-tree-picker/project-tree-picker-actions";
+
+ export const MOVE_TO_DIALOG = 'moveToDialog';
+
+ export const openMoveToDialog = () =>
+ (dispatch: Dispatch) => {
++ dispatch<any>(resetPickerProjectTree());
+ dispatch(dialogActions.OPEN_DIALOG({ id: MOVE_TO_DIALOG, data: {} }));
+ };
+
+ export const MoveToProjectDialog = compose(
+ withDialog(MOVE_TO_DIALOG),
+ reduxForm({
+ form: MOVE_TO_DIALOG,
+ onSubmit: (data, dispatch) => {
+ dispatch(startSubmit(MOVE_TO_DIALOG));
+ setTimeout(() => dispatch(stopSubmit(MOVE_TO_DIALOG, { name: 'Invalid path' })), 2000);
+ }
+ })
+ )(MoveToDialog);
import { RootState } from "~/store/store";
import { ServiceRepository } from "~/services/services";
import { FilterBuilder } from "~/common/api/filter-builder";
-import { mockProjectResource } from "~/models/test-utils";
- type ProjectTreePickerProps = Pick<TreeProps<ProjectResource>, 'toggleItemActive' | 'toggleItemOpen'>;
+ type ProjectTreePickerProps = Pick<TreePickerProps, 'toggleItemActive' | 'toggleItemOpen'>;
- const mapDispatchToProps = (dispatch: Dispatch, props: {onChange: (projectUuid: string) => void}): ProjectTreePickerProps => ({
- toggleItemActive: id => {
- dispatch(treePickerActions.TOGGLE_TREE_PICKER_NODE_SELECT({ id }));
- props.onChange(id);
+ const mapDispatchToProps = (dispatch: Dispatch, props: { onChange: (projectUuid: string) => void }): ProjectTreePickerProps => ({
+ toggleItemActive: (nodeId, status, pickerId) => {
+ getNotSelectedTreePickerKind(pickerId)
+ .forEach(pickerId => dispatch(treePickerActions.TOGGLE_TREE_PICKER_NODE_SELECT({ nodeId: '', pickerId })));
+ dispatch(treePickerActions.TOGGLE_TREE_PICKER_NODE_SELECT({ nodeId, pickerId }));
+
+ props.onChange(nodeId);
},
- toggleItemOpen: (id, status) => {
- status === TreeItemStatus.INITIAL
- ? dispatch<any>(loadProjectTreePickerProjects(id))
- : dispatch(treePickerActions.TOGGLE_TREE_PICKER_NODE_COLLAPSE({ id }));
+ toggleItemOpen: (nodeId, status, pickerId) => {
+ dispatch<any>(toggleItemOpen(nodeId, status, pickerId));
}
});
isActive={item.active}
hasMargin={true} />;
+
// TODO: move action creator to store directory
- const receiveProjectTreePickerData = (id: string, projects: ProjectResource[]) =>
+ export const receiveTreePickerData = (nodeId: string, projects: ProjectResource[], pickerId: string) =>
(dispatch: Dispatch) => {
dispatch(treePickerActions.LOAD_TREE_PICKER_NODE_SUCCESS({
- id,
- nodes: projects.map(project => createTreePickerNode({ id: project.uuid, value: project }))
+ nodeId,
+ nodes: projects.map(project => createTreePickerNode({ nodeId: project.uuid, value: project })),
+ pickerId,
}));
- dispatch(treePickerActions.TOGGLE_TREE_PICKER_NODE_COLLAPSE({ id }));
+
+ dispatch(treePickerActions.TOGGLE_TREE_PICKER_NODE_COLLAPSE({ nodeId, pickerId }));
};
-export const initPickerProjectTree = () => (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
- const uuid = services.authService.getUuid();
-
- dispatch<any>(getPickerTreeProjects(uuid));
- dispatch<any>(getSharedWithMeProjectsPickerTree(uuid));
- dispatch<any>(getFavoritesProjectsPickerTree(uuid));
-};
-
-const getPickerTreeProjects = (uuid: string = '') => {
- return getProjectsPickerTree(uuid, TreePickerId.PROJECTS);
-};
-
-const getSharedWithMeProjectsPickerTree = (uuid: string = '') => {
- return getProjectsPickerTree(uuid, TreePickerId.SHARED_WITH_ME);
-};
-
-const getFavoritesProjectsPickerTree = (uuid: string = '') => {
- return getProjectsPickerTree(uuid, TreePickerId.FAVORITES);
-};
-
-const getProjectsPickerTree = (uuid: string, kind: string) => {
- return receiveTreePickerData(
- '',
- [mockProjectResource({ uuid, name: kind })],
- kind
- );
-};
+
+
<CreateProjectDialog />
<CreateCollectionDialog />
<RenameFileDialog />
+ <MoveToProjectDialog />
<DialogCollectionCreateWithSelectedFile />
<FileRemoveDialog />
+ <MakeACopyToProjectDialog />
<MultipleFilesRemoveDialog />
<UpdateCollectionDialog />
<UploadCollectionFilesDialog />