16592: Restores 'Rename' action. Adds support for baseURL w/o trailing slash.
[arvados-workbench2.git] / src / store / collection-panel / collection-panel-files / collection-panel-files-reducer.ts
1 // Copyright (C) The Arvados Authors. All rights reserved.
2 //
3 // SPDX-License-Identifier: AGPL-3.0
4
5 import { CollectionPanelFilesState, CollectionPanelFile, CollectionPanelDirectory, mapCollectionFileToCollectionPanelFile, mergeCollectionPanelFilesStates } from './collection-panel-files-state';
6 import { CollectionPanelFilesAction, collectionPanelFilesAction } from "./collection-panel-files-actions";
7 import { createTree, mapTreeValues, getNode, setNode, getNodeAncestorsIds, getNodeDescendantsIds, setNodeValueWith, mapTree } from "~/models/tree";
8 import { CollectionFileType } from "~/models/collection-file";
9
10 export const collectionPanelFilesReducer = (state: CollectionPanelFilesState = createTree(), action: CollectionPanelFilesAction) => {
11     // Low-level tree handling setNode() func does in-place data modifications
12     // for performance reasons, so we pass a copy of 'state' to avoid side effects.
13     return collectionPanelFilesAction.match(action, {
14         SET_COLLECTION_FILES: files =>
15             mergeCollectionPanelFilesStates({...state}, mapTree(mapCollectionFileToCollectionPanelFile)(files)),
16
17         TOGGLE_COLLECTION_FILE_COLLAPSE: data =>
18             toggleCollapse(data.id)({...state}),
19
20         TOGGLE_COLLECTION_FILE_SELECTION: data => [{...state}]
21             .map(toggleSelected(data.id))
22             .map(toggleAncestors(data.id))
23             .map(toggleDescendants(data.id))[0],
24
25         SELECT_ALL_COLLECTION_FILES: () =>
26             mapTreeValues(v => ({ ...v, selected: true }))({...state}),
27
28         UNSELECT_ALL_COLLECTION_FILES: () =>
29             mapTreeValues(v => ({ ...v, selected: false }))({...state}),
30
31         default: () => state
32     }) as CollectionPanelFilesState;
33 };
34
35 const toggleCollapse = (id: string) => (tree: CollectionPanelFilesState) =>
36     setNodeValueWith((v: CollectionPanelDirectory | CollectionPanelFile) =>
37         v.type === CollectionFileType.DIRECTORY
38             ? { ...v, collapsed: !v.collapsed }
39             : v)(id)(tree);
40
41
42 const toggleSelected = (id: string) => (tree: CollectionPanelFilesState) =>
43     setNodeValueWith((v: CollectionPanelDirectory | CollectionPanelFile) => ({ ...v, selected: !v.selected }))(id)(tree);
44
45
46 const toggleDescendants = (id: string) => (tree: CollectionPanelFilesState) => {
47     const node = getNode(id)(tree);
48     if (node && node.value.type === CollectionFileType.DIRECTORY) {
49         return getNodeDescendantsIds(id)(tree)
50             .reduce((newTree, id) =>
51                 setNodeValueWith(v => ({ ...v, selected: node.value.selected }))(id)(newTree), tree);
52     }
53     return tree;
54 };
55
56 const toggleAncestors = (id: string) => (tree: CollectionPanelFilesState) => {
57     const ancestors = getNodeAncestorsIds(id)(tree).reverse();
58     return ancestors.reduce((newTree, parent) => parent ? toggleParentNode(parent)(newTree) : newTree, tree);
59 };
60
61 const toggleParentNode = (id: string) => (tree: CollectionPanelFilesState) => {
62     const node = getNode(id)(tree);
63     if (node) {
64         const parentNode = getNode(node.id)(tree);
65         if (parentNode) {
66             const selected = parentNode.children
67                 .map(id => getNode(id)(tree))
68                 .every(node => node !== undefined && node.value.selected);
69             return setNodeValueWith(v => ({ ...v, selected }))(parentNode.id)(tree);
70         }
71         return setNode(node)(tree);
72     }
73     return tree;
74 };
75
76