1 // Copyright (C) The Arvados Authors. All rights reserved.
3 // SPDX-License-Identifier: AGPL-3.0
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";
10 export const collectionPanelFilesReducer = (state: CollectionPanelFilesState = createTree(), action: CollectionPanelFilesAction) => {
11 // Low-level tree handling setNode() func does in-place data modifications
12 // for performance reasons, so we pass a copy of 'state' to avoid side effects.
13 return collectionPanelFilesAction.match(action, {
14 SET_COLLECTION_FILES: files =>
15 mergeCollectionPanelFilesStates({...state}, mapTree(mapCollectionFileToCollectionPanelFile)(files)),
17 TOGGLE_COLLECTION_FILE_COLLAPSE: data =>
18 toggleCollapse(data.id)({...state}),
20 TOGGLE_COLLECTION_FILE_SELECTION: data => [{...state}]
21 .map(toggleSelected(data.id))
22 .map(toggleAncestors(data.id))
23 .map(toggleDescendants(data.id))[0],
25 SELECT_ALL_COLLECTION_FILES: () =>
26 mapTreeValues(v => ({ ...v, selected: true }))({...state}),
28 UNSELECT_ALL_COLLECTION_FILES: () =>
29 mapTreeValues(v => ({ ...v, selected: false }))({...state}),
32 }) as CollectionPanelFilesState;
35 const toggleCollapse = (id: string) => (tree: CollectionPanelFilesState) =>
36 setNodeValueWith((v: CollectionPanelDirectory | CollectionPanelFile) =>
37 v.type === CollectionFileType.DIRECTORY
38 ? { ...v, collapsed: !v.collapsed }
42 const toggleSelected = (id: string) => (tree: CollectionPanelFilesState) =>
43 setNodeValueWith((v: CollectionPanelDirectory | CollectionPanelFile) => ({ ...v, selected: !v.selected }))(id)(tree);
46 const toggleDescendants = (id: string) => (tree: CollectionPanelFilesState) => {
47 const node = getNode(id)(tree);
48 if (node && node.value.type === CollectionFileType.DIRECTORY) {
49 return getNodeDescendantsIds(id)(tree)
50 .reduce((newTree, id) =>
51 setNodeValueWith(v => ({ ...v, selected: node.value.selected }))(id)(newTree), tree);
56 const toggleAncestors = (id: string) => (tree: CollectionPanelFilesState) => {
57 const ancestors = getNodeAncestorsIds(id)(tree).reverse();
58 return ancestors.reduce((newTree, parent) => parent ? toggleParentNode(parent)(newTree) : newTree, tree);
61 const toggleParentNode = (id: string) => (tree: CollectionPanelFilesState) => {
62 const node = getNode(id)(tree);
64 const parentNode = getNode(node.id)(tree);
66 const selected = parentNode.children
67 .map(id => getNode(id)(tree))
68 .every(node => node !== undefined && node.value.selected);
69 return setNodeValueWith(v => ({ ...v, selected }))(parentNode.id)(tree);
71 return setNode(node)(tree);