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