44e6a9bfe2a94720b4ecfc6db81409bac2e21993
[arvados-workbench2.git] / src / store / collections / collection-partial-move-actions.ts
1 // Copyright (C) The Arvados Authors. All rights reserved.
2 //
3 // SPDX-License-Identifier: AGPL-3.0
4
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";
18
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';
22
23 export interface CollectionPartialMoveToNewCollectionFormData {
24     name: string;
25     description: string;
26     projectUuid: string;
27 }
28
29 export interface CollectionPartialMoveToExistingCollectionFormData {
30     destination: FileOperationLocation;
31 }
32
33 export interface CollectionPartialMoveToSeparateCollectionsFormData {
34     name: string;
35     projectUuid: string;
36 }
37
38 export const openCollectionPartialMoveToNewCollectionDialog = () =>
39     (dispatch: Dispatch, getState: () => RootState) => {
40         const currentCollection = getState().collectionPanel.item;
41         if (currentCollection) {
42             const initialData = {
43                 name: `Files moved from: ${currentCollection.name}`,
44                 description: currentCollection.description,
45                 projectUuid: undefined
46             };
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: {} }));
50         }
51     };
52
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;
58
59         if (sourceCollection) {
60             try {
61                 dispatch(startSubmit(COLLECTION_PARTIAL_MOVE_TO_NEW_COLLECTION));
62                 dispatch(progressIndicatorActions.START_WORKING(COLLECTION_PARTIAL_MOVE_TO_NEW_COLLECTION));
63
64                 // Get selected files
65                 const paths = filterCollectionFilesBySelection(state.collectionPanelFiles, true)
66                     .map(file => file.id.replace(new RegExp(`(^${sourceCollection.uuid})`), ''));
67
68                 // Move files
69                 const updatedCollection = await services.collectionService.moveFiles(
70                     sourceCollection.uuid,
71                     sourceCollection.portableDataHash,
72                     paths,
73                     {
74                         name,
75                         description,
76                         ownerUuid: projectUuid,
77                         uuid: undefined,
78                     },
79                     '/',
80                     false
81                 );
82                 dispatch(updateResources([updatedCollection]));
83                 dispatch<any>(navigateTo(updatedCollection.uuid))
84
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.',
88                     hideDuration: 2000,
89                     kind: SnackbarKind.SUCCESS
90                 }));
91                 dispatch(progressIndicatorActions.STOP_WORKING(COLLECTION_PARTIAL_MOVE_TO_NEW_COLLECTION));
92             } catch (e) {
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 }));
97                 }
98                 dispatch(progressIndicatorActions.STOP_WORKING(COLLECTION_PARTIAL_MOVE_TO_NEW_COLLECTION));
99             }
100         }
101     };
102
103 export const openCollectionPartialMoveToExistingCollectionDialog = () =>
104     (dispatch: Dispatch, getState: () => RootState) => {
105         const currentCollection = getState().collectionPanel.item;
106         if (currentCollection) {
107             const initialData = {
108                 destination: {uuid: '', path: ''}
109             };
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: {} }));
113         }
114     };
115
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;
121
122         if (sourceCollection && destination.uuid) {
123             try {
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})`), ''));
129
130                 // Move files
131                 const updatedCollection = await services.collectionService.moveFiles(sourceCollection.uuid, sourceCollection.portableDataHash, paths, {uuid: destination.uuid}, destination.path || '/', false);
132                 dispatch(updateResources([updatedCollection]));
133
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.',
137                     hideDuration: 2000,
138                     kind: SnackbarKind.SUCCESS
139                 }));
140                 dispatch(progressIndicatorActions.STOP_WORKING(COLLECTION_PARTIAL_MOVE_TO_SELECTED_COLLECTION));
141             } catch (e) {
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 }));
146                 }
147                 dispatch(progressIndicatorActions.STOP_WORKING(COLLECTION_PARTIAL_MOVE_TO_SELECTED_COLLECTION));
148             }
149         }
150     };
151
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
159             };
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: {} }));
163         }
164     };
165
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;
171
172         if (sourceCollection) {
173             try {
174                 dispatch(startSubmit(COLLECTION_PARTIAL_MOVE_TO_SEPARATE_COLLECTIONS));
175                 dispatch(progressIndicatorActions.START_WORKING(COLLECTION_PARTIAL_MOVE_TO_SEPARATE_COLLECTIONS));
176
177                 // Get selected files
178                 const paths = filterCollectionFilesBySelection(state.collectionPanelFiles, true)
179                     .map(file => file.id.replace(new RegExp(`(^${sourceCollection.uuid})`), ''));
180
181                 // Move files
182                 const collections = await Promise.all(paths.map((path) =>
183                     services.collectionService.moveFiles(
184                         sourceCollection.uuid,
185                         sourceCollection.portableDataHash,
186                         [path],
187                         {
188                             name: `File split from collection ${name}${path}`,
189                             ownerUuid: projectUuid,
190                             uuid: undefined,
191                         },
192                         '/',
193                         false
194                     )
195                 ));
196                 dispatch(updateResources(collections));
197
198                 dispatch(dialogActions.CLOSE_DIALOG({ id: COLLECTION_PARTIAL_MOVE_TO_SEPARATE_COLLECTIONS }));
199                 dispatch(snackbarActions.OPEN_SNACKBAR({
200                     message: 'New collections created.',
201                     hideDuration: 2000,
202                     kind: SnackbarKind.SUCCESS
203                 }));
204                 dispatch(progressIndicatorActions.STOP_WORKING(COLLECTION_PARTIAL_MOVE_TO_SEPARATE_COLLECTIONS));
205             } catch (e) {
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 }));
212                 } else {
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 }));
215                 }
216                 dispatch(progressIndicatorActions.STOP_WORKING(COLLECTION_PARTIAL_MOVE_TO_SEPARATE_COLLECTIONS));
217             }
218         }
219     };