16243: Added reducer changes in order to make file download valid
[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 let fetchedFiles: any = {};
11
12 export const collectionPanelFilesReducer = (state: CollectionPanelFilesState = createTree(), action: CollectionPanelFilesAction) => {
13     // Low-level tree handling setNode() func does in-place data modifications
14     // for performance reasons, so we pass a copy of 'state' to avoid side effects.
15     return collectionPanelFilesAction.match(action, {
16         SET_COLLECTION_FILES: files => {
17             fetchedFiles = files;
18             return mergeCollectionPanelFilesStates({ ...state }, mapTree(mapCollectionFileToCollectionPanelFile)(files));
19         },
20
21         TOGGLE_COLLECTION_FILE_COLLAPSE: data =>
22             toggleCollapse(data.id)({ ...state }),
23
24         TOGGLE_COLLECTION_FILE_SELECTION: data => [{ ...state }]
25             .map(toggleSelected(data.id))
26             .map(toggleAncestors(data.id))
27             .map(toggleDescendants(data.id))[0],
28
29         ON_SEARCH_CHANGE: (searchValue) => {
30             // node.children = children.filter((id: string) => id.replace(parentId, '').toLowerCase().indexOf(searchValue.toLowerCase()) > -1);
31             const ids: string[] = [];
32             const filteredFiles = Object.keys(fetchedFiles)
33                 .filter((key: string) => {
34                     const node = fetchedFiles[key];
35
36                     if (node.value === undefined) {
37                         return false;
38                     }
39
40                     const { id, value: { type, name } } = node;
41
42                     if (type === CollectionFileType.DIRECTORY) {
43                         ids.push(id);
44                         return true;
45                     }
46
47                     const includeFile = name.toLowerCase().indexOf(searchValue.toLowerCase()) > -1;
48
49                     if (includeFile) {
50                         ids.push(id);
51                     }
52
53                     return includeFile;
54                 })
55                 .reduce((prev, next) => {
56                     const node = JSON.parse(JSON.stringify(fetchedFiles[next]));
57                     node.children = node.children.filter((key: string) => ids.indexOf(key) > -1);
58                     prev[next] = node;
59                     return prev;
60                 }, {});
61
62             return mapTreeValues((v: CollectionPanelDirectory | CollectionPanelFile) => {
63                 if (v.type === CollectionFileType.DIRECTORY) {
64                     return ({ 
65                         ...v,
66                         collapsed: searchValue.length === 0,
67                     });
68                 }
69
70                 return ({ ...v });
71             })({ ...filteredFiles });
72         },
73
74         SELECT_ALL_COLLECTION_FILES: () =>
75             mapTreeValues(v => ({ ...v, selected: true }))({ ...state }),
76
77         UNSELECT_ALL_COLLECTION_FILES: () =>
78             mapTreeValues(v => ({ ...v, selected: false }))({ ...state }),
79
80         default: () => state
81     }) as CollectionPanelFilesState;
82 };
83
84 const toggleCollapse = (id: string) => (tree: CollectionPanelFilesState) =>
85     setNodeValueWith((v: CollectionPanelDirectory | CollectionPanelFile) =>
86         v.type === CollectionFileType.DIRECTORY
87             ? { ...v, collapsed: !v.collapsed }
88             : v)(id)(tree);
89
90
91 const toggleSelected = (id: string) => (tree: CollectionPanelFilesState) =>
92     setNodeValueWith((v: CollectionPanelDirectory | CollectionPanelFile) => ({ ...v, selected: !v.selected }))(id)(tree);
93
94
95 const toggleDescendants = (id: string) => (tree: CollectionPanelFilesState) => {
96     const node = getNode(id)(tree);
97     if (node && node.value.type === CollectionFileType.DIRECTORY) {
98         return getNodeDescendantsIds(id)(tree)
99             .reduce((newTree, id) =>
100                 setNodeValueWith(v => ({ ...v, selected: node.value.selected }))(id)(newTree), tree);
101     }
102     return tree;
103 };
104
105 const toggleAncestors = (id: string) => (tree: CollectionPanelFilesState) => {
106     const ancestors = getNodeAncestorsIds(id)(tree).reverse();
107     return ancestors.reduce((newTree, parent) => parent ? toggleParentNode(parent)(newTree) : newTree, tree);
108 };
109
110 const toggleParentNode = (id: string) => (tree: CollectionPanelFilesState) => {
111     const node = getNode(id)(tree);
112     if (node) {
113         const parentNode = getNode(node.id)(tree);
114         if (parentNode) {
115             const selected = parentNode.children
116                 .map(id => getNode(id)(tree))
117                 .every(node => node !== undefined && node.value.selected);
118             return setNodeValueWith(v => ({ ...v, selected }))(parentNode.id)(tree);
119         }
120         return setNode(node)(tree);
121     }
122     return tree;
123 };
124
125