[arvados-workbench2] created: 2.6.0-4-gbc0041ff
git repository hosting
git at public.arvados.org
Mon Apr 17 19:46:05 UTC 2023
at bc0041ffcbc42f92edc6bde05c8ba3ce392873fc (commit)
commit bc0041ffcbc42f92edc6bde05c8ba3ce392873fc
Author: Stephen Smith <stephen at curii.com>
Date: Mon Apr 17 15:39:53 2023 -0400
20031: Add includeDirectories flag to ProjectsTreePicker and add collection/directory only picker
Arvados-DCO-1.1-Signed-off-by: Stephen Smith <stephen at curii.com>
diff --git a/src/models/collection-file.ts b/src/models/collection-file.ts
index 91008d1f..3688557a 100644
--- a/src/models/collection-file.ts
+++ b/src/models/collection-file.ts
@@ -71,10 +71,10 @@ export const createCollectionFilesTree = (data: Array<CollectionDirectory | Coll
const getParentId = (item: CollectionDirectory | CollectionFile) =>
item.path
- ? join('', [getCollectionId(item.id), item.path])
+ ? join('', [getCollectionResourceCollectionUuid(item.id), item.path])
: item.path;
-const getCollectionId = pipe(
+export const getCollectionResourceCollectionUuid = pipe(
split('/'),
head,
-);
\ No newline at end of file
+);
diff --git a/src/store/collections/collection-partial-copy-actions.ts b/src/store/collections/collection-partial-copy-actions.ts
index 359b6b83..2a3fd5de 100644
--- a/src/store/collections/collection-partial-copy-actions.ts
+++ b/src/store/collections/collection-partial-copy-actions.ts
@@ -25,7 +25,7 @@ export interface CollectionPartialCopyToNewCollectionFormData {
}
export interface CollectionPartialCopyToExistingCollectionFormData {
- collectionUuid: string;
+ destination: {uuid: string, path?: string};
}
export const openCollectionPartialCopyToNewCollectionDialog = () =>
@@ -102,7 +102,7 @@ export const openCollectionPartialCopyToExistingCollectionDialog = () =>
const currentCollection = getState().collectionPanel.item;
if (currentCollection) {
const initialData = {
- collectionUuid: ''
+ destination: {uuid: '', destinationPath: ''}
};
dispatch(initialize(COLLECTION_PARTIAL_COPY_TO_SELECTED_COLLECTION, initialData));
dispatch<any>(resetPickerProjectTree());
@@ -111,13 +111,13 @@ export const openCollectionPartialCopyToExistingCollectionDialog = () =>
}
};
-export const copyCollectionPartialToExistingCollection = ({ collectionUuid }: CollectionPartialCopyToExistingCollectionFormData) =>
+export const copyCollectionPartialToExistingCollection = ({ destination }: CollectionPartialCopyToExistingCollectionFormData) =>
async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
const state = getState();
// Get current collection
const sourceCollection = state.collectionPanel.item;
- if (sourceCollection) {
+ if (sourceCollection && destination.uuid) {
try {
dispatch(startSubmit(COLLECTION_PARTIAL_COPY_TO_SELECTED_COLLECTION));
dispatch(progressIndicatorActions.START_WORKING(COLLECTION_PARTIAL_COPY_TO_SELECTED_COLLECTION));
@@ -126,7 +126,7 @@ export const copyCollectionPartialToExistingCollection = ({ collectionUuid }: Co
.map(file => file.id.replace(new RegExp(`(^${sourceCollection.uuid})`), ''));
// Copy files
- const updatedCollection = await services.collectionService.copyFiles(sourceCollection.portableDataHash, paths, {uuid: collectionUuid}, '/', false);
+ const updatedCollection = await services.collectionService.copyFiles(sourceCollection.portableDataHash, paths, {uuid: destination.uuid}, destination.path || '/', false);
dispatch(updateResources([updatedCollection]));
dispatch(dialogActions.CLOSE_DIALOG({ id: COLLECTION_PARTIAL_COPY_TO_SELECTED_COLLECTION }));
diff --git a/src/store/collections/collection-partial-move-actions.ts b/src/store/collections/collection-partial-move-actions.ts
index de64d99a..6d3c45f3 100644
--- a/src/store/collections/collection-partial-move-actions.ts
+++ b/src/store/collections/collection-partial-move-actions.ts
@@ -3,7 +3,7 @@
// SPDX-License-Identifier: AGPL-3.0
import { Dispatch } from "redux";
-import { initialize, startSubmit } from "redux-form";
+import { FormErrors, initialize, startSubmit, stopSubmit } from "redux-form";
import { CommonResourceServiceError, getCommonResourceServiceError } from "services/common-service/common-resource-service";
import { ServiceRepository } from "services/services";
import { filterCollectionFilesBySelection } from "store/collection-panel/collection-panel-files/collection-panel-files-state";
@@ -15,8 +15,8 @@ import { SnackbarKind, snackbarActions } from "store/snackbar/snackbar-actions";
import { RootState } from "store/store";
import { initProjectsTreePicker } from "store/tree-picker/tree-picker-actions";
-export const COLLECTION_PARTIAL_MOVE_TO_NEW_COLLECTION = 'COLLECTION_PARTIAL_MOVE_TO_NEW_COLLECTION_DIALOG';
-export const COLLECTION_PARTIAL_MOVE_TO_SELECTED_COLLECTION = 'COLLECTION_PARTIAL_MOVE_TO_SELECTED_COLLECTION_DIALOG';
+export const COLLECTION_PARTIAL_MOVE_TO_NEW_COLLECTION = 'COLLECTION_PARTIAL_MOVE_TO_NEW_DIALOG';
+export const COLLECTION_PARTIAL_MOVE_TO_SELECTED_COLLECTION = 'COLLECTION_PARTIAL_MOVE_TO_SELECTED_DIALOG';
export interface CollectionPartialMoveToNewCollectionFormData {
name: string;
@@ -25,7 +25,7 @@ export interface CollectionPartialMoveToNewCollectionFormData {
}
export interface CollectionPartialMoveToExistingCollectionFormData {
- collectionUuid: string;
+ destination: {uuid: string, path?: string};
}
export const openCollectionPartialMoveToNewCollectionDialog = () =>
@@ -98,7 +98,7 @@ export const openCollectionPartialMoveToExistingCollectionDialog = () =>
const currentCollection = getState().collectionPanel.item;
if (currentCollection) {
const initialData = {
- collectionUuid: ''
+ destination: {uuid: '', path: ''}
};
dispatch(initialize(COLLECTION_PARTIAL_MOVE_TO_SELECTED_COLLECTION, initialData));
dispatch<any>(resetPickerProjectTree());
@@ -107,13 +107,13 @@ export const openCollectionPartialMoveToExistingCollectionDialog = () =>
}
};
-export const moveCollectionPartialToExistingCollection = ({ collectionUuid }: CollectionPartialMoveToExistingCollectionFormData) =>
+export const moveCollectionPartialToExistingCollection = ({ destination }: CollectionPartialMoveToExistingCollectionFormData) =>
async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
const state = getState();
// Get current collection
const sourceCollection = state.collectionPanel.item;
- if (sourceCollection) {
+ if (sourceCollection && destination.uuid) {
try {
dispatch(startSubmit(COLLECTION_PARTIAL_MOVE_TO_SELECTED_COLLECTION));
dispatch(progressIndicatorActions.START_WORKING(COLLECTION_PARTIAL_MOVE_TO_SELECTED_COLLECTION));
@@ -122,7 +122,7 @@ export const moveCollectionPartialToExistingCollection = ({ collectionUuid }: Co
.map(file => file.id.replace(new RegExp(`(^${sourceCollection.uuid})`), ''));
// Move files
- const updatedCollection = await services.collectionService.moveFiles(sourceCollection.uuid, sourceCollection.portableDataHash, paths, {uuid: collectionUuid}, '/', false);
+ const updatedCollection = await services.collectionService.moveFiles(sourceCollection.uuid, sourceCollection.portableDataHash, paths, {uuid: destination.uuid}, destination.path || '/', false);
dispatch(updateResources([updatedCollection]));
dispatch(dialogActions.CLOSE_DIALOG({ id: COLLECTION_PARTIAL_MOVE_TO_SELECTED_COLLECTION }));
diff --git a/src/store/tree-picker/tree-picker-actions.ts b/src/store/tree-picker/tree-picker-actions.ts
index 460a23e3..b8003aa1 100644
--- a/src/store/tree-picker/tree-picker-actions.ts
+++ b/src/store/tree-picker/tree-picker-actions.ts
@@ -4,7 +4,7 @@
import { unionize, ofType, UnionOf } from "common/unionize";
import { TreeNode, initTreeNode, getNodeDescendants, TreeNodeStatus, getNode, TreePickerId, Tree } from 'models/tree';
-import { createCollectionFilesTree } from "models/collection-file";
+import { CollectionFileType, createCollectionFilesTree } from "models/collection-file";
import { Dispatch } from 'redux';
import { RootState } from 'store/store';
import { getUserUuid } from "common/getuser";
@@ -42,6 +42,7 @@ export type TreePickerAction = UnionOf<typeof treePickerActions>;
export interface LoadProjectParams {
includeCollections?: boolean;
+ includeDirectories?: boolean;
includeFiles?: boolean;
includeFilterGroups?: boolean;
options?: { showOnlyOwned: boolean; showOnlyWritable: boolean; };
@@ -123,7 +124,17 @@ interface LoadProjectParamsWithId extends LoadProjectParams {
export const loadProject = (params: LoadProjectParamsWithId) =>
async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
- const { id, pickerId, includeCollections = false, includeFiles = false, includeFilterGroups = false, loadShared = false, options, searchProjects = false } = params;
+ const {
+ id,
+ pickerId,
+ includeCollections = false,
+ includeDirectories = false,
+ includeFiles = false,
+ includeFilterGroups = false,
+ loadShared = false,
+ options,
+ searchProjects = false
+ } = params;
dispatch(treePickerActions.LOAD_TREE_PICKER_NODE({ id, pickerId }));
@@ -194,14 +205,14 @@ export const loadProject = (params: LoadProjectParamsWithId) =>
value: item,
status: item.kind === ResourceKind.PROJECT
? TreeNodeStatus.INITIAL
- : includeFiles
+ : includeDirectories || includeFiles
? TreeNodeStatus.INITIAL
: TreeNodeStatus.LOADED
}),
}));
};
-export const loadCollection = (id: string, pickerId: string) =>
+export const loadCollection = (id: string, pickerId: string, includeDirectories?: boolean, includeFiles?: boolean) =>
async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
dispatch(treePickerActions.LOAD_TREE_PICKER_NODE({ id, pickerId }));
@@ -210,7 +221,11 @@ export const loadCollection = (id: string, pickerId: string) =>
const node = getNode(id)(picker);
if (node && 'kind' in node.value && node.value.kind === ResourceKind.COLLECTION) {
- const files = await services.collectionService.files(node.value.uuid);
+ const files = (await services.collectionService.files(node.value.uuid))
+ .filter((file) => (
+ (includeFiles) ||
+ (includeDirectories && file.type === CollectionFileType.DIRECTORY)
+ ));
const tree = createCollectionFilesTree(files);
const sorted = sortFilesTree(tree);
const filesTree = mapTreeValues(services.collectionService.extendFileURL)(sorted);
@@ -244,11 +259,11 @@ export const initUserProject = (pickerId: string) =>
}));
}
};
-export const loadUserProject = (pickerId: string, includeCollections = false, includeFiles = false, options?: { showOnlyOwned: boolean, showOnlyWritable: boolean }) =>
+export const loadUserProject = (pickerId: string, includeCollections = false, includeDirectories = false, includeFiles = false, options?: { showOnlyOwned: boolean, showOnlyWritable: boolean }) =>
async (dispatch: Dispatch<any>, getState: () => RootState, services: ServiceRepository) => {
const uuid = getUserUuid(getState());
if (uuid) {
- dispatch(loadProject({ id: uuid, pickerId, includeCollections, includeFiles, options }));
+ dispatch(loadProject({ id: uuid, pickerId, includeCollections, includeDirectories, includeFiles, options }));
}
};
@@ -316,6 +331,7 @@ export const initSearchProject = (pickerId: string) =>
interface LoadFavoritesProjectParams {
pickerId: string;
includeCollections?: boolean;
+ includeDirectories?: boolean;
includeFiles?: boolean;
options?: { showOnlyOwned: boolean, showOnlyWritable: boolean };
}
@@ -323,7 +339,7 @@ interface LoadFavoritesProjectParams {
export const loadFavoritesProject = (params: LoadFavoritesProjectParams,
options: { showOnlyOwned: boolean, showOnlyWritable: boolean } = { showOnlyOwned: true, showOnlyWritable: false }) =>
async (dispatch: Dispatch<any>, getState: () => RootState, services: ServiceRepository) => {
- const { pickerId, includeCollections = false, includeFiles = false } = params;
+ const { pickerId, includeCollections = false, includeDirectories = false, includeFiles = false } = params;
const uuid = getUserUuid(getState());
if (uuid) {
const filters = pipe(
@@ -354,7 +370,7 @@ export const loadFavoritesProject = (params: LoadFavoritesProjectParams,
value: item,
status: item.kind === ResourceKind.PROJECT
? TreeNodeStatus.INITIAL
- : includeFiles
+ : includeDirectories || includeFiles
? TreeNodeStatus.INITIAL
: TreeNodeStatus.LOADED
}),
@@ -364,7 +380,7 @@ export const loadFavoritesProject = (params: LoadFavoritesProjectParams,
export const loadPublicFavoritesProject = (params: LoadFavoritesProjectParams) =>
async (dispatch: Dispatch<any>, getState: () => RootState, services: ServiceRepository) => {
- const { pickerId, includeCollections = false, includeFiles = false } = params;
+ const { pickerId, includeCollections = false, includeDirectories = false, includeFiles = false } = params;
const uuidPrefix = getState().auth.config.uuidPrefix;
const publicProjectUuid = `${uuidPrefix}-j7d0g-publicfavorites`;
@@ -395,7 +411,7 @@ export const loadPublicFavoritesProject = (params: LoadFavoritesProjectParams) =
value: item,
status: item.headKind === ResourceKind.PROJECT
? TreeNodeStatus.INITIAL
- : includeFiles
+ : includeDirectories || includeFiles
? TreeNodeStatus.INITIAL
: TreeNodeStatus.LOADED
}),
diff --git a/src/views-components/dialog-copy/dialog-collection-partial-copy-to-existing-collection.tsx b/src/views-components/dialog-copy/dialog-collection-partial-copy-to-existing-collection.tsx
index 1af9dfee..f6d4db21 100644
--- a/src/views-components/dialog-copy/dialog-collection-partial-copy-to-existing-collection.tsx
+++ b/src/views-components/dialog-copy/dialog-collection-partial-copy-to-existing-collection.tsx
@@ -9,7 +9,7 @@ import { WithDialogProps } from 'store/dialog/with-dialog';
import { InjectedFormProps } from 'redux-form';
import { CollectionPartialCopyToExistingCollectionFormData } from 'store/collections/collection-partial-copy-actions';
import { PickerIdProp } from "store/tree-picker/picker-id";
-import { CollectionPickerField } from 'views-components/form-fields/collection-form-fields';
+import { DirectoryPickerField } from 'views-components/form-fields/collection-form-fields';
type DialogCollectionPartialCopyProps = WithDialogProps<string> & InjectedFormProps<CollectionPartialCopyToExistingCollectionFormData>;
@@ -25,5 +25,5 @@ const CollectionPartialCopyFields = memoize(
(pickerId: string) =>
() =>
<>
- <CollectionPickerField {...{ pickerId }}/>
+ <DirectoryPickerField {...{ pickerId }}/>
</>);
diff --git a/src/views-components/dialog-move/dialog-collection-partial-move-to-existing-collection.tsx b/src/views-components/dialog-move/dialog-collection-partial-move-to-existing-collection.tsx
index e8081037..f95bd24f 100644
--- a/src/views-components/dialog-move/dialog-collection-partial-move-to-existing-collection.tsx
+++ b/src/views-components/dialog-move/dialog-collection-partial-move-to-existing-collection.tsx
@@ -9,7 +9,7 @@ import { WithDialogProps } from 'store/dialog/with-dialog';
import { InjectedFormProps } from 'redux-form';
import { CollectionPartialMoveToExistingCollectionFormData } from "store/collections/collection-partial-move-actions";
import { PickerIdProp } from "store/tree-picker/picker-id";
-import { CollectionPickerField } from 'views-components/form-fields/collection-form-fields';
+import { DirectoryPickerField } from 'views-components/form-fields/collection-form-fields';
type DialogCollectionPartialMoveProps = WithDialogProps<string> & InjectedFormProps<CollectionPartialMoveToExistingCollectionFormData>;
@@ -25,5 +25,5 @@ const CollectionPartialMoveFields = memoize(
(pickerId: string) =>
() =>
<>
- <CollectionPickerField {...{ pickerId }}/>
+ <DirectoryPickerField {...{ pickerId }}/>
</>);
diff --git a/src/views-components/form-fields/collection-form-fields.tsx b/src/views-components/form-fields/collection-form-fields.tsx
index 7e18111a..0faa59b5 100644
--- a/src/views-components/form-fields/collection-form-fields.tsx
+++ b/src/views-components/form-fields/collection-form-fields.tsx
@@ -9,12 +9,13 @@ import {
COLLECTION_NAME_VALIDATION, COLLECTION_NAME_VALIDATION_ALLOW_SLASH,
COLLECTION_DESCRIPTION_VALIDATION, COLLECTION_PROJECT_VALIDATION
} from "validators/validators";
-import { ProjectTreePickerField, CollectionTreePickerField } from "views-components/projects-tree-picker/tree-picker-field";
+import { ProjectTreePickerField, CollectionTreePickerField, DirectoryTreePickerField } from "views-components/projects-tree-picker/tree-picker-field";
import { PickerIdProp } from 'store/tree-picker/picker-id';
import { connect } from "react-redux";
import { RootState } from "store/store";
import { MultiCheckboxField } from "components/checkbox-field/checkbox-field";
import { getStorageClasses } from "common/config";
+import { ERROR_MESSAGE } from "validators/require";
interface CollectionNameFieldProps {
validate: Validator[];
@@ -58,6 +59,15 @@ export const CollectionPickerField = (props: PickerIdProp) =>
component={CollectionTreePickerField}
validate={COLLECTION_PROJECT_VALIDATION} />;
+const validateDirectory = (val) => (val ? undefined : ERROR_MESSAGE);
+
+export const DirectoryPickerField = (props: PickerIdProp) =>
+ <Field
+ name="destination"
+ pickerId={props.pickerId}
+ component={DirectoryTreePickerField}
+ validate={validateDirectory} />;
+
interface StorageClassesProps {
items: string[];
defaultClasses?: string[];
@@ -78,4 +88,4 @@ export const CollectionStorageClassesField = connect(
defaultValues={props.defaultClasses}
helperText='At least one class should be selected'
component={MultiCheckboxField}
- items={props.items} />);
\ No newline at end of file
+ items={props.items} />);
diff --git a/src/views-components/projects-tree-picker/favorites-tree-picker.tsx b/src/views-components/projects-tree-picker/favorites-tree-picker.tsx
index 6ab2b42d..7e63152b 100644
--- a/src/views-components/projects-tree-picker/favorites-tree-picker.tsx
+++ b/src/views-components/projects-tree-picker/favorites-tree-picker.tsx
@@ -11,7 +11,7 @@ import { loadFavoritesProject } from 'store/tree-picker/tree-picker-actions';
export const FavoritesTreePicker = connect(() => ({
rootItemIcon: FavoriteIcon,
}), (dispatch: Dispatch): Pick<ProjectsTreePickerProps, 'loadRootItem'> => ({
- loadRootItem: (_, pickerId, includeCollections, includeFiles, options) => {
- dispatch<any>(loadFavoritesProject({ pickerId, includeCollections, includeFiles }, options));
+ loadRootItem: (_, pickerId, includeCollections, includeDirectories, includeFiles, options) => {
+ dispatch<any>(loadFavoritesProject({ pickerId, includeCollections, includeDirectories, includeFiles }, options));
},
-}))(ProjectsTreePicker);
\ No newline at end of file
+}))(ProjectsTreePicker);
diff --git a/src/views-components/projects-tree-picker/generic-projects-tree-picker.tsx b/src/views-components/projects-tree-picker/generic-projects-tree-picker.tsx
index 11b51caa..1ed2a551 100644
--- a/src/views-components/projects-tree-picker/generic-projects-tree-picker.tsx
+++ b/src/views-components/projects-tree-picker/generic-projects-tree-picker.tsx
@@ -22,6 +22,7 @@ type PickedTreePickerProps = Pick<TreePickerProps<ProjectsTreePickerItem>, 'onCo
export interface ProjectsTreePickerDataProps {
includeCollections?: boolean;
+ includeDirectories?: boolean;
includeFiles?: boolean;
rootItemIcon: IconType;
showSelection?: boolean;
@@ -29,7 +30,7 @@ export interface ProjectsTreePickerDataProps {
disableActivation?: string[];
options?: { showOnlyOwned: boolean, showOnlyWritable: boolean };
loadRootItem: (item: TreeItem<ProjectsTreePickerRootItem>, pickerId: string,
- includeCollections?: boolean, includeFiles?: boolean, options?: { showOnlyOwned: boolean, showOnlyWritable: boolean }) => void;
+ includeCollections?: boolean, includeDirectories?: boolean, includeFiles?: boolean, options?: { showOnlyOwned: boolean, showOnlyWritable: boolean }) => void;
}
export type ProjectsTreePickerProps = ProjectsTreePickerDataProps & Partial<PickedTreePickerProps>;
@@ -39,7 +40,7 @@ const mapStateToProps = (_: any, { rootItemIcon, showSelection }: ProjectsTreePi
showSelection: isSelectionVisible(showSelection),
});
-const mapDispatchToProps = (dispatch: Dispatch, { loadRootItem, includeCollections, includeFiles, relatedTreePickers, options, ...props }: ProjectsTreePickerProps): PickedTreePickerProps => ({
+const mapDispatchToProps = (dispatch: Dispatch, { loadRootItem, includeCollections, includeDirectories, includeFiles, relatedTreePickers, options, ...props }: ProjectsTreePickerProps): PickedTreePickerProps => ({
onContextMenu: () => { return; },
toggleItemActive: (event, item, pickerId) => {
@@ -59,11 +60,11 @@ const mapDispatchToProps = (dispatch: Dispatch, { loadRootItem, includeCollectio
if ('kind' in data) {
dispatch<any>(
data.kind === ResourceKind.COLLECTION
- ? loadCollection(id, pickerId)
- : loadProject({ id, pickerId, includeCollections, includeFiles, options })
+ ? loadCollection(id, pickerId, includeDirectories, includeFiles)
+ : loadProject({ id, pickerId, includeCollections, includeDirectories, includeFiles, options })
);
} else if (!('type' in data) && loadRootItem) {
- loadRootItem(item as TreeItem<ProjectsTreePickerRootItem>, pickerId, includeCollections, includeFiles, options);
+ loadRootItem(item as TreeItem<ProjectsTreePickerRootItem>, pickerId, includeCollections, includeDirectories, includeFiles, options);
}
} else if (status === TreeItemStatus.LOADED) {
dispatch(treePickerActions.TOGGLE_TREE_PICKER_NODE_COLLAPSE({ id, pickerId }));
diff --git a/src/views-components/projects-tree-picker/home-tree-picker.tsx b/src/views-components/projects-tree-picker/home-tree-picker.tsx
index 3133c5db..3f71a58e 100644
--- a/src/views-components/projects-tree-picker/home-tree-picker.tsx
+++ b/src/views-components/projects-tree-picker/home-tree-picker.tsx
@@ -11,7 +11,7 @@ import { ProjectsIcon } from 'components/icon/icon';
export const HomeTreePicker = connect(() => ({
rootItemIcon: ProjectsIcon,
}), (dispatch: Dispatch): Pick<ProjectsTreePickerProps, 'loadRootItem'> => ({
- loadRootItem: (_, pickerId, includeCollections, includeFiles, options) => {
- dispatch<any>(loadUserProject(pickerId, includeCollections, includeFiles, options));
+ loadRootItem: (_, pickerId, includeCollections, includeDirectories, includeFiles, options) => {
+ dispatch<any>(loadUserProject(pickerId, includeCollections, includeDirectories, includeFiles, options));
},
}))(ProjectsTreePicker);
diff --git a/src/views-components/projects-tree-picker/projects-tree-picker.tsx b/src/views-components/projects-tree-picker/projects-tree-picker.tsx
index 9ac0b64f..1f036829 100644
--- a/src/views-components/projects-tree-picker/projects-tree-picker.tsx
+++ b/src/views-components/projects-tree-picker/projects-tree-picker.tsx
@@ -25,6 +25,7 @@ import { ArvadosTheme } from 'common/custom-theme';
export interface ToplevelPickerProps {
pickerId: string;
includeCollections?: boolean;
+ includeDirectories?: boolean;
includeFiles?: boolean;
showSelection?: boolean;
options?: { showOnlyOwned: boolean, showOnlyWritable: boolean };
@@ -55,6 +56,7 @@ const mapDispatchToProps = (dispatch: Dispatch, props: ToplevelPickerProps): (Pr
const { home, shared, favorites, publicFavorites, search } = getProjectsTreePickerIds(props.pickerId);
const params = {
includeCollections: props.includeCollections,
+ includeDirectories: props.includeDirectories,
includeFiles: props.includeFiles,
options: props.options
};
@@ -133,6 +135,7 @@ export const ProjectsTreePicker = connect(mapStateToProps, mapDispatchToProps)(
const relatedTreePickers = getRelatedTreePickers(pickerId);
const p = {
includeCollections: this.props.includeCollections,
+ includeDirectories: this.props.includeDirectories,
includeFiles: this.props.includeFiles,
showSelection: this.props.showSelection,
options: this.props.options,
diff --git a/src/views-components/projects-tree-picker/public-favorites-tree-picker.tsx b/src/views-components/projects-tree-picker/public-favorites-tree-picker.tsx
index 91551c9a..ca03f728 100644
--- a/src/views-components/projects-tree-picker/public-favorites-tree-picker.tsx
+++ b/src/views-components/projects-tree-picker/public-favorites-tree-picker.tsx
@@ -11,7 +11,7 @@ import { loadPublicFavoritesProject } from 'store/tree-picker/tree-picker-action
export const PublicFavoritesTreePicker = connect(() => ({
rootItemIcon: PublicFavoriteIcon,
}), (dispatch: Dispatch): Pick<ProjectsTreePickerProps, 'loadRootItem'> => ({
- loadRootItem: (_, pickerId, includeCollections, includeFiles, options) => {
- dispatch<any>(loadPublicFavoritesProject({ pickerId, includeCollections, includeFiles, options }));
+ loadRootItem: (_, pickerId, includeCollections, includeDirectories, includeFiles, options) => {
+ dispatch<any>(loadPublicFavoritesProject({ pickerId, includeCollections, includeDirectories, includeFiles, options }));
},
-}))(ProjectsTreePicker);
\ No newline at end of file
+}))(ProjectsTreePicker);
diff --git a/src/views-components/projects-tree-picker/search-projects-picker.tsx b/src/views-components/projects-tree-picker/search-projects-picker.tsx
index 7bad8ef7..2888050b 100644
--- a/src/views-components/projects-tree-picker/search-projects-picker.tsx
+++ b/src/views-components/projects-tree-picker/search-projects-picker.tsx
@@ -12,7 +12,7 @@ import { SEARCH_PROJECT_ID } from 'store/tree-picker/tree-picker-actions';
export const SearchProjectsPicker = connect(() => ({
rootItemIcon: SearchIcon,
}), (dispatch: Dispatch): Pick<ProjectsTreePickerProps, 'loadRootItem'> => ({
- loadRootItem: (_, pickerId, includeCollections, includeFiles, options) => {
- dispatch<any>(loadProject({ id: SEARCH_PROJECT_ID, pickerId, includeCollections, includeFiles, searchProjects: true, options }));
+ loadRootItem: (_, pickerId, includeCollections, includeDirectories, includeFiles, options) => {
+ dispatch<any>(loadProject({ id: SEARCH_PROJECT_ID, pickerId, includeCollections, includeDirectories, includeFiles, searchProjects: true, options }));
},
}))(ProjectsTreePicker);
diff --git a/src/views-components/projects-tree-picker/shared-tree-picker.tsx b/src/views-components/projects-tree-picker/shared-tree-picker.tsx
index c15df6ba..1914cd9d 100644
--- a/src/views-components/projects-tree-picker/shared-tree-picker.tsx
+++ b/src/views-components/projects-tree-picker/shared-tree-picker.tsx
@@ -12,7 +12,7 @@ import { SHARED_PROJECT_ID } from 'store/tree-picker/tree-picker-actions';
export const SharedTreePicker = connect(() => ({
rootItemIcon: ShareMeIcon,
}), (dispatch: Dispatch): Pick<ProjectsTreePickerProps, 'loadRootItem'> => ({
- loadRootItem: (_, pickerId, includeCollections, includeFiles, options) => {
- dispatch<any>(loadProject({ id: SHARED_PROJECT_ID, pickerId, includeCollections, includeFiles, loadShared: true, options }));
+ loadRootItem: (_, pickerId, includeCollections, includeDirectories, includeFiles, options) => {
+ dispatch<any>(loadProject({ id: SHARED_PROJECT_ID, pickerId, includeCollections, includeDirectories, includeFiles, loadShared: true, options }));
},
}))(ProjectsTreePicker);
diff --git a/src/views-components/projects-tree-picker/tree-picker-field.tsx b/src/views-components/projects-tree-picker/tree-picker-field.tsx
index 2afa606e..1414d18f 100644
--- a/src/views-components/projects-tree-picker/tree-picker-field.tsx
+++ b/src/views-components/projects-tree-picker/tree-picker-field.tsx
@@ -9,6 +9,8 @@ import { WrappedFieldProps } from 'redux-form';
import { ProjectsTreePicker } from 'views-components/projects-tree-picker/projects-tree-picker';
import { ProjectsTreePickerItem } from 'store/tree-picker/tree-picker-middleware';
import { PickerIdProp } from 'store/tree-picker/picker-id';
+import { CollectionFileType, getCollectionResourceCollectionUuid } from "models/collection-file";
+import { ResourceKind } from "models/resource";
export const ProjectTreePickerField = (props: WrappedFieldProps & PickerIdProp) =>
<div style={{ display: 'flex', minHeight: 0, flexDirection: 'column' }}>
@@ -42,3 +44,36 @@ export const CollectionTreePickerField = (props: WrappedFieldProps & PickerIdPro
</Typography>}
</div>
</div>;
+
+const handleDirectoryChange = (props: WrappedFieldProps) =>
+ (_: any, data: TreeItem<ProjectsTreePickerItem>) => {
+ if ('kind' in data.data && data.data.kind === ResourceKind.COLLECTION) {
+ props.input.onChange({
+ uuid: data.data.uuid,
+ path: '/'
+ });
+ } else if ('type' in data.data && data.data.type === CollectionFileType.DIRECTORY) {
+ props.input.onChange({
+ uuid: getCollectionResourceCollectionUuid(data.data.id),
+ path: [data.data.path, data.data.name].join('/')
+ });
+ } else {
+ props.input.onChange('');
+ }
+ }
+
+export const DirectoryTreePickerField = (props: WrappedFieldProps & PickerIdProp) =>
+ <div style={{ display: 'flex', minHeight: 0, flexDirection: 'column' }}>
+ <div style={{ flexBasis: '275px', flexShrink: 1, minHeight: 0, display: 'flex', flexDirection: 'column' }}>
+ <ProjectsTreePicker
+ pickerId={props.pickerId}
+ toggleItemActive={handleDirectoryChange(props)}
+ options={{ showOnlyOwned: false, showOnlyWritable: true }}
+ includeCollections
+ includeDirectories />
+ {props.meta.dirty && props.meta.error &&
+ <Typography variant='caption' color='error'>
+ {props.meta.error}
+ </Typography>}
+ </div>
+ </div>;
diff --git a/src/views/run-process-panel/inputs/file-array-input.tsx b/src/views/run-process-panel/inputs/file-array-input.tsx
index a2f884e3..1e1a4299 100644
--- a/src/views/run-process-panel/inputs/file-array-input.tsx
+++ b/src/views/run-process-panel/inputs/file-array-input.tsx
@@ -254,6 +254,7 @@ const FileArrayInputComponent = connect(mapStateToProps)(
<ProjectsTreePicker
pickerId={this.props.commandInput.id}
includeCollections
+ includeDirectories
includeFiles
showSelection
options={this.props.options}
diff --git a/src/views/run-process-panel/inputs/file-input.tsx b/src/views/run-process-panel/inputs/file-input.tsx
index b0206e14..5f48f837 100644
--- a/src/views/run-process-panel/inputs/file-input.tsx
+++ b/src/views/run-process-panel/inputs/file-input.tsx
@@ -142,6 +142,7 @@ const FileInputComponent = connect()(
<ProjectsTreePicker
pickerId={this.props.commandInput.id}
includeCollections
+ includeDirectories
includeFiles
options={this.props.options}
toggleItemActive={this.setFile} />
commit 80b9537fc9d1ae6f8c907efdf01be0505c693eb8
Author: Stephen Smith <stephen at curii.com>
Date: Wed Apr 12 09:38:27 2023 -0400
20031: Add ability to create and update collections during replace_files, moveFiles, copyFiles.
Used to optimize move/copy to new to reduce api calls
Arvados-DCO-1.1-Signed-off-by: Stephen Smith <stephen at curii.com>
diff --git a/src/services/collection-service/collection-service.test.ts b/src/services/collection-service/collection-service.test.ts
index 304cbfd3..c8e907d3 100644
--- a/src/services/collection-service/collection-service.test.ts
+++ b/src/services/collection-service/collection-service.test.ts
@@ -234,7 +234,7 @@ describe('collection-service', () => {
const destinationPath = '/destinationPath';
// when
- await collectionService.copyFiles(sourcePdh, filePaths, destinationUuid, destinationPath);
+ await collectionService.copyFiles(sourcePdh, filePaths, {uuid: destinationUuid}, destinationPath);
// then
expect(serverApi.put).toHaveBeenCalledTimes(1);
@@ -261,7 +261,7 @@ describe('collection-service', () => {
const destinationUuid = 'zzzzz-4zz18-ywq0rvhwwhkjnfq';
const destinationPath = '/destinationPath';
- await collectionService.copyFiles(sourcePdh, filePaths, destinationUuid, destinationPath);
+ await collectionService.copyFiles(sourcePdh, filePaths, {uuid: destinationUuid}, destinationPath);
expect(serverApi.put).toHaveBeenCalledTimes(1);
expect(serverApi.put).toHaveBeenCalledWith(
@@ -285,7 +285,7 @@ describe('collection-service', () => {
const destinationUuid = 'zzzzz-4zz18-ywq0rvhwwhkjnfq';
const destinationPath = '/';
- await collectionService.copyFiles(sourcePdh, filePaths, destinationUuid, destinationPath);
+ await collectionService.copyFiles(sourcePdh, filePaths, {uuid: destinationUuid}, destinationPath);
expect(serverApi.put).toHaveBeenCalledTimes(1);
expect(serverApi.put).toHaveBeenCalledWith(
@@ -313,7 +313,7 @@ describe('collection-service', () => {
const destinationPath = '/destinationPath';
// when
- await collectionService.moveFiles(srcCollectionUUID, srcCollectionPdh, filePaths, destinationUuid, destinationPath);
+ await collectionService.moveFiles(srcCollectionUUID, srcCollectionPdh, filePaths, {uuid: destinationUuid}, destinationPath);
// then
expect(serverApi.put).toHaveBeenCalledTimes(2);
@@ -357,7 +357,7 @@ describe('collection-service', () => {
const destinationPath = '/destinationPath';
// when
- await collectionService.moveFiles(srcCollectionUUID, srcCollectionPdh, filePaths, srcCollectionUUID, destinationPath);
+ await collectionService.moveFiles(srcCollectionUUID, srcCollectionPdh, filePaths, {uuid: srcCollectionUUID}, destinationPath);
// then
expect(serverApi.put).toHaveBeenCalledTimes(1);
@@ -399,7 +399,7 @@ describe('collection-service', () => {
// when
try {
- await collectionService.moveFiles(srcCollectionUUID, srcCollectionPdh, filePaths, destinationUuid, destinationPath);
+ await collectionService.moveFiles(srcCollectionUUID, srcCollectionPdh, filePaths, {uuid: destinationUuid}, destinationPath);
} catch {}
// then
diff --git a/src/services/collection-service/collection-service.ts b/src/services/collection-service/collection-service.ts
index 74cf7595..7d734fa6 100644
--- a/src/services/collection-service/collection-service.ts
+++ b/src/services/collection-service/collection-service.ts
@@ -12,8 +12,11 @@ import { TrashableResourceService } from "services/common-service/trashable-reso
import { ApiActions } from "services/api/api-actions";
import { Session } from "models/session";
import { CommonService } from "services/common-service/common-service";
+import { snakeCase } from "lodash";
export type UploadProgress = (fileId: number, loaded: number, total: number, currentTime: number) => void;
+type CollectionPartialUpdateOrCreate = Partial<CollectionResource> & Pick<CollectionResource, "uuid"> |
+ Partial<CollectionResource> & Pick<CollectionResource, "ownerUuid">;
export const emptyCollectionPdh = 'd41d8cd98f00b204e9800998ecf8427e+0';
@@ -67,21 +70,33 @@ export class CollectionService extends TrashableResourceService<CollectionResour
}, "/");
}
- private replaceFiles(collectionUuid: string, fileMap: {}, showErrors?: boolean) {
+ private replaceFiles(data: CollectionPartialUpdateOrCreate, fileMap: {}, showErrors?: boolean) {
const payload = {
collection: {
- preserve_version: true
+ preserve_version: true,
+ ...CommonService.mapKeys(snakeCase)(data),
+ // Don't send uuid in payload when creating
+ uuid: undefined,
},
replace_files: fileMap
};
-
- return CommonService.defaultResponse(
- this.serverApi
- .put<CollectionResource>(`/${this.resourceType}/${collectionUuid}`, payload),
- this.actions,
- true, // mapKeys
- showErrors
- );
+ if (data.uuid) {
+ return CommonService.defaultResponse(
+ this.serverApi
+ .put<CollectionResource>(`/${this.resourceType}/${data.uuid}`, payload),
+ this.actions,
+ true, // mapKeys
+ showErrors
+ );
+ } else {
+ return CommonService.defaultResponse(
+ this.serverApi
+ .post<CollectionResource>(`/${this.resourceType}`, payload),
+ this.actions,
+ true, // mapKeys
+ showErrors
+ );
+ }
}
async uploadFiles(collectionUuid: string, files: File[], onProgress?: UploadProgress, targetLocation: string = '') {
@@ -94,7 +109,7 @@ export class CollectionService extends TrashableResourceService<CollectionResour
}
async renameFile(collectionUuid: string, collectionPdh: string, oldPath: string, newPath: string) {
- return this.replaceFiles(collectionUuid, {
+ return this.replaceFiles({uuid: collectionUuid}, {
[this.combineFilePath([newPath])]: `${collectionPdh}${this.combineFilePath([oldPath])}`,
[this.combineFilePath([oldPath])]: '',
});
@@ -152,10 +167,10 @@ export class CollectionService extends TrashableResourceService<CollectionResour
}
}, {})
- return this.replaceFiles(collectionUuid, fileMap, showErrors);
+ return this.replaceFiles({uuid: collectionUuid}, fileMap, showErrors);
}
- copyFiles(sourcePdh: string, files: string[], destinationCollectionUuid: string, destinationPath: string, showErrors?: boolean) {
+ copyFiles(sourcePdh: string, files: string[], destinationCollection: CollectionPartialUpdateOrCreate, destinationPath: string, showErrors?: boolean) {
const fileMap = files.reduce((obj, sourceFile) => {
const sourceFileName = sourceFile.split('/').filter(Boolean).slice(-1).join("");
return {
@@ -164,11 +179,11 @@ export class CollectionService extends TrashableResourceService<CollectionResour
};
}, {});
- return this.replaceFiles(destinationCollectionUuid, fileMap, showErrors);
+ return this.replaceFiles(destinationCollection, fileMap, showErrors);
}
- moveFiles(sourceUuid: string, sourcePdh: string, files: string[], destinationCollectionUuid: string, destinationPath: string, showErrors?: boolean) {
- if (sourceUuid === destinationCollectionUuid) {
+ moveFiles(sourceUuid: string, sourcePdh: string, files: string[], destinationCollection: CollectionPartialUpdateOrCreate, destinationPath: string, showErrors?: boolean) {
+ if (sourceUuid === destinationCollection.uuid) {
const fileMap = files.reduce((obj, sourceFile) => {
const sourceFileName = sourceFile.split('/').filter(Boolean).slice(-1).join("");
return {
@@ -178,9 +193,9 @@ export class CollectionService extends TrashableResourceService<CollectionResour
};
}, {});
- return this.replaceFiles(sourceUuid, fileMap, showErrors)
+ return this.replaceFiles({uuid: sourceUuid}, fileMap, showErrors)
} else {
- return this.copyFiles(sourcePdh, files, destinationCollectionUuid, destinationPath, showErrors)
+ return this.copyFiles(sourcePdh, files, destinationCollection, destinationPath, showErrors)
.then(() => {
return this.deleteFiles(sourceUuid, files, showErrors);
});
@@ -190,7 +205,7 @@ export class CollectionService extends TrashableResourceService<CollectionResour
createDirectory(collectionUuid: string, path: string, showErrors?: boolean) {
const fileMap = {[this.combineFilePath([path])]: emptyCollectionPdh};
- return this.replaceFiles(collectionUuid, fileMap, showErrors);
+ return this.replaceFiles({uuid: collectionUuid}, fileMap, showErrors);
}
}
diff --git a/src/store/collections/collection-partial-copy-actions.ts b/src/store/collections/collection-partial-copy-actions.ts
index cccd7023..359b6b83 100644
--- a/src/store/collections/collection-partial-copy-actions.ts
+++ b/src/store/collections/collection-partial-copy-actions.ts
@@ -55,20 +55,23 @@ export const copyCollectionPartialToNewCollection = ({ name, description, projec
dispatch(startSubmit(COLLECTION_PARTIAL_COPY_FORM_NAME));
dispatch(progressIndicatorActions.START_WORKING(COLLECTION_PARTIAL_COPY_FORM_NAME));
- // Create new collection
- const newCollection = await services.collectionService.create({
- name,
- description,
- ownerUuid: projectUuid,
- uuid: undefined,
- });
-
// Get selected files
const paths = filterCollectionFilesBySelection(state.collectionPanelFiles, true)
.map(file => file.id.replace(new RegExp(`(^${sourceCollection.uuid})`), ''));
// Copy files
- const updatedCollection = await services.collectionService.copyFiles(sourceCollection.portableDataHash, paths, newCollection.uuid, '/', false);
+ const updatedCollection = await services.collectionService.copyFiles(
+ sourceCollection.portableDataHash,
+ paths,
+ {
+ name,
+ description,
+ ownerUuid: projectUuid,
+ uuid: undefined,
+ },
+ '/',
+ false
+ );
dispatch(updateResources([updatedCollection]));
dispatch(dialogActions.CLOSE_DIALOG({ id: COLLECTION_PARTIAL_COPY_FORM_NAME }));
@@ -123,7 +126,7 @@ export const copyCollectionPartialToExistingCollection = ({ collectionUuid }: Co
.map(file => file.id.replace(new RegExp(`(^${sourceCollection.uuid})`), ''));
// Copy files
- const updatedCollection = await services.collectionService.copyFiles(sourceCollection.portableDataHash, paths, collectionUuid, '/', false);
+ const updatedCollection = await services.collectionService.copyFiles(sourceCollection.portableDataHash, paths, {uuid: collectionUuid}, '/', false);
dispatch(updateResources([updatedCollection]));
dispatch(dialogActions.CLOSE_DIALOG({ id: COLLECTION_PARTIAL_COPY_TO_SELECTED_COLLECTION }));
diff --git a/src/store/collections/collection-partial-move-actions.ts b/src/store/collections/collection-partial-move-actions.ts
index 19cac52d..de64d99a 100644
--- a/src/store/collections/collection-partial-move-actions.ts
+++ b/src/store/collections/collection-partial-move-actions.ts
@@ -55,20 +55,24 @@ export const moveCollectionPartialToNewCollection = ({ name, description, projec
dispatch(startSubmit(COLLECTION_PARTIAL_MOVE_TO_NEW_COLLECTION));
dispatch(progressIndicatorActions.START_WORKING(COLLECTION_PARTIAL_MOVE_TO_NEW_COLLECTION));
- // Create new collection
- const newCollection = await services.collectionService.create({
- name,
- description,
- ownerUuid: projectUuid,
- uuid: undefined,
- });
-
// Get selected files
const paths = filterCollectionFilesBySelection(state.collectionPanelFiles, true)
.map(file => file.id.replace(new RegExp(`(^${sourceCollection.uuid})`), ''));
// Move files
- const updatedCollection = await services.collectionService.moveFiles(sourceCollection.uuid, sourceCollection.portableDataHash, paths, newCollection.uuid, '/', false);
+ const updatedCollection = await services.collectionService.moveFiles(
+ sourceCollection.uuid,
+ sourceCollection.portableDataHash,
+ paths,
+ {
+ name,
+ description,
+ ownerUuid: projectUuid,
+ uuid: undefined,
+ },
+ '/',
+ false
+ );
dispatch(updateResources([updatedCollection]));
dispatch(dialogActions.CLOSE_DIALOG({ id: COLLECTION_PARTIAL_MOVE_TO_NEW_COLLECTION }));
@@ -118,7 +122,7 @@ export const moveCollectionPartialToExistingCollection = ({ collectionUuid }: Co
.map(file => file.id.replace(new RegExp(`(^${sourceCollection.uuid})`), ''));
// Move files
- const updatedCollection = await services.collectionService.moveFiles(sourceCollection.uuid, sourceCollection.portableDataHash, paths, collectionUuid, '/', false);
+ const updatedCollection = await services.collectionService.moveFiles(sourceCollection.uuid, sourceCollection.portableDataHash, paths, {uuid: collectionUuid}, '/', false);
dispatch(updateResources([updatedCollection]));
dispatch(dialogActions.CLOSE_DIALOG({ id: COLLECTION_PARTIAL_MOVE_TO_SELECTED_COLLECTION }));
commit b6c145e81728b3916370000a83648280bbfb94d2
Author: Stephen Smith <stephen at curii.com>
Date: Mon Apr 10 15:29:32 2023 -0400
20031: Use replace files api for copy to new collection
Arvados-DCO-1.1-Signed-off-by: Stephen Smith <stephen at curii.com>
diff --git a/src/store/collections/collection-partial-copy-actions.ts b/src/store/collections/collection-partial-copy-actions.ts
index 73d117c2..cccd7023 100644
--- a/src/store/collections/collection-partial-copy-actions.ts
+++ b/src/store/collections/collection-partial-copy-actions.ts
@@ -46,30 +46,31 @@ export const openCollectionPartialCopyToNewCollectionDialog = () =>
export const copyCollectionPartialToNewCollection = ({ name, description, projectUuid }: CollectionPartialCopyToNewCollectionFormData) =>
async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
- dispatch(startSubmit(COLLECTION_PARTIAL_COPY_FORM_NAME));
const state = getState();
- const currentCollection = state.collectionPanel.item;
- if (currentCollection) {
+ // Get current collection
+ const sourceCollection = state.collectionPanel.item;
+
+ if (sourceCollection) {
try {
+ dispatch(startSubmit(COLLECTION_PARTIAL_COPY_FORM_NAME));
dispatch(progressIndicatorActions.START_WORKING(COLLECTION_PARTIAL_COPY_FORM_NAME));
- const collectionManifestText = await services.collectionService.get(currentCollection.uuid, undefined, ['manifestText']);
- const collectionCopy = {
+
+ // Create new collection
+ const newCollection = await services.collectionService.create({
name,
description,
ownerUuid: projectUuid,
uuid: undefined,
- manifestText: collectionManifestText.manifestText,
- };
- const newCollection = await services.collectionService.create(collectionCopy);
- const copiedFiles = await services.collectionService.files(newCollection.uuid);
- const paths = filterCollectionFilesBySelection(state.collectionPanelFiles, true).map(file => file.id);
- const filesToDelete = copiedFiles.map(({ id }) => id).filter(file => {
- return !paths.find(path => path.indexOf(file.replace(newCollection.uuid, '')) > -1);
});
- await services.collectionService.deleteFiles(
- newCollection.uuid,
- filesToDelete
- );
+
+ // Get selected files
+ const paths = filterCollectionFilesBySelection(state.collectionPanelFiles, true)
+ .map(file => file.id.replace(new RegExp(`(^${sourceCollection.uuid})`), ''));
+
+ // Copy files
+ const updatedCollection = await services.collectionService.copyFiles(sourceCollection.portableDataHash, paths, newCollection.uuid, '/', false);
+ dispatch(updateResources([updatedCollection]));
+
dispatch(dialogActions.CLOSE_DIALOG({ id: COLLECTION_PARTIAL_COPY_FORM_NAME }));
dispatch(snackbarActions.OPEN_SNACKBAR({
message: 'New collection created.',
commit c6b3db3a8e2fcb7fe8d44cdd60e12fe7e2be571f
Author: Stephen Smith <stephen at curii.com>
Date: Mon Apr 10 15:16:39 2023 -0400
20031: Add collection partial move/copy to new/existing collection
Arvados-DCO-1.1-Signed-off-by: Stephen Smith <stephen at curii.com>
diff --git a/cypress/integration/collection.spec.js b/cypress/integration/collection.spec.js
index 1a66600c..977a28c2 100644
--- a/cypress/integration/collection.spec.js
+++ b/cypress/integration/collection.spec.js
@@ -922,7 +922,7 @@ describe('Collection panel tests', function () {
});
cy.get('[data-cy=collection-files-panel-options-btn]').click();
- cy.get('[data-cy=context-menu]').contains('Create a new collection with selected').click();
+ cy.get('[data-cy=context-menu]').contains('Copy selected into new collection').click();
cy.get('[data-cy=form-dialog]').contains('Projects').click();
diff --git a/src/store/collection-panel/collection-panel-files/collection-panel-files-state.ts b/src/store/collection-panel/collection-panel-files/collection-panel-files-state.ts
index 0044a66d..405e7eac 100644
--- a/src/store/collection-panel/collection-panel-files/collection-panel-files-state.ts
+++ b/src/store/collection-panel/collection-panel-files/collection-panel-files-state.ts
@@ -40,5 +40,8 @@ export const filterCollectionFilesBySelection = (tree: CollectionPanelFilesState
const allFiles = getNodeDescendants('')(tree).map(node => node.value);
const selectedDirectories = allFiles.filter(file => file.selected === selected && file.type === CollectionFileType.DIRECTORY);
const selectedFiles = allFiles.filter(file => file.selected === selected && !selectedDirectories.some(dir => dir.id === file.path));
- return [...selectedDirectories, ...selectedFiles];
+ return [...selectedDirectories, ...selectedFiles]
+ .filter((value, index, array) => (
+ array.indexOf(value) === index
+ ));
};
diff --git a/src/store/collections/collection-partial-copy-actions.ts b/src/store/collections/collection-partial-copy-actions.ts
index a0c4e4e4..73d117c2 100644
--- a/src/store/collections/collection-partial-copy-actions.ts
+++ b/src/store/collections/collection-partial-copy-actions.ts
@@ -3,7 +3,6 @@
// SPDX-License-Identifier: AGPL-3.0
import { Dispatch } from 'redux';
-import { difference } from "lodash";
import { RootState } from 'store/store';
import { FormErrors, initialize, startSubmit, stopSubmit } from 'redux-form';
import { resetPickerProjectTree } from 'store/project-tree-picker/project-tree-picker-actions';
@@ -14,21 +13,22 @@ import { snackbarActions, SnackbarKind } from 'store/snackbar/snackbar-actions';
import { getCommonResourceServiceError, CommonResourceServiceError } from 'services/common-service/common-resource-service';
import { progressIndicatorActions } from "store/progress-indicator/progress-indicator-actions";
import { initProjectsTreePicker } from "store/tree-picker/tree-picker-actions";
+import { updateResources } from 'store/resources/resources-actions';
export const COLLECTION_PARTIAL_COPY_FORM_NAME = 'COLLECTION_PARTIAL_COPY_DIALOG';
export const COLLECTION_PARTIAL_COPY_TO_SELECTED_COLLECTION = 'COLLECTION_PARTIAL_COPY_TO_SELECTED_DIALOG';
-export interface CollectionPartialCopyFormData {
+export interface CollectionPartialCopyToNewCollectionFormData {
name: string;
description: string;
projectUuid: string;
}
-export interface CollectionPartialCopyToSelectedCollectionFormData {
+export interface CollectionPartialCopyToExistingCollectionFormData {
collectionUuid: string;
}
-export const openCollectionPartialCopyDialog = () =>
+export const openCollectionPartialCopyToNewCollectionDialog = () =>
(dispatch: Dispatch, getState: () => RootState) => {
const currentCollection = getState().collectionPanel.item;
if (currentCollection) {
@@ -44,7 +44,7 @@ export const openCollectionPartialCopyDialog = () =>
}
};
-export const copyCollectionPartial = ({ name, description, projectUuid }: CollectionPartialCopyFormData) =>
+export const copyCollectionPartialToNewCollection = ({ name, description, projectUuid }: CollectionPartialCopyToNewCollectionFormData) =>
async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
dispatch(startSubmit(COLLECTION_PARTIAL_COPY_FORM_NAME));
const state = getState();
@@ -93,7 +93,7 @@ export const copyCollectionPartial = ({ name, description, projectUuid }: Collec
}
};
-export const openCollectionPartialCopyToSelectedCollectionDialog = () =>
+export const openCollectionPartialCopyToExistingCollectionDialog = () =>
(dispatch: Dispatch, getState: () => RootState) => {
const currentCollection = getState().collectionPanel.item;
if (currentCollection) {
@@ -107,37 +107,24 @@ export const openCollectionPartialCopyToSelectedCollectionDialog = () =>
}
};
-export const copyCollectionPartialToSelectedCollection = ({ collectionUuid }: CollectionPartialCopyToSelectedCollectionFormData) =>
+export const copyCollectionPartialToExistingCollection = ({ collectionUuid }: CollectionPartialCopyToExistingCollectionFormData) =>
async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
- dispatch(startSubmit(COLLECTION_PARTIAL_COPY_TO_SELECTED_COLLECTION));
const state = getState();
- const currentCollection = state.collectionPanel.item;
-
- if (currentCollection && !currentCollection.manifestText) {
- const fetchedCurrentCollection = await services.collectionService.get(currentCollection.uuid, undefined, ['manifestText']);
- currentCollection.manifestText = fetchedCurrentCollection.manifestText;
- currentCollection.unsignedManifestText = fetchedCurrentCollection.unsignedManifestText;
- }
+ // Get current collection
+ const sourceCollection = state.collectionPanel.item;
- if (currentCollection) {
+ if (sourceCollection) {
try {
+ dispatch(startSubmit(COLLECTION_PARTIAL_COPY_TO_SELECTED_COLLECTION));
dispatch(progressIndicatorActions.START_WORKING(COLLECTION_PARTIAL_COPY_TO_SELECTED_COLLECTION));
- const selectedCollection = await services.collectionService.get(collectionUuid);
- const paths = filterCollectionFilesBySelection(state.collectionPanelFiles, false).map(file => file.id);
- const pathsToRemove = paths.filter(path => {
- const a = path.split('/');
- const fileExistsInSelectedCollection = selectedCollection.manifestText.includes(a[1]);
- if (fileExistsInSelectedCollection) {
- return path;
- } else {
- return null;
- }
- });
- const diffPathToRemove = difference(paths, pathsToRemove);
- await services.collectionService.deleteFiles(selectedCollection.uuid, pathsToRemove.map(path => path.replace(currentCollection.uuid, collectionUuid)));
- const collectionWithDeletedFiles = await services.collectionService.get(collectionUuid, undefined, ['uuid', 'manifestText']);
- await services.collectionService.update(collectionUuid, { manifestText: `${collectionWithDeletedFiles.manifestText}${(currentCollection.manifestText ? currentCollection.manifestText : currentCollection.unsignedManifestText) || ''}` });
- await services.collectionService.deleteFiles(collectionWithDeletedFiles.uuid, diffPathToRemove.map(path => path.replace(currentCollection.uuid, collectionUuid)));
+ // Get selected files
+ const paths = filterCollectionFilesBySelection(state.collectionPanelFiles, true)
+ .map(file => file.id.replace(new RegExp(`(^${sourceCollection.uuid})`), ''));
+
+ // Copy files
+ const updatedCollection = await services.collectionService.copyFiles(sourceCollection.portableDataHash, paths, collectionUuid, '/', false);
+ dispatch(updateResources([updatedCollection]));
+
dispatch(dialogActions.CLOSE_DIALOG({ id: COLLECTION_PARTIAL_COPY_TO_SELECTED_COLLECTION }));
dispatch(snackbarActions.OPEN_SNACKBAR({
message: 'Files has been copied to selected collection.',
@@ -154,4 +141,4 @@ export const copyCollectionPartialToSelectedCollection = ({ collectionUuid }: Co
dispatch(progressIndicatorActions.STOP_WORKING(COLLECTION_PARTIAL_COPY_TO_SELECTED_COLLECTION));
}
}
- };
\ No newline at end of file
+ };
diff --git a/src/store/collections/collection-partial-move-actions.ts b/src/store/collections/collection-partial-move-actions.ts
new file mode 100644
index 00000000..19cac52d
--- /dev/null
+++ b/src/store/collections/collection-partial-move-actions.ts
@@ -0,0 +1,140 @@
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+import { Dispatch } from "redux";
+import { initialize, startSubmit } from "redux-form";
+import { CommonResourceServiceError, getCommonResourceServiceError } from "services/common-service/common-resource-service";
+import { ServiceRepository } from "services/services";
+import { filterCollectionFilesBySelection } from "store/collection-panel/collection-panel-files/collection-panel-files-state";
+import { dialogActions } from "store/dialog/dialog-actions";
+import { progressIndicatorActions } from "store/progress-indicator/progress-indicator-actions";
+import { resetPickerProjectTree } from "store/project-tree-picker/project-tree-picker-actions";
+import { updateResources } from "store/resources/resources-actions";
+import { SnackbarKind, snackbarActions } from "store/snackbar/snackbar-actions";
+import { RootState } from "store/store";
+import { initProjectsTreePicker } from "store/tree-picker/tree-picker-actions";
+
+export const COLLECTION_PARTIAL_MOVE_TO_NEW_COLLECTION = 'COLLECTION_PARTIAL_MOVE_TO_NEW_COLLECTION_DIALOG';
+export const COLLECTION_PARTIAL_MOVE_TO_SELECTED_COLLECTION = 'COLLECTION_PARTIAL_MOVE_TO_SELECTED_COLLECTION_DIALOG';
+
+export interface CollectionPartialMoveToNewCollectionFormData {
+ name: string;
+ description: string;
+ projectUuid: string;
+}
+
+export interface CollectionPartialMoveToExistingCollectionFormData {
+ collectionUuid: string;
+}
+
+export const openCollectionPartialMoveToNewCollectionDialog = () =>
+ (dispatch: Dispatch, getState: () => RootState) => {
+ const currentCollection = getState().collectionPanel.item;
+ if (currentCollection) {
+ const initialData = {
+ name: `Files moved from: ${currentCollection.name}`,
+ description: currentCollection.description,
+ projectUuid: undefined
+ };
+ dispatch(initialize(COLLECTION_PARTIAL_MOVE_TO_NEW_COLLECTION, initialData));
+ dispatch<any>(resetPickerProjectTree());
+ dispatch<any>(initProjectsTreePicker(COLLECTION_PARTIAL_MOVE_TO_NEW_COLLECTION));
+ dispatch(dialogActions.OPEN_DIALOG({ id: COLLECTION_PARTIAL_MOVE_TO_NEW_COLLECTION, data: {} }));
+ }
+ };
+
+export const moveCollectionPartialToNewCollection = ({ name, description, projectUuid }: CollectionPartialMoveToNewCollectionFormData) =>
+ async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
+ const state = getState();
+ // Get current collection
+ const sourceCollection = state.collectionPanel.item;
+
+ if (sourceCollection) {
+ try {
+ dispatch(startSubmit(COLLECTION_PARTIAL_MOVE_TO_NEW_COLLECTION));
+ dispatch(progressIndicatorActions.START_WORKING(COLLECTION_PARTIAL_MOVE_TO_NEW_COLLECTION));
+
+ // Create new collection
+ const newCollection = await services.collectionService.create({
+ name,
+ description,
+ ownerUuid: projectUuid,
+ uuid: undefined,
+ });
+
+ // Get selected files
+ const paths = filterCollectionFilesBySelection(state.collectionPanelFiles, true)
+ .map(file => file.id.replace(new RegExp(`(^${sourceCollection.uuid})`), ''));
+
+ // Move files
+ const updatedCollection = await services.collectionService.moveFiles(sourceCollection.uuid, sourceCollection.portableDataHash, paths, newCollection.uuid, '/', false);
+ dispatch(updateResources([updatedCollection]));
+
+ dispatch(dialogActions.CLOSE_DIALOG({ id: COLLECTION_PARTIAL_MOVE_TO_NEW_COLLECTION }));
+ dispatch(snackbarActions.OPEN_SNACKBAR({
+ message: 'Files have been moved to selected collection.',
+ hideDuration: 2000,
+ kind: SnackbarKind.SUCCESS
+ }));
+ dispatch(progressIndicatorActions.STOP_WORKING(COLLECTION_PARTIAL_MOVE_TO_NEW_COLLECTION));
+ } catch (e) {
+ const error = getCommonResourceServiceError(e);
+ if (error === CommonResourceServiceError.UNKNOWN) {
+ dispatch(dialogActions.CLOSE_DIALOG({ id: COLLECTION_PARTIAL_MOVE_TO_NEW_COLLECTION }));
+ dispatch(snackbarActions.OPEN_SNACKBAR({ message: 'Could not move files to selected collection', hideDuration: 2000, kind: SnackbarKind.ERROR }));
+ }
+ dispatch(progressIndicatorActions.STOP_WORKING(COLLECTION_PARTIAL_MOVE_TO_NEW_COLLECTION));
+ }
+ }
+ };
+
+export const openCollectionPartialMoveToExistingCollectionDialog = () =>
+ (dispatch: Dispatch, getState: () => RootState) => {
+ const currentCollection = getState().collectionPanel.item;
+ if (currentCollection) {
+ const initialData = {
+ collectionUuid: ''
+ };
+ dispatch(initialize(COLLECTION_PARTIAL_MOVE_TO_SELECTED_COLLECTION, initialData));
+ dispatch<any>(resetPickerProjectTree());
+ dispatch<any>(initProjectsTreePicker(COLLECTION_PARTIAL_MOVE_TO_SELECTED_COLLECTION));
+ dispatch(dialogActions.OPEN_DIALOG({ id: COLLECTION_PARTIAL_MOVE_TO_SELECTED_COLLECTION, data: {} }));
+ }
+ };
+
+export const moveCollectionPartialToExistingCollection = ({ collectionUuid }: CollectionPartialMoveToExistingCollectionFormData) =>
+ async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
+ const state = getState();
+ // Get current collection
+ const sourceCollection = state.collectionPanel.item;
+
+ if (sourceCollection) {
+ try {
+ dispatch(startSubmit(COLLECTION_PARTIAL_MOVE_TO_SELECTED_COLLECTION));
+ dispatch(progressIndicatorActions.START_WORKING(COLLECTION_PARTIAL_MOVE_TO_SELECTED_COLLECTION));
+ // Get selected files
+ const paths = filterCollectionFilesBySelection(state.collectionPanelFiles, true)
+ .map(file => file.id.replace(new RegExp(`(^${sourceCollection.uuid})`), ''));
+
+ // Move files
+ const updatedCollection = await services.collectionService.moveFiles(sourceCollection.uuid, sourceCollection.portableDataHash, paths, collectionUuid, '/', false);
+ dispatch(updateResources([updatedCollection]));
+
+ dispatch(dialogActions.CLOSE_DIALOG({ id: COLLECTION_PARTIAL_MOVE_TO_SELECTED_COLLECTION }));
+ dispatch(snackbarActions.OPEN_SNACKBAR({
+ message: 'Files have been moved to selected collection.',
+ hideDuration: 2000,
+ kind: SnackbarKind.SUCCESS
+ }));
+ dispatch(progressIndicatorActions.STOP_WORKING(COLLECTION_PARTIAL_MOVE_TO_SELECTED_COLLECTION));
+ } catch (e) {
+ const error = getCommonResourceServiceError(e);
+ if (error === CommonResourceServiceError.UNKNOWN) {
+ dispatch(dialogActions.CLOSE_DIALOG({ id: COLLECTION_PARTIAL_MOVE_TO_SELECTED_COLLECTION }));
+ dispatch(snackbarActions.OPEN_SNACKBAR({ message: 'Could not copy this files to selected collection', hideDuration: 2000, kind: SnackbarKind.ERROR }));
+ }
+ dispatch(progressIndicatorActions.STOP_WORKING(COLLECTION_PARTIAL_MOVE_TO_SELECTED_COLLECTION));
+ }
+ }
+ };
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 f34f2868..3e6e1a20 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
@@ -5,10 +5,10 @@
import { ContextMenuActionSet } from "views-components/context-menu/context-menu-action-set";
import { collectionPanelFilesAction, openMultipleFilesRemoveDialog } from "store/collection-panel/collection-panel-files/collection-panel-files-actions";
import {
- openCollectionPartialCopyDialog,
- // Disabled while addressing #18587
- // openCollectionPartialCopyToSelectedCollectionDialog
+ openCollectionPartialCopyToNewCollectionDialog,
+ openCollectionPartialCopyToExistingCollectionDialog
} from 'store/collections/collection-partial-copy-actions';
+import { openCollectionPartialMoveToExistingCollectionDialog, openCollectionPartialMoveToNewCollectionDialog } from "store/collections/collection-partial-move-actions";
// These action sets are used on the multi-select actions button.
export const readOnlyCollectionFilesActionSet: ContextMenuActionSet = [[
@@ -25,18 +25,17 @@ export const readOnlyCollectionFilesActionSet: ContextMenuActionSet = [[
}
},
{
- name: "Create a new collection with selected",
+ name: "Copy selected into new collection",
execute: dispatch => {
- dispatch<any>(openCollectionPartialCopyDialog());
+ dispatch<any>(openCollectionPartialCopyToNewCollectionDialog());
}
},
- // Disabled while addressing #18587
- // {
- // name: "Copy selected into the collection",
- // execute: dispatch => {
- // dispatch<any>(openCollectionPartialCopyToSelectedCollectionDialog());
- // }
- // }
+ {
+ name: "Copy selected into existing collection",
+ execute: dispatch => {
+ dispatch<any>(openCollectionPartialCopyToExistingCollectionDialog());
+ }
+ }
]];
export const collectionFilesActionSet: ContextMenuActionSet = readOnlyCollectionFilesActionSet.concat([[
@@ -46,4 +45,16 @@ export const collectionFilesActionSet: ContextMenuActionSet = readOnlyCollection
dispatch(openMultipleFilesRemoveDialog());
}
},
+ {
+ name: "Move selected into new collection",
+ execute: dispatch => {
+ dispatch<any>(openCollectionPartialMoveToNewCollectionDialog());
+ }
+ },
+ {
+ name: "Move selected into existing collection",
+ execute: dispatch => {
+ dispatch<any>(openCollectionPartialMoveToExistingCollectionDialog());
+ }
+ }
]]);
diff --git a/src/views-components/dialog-copy/dialog-partial-copy-to-collection.tsx b/src/views-components/dialog-copy/dialog-collection-partial-copy-to-existing-collection.tsx
similarity index 76%
copy from src/views-components/dialog-copy/dialog-partial-copy-to-collection.tsx
copy to src/views-components/dialog-copy/dialog-collection-partial-copy-to-existing-collection.tsx
index 4e9dde6a..1af9dfee 100644
--- a/src/views-components/dialog-copy/dialog-partial-copy-to-collection.tsx
+++ b/src/views-components/dialog-copy/dialog-collection-partial-copy-to-existing-collection.tsx
@@ -7,21 +7,21 @@ import { memoize } from "lodash/fp";
import { FormDialog } from 'components/form-dialog/form-dialog';
import { WithDialogProps } from 'store/dialog/with-dialog';
import { InjectedFormProps } from 'redux-form';
-import { CollectionPartialCopyToSelectedCollectionFormData } from 'store/collections/collection-partial-copy-actions';
+import { CollectionPartialCopyToExistingCollectionFormData } from 'store/collections/collection-partial-copy-actions';
import { PickerIdProp } from "store/tree-picker/picker-id";
import { CollectionPickerField } from 'views-components/form-fields/collection-form-fields';
-type DialogCollectionPartialCopyProps = WithDialogProps<string> & InjectedFormProps<CollectionPartialCopyToSelectedCollectionFormData>;
+type DialogCollectionPartialCopyProps = WithDialogProps<string> & InjectedFormProps<CollectionPartialCopyToExistingCollectionFormData>;
-export const DialogCollectionPartialCopyToSelectedCollection = (props: DialogCollectionPartialCopyProps & PickerIdProp) =>
+export const DialogCollectionPartialCopyToExistingCollection = (props: DialogCollectionPartialCopyProps & PickerIdProp) =>
<FormDialog
- dialogTitle='Choose collection'
+ dialogTitle='Copy to existing collection'
formFields={CollectionPartialCopyFields(props.pickerId)}
submitLabel='Copy files'
{...props}
/>;
-export const CollectionPartialCopyFields = memoize(
+const CollectionPartialCopyFields = memoize(
(pickerId: string) =>
() =>
<>
diff --git a/src/views-components/dialog-copy/dialog-collection-partial-copy.tsx b/src/views-components/dialog-copy/dialog-collection-partial-copy-to-new-collection.tsx
similarity index 68%
copy from src/views-components/dialog-copy/dialog-collection-partial-copy.tsx
copy to src/views-components/dialog-copy/dialog-collection-partial-copy-to-new-collection.tsx
index 3c584e4f..6b5a7759 100644
--- a/src/views-components/dialog-copy/dialog-collection-partial-copy.tsx
+++ b/src/views-components/dialog-copy/dialog-collection-partial-copy-to-new-collection.tsx
@@ -8,20 +8,20 @@ import { FormDialog } from 'components/form-dialog/form-dialog';
import { CollectionNameField, CollectionDescriptionField, CollectionProjectPickerField } from 'views-components/form-fields/collection-form-fields';
import { WithDialogProps } from 'store/dialog/with-dialog';
import { InjectedFormProps } from 'redux-form';
-import { CollectionPartialCopyFormData } from 'store/collections/collection-partial-copy-actions';
+import { CollectionPartialCopyToNewCollectionFormData } from 'store/collections/collection-partial-copy-actions';
import { PickerIdProp } from "store/tree-picker/picker-id";
-type DialogCollectionPartialCopyProps = WithDialogProps<string> & InjectedFormProps<CollectionPartialCopyFormData>;
+type DialogCollectionPartialCopyProps = WithDialogProps<string> & InjectedFormProps<CollectionPartialCopyToNewCollectionFormData>;
-export const DialogCollectionPartialCopy = (props: DialogCollectionPartialCopyProps & PickerIdProp) =>
+export const DialogCollectionPartialCopyToNewCollection = (props: DialogCollectionPartialCopyProps & PickerIdProp) =>
<FormDialog
- dialogTitle='Create a collection'
+ dialogTitle='Copy to new collection'
formFields={CollectionPartialCopyFields(props.pickerId)}
- submitLabel='Create a collection'
+ submitLabel='Create collection'
{...props}
/>;
-export const CollectionPartialCopyFields = memoize(
+const CollectionPartialCopyFields = memoize(
(pickerId: string) =>
() =>
<>
diff --git a/src/views-components/dialog-forms/partial-copy-collection-dialog.ts b/src/views-components/dialog-forms/partial-copy-collection-dialog.ts
deleted file mode 100644
index 3630ffb7..00000000
--- a/src/views-components/dialog-forms/partial-copy-collection-dialog.ts
+++ /dev/null
@@ -1,22 +0,0 @@
-// Copyright (C) The Arvados Authors. All rights reserved.
-//
-// SPDX-License-Identifier: AGPL-3.0
-
-import { compose } from "redux";
-import { reduxForm } from 'redux-form';
-import { withDialog, } from 'store/dialog/with-dialog';
-import { CollectionPartialCopyFormData, copyCollectionPartial, COLLECTION_PARTIAL_COPY_FORM_NAME } from 'store/collections/collection-partial-copy-actions';
-import { DialogCollectionPartialCopy } from "views-components/dialog-copy/dialog-collection-partial-copy";
-import { pickerId } from "store/tree-picker/picker-id";
-
-
-export const PartialCopyCollectionDialog = compose(
- withDialog(COLLECTION_PARTIAL_COPY_FORM_NAME),
- reduxForm<CollectionPartialCopyFormData>({
- form: COLLECTION_PARTIAL_COPY_FORM_NAME,
- onSubmit: (data, dispatch) => {
- dispatch(copyCollectionPartial(data));
- }
- }),
- pickerId(COLLECTION_PARTIAL_COPY_FORM_NAME),
-)(DialogCollectionPartialCopy);
\ No newline at end of file
diff --git a/src/views-components/dialog-forms/partial-copy-to-collection-dialog.ts b/src/views-components/dialog-forms/partial-copy-to-existing-collection-dialog.ts
similarity index 54%
rename from src/views-components/dialog-forms/partial-copy-to-collection-dialog.ts
rename to src/views-components/dialog-forms/partial-copy-to-existing-collection-dialog.ts
index d7b33929..8fe57f2d 100644
--- a/src/views-components/dialog-forms/partial-copy-to-collection-dialog.ts
+++ b/src/views-components/dialog-forms/partial-copy-to-existing-collection-dialog.ts
@@ -5,17 +5,17 @@
import { compose } from "redux";
import { reduxForm } from 'redux-form';
import { withDialog, } from 'store/dialog/with-dialog';
-import { CollectionPartialCopyToSelectedCollectionFormData, copyCollectionPartialToSelectedCollection, COLLECTION_PARTIAL_COPY_TO_SELECTED_COLLECTION } from 'store/collections/collection-partial-copy-actions';
-import { DialogCollectionPartialCopyToSelectedCollection } from "views-components/dialog-copy/dialog-partial-copy-to-collection";
+import { CollectionPartialCopyToExistingCollectionFormData, copyCollectionPartialToExistingCollection, COLLECTION_PARTIAL_COPY_TO_SELECTED_COLLECTION } from 'store/collections/collection-partial-copy-actions';
+import { DialogCollectionPartialCopyToExistingCollection } from "views-components/dialog-copy/dialog-collection-partial-copy-to-existing-collection";
import { pickerId } from "store/tree-picker/picker-id";
-export const PartialCopyToCollectionDialog = compose(
+export const PartialCopyToExistingCollectionDialog = compose(
withDialog(COLLECTION_PARTIAL_COPY_TO_SELECTED_COLLECTION),
- reduxForm<CollectionPartialCopyToSelectedCollectionFormData>({
+ reduxForm<CollectionPartialCopyToExistingCollectionFormData>({
form: COLLECTION_PARTIAL_COPY_TO_SELECTED_COLLECTION,
onSubmit: (data, dispatch) => {
- dispatch(copyCollectionPartialToSelectedCollection(data));
+ dispatch(copyCollectionPartialToExistingCollection(data));
}
}),
pickerId(COLLECTION_PARTIAL_COPY_TO_SELECTED_COLLECTION),
-)(DialogCollectionPartialCopyToSelectedCollection);
\ No newline at end of file
+)(DialogCollectionPartialCopyToExistingCollection);
diff --git a/src/views-components/dialog-forms/partial-copy-to-new-collection-dialog.ts b/src/views-components/dialog-forms/partial-copy-to-new-collection-dialog.ts
new file mode 100644
index 00000000..40bb1478
--- /dev/null
+++ b/src/views-components/dialog-forms/partial-copy-to-new-collection-dialog.ts
@@ -0,0 +1,21 @@
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+import { compose } from "redux";
+import { reduxForm } from 'redux-form';
+import { withDialog, } from 'store/dialog/with-dialog';
+import { CollectionPartialCopyToNewCollectionFormData, copyCollectionPartialToNewCollection, COLLECTION_PARTIAL_COPY_FORM_NAME } from 'store/collections/collection-partial-copy-actions';
+import { DialogCollectionPartialCopyToNewCollection } from "views-components/dialog-copy/dialog-collection-partial-copy-to-new-collection";
+import { pickerId } from "store/tree-picker/picker-id";
+
+export const PartialCopyToNewCollectionDialog = compose(
+ withDialog(COLLECTION_PARTIAL_COPY_FORM_NAME),
+ reduxForm<CollectionPartialCopyToNewCollectionFormData>({
+ form: COLLECTION_PARTIAL_COPY_FORM_NAME,
+ onSubmit: (data, dispatch) => {
+ dispatch(copyCollectionPartialToNewCollection(data));
+ }
+ }),
+ pickerId(COLLECTION_PARTIAL_COPY_FORM_NAME),
+)(DialogCollectionPartialCopyToNewCollection);
diff --git a/src/views-components/dialog-forms/partial-move-to-existing-collection-dialog.ts b/src/views-components/dialog-forms/partial-move-to-existing-collection-dialog.ts
new file mode 100644
index 00000000..cb18a663
--- /dev/null
+++ b/src/views-components/dialog-forms/partial-move-to-existing-collection-dialog.ts
@@ -0,0 +1,21 @@
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+import { compose } from "redux";
+import { reduxForm } from 'redux-form';
+import { withDialog, } from 'store/dialog/with-dialog';
+import { COLLECTION_PARTIAL_MOVE_TO_SELECTED_COLLECTION, CollectionPartialMoveToExistingCollectionFormData, moveCollectionPartialToExistingCollection } from "store/collections/collection-partial-move-actions";
+import { DialogCollectionPartialMoveToExistingCollection } from "views-components/dialog-move/dialog-collection-partial-move-to-existing-collection";
+import { pickerId } from "store/tree-picker/picker-id";
+
+export const PartialMoveToExistingCollectionDialog = compose(
+ withDialog(COLLECTION_PARTIAL_MOVE_TO_SELECTED_COLLECTION),
+ reduxForm<CollectionPartialMoveToExistingCollectionFormData>({
+ form: COLLECTION_PARTIAL_MOVE_TO_SELECTED_COLLECTION,
+ onSubmit: (data, dispatch) => {
+ dispatch(moveCollectionPartialToExistingCollection(data));
+ }
+ }),
+ pickerId(COLLECTION_PARTIAL_MOVE_TO_SELECTED_COLLECTION),
+)(DialogCollectionPartialMoveToExistingCollection);
diff --git a/src/views-components/dialog-forms/partial-move-to-new-collection-dialog.ts b/src/views-components/dialog-forms/partial-move-to-new-collection-dialog.ts
new file mode 100644
index 00000000..d7251c8a
--- /dev/null
+++ b/src/views-components/dialog-forms/partial-move-to-new-collection-dialog.ts
@@ -0,0 +1,21 @@
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+import { compose } from "redux";
+import { reduxForm } from 'redux-form';
+import { withDialog, } from 'store/dialog/with-dialog';
+import { COLLECTION_PARTIAL_MOVE_TO_NEW_COLLECTION, CollectionPartialMoveToNewCollectionFormData, moveCollectionPartialToNewCollection } from "store/collections/collection-partial-move-actions";
+import { DialogCollectionPartialMoveToNewCollection } from "views-components/dialog-move/dialog-collection-partial-move-to-new-collection";
+import { pickerId } from "store/tree-picker/picker-id";
+
+export const PartialMoveToNewCollectionDialog = compose(
+ withDialog(COLLECTION_PARTIAL_MOVE_TO_NEW_COLLECTION),
+ reduxForm<CollectionPartialMoveToNewCollectionFormData>({
+ form: COLLECTION_PARTIAL_MOVE_TO_NEW_COLLECTION,
+ onSubmit: (data, dispatch) => {
+ dispatch(moveCollectionPartialToNewCollection(data));
+ }
+ }),
+ pickerId(COLLECTION_PARTIAL_MOVE_TO_NEW_COLLECTION),
+)(DialogCollectionPartialMoveToNewCollection);
diff --git a/src/views-components/dialog-copy/dialog-partial-copy-to-collection.tsx b/src/views-components/dialog-move/dialog-collection-partial-move-to-existing-collection.tsx
similarity index 54%
rename from src/views-components/dialog-copy/dialog-partial-copy-to-collection.tsx
rename to src/views-components/dialog-move/dialog-collection-partial-move-to-existing-collection.tsx
index 4e9dde6a..e8081037 100644
--- a/src/views-components/dialog-copy/dialog-partial-copy-to-collection.tsx
+++ b/src/views-components/dialog-move/dialog-collection-partial-move-to-existing-collection.tsx
@@ -7,21 +7,21 @@ import { memoize } from "lodash/fp";
import { FormDialog } from 'components/form-dialog/form-dialog';
import { WithDialogProps } from 'store/dialog/with-dialog';
import { InjectedFormProps } from 'redux-form';
-import { CollectionPartialCopyToSelectedCollectionFormData } from 'store/collections/collection-partial-copy-actions';
+import { CollectionPartialMoveToExistingCollectionFormData } from "store/collections/collection-partial-move-actions";
import { PickerIdProp } from "store/tree-picker/picker-id";
import { CollectionPickerField } from 'views-components/form-fields/collection-form-fields';
-type DialogCollectionPartialCopyProps = WithDialogProps<string> & InjectedFormProps<CollectionPartialCopyToSelectedCollectionFormData>;
+type DialogCollectionPartialMoveProps = WithDialogProps<string> & InjectedFormProps<CollectionPartialMoveToExistingCollectionFormData>;
-export const DialogCollectionPartialCopyToSelectedCollection = (props: DialogCollectionPartialCopyProps & PickerIdProp) =>
+export const DialogCollectionPartialMoveToExistingCollection = (props: DialogCollectionPartialMoveProps & PickerIdProp) =>
<FormDialog
- dialogTitle='Choose collection'
- formFields={CollectionPartialCopyFields(props.pickerId)}
- submitLabel='Copy files'
+ dialogTitle='Move to existing collection'
+ formFields={CollectionPartialMoveFields(props.pickerId)}
+ submitLabel='Move files'
{...props}
/>;
-export const CollectionPartialCopyFields = memoize(
+const CollectionPartialMoveFields = memoize(
(pickerId: string) =>
() =>
<>
diff --git a/src/views-components/dialog-copy/dialog-collection-partial-copy.tsx b/src/views-components/dialog-move/dialog-collection-partial-move-to-new-collection.tsx
similarity index 59%
rename from src/views-components/dialog-copy/dialog-collection-partial-copy.tsx
rename to src/views-components/dialog-move/dialog-collection-partial-move-to-new-collection.tsx
index 3c584e4f..a33f377c 100644
--- a/src/views-components/dialog-copy/dialog-collection-partial-copy.tsx
+++ b/src/views-components/dialog-move/dialog-collection-partial-move-to-new-collection.tsx
@@ -8,20 +8,20 @@ import { FormDialog } from 'components/form-dialog/form-dialog';
import { CollectionNameField, CollectionDescriptionField, CollectionProjectPickerField } from 'views-components/form-fields/collection-form-fields';
import { WithDialogProps } from 'store/dialog/with-dialog';
import { InjectedFormProps } from 'redux-form';
-import { CollectionPartialCopyFormData } from 'store/collections/collection-partial-copy-actions';
+import { CollectionPartialMoveToNewCollectionFormData } from "store/collections/collection-partial-move-actions";
import { PickerIdProp } from "store/tree-picker/picker-id";
-type DialogCollectionPartialCopyProps = WithDialogProps<string> & InjectedFormProps<CollectionPartialCopyFormData>;
+type DialogCollectionPartialMoveProps = WithDialogProps<string> & InjectedFormProps<CollectionPartialMoveToNewCollectionFormData>;
-export const DialogCollectionPartialCopy = (props: DialogCollectionPartialCopyProps & PickerIdProp) =>
+export const DialogCollectionPartialMoveToNewCollection = (props: DialogCollectionPartialMoveProps & PickerIdProp) =>
<FormDialog
- dialogTitle='Create a collection'
- formFields={CollectionPartialCopyFields(props.pickerId)}
- submitLabel='Create a collection'
+ dialogTitle='Move to new collection'
+ formFields={CollectionPartialMoveFields(props.pickerId)}
+ submitLabel='Create collection'
{...props}
/>;
-export const CollectionPartialCopyFields = memoize(
+const CollectionPartialMoveFields = memoize(
(pickerId: string) =>
() =>
<>
diff --git a/src/views/workbench/workbench.tsx b/src/views/workbench/workbench.tsx
index d549c529..5b1fff39 100644
--- a/src/views/workbench/workbench.tsx
+++ b/src/views/workbench/workbench.tsx
@@ -32,7 +32,10 @@ import { MoveProcessDialog } from 'views-components/dialog-forms/move-process-di
import { MoveProjectDialog } from 'views-components/dialog-forms/move-project-dialog';
import { MoveCollectionDialog } from 'views-components/dialog-forms/move-collection-dialog';
import { FilesUploadCollectionDialog } from 'views-components/dialog-forms/files-upload-collection-dialog';
-import { PartialCopyCollectionDialog } from 'views-components/dialog-forms/partial-copy-collection-dialog';
+import { PartialCopyToNewCollectionDialog } from 'views-components/dialog-forms/partial-copy-to-new-collection-dialog';
+import { PartialCopyToExistingCollectionDialog } from 'views-components/dialog-forms/partial-copy-to-existing-collection-dialog';
+import { PartialMoveToNewCollectionDialog } from 'views-components/dialog-forms/partial-move-to-new-collection-dialog';
+import { PartialMoveToExistingCollectionDialog } from 'views-components/dialog-forms/partial-move-to-existing-collection-dialog';
import { RemoveProcessDialog } from 'views-components/process-remove-dialog/process-remove-dialog';
import { MainContentBar } from 'views-components/main-content-bar/main-content-bar';
import { Grid } from '@material-ui/core';
@@ -88,7 +91,6 @@ import { GroupAttributesDialog } from 'views-components/groups-dialog/attributes
import { GroupDetailsPanel } from 'views/group-details-panel/group-details-panel';
import { RemoveGroupMemberDialog } from 'views-components/groups-dialog/member-remove-dialog';
import { GroupMemberAttributesDialog } from 'views-components/groups-dialog/member-attributes-dialog';
-import { PartialCopyToCollectionDialog } from 'views-components/dialog-forms/partial-copy-to-collection-dialog';
import { PublicFavoritePanel } from 'views/public-favorites-panel/public-favorites-panel';
import { LinkAccountPanel } from 'views/link-account-panel/link-account-panel';
import { FedLogin } from './fed-login';
@@ -264,8 +266,10 @@ export const WorkbenchPanel =
<MoveProjectDialog />
<MultipleFilesRemoveDialog />
<PublicKeyDialog />
- <PartialCopyCollectionDialog />
- <PartialCopyToCollectionDialog />
+ <PartialCopyToNewCollectionDialog />
+ <PartialCopyToExistingCollectionDialog />
+ <PartialMoveToNewCollectionDialog />
+ <PartialMoveToExistingCollectionDialog />
<ProcessInputDialog />
<RestoreCollectionVersionDialog />
<RemoveApiClientAuthorizationDialog />
-----------------------------------------------------------------------
hooks/post-receive
--
More information about the arvados-commits
mailing list