Merge branch '21128-toolbar-context-menu'
[arvados-workbench2.git] / src / store / tree-picker / tree-picker-reducer.ts
1 // Copyright (C) The Arvados Authors. All rights reserved.
2 //
3 // SPDX-License-Identifier: AGPL-3.0
4
5 import {
6     createTree, TreeNode, setNode, Tree, TreeNodeStatus, setNodeStatus,
7     expandNode, deactivateNode, selectNodes, deselectNodes,
8     activateNode, getNode, toggleNodeCollapse, toggleNodeSelection, appendSubtree, expandNodeAncestors
9 } from 'models/tree';
10 import { TreePicker } from "./tree-picker";
11 import { treePickerActions, treePickerSearchActions, TreePickerAction, TreePickerSearchAction, LoadProjectParams } from "./tree-picker-actions";
12 import { compose } from "redux";
13 import { pipe } from 'lodash/fp';
14
15 export const treePickerReducer = (state: TreePicker = {}, action: TreePickerAction) =>
16     treePickerActions.match(action, {
17         LOAD_TREE_PICKER_NODE: ({ id, pickerId }) =>
18             updateOrCreatePicker(state, pickerId, setNodeStatus(id)(TreeNodeStatus.PENDING)),
19
20         LOAD_TREE_PICKER_NODE_SUCCESS: ({ id, nodes, pickerId }) =>
21             updateOrCreatePicker(state, pickerId, compose(receiveNodes(nodes)(id), setNodeStatus(id)(TreeNodeStatus.LOADED))),
22
23         APPEND_TREE_PICKER_NODE_SUBTREE: ({ id, subtree, pickerId }) =>
24             updateOrCreatePicker(state, pickerId, compose(appendSubtree(id, subtree), setNodeStatus(id)(TreeNodeStatus.LOADED))),
25
26         TOGGLE_TREE_PICKER_NODE_COLLAPSE: ({ id, pickerId }) =>
27             updateOrCreatePicker(state, pickerId, toggleNodeCollapse(id)),
28
29         EXPAND_TREE_PICKER_NODE: ({ id, pickerId }) =>
30             updateOrCreatePicker(state, pickerId, expandNode(id)),
31
32         EXPAND_TREE_PICKER_NODE_ANCESTORS: ({ id, pickerId }) =>
33             updateOrCreatePicker(state, pickerId, expandNodeAncestors(id)),
34
35         ACTIVATE_TREE_PICKER_NODE: ({ id, pickerId, relatedTreePickers = [] }) =>
36             pipe(
37                 () => relatedTreePickers.reduce(
38                     (state, relatedPickerId) => updateOrCreatePicker(state, relatedPickerId, deactivateNode),
39                     state
40                 ),
41                 state => updateOrCreatePicker(state, pickerId, activateNode(id))
42             )(),
43
44         DEACTIVATE_TREE_PICKER_NODE: ({ pickerId }) =>
45             updateOrCreatePicker(state, pickerId, deactivateNode),
46
47         TOGGLE_TREE_PICKER_NODE_SELECTION: ({ id, pickerId, cascade }) =>
48             updateOrCreatePicker(state, pickerId, toggleNodeSelection(id, cascade)),
49
50         SELECT_TREE_PICKER_NODE: ({ id, pickerId, cascade }) =>
51             updateOrCreatePicker(state, pickerId, selectNodes(id, cascade)),
52
53         DESELECT_TREE_PICKER_NODE: ({ id, pickerId, cascade }) =>
54             updateOrCreatePicker(state, pickerId, deselectNodes(id, cascade)),
55
56         RESET_TREE_PICKER: ({ pickerId }) =>
57             updateOrCreatePicker(state, pickerId, createTree),
58
59         EXPAND_TREE_PICKER_NODES: ({ pickerId, ids }) =>
60             updateOrCreatePicker(state, pickerId, expandNode(...ids)),
61
62         default: () => state
63     });
64
65 const updateOrCreatePicker = <V>(state: TreePicker, pickerId: string, func: (value: Tree<V>) => Tree<V>) => {
66     const picker = state[pickerId] || createTree();
67     const updatedPicker = func(picker);
68     return { ...state, [pickerId]: updatedPicker };
69 };
70
71 const receiveNodes = <V>(nodes: Array<TreeNode<V>>) => (parent: string) => (state: Tree<V>) => {
72     const parentNode = getNode(parent)(state);
73     let newState = state;
74     if (parentNode) {
75         newState = setNode({ ...parentNode, children: [] })(state);
76     }
77     return nodes.reduce((tree, node) => {
78         const preexistingNode = getNode(node.id)(state);
79         if (preexistingNode) {
80             node = { ...preexistingNode, value: node.value };
81         }
82         return setNode({ ...node, parent })(tree);
83     }, newState);
84 };
85
86 interface TreePickerSearch {
87     projectSearchValues: { [pickerId: string]: string };
88     collectionFilterValues: { [pickerId: string]: string };
89     loadProjectParams: { [pickerId: string]: LoadProjectParams };
90 }
91
92 export const treePickerSearchReducer = (state: TreePickerSearch = { projectSearchValues: {}, collectionFilterValues: {}, loadProjectParams: {} }, action: TreePickerSearchAction) =>
93     treePickerSearchActions.match(action, {
94         SET_TREE_PICKER_PROJECT_SEARCH: ({ pickerId, projectSearchValue }) => ({
95             ...state, projectSearchValues: { ...state.projectSearchValues, [pickerId]: projectSearchValue }
96         }),
97
98         SET_TREE_PICKER_COLLECTION_FILTER: ({ pickerId, collectionFilterValue }) => ({
99             ...state, collectionFilterValues: { ...state.collectionFilterValues, [pickerId]: collectionFilterValue }
100         }),
101
102         SET_TREE_PICKER_LOAD_PARAMS: ({ pickerId, params }) => ({
103             ...state, loadProjectParams: { ...state.loadProjectParams, [pickerId]: params }
104         }),
105
106         default: () => state
107     });