19302: myfavorites works Arvados-DCO-1.1-Signed-off-by: Lisa Knox <lisa.knox@curii...
[arvados.git] / src / store / side-panel-tree / side-panel-tree-actions.ts
1 // Copyright (C) The Arvados Authors. All rights reserved.
2 //
3 // SPDX-License-Identifier: AGPL-3.0
4
5 import { Dispatch } from 'redux';
6 import { treePickerActions } from "store/tree-picker/tree-picker-actions";
7 import { RootState } from 'store/store';
8 import { getUserUuid } from "common/getuser";
9 import { ServiceRepository } from 'services/services';
10 import { FilterBuilder } from 'services/api/filter-builder';
11 import { resourcesActions } from 'store/resources/resources-actions';
12 import { getTreePicker, TreePicker } from 'store/tree-picker/tree-picker';
13 import { getNodeAncestors, getNodeAncestorsIds, getNode, TreeNode, initTreeNode, TreeNodeStatus } from 'models/tree';
14 import { ProjectResource } from 'models/project';
15 import { OrderBuilder } from 'services/api/order-builder';
16 import { ResourceKind } from 'models/resource';
17 import { GroupContentsResourcePrefix } from 'services/groups-service/groups-service';
18 import { GroupClass } from 'models/group';
19 import { CategoriesListReducer } from 'common/plugintypes';
20 import { pluginConfig } from 'plugins';
21 import { LinkClass } from 'models/link';
22
23 export enum SidePanelTreeCategory {
24     PROJECTS = 'Home Projects',
25     SHARED_WITH_ME = 'Shared with me',
26     PUBLIC_FAVORITES = 'Public Favorites',
27     FAVORITES = 'My Favorites',
28     TRASH = 'Trash',
29     ALL_PROCESSES = 'All Processes',
30     GROUPS = 'Groups',
31 }
32
33 export const SIDE_PANEL_TREE = 'sidePanelTree';
34
35 export const getSidePanelTree = (treePicker: TreePicker) =>
36     getTreePicker<ProjectResource | string>(SIDE_PANEL_TREE)(treePicker);
37
38 export const getSidePanelTreeBranch = (uuid: string) => (treePicker: TreePicker): Array<TreeNode<ProjectResource | string>> => {
39     const tree = getSidePanelTree(treePicker);
40     if (tree) {
41         const ancestors = getNodeAncestors(uuid)(tree);
42         const node = getNode(uuid)(tree);
43         if (node) {
44             return [...ancestors, node];
45         }
46     }
47     return [];
48 };
49
50 let SIDE_PANEL_CATEGORIES: string[] = [
51     SidePanelTreeCategory.PROJECTS,
52     SidePanelTreeCategory.SHARED_WITH_ME,
53     SidePanelTreeCategory.PUBLIC_FAVORITES,
54     SidePanelTreeCategory.FAVORITES,
55     SidePanelTreeCategory.GROUPS,
56     SidePanelTreeCategory.ALL_PROCESSES,
57     SidePanelTreeCategory.TRASH
58 ];
59
60 const reduceCatsFn: (a: string[],
61     b: CategoriesListReducer) => string[] = (a, b) => b(a);
62
63 SIDE_PANEL_CATEGORIES = pluginConfig.sidePanelCategories.reduce(reduceCatsFn, SIDE_PANEL_CATEGORIES);
64
65 export const isSidePanelTreeCategory = (id: string) => SIDE_PANEL_CATEGORIES.some(category => category === id);
66
67
68 export const initSidePanelTree = () =>
69     (dispatch: Dispatch, getState: () => RootState, { authService }: ServiceRepository) => {
70         const rootProjectUuid = getUserUuid(getState());
71         if (!rootProjectUuid) { return; }
72         const nodes = SIDE_PANEL_CATEGORIES.map(id => {
73             if (id === SidePanelTreeCategory.PROJECTS) {
74                 return initTreeNode({ id: rootProjectUuid, value: SidePanelTreeCategory.PROJECTS });
75             } else {
76                 return initTreeNode({ id, value: id });
77             }
78         });
79         dispatch(treePickerActions.LOAD_TREE_PICKER_NODE_SUCCESS({
80             id: '',
81             pickerId: SIDE_PANEL_TREE,
82             nodes
83         }));
84         SIDE_PANEL_CATEGORIES.forEach(category => {
85                 if (category !== SidePanelTreeCategory.PROJECTS && category !== SidePanelTreeCategory.FAVORITES && category !== SidePanelTreeCategory.PUBLIC_FAVORITES ) {
86                 dispatch(treePickerActions.LOAD_TREE_PICKER_NODE_SUCCESS({
87                     id: category,
88                     pickerId: SIDE_PANEL_TREE,
89                     nodes: []
90                 }));
91             }
92         });
93     };
94
95 export const loadSidePanelTreeProjects = (projectUuid: string) =>
96     async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
97         const treePicker = getTreePicker(SIDE_PANEL_TREE)(getState().treePicker);
98         const node = treePicker ? getNode(projectUuid)(treePicker) : undefined;
99         if (projectUuid === SidePanelTreeCategory.PUBLIC_FAVORITES) {
100             await dispatch<any>(loadPublicFavorites);
101         } else if (projectUuid === SidePanelTreeCategory.FAVORITES) {
102             await dispatch<any>(loadFavorites);
103         } else if (node || projectUuid !== '') {
104             await dispatch<any>(loadProject(projectUuid));
105         }
106     };
107
108 const loadProject = (projectUuid: string) =>
109     async (dispatch: Dispatch, _: () => RootState, services: ServiceRepository) => {
110         dispatch(treePickerActions.LOAD_TREE_PICKER_NODE({ id: projectUuid, pickerId: SIDE_PANEL_TREE }));
111         const params = {
112             filters: new FilterBuilder()
113                 .addEqual('owner_uuid', projectUuid)
114                 .getFilters(),
115             order: new OrderBuilder<ProjectResource>()
116                 .addAsc('name')
117                 .getOrder()
118         };
119         const { items } = await services.projectService.list(params);
120         dispatch(treePickerActions.LOAD_TREE_PICKER_NODE_SUCCESS({
121             id: projectUuid,
122             pickerId: SIDE_PANEL_TREE,
123             nodes: items.map(item => initTreeNode({ id: item.uuid, value: item })),
124         }));
125         dispatch(resourcesActions.SET_RESOURCES(items));
126     };
127
128 const loadFavorites = async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
129     dispatch(treePickerActions.LOAD_TREE_PICKER_NODE({ id: SidePanelTreeCategory.FAVORITES, pickerId: SIDE_PANEL_TREE }));
130
131     const params = {
132         filters: new FilterBuilder()
133                         .addEqual("link_class", LinkClass.STAR)
134                         .addEqual('tail_uuid', getUserUuid(getState()))
135                         .addEqual('tail_kind', ResourceKind.USER)
136                         .getFilters(),
137         // order: new OrderBuilder<ProjectResource>()
138         //     .addAsc('name', GroupContentsResourcePrefix.PROJECT)
139         //     .getOrder(),
140         // limit: 50
141     }
142
143     const { items } = await services.linkService.list(params);
144
145     dispatch(treePickerActions.LOAD_TREE_PICKER_NODE_SUCCESS({
146         id: SidePanelTreeCategory.FAVORITES,
147         pickerId: SIDE_PANEL_TREE,
148         nodes: items.map(item => initTreeNode({ id: item.headUuid, value: item })),
149     }));
150
151     dispatch(resourcesActions.SET_RESOURCES(items));
152 };
153
154 const loadPublicFavorites = async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
155     // dispatch(treePickerActions.LOAD_TREE_PICKER_NODE({ id: SidePanelTreeCategory.PUBLIC_FAVORITES, pickerId: SIDE_PANEL_TREE }));
156
157     // const params = {
158     //     filters: `[${new FilterBuilder()
159     //         .addIsA('uuid', ResourceKind.PROJECT)
160     //         .addIn('group_class', [GroupClass.PROJECT, GroupClass.FILTER])
161     //         .addDistinct('uuid', getState().auth.config.uuidPrefix + '-j7d0g-publicfavorites')
162     //         .getFilters()}]`,
163     //     order: new OrderBuilder<ProjectResource>()
164     //         .addAsc('name', GroupContentsResourcePrefix.PROJECT)
165     //         .getOrder(),
166     //     limit: 1000
167     // };
168
169     // const { items } = await services.groupsService.shared(params);
170
171     // dispatch(treePickerActions.LOAD_TREE_PICKER_NODE_SUCCESS({
172     //     id: SidePanelTreeCategory.PUBLIC_FAVORITES,
173     //     pickerId: SIDE_PANEL_TREE,
174     //     nodes: items.map(item => initTreeNode({ id: item.uuid, value: item })),
175     // }));
176
177     // dispatch(resourcesActions.SET_RESOURCES(items));
178 };
179
180 export const activateSidePanelTreeItem = (id: string) =>
181     async (dispatch: Dispatch, getState: () => RootState) => {
182         const node = getSidePanelTreeNode(id)(getState().treePicker);
183         console.log(id)
184         if (node && !node.active) {
185             dispatch(treePickerActions.ACTIVATE_TREE_PICKER_NODE({ id, pickerId: SIDE_PANEL_TREE }));
186         }
187         if (!isSidePanelTreeCategory(id)) {
188             await dispatch<any>(activateSidePanelTreeProject(id));
189         }
190     };
191
192 export const activateSidePanelTreeProject = (id: string) =>
193     async (dispatch: Dispatch, getState: () => RootState) => {
194         const { treePicker } = getState();
195         const node = getSidePanelTreeNode(id)(treePicker);
196         if (node && node.status !== TreeNodeStatus.LOADED) {
197             await dispatch<any>(loadSidePanelTreeProjects(id));
198         } else if (node === undefined) {
199             await dispatch<any>(activateSidePanelTreeBranch(id));
200         }
201         dispatch(treePickerActions.EXPAND_TREE_PICKER_NODES({
202             ids: getSidePanelTreeNodeAncestorsIds(id)(treePicker),
203             pickerId: SIDE_PANEL_TREE
204         }));
205         dispatch<any>(expandSidePanelTreeItem(id));
206     };
207
208 export const activateSidePanelTreeBranch = (id: string) =>
209     async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
210         const userUuid = getUserUuid(getState());
211         if (!userUuid) { return; }
212         const ancestors = await services.ancestorsService.ancestors(id, userUuid);
213         // const isShared = ancestors.every(({ uuid }) => uuid !== userUuid);
214         // if (isShared) {
215         //     await dispatch<any>(loadSidePanelTreeProjects(SidePanelTreeCategory.SHARED_WITH_ME));
216         // }
217         for (const ancestor of ancestors) {
218             await dispatch<any>(loadSidePanelTreeProjects(ancestor.uuid));
219         }
220         dispatch(treePickerActions.EXPAND_TREE_PICKER_NODES({
221             ids: [
222                 // ...(isShared ? [SidePanelTreeCategory.SHARED_WITH_ME] : []),
223                 ...ancestors.map(ancestor => ancestor.uuid)
224             ],
225             pickerId: SIDE_PANEL_TREE
226         }));
227         dispatch(treePickerActions.ACTIVATE_TREE_PICKER_NODE({ id, pickerId: SIDE_PANEL_TREE }));
228     };
229
230 export const toggleSidePanelTreeItemCollapse = (id: string) =>
231     async (dispatch: Dispatch, getState: () => RootState) => {
232         const node = getSidePanelTreeNode(id)(getState().treePicker);
233         if (node && node.status === TreeNodeStatus.INITIAL) {
234             await dispatch<any>(loadSidePanelTreeProjects(node.id));
235         }
236         dispatch(treePickerActions.TOGGLE_TREE_PICKER_NODE_COLLAPSE({ id, pickerId: SIDE_PANEL_TREE }));
237     };
238
239 export const expandSidePanelTreeItem = (id: string) =>
240     async (dispatch: Dispatch, getState: () => RootState) => {
241         const node = getSidePanelTreeNode(id)(getState().treePicker);
242         if (node && !node.expanded) {
243             dispatch(treePickerActions.TOGGLE_TREE_PICKER_NODE_COLLAPSE({ id, pickerId: SIDE_PANEL_TREE }));
244         }
245     };
246
247 export const getSidePanelTreeNode = (id: string) => (treePicker: TreePicker) => {
248     const sidePanelTree = getTreePicker(SIDE_PANEL_TREE)(treePicker);
249     return sidePanelTree
250         ? getNode(id)(sidePanelTree)
251         : undefined;
252 };
253
254 export const getSidePanelTreeNodeAncestorsIds = (id: string) => (treePicker: TreePicker) => {
255     const sidePanelTree = getTreePicker(SIDE_PANEL_TREE)(treePicker);
256     return sidePanelTree
257         ? getNodeAncestorsIds(id)(sidePanelTree)
258         : [];
259 };