Merge branch '21128-toolbar-context-menu'
[arvados-workbench2.git] / src / store / collection-panel / collection-panel-files / collection-panel-files-state.ts
1 // Copyright (C) The Arvados Authors. All rights reserved.
2 //
3 // SPDX-License-Identifier: AGPL-3.0
4
5 import { Tree, TreeNode, mapTreeValues, getNodeValue, getNodeDescendants } from 'models/tree';
6 import { CollectionFile, CollectionDirectory, CollectionFileType } from 'models/collection-file';
7 import { ContextMenuResource } from 'store/context-menu/context-menu-actions';
8 import { CollectionResource } from 'models/collection';
9
10 export type CollectionPanelFilesState = Tree<CollectionPanelDirectory | CollectionPanelFile>;
11
12 export interface CollectionPanelDirectory extends CollectionDirectory {
13     collapsed: boolean;
14     selected: boolean;
15 }
16
17 export interface CollectionPanelFile extends CollectionFile {
18     selected: boolean;
19 }
20
21 export interface CollectionFileSelection {
22     collection: CollectionResource;
23     selectedPaths: string[];
24 }
25
26 export const mapCollectionFileToCollectionPanelFile = (node: TreeNode<CollectionDirectory | CollectionFile>): TreeNode<CollectionPanelDirectory | CollectionPanelFile> => {
27     return {
28         ...node,
29         value: node.value.type === CollectionFileType.DIRECTORY
30             ? { ...node.value, selected: false, collapsed: true }
31             : { ...node.value, selected: false }
32     };
33 };
34
35 export const mergeCollectionPanelFilesStates = (oldState: CollectionPanelFilesState, newState: CollectionPanelFilesState) => {
36     return mapTreeValues((value: CollectionPanelDirectory | CollectionPanelFile) => {
37         const oldValue = getNodeValue(value.id)(oldState);
38         return oldValue
39             ? oldValue.type === CollectionFileType.DIRECTORY
40                 ? { ...value, collapsed: oldValue.collapsed, selected: oldValue.selected }
41                 : { ...value, selected: oldValue.selected }
42             : value;
43     })(newState);
44 };
45
46 export const filterCollectionFilesBySelection = (tree: CollectionPanelFilesState, selected: boolean): (CollectionPanelFile | CollectionPanelDirectory)[] => {
47     const allFiles = getNodeDescendants('')(tree).map(node => node.value);
48     const selectedDirectories = allFiles.filter(file => file.selected === selected && file.type === CollectionFileType.DIRECTORY);
49     const selectedFiles = allFiles.filter(file => file.selected === selected && !selectedDirectories.some(dir => dir.id === file.path));
50     return [...selectedDirectories, ...selectedFiles]
51         .filter((value, index, array) => (
52             array.indexOf(value) === index
53         ));
54 };
55
56 export const getCollectionSelection = (sourceCollection: CollectionResource, selectedItems: (CollectionPanelDirectory | CollectionPanelFile | ContextMenuResource)[]) => ({
57     collection: sourceCollection,
58     selectedPaths: selectedItems.map(itemsToPaths).map(trimPathUuids(sourceCollection.uuid)),
59 })
60
61 const itemsToPaths = (item: (CollectionPanelDirectory | CollectionPanelFile | ContextMenuResource)): string => ('uuid' in item) ? item.uuid : item.id;
62
63 const trimPathUuids = (parentCollectionUuid: string) => (path: string) => path.replace(new RegExp(`(^${parentCollectionUuid})`), '');