1 // Copyright (C) The Arvados Authors. All rights reserved.
3 // SPDX-License-Identifier: AGPL-3.0
5 import { Dispatch } from "redux";
6 import { initialize, startSubmit } from "redux-form";
7 import { CommonResourceServiceError, getCommonResourceServiceError } from "services/common-service/common-resource-service";
8 import { ServiceRepository } from "services/services";
9 import { filterCollectionFilesBySelection } from "store/collection-panel/collection-panel-files/collection-panel-files-state";
10 import { dialogActions } from "store/dialog/dialog-actions";
11 import { navigateTo } from "store/navigation/navigation-action";
12 import { progressIndicatorActions } from "store/progress-indicator/progress-indicator-actions";
13 import { resetPickerProjectTree } from "store/project-tree-picker/project-tree-picker-actions";
14 import { updateResources } from "store/resources/resources-actions";
15 import { SnackbarKind, snackbarActions } from "store/snackbar/snackbar-actions";
16 import { RootState } from "store/store";
17 import { FileOperationLocation } from "store/tree-picker/tree-picker-actions";
19 export const COLLECTION_PARTIAL_MOVE_TO_NEW_COLLECTION = 'COLLECTION_PARTIAL_MOVE_TO_NEW_DIALOG';
20 export const COLLECTION_PARTIAL_MOVE_TO_SELECTED_COLLECTION = 'COLLECTION_PARTIAL_MOVE_TO_SELECTED_DIALOG';
21 export const COLLECTION_PARTIAL_MOVE_TO_SEPARATE_COLLECTIONS = 'COLLECTION_PARTIAL_MOVE_TO_SEPARATE_DIALOG';
23 export interface CollectionPartialMoveToNewCollectionFormData {
29 export interface CollectionPartialMoveToExistingCollectionFormData {
30 destination: FileOperationLocation;
33 export interface CollectionPartialMoveToSeparateCollectionsFormData {
38 export const openCollectionPartialMoveToNewCollectionDialog = () =>
39 (dispatch: Dispatch, getState: () => RootState) => {
40 const currentCollection = getState().collectionPanel.item;
41 if (currentCollection) {
43 name: `Files moved from: ${currentCollection.name}`,
44 description: currentCollection.description,
45 projectUuid: undefined
47 dispatch(initialize(COLLECTION_PARTIAL_MOVE_TO_NEW_COLLECTION, initialData));
48 dispatch<any>(resetPickerProjectTree());
49 dispatch(dialogActions.OPEN_DIALOG({ id: COLLECTION_PARTIAL_MOVE_TO_NEW_COLLECTION, data: {} }));
53 export const moveCollectionPartialToNewCollection = ({ name, description, projectUuid }: CollectionPartialMoveToNewCollectionFormData) =>
54 async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
55 const state = getState();
56 // Get current collection
57 const sourceCollection = state.collectionPanel.item;
59 if (sourceCollection) {
61 dispatch(startSubmit(COLLECTION_PARTIAL_MOVE_TO_NEW_COLLECTION));
62 dispatch(progressIndicatorActions.START_WORKING(COLLECTION_PARTIAL_MOVE_TO_NEW_COLLECTION));
65 const paths = filterCollectionFilesBySelection(state.collectionPanelFiles, true)
66 .map(file => file.id.replace(new RegExp(`(^${sourceCollection.uuid})`), ''));
69 const updatedCollection = await services.collectionService.moveFiles(
70 sourceCollection.uuid,
71 sourceCollection.portableDataHash,
76 ownerUuid: projectUuid,
82 dispatch(updateResources([updatedCollection]));
83 dispatch<any>(navigateTo(updatedCollection.uuid));
85 dispatch(dialogActions.CLOSE_DIALOG({ id: COLLECTION_PARTIAL_MOVE_TO_NEW_COLLECTION }));
86 dispatch(snackbarActions.OPEN_SNACKBAR({
87 message: 'Files have been moved to selected collection.',
89 kind: SnackbarKind.SUCCESS
91 dispatch(progressIndicatorActions.STOP_WORKING(COLLECTION_PARTIAL_MOVE_TO_NEW_COLLECTION));
93 const error = getCommonResourceServiceError(e);
94 if (error === CommonResourceServiceError.UNKNOWN) {
95 dispatch(dialogActions.CLOSE_DIALOG({ id: COLLECTION_PARTIAL_MOVE_TO_NEW_COLLECTION }));
96 dispatch(snackbarActions.OPEN_SNACKBAR({ message: 'Could not move files to selected collection', hideDuration: 2000, kind: SnackbarKind.ERROR }));
98 dispatch(progressIndicatorActions.STOP_WORKING(COLLECTION_PARTIAL_MOVE_TO_NEW_COLLECTION));
103 export const openCollectionPartialMoveToExistingCollectionDialog = () =>
104 (dispatch: Dispatch, getState: () => RootState) => {
105 const currentCollection = getState().collectionPanel.item;
106 if (currentCollection) {
107 const initialData = {
108 destination: {uuid: currentCollection.uuid, path: ''}
110 dispatch(initialize(COLLECTION_PARTIAL_MOVE_TO_SELECTED_COLLECTION, initialData));
111 dispatch<any>(resetPickerProjectTree());
112 dispatch(dialogActions.OPEN_DIALOG({ id: COLLECTION_PARTIAL_MOVE_TO_SELECTED_COLLECTION, data: {} }));
116 export const moveCollectionPartialToExistingCollection = ({ destination }: CollectionPartialMoveToExistingCollectionFormData) =>
117 async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
118 const state = getState();
119 // Get current collection
120 const sourceCollection = state.collectionPanel.item;
122 if (sourceCollection && destination && destination.uuid) {
124 dispatch(startSubmit(COLLECTION_PARTIAL_MOVE_TO_SELECTED_COLLECTION));
125 dispatch(progressIndicatorActions.START_WORKING(COLLECTION_PARTIAL_MOVE_TO_SELECTED_COLLECTION));
126 // Get selected files
127 const paths = filterCollectionFilesBySelection(state.collectionPanelFiles, true)
128 .map(file => file.id.replace(new RegExp(`(^${sourceCollection.uuid})`), ''));
131 const updatedCollection = await services.collectionService.moveFiles(sourceCollection.uuid, sourceCollection.portableDataHash, paths, {uuid: destination.uuid}, destination.path || '/', false);
132 dispatch(updateResources([updatedCollection]));
134 dispatch(dialogActions.CLOSE_DIALOG({ id: COLLECTION_PARTIAL_MOVE_TO_SELECTED_COLLECTION }));
135 dispatch(snackbarActions.OPEN_SNACKBAR({
136 message: 'Files have been moved to selected collection.',
138 kind: SnackbarKind.SUCCESS
140 dispatch(progressIndicatorActions.STOP_WORKING(COLLECTION_PARTIAL_MOVE_TO_SELECTED_COLLECTION));
142 const error = getCommonResourceServiceError(e);
143 if (error === CommonResourceServiceError.UNKNOWN) {
144 dispatch(dialogActions.CLOSE_DIALOG({ id: COLLECTION_PARTIAL_MOVE_TO_SELECTED_COLLECTION }));
145 dispatch(snackbarActions.OPEN_SNACKBAR({ message: 'Could not copy this files to selected collection', hideDuration: 2000, kind: SnackbarKind.ERROR }));
147 dispatch(progressIndicatorActions.STOP_WORKING(COLLECTION_PARTIAL_MOVE_TO_SELECTED_COLLECTION));
152 export const openCollectionPartialMoveToSeparateCollectionsDialog = () =>
153 (dispatch: Dispatch, getState: () => RootState) => {
154 const currentCollection = getState().collectionPanel.item;
155 if (currentCollection) {
156 const initialData = {
157 name: currentCollection.name,
158 projectUuid: undefined
160 dispatch(initialize(COLLECTION_PARTIAL_MOVE_TO_SEPARATE_COLLECTIONS, initialData));
161 dispatch<any>(resetPickerProjectTree());
162 dispatch(dialogActions.OPEN_DIALOG({ id: COLLECTION_PARTIAL_MOVE_TO_SEPARATE_COLLECTIONS, data: {} }));
166 export const moveCollectionPartialToSeparateCollections = ({ name, projectUuid }: CollectionPartialMoveToSeparateCollectionsFormData) =>
167 async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
168 const state = getState();
169 // Get current collection
170 const sourceCollection = state.collectionPanel.item;
172 if (sourceCollection) {
174 dispatch(startSubmit(COLLECTION_PARTIAL_MOVE_TO_SEPARATE_COLLECTIONS));
175 dispatch(progressIndicatorActions.START_WORKING(COLLECTION_PARTIAL_MOVE_TO_SEPARATE_COLLECTIONS));
177 // Get selected files
178 const paths = filterCollectionFilesBySelection(state.collectionPanelFiles, true)
179 .map(file => file.id.replace(new RegExp(`(^${sourceCollection.uuid})`), ''));
182 const collections = await Promise.all(paths.map((path) =>
183 services.collectionService.moveFiles(
184 sourceCollection.uuid,
185 sourceCollection.portableDataHash,
188 name: `File moved from collection ${name}${path}`,
189 ownerUuid: projectUuid,
196 dispatch(updateResources(collections));
198 dispatch(dialogActions.CLOSE_DIALOG({ id: COLLECTION_PARTIAL_MOVE_TO_SEPARATE_COLLECTIONS }));
199 dispatch(snackbarActions.OPEN_SNACKBAR({
200 message: 'New collections created.',
202 kind: SnackbarKind.SUCCESS
204 dispatch(progressIndicatorActions.STOP_WORKING(COLLECTION_PARTIAL_MOVE_TO_SEPARATE_COLLECTIONS));
206 const error = getCommonResourceServiceError(e);
207 if (error === CommonResourceServiceError.UNIQUE_NAME_VIOLATION) {
208 dispatch(snackbarActions.OPEN_SNACKBAR({ message: 'Collection from one or more files already exists', hideDuration: 2000, kind: SnackbarKind.ERROR }));
209 } else if (error === CommonResourceServiceError.UNKNOWN) {
210 dispatch(dialogActions.CLOSE_DIALOG({ id: COLLECTION_PARTIAL_MOVE_TO_SEPARATE_COLLECTIONS }));
211 dispatch(snackbarActions.OPEN_SNACKBAR({ message: 'Could not create a copy of collection', hideDuration: 2000, kind: SnackbarKind.ERROR }));
213 dispatch(dialogActions.CLOSE_DIALOG({ id: COLLECTION_PARTIAL_MOVE_TO_SEPARATE_COLLECTIONS }));
214 dispatch(snackbarActions.OPEN_SNACKBAR({ message: 'Collection has been copied but may contain incorrect files.', hideDuration: 2000, kind: SnackbarKind.ERROR }));
216 dispatch(progressIndicatorActions.STOP_WORKING(COLLECTION_PARTIAL_MOVE_TO_SEPARATE_COLLECTIONS));