Replace go back item with browser back button support
[arvados-workbench2.git] / src / store / project / project-reducer.ts
1 // Copyright (C) The Arvados Authors. All rights reserved.
2 //
3 // SPDX-License-Identifier: AGPL-3.0
4
5 import * as _ from "lodash";
6
7 import { Project } from "../../models/project";
8 import actions, { ProjectAction } from "./project-action";
9 import { TreeItem, TreeItemStatus } from "../../components/tree/tree";
10
11 export type ProjectState = {
12     items: Array<TreeItem<Project>>,
13     currentItemId: string
14 };
15
16 export function findTreeItem<T>(tree: Array<TreeItem<T>>, itemId: string): TreeItem<T> | undefined {
17     let item;
18     for (const t of tree) {
19         item = t.id === itemId
20             ? t
21             : findTreeItem(t.items ? t.items : [], itemId);
22         if (item) {
23             break;
24         }
25     }
26     return item;
27 }
28
29 export function getActiveTreeItem<T>(tree: Array<TreeItem<T>>): TreeItem<T> | undefined {
30     let item;
31     for (const t of tree) {
32         item = t.active
33             ? t
34             : getActiveTreeItem(t.items ? t.items : []);
35         if (item) {
36             break;
37         }
38     }
39     return item;
40 }
41
42 export function getTreePath<T>(tree: Array<TreeItem<T>>, itemId: string): Array<TreeItem<T>> {
43     for (const item of tree){
44         if(item.id === itemId){
45             return [item];
46         } else {
47             const branch = getTreePath(item.items || [], itemId);
48             if(branch.length > 0){
49                 return [item, ...branch];
50             }
51         }
52     }
53     return [];
54 }
55
56 function resetTreeActivity<T>(tree: Array<TreeItem<T>>) {
57     for (const t of tree) {
58         t.active = false;
59         resetTreeActivity(t.items ? t.items : []);
60     }
61 }
62
63 function updateProjectTree(tree: Array<TreeItem<Project>>, projects: Project[], parentItemId?: string): Array<TreeItem<Project>> {
64     let treeItem;
65     if (parentItemId) {
66         treeItem = findTreeItem(tree, parentItemId);
67         if (treeItem) {
68             treeItem.status = TreeItemStatus.Loaded;
69         }
70     }
71     const items = projects.map(p => ({
72         id: p.uuid,
73         open: false,
74         active: false,
75         status: TreeItemStatus.Initial,
76         data: p,
77         items: []
78     } as TreeItem<Project>));
79
80     if (treeItem) {
81         treeItem.items = items;
82         return tree;
83     }
84
85     return items;
86 }
87
88 const projectsReducer = (state: ProjectState = { items: [], currentItemId: "" }, action: ProjectAction) => {
89     return actions.match(action, {
90         CREATE_PROJECT: project => ({
91             ...state,
92             items: state.items.concat({
93                 id: project.uuid,
94                 open: false,
95                 active: false,
96                 status: TreeItemStatus.Loaded,
97                 toggled: false,
98                 items: [],
99                 data: project
100             })
101         }),
102         REMOVE_PROJECT: () => state,
103         PROJECTS_REQUEST: itemId => {
104             const items = _.cloneDeep(state.items);
105             const item = findTreeItem(items, itemId);
106             if (item) {
107                 item.status = TreeItemStatus.Pending;
108                 state.items = items;
109             }
110             return state;
111         },
112         PROJECTS_SUCCESS: ({ projects, parentItemId }) => {
113             return {
114                 ...state,
115                 items: updateProjectTree(state.items, projects, parentItemId)
116             };
117         },
118         TOGGLE_PROJECT_TREE_ITEM_OPEN: itemId => {
119             const items = _.cloneDeep(state.items);
120             const item = findTreeItem(items, itemId);
121             if (item) {
122                 item.toggled = true;
123                 item.open = !item.open;
124             }
125             return {
126                 items,
127                 currentItemId: itemId
128             };
129         },
130         TOGGLE_PROJECT_TREE_ITEM_ACTIVE: itemId => {
131             const items = _.cloneDeep(state.items);
132             resetTreeActivity(items);
133             const item = findTreeItem(items, itemId);
134             if (item) {
135                 item.toggled = true;
136                 item.active = true;
137             }
138             return {
139                 items,
140                 currentItemId: itemId
141             };
142         },
143         RESET_PROJECT_TREE_ACTIVITY: () => {
144             const items = _.cloneDeep(state.items);
145             resetTreeActivity(items);
146             return {
147                 items,
148                 currentItemId: ""
149             };
150         },
151         default: () => state
152     });
153 };
154
155 export default projectsReducer;