Merge branch 'master'
[arvados.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 { createTree, setNodeValueWith, TreeNode, setNode, mapTreeValues, Tree } from "~/models/tree";
6 import { TreePicker, TreePickerNode } from "./tree-picker";
7 import { treePickerActions, TreePickerAction } from "./tree-picker-actions";
8 import { TreeItemStatus } from "~/components/tree/tree";
9 import { compose } from "redux";
10
11 export const treePickerReducer = (state: TreePicker = {}, action: TreePickerAction) =>
12     treePickerActions.match(action, {
13         LOAD_TREE_PICKER_NODE: ({ nodeId, pickerId }) =>
14             updateOrCreatePicker(state, pickerId, setNodeValueWith(setPending)(nodeId)),
15         LOAD_TREE_PICKER_NODE_SUCCESS: ({ nodeId, nodes, pickerId }) =>
16             updateOrCreatePicker(state, pickerId, compose(receiveNodes(nodes)(nodeId), setNodeValueWith(setLoaded)(nodeId))),
17         TOGGLE_TREE_PICKER_NODE_COLLAPSE: ({ nodeId, pickerId }) =>
18             updateOrCreatePicker(state, pickerId, setNodeValueWith(toggleCollapse)(nodeId)),
19         TOGGLE_TREE_PICKER_NODE_SELECT: ({ nodeId, pickerId }) =>
20             updateOrCreatePicker(state, pickerId, mapTreeValues(toggleSelect(nodeId))),
21         RESET_TREE_PICKER: ({ pickerId }) =>
22             updateOrCreatePicker(state, pickerId, createTree),
23         EXPAND_TREE_PICKER_NODES: ({ pickerId, nodeIds }) =>
24             updateOrCreatePicker(state, pickerId, mapTreeValues(expand(nodeIds))),
25         default: () => state
26     });
27
28 const updateOrCreatePicker = (state: TreePicker, pickerId: string, func: (value: Tree<TreePickerNode>) => Tree<TreePickerNode>) => {
29     const picker = state[pickerId] || createTree();
30     const updatedPicker = func(picker);
31     return { ...state, [pickerId]: updatedPicker };
32 };
33
34 const expand = (ids: string[]) => (node: TreePickerNode): TreePickerNode =>
35     ids.some(id => id === node.nodeId)
36         ? { ...node, collapsed: false }
37         : node;
38
39 const setPending = (value: TreePickerNode): TreePickerNode =>
40     ({ ...value, status: TreeItemStatus.PENDING });
41
42 const setLoaded = (value: TreePickerNode): TreePickerNode =>
43     ({ ...value, status: TreeItemStatus.LOADED });
44
45 const toggleCollapse = (value: TreePickerNode): TreePickerNode =>
46     ({ ...value, collapsed: !value.collapsed });
47
48 const toggleSelect = (nodeId: string) => (value: TreePickerNode): TreePickerNode =>
49     value.nodeId === nodeId
50         ? ({ ...value, selected: !value.selected })
51         : ({ ...value, selected: false });
52
53 const receiveNodes = (nodes: Array<TreePickerNode>) => (parent: string) => (state: Tree<TreePickerNode>) =>
54     nodes.reduce((tree, node) =>
55         setNode(
56             createTreeNode(parent)(node)
57         )(tree), state);
58
59 const createTreeNode = (parent: string) => (node: TreePickerNode): TreeNode<TreePickerNode> => ({
60     children: [],
61     id: node.nodeId,
62     parent,
63     value: node
64 });