Merge branch 'master' into 13988-make-a-copy-popup
[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     return collectionPanelFilesAction.match(action, {
12         SET_COLLECTION_FILES: files =>
13             mergeCollectionPanelFilesStates(state, mapTree(mapCollectionFileToCollectionPanelFile)(files)),
14
15         TOGGLE_COLLECTION_FILE_COLLAPSE: data =>
16             toggleCollapse(data.id)(state),
17
18         TOGGLE_COLLECTION_FILE_SELECTION: data => [state]
19             .map(toggleSelected(data.id))
20             .map(toggleAncestors(data.id))
21             .map(toggleDescendants(data.id))[0],
22
23         SELECT_ALL_COLLECTION_FILES: () =>
24             mapTreeValues(v => ({ ...v, selected: true }))(state),
25
26         UNSELECT_ALL_COLLECTION_FILES: () =>
27             mapTreeValues(v => ({ ...v, selected: false }))(state),
28
29         default: () => state
30     }) as CollectionPanelFilesState;
31 };
32
33 const toggleCollapse = (id: string) => (tree: CollectionPanelFilesState) =>
34     setNodeValueWith((v: CollectionPanelDirectory | CollectionPanelFile) =>
35         v.type === CollectionFileType.DIRECTORY
36             ? { ...v, collapsed: !v.collapsed }
37             : v)(id)(tree);
38
39
40 const toggleSelected = (id: string) => (tree: CollectionPanelFilesState) =>
41     setNodeValueWith((v: CollectionPanelDirectory | CollectionPanelFile) => ({ ...v, selected: !v.selected }))(id)(tree);
42
43
44 const toggleDescendants = (id: string) => (tree: CollectionPanelFilesState) => {
45     const node = getNode(id)(tree);
46     if (node && node.value.type === CollectionFileType.DIRECTORY) {
47         return getNodeDescendantsIds(id)(tree)
48             .reduce((newTree, id) =>
49                 setNodeValueWith(v => ({ ...v, selected: node.value.selected }))(id)(newTree), tree);
50     }
51     return tree;
52 };
53
54 const toggleAncestors = (id: string) => (tree: CollectionPanelFilesState) => {
55     const ancestors = getNodeAncestorsIds(id)(tree).reverse();
56     return ancestors.reduce((newTree, parent) => parent ? toggleParentNode(parent)(newTree) : newTree, tree);
57 };
58
59 const toggleParentNode = (id: string) => (tree: CollectionPanelFilesState) => {
60     const node = getNode(id)(tree);
61     if (node) {
62         const parentNode = getNode(node.id)(tree);
63         if (parentNode) {
64             const selected = parentNode.children
65                 .map(id => getNode(id)(tree))
66                 .every(node => node !== undefined && node.value.selected);
67             return setNodeValueWith(v => ({ ...v, selected }))(parentNode.id)(tree);
68         }
69         return setNode(node)(tree);
70     }
71     return tree;
72 };
73
74