[ARVADOS-WORKBENCH2] updated: 1.2.0-132-g1e32e61

Git user git at public.curoverse.com
Sat Aug 25 16:48:35 EDT 2018


Summary of changes:
 src/store/breadcrumbs/breadcrumbs-actions.ts       | 46 ++++++++++++++++++++++
 src/store/navigation/navigation-action.ts          | 25 ++++++++----
 .../side-panel-tree/side-panel-tree-actions.ts     | 26 +++++++++---
 src/store/side-panel/side-panel-action.ts          |  4 +-
 src/store/tree-picker/tree-picker.ts               |  7 ++--
 src/views-components/breadcrumbs/breadcrumbs.ts    | 43 +++++---------------
 src/views-components/main-app-bar/main-app-bar.tsx | 15 +++----
 src/views/favorite-panel/favorite-panel.tsx        |  4 +-
 src/views/project-panel/project-panel.tsx          |  6 +--
 src/views/workbench/workbench.tsx                  | 14 ++++---
 10 files changed, 115 insertions(+), 75 deletions(-)
 create mode 100644 src/store/breadcrumbs/breadcrumbs-actions.ts

       via  1e32e6134cd53c30cd8a9410572a443b37f76f2e (commit)
      from  1c0f11ed17dea5be97e8ca63385f979925f1fb08 (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.


commit 1e32e6134cd53c30cd8a9410572a443b37f76f2e
Author: Michal Klobukowski <michal.klobukowski at contractors.roche.com>
Date:   Sat Aug 25 22:48:14 2018 +0200

    Create breadcrumbs actions
    
    Feature #14102
    
    Arvados-DCO-1.1-Signed-off-by: Michal Klobukowski <michal.klobukowski at contractors.roche.com>

diff --git a/src/store/breadcrumbs/breadcrumbs-actions.ts b/src/store/breadcrumbs/breadcrumbs-actions.ts
new file mode 100644
index 0000000..254a8d3
--- /dev/null
+++ b/src/store/breadcrumbs/breadcrumbs-actions.ts
@@ -0,0 +1,46 @@
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+import { Dispatch } from 'redux';
+import { RootState } from '~/store/store';
+import { Breadcrumb } from '~/components/breadcrumbs/breadcrumbs';
+import { getResource } from '~/store/resources/resources';
+import { TreePicker } from '../tree-picker/tree-picker';
+import { getSidePanelTreeBranch } from '../side-panel-tree/side-panel-tree-actions';
+import { propertiesActions } from '../properties/properties-actions';
+
+export const BREADCRUMBS = 'breadcrumbs';
+
+export interface ResourceBreadcrumb extends Breadcrumb {
+    uuid: string;
+}
+
+export const setBreadcrumbs = (breadcrumbs: Breadcrumb[]) =>
+    propertiesActions.SET_PROPERTY({ key: BREADCRUMBS, value: breadcrumbs });
+
+const getSidePanelTreeBreadcrumbs = (uuid: string) => (treePicker: TreePicker): ResourceBreadcrumb[] => {
+    const nodes = getSidePanelTreeBranch(uuid)(treePicker);
+    return nodes.map(node =>
+        typeof node.value === 'string'
+            ? { label: node.value, uuid: node.nodeId }
+            : { label: node.value.name, uuid: node.value.uuid });
+};
+
+export const setSidePanelBreadcrumbs = (uuid: string) =>
+    (dispatch: Dispatch, getState: () => RootState) => {
+        const { treePicker } = getState();
+        const breadcrumbs = getSidePanelTreeBreadcrumbs(uuid)(treePicker);
+        dispatch(setBreadcrumbs(breadcrumbs));
+    };
+
+export const setProjectBreadcrumbs = setSidePanelBreadcrumbs;
+
+export const setCollectionBreadcrumbs = (collectionUuid: string) =>
+    (dispatch: Dispatch, getState: () => RootState) => {
+        const { resources } = getState();
+        const collection = getResource(collectionUuid)(resources);
+        if (collection) {
+            dispatch<any>(setProjectBreadcrumbs(collection.ownerUuid));
+        }
+    };
diff --git a/src/store/navigation/navigation-action.ts b/src/store/navigation/navigation-action.ts
index db8efbf..b5dc5e9 100644
--- a/src/store/navigation/navigation-action.ts
+++ b/src/store/navigation/navigation-action.ts
@@ -5,7 +5,7 @@
 import { Dispatch, compose } from 'redux';
 import { push } from "react-router-redux";
 import { RootState } from "../store";
-import { ResourceKind, Resource } from '~/models/resource';
+import { ResourceKind, Resource, extractUuidKind } from '~/models/resource';
 import { getCollectionUrl } from "~/models/collection";
 import { getProjectUrl } from "~/models/project";
 import { getResource } from '~/store/resources/resources';
@@ -23,12 +23,18 @@ import { favoritePanelActions } from '~/store/favorite-panel/favorite-panel-acti
 import { projectPanelColumns } from '~/views/project-panel/project-panel';
 import { favoritePanelColumns } from '~/views/favorite-panel/favorite-panel';
 import { matchRootRoute } from '~/routes/routes';
+import { setCollectionBreadcrumbs, setProjectBreadcrumbs, setSidePanelBreadcrumbs } from '../breadcrumbs/breadcrumbs-actions';
 
-export const navigateToResource = (uuid: string) =>
+export const navigateTo = (uuid: string) =>
     async (dispatch: Dispatch, getState: () => RootState) => {
-        const resource = getResource(uuid)(getState().resources);
-        if (resource) {
-            dispatch<any>(getResourceNavigationAction(resource));
+        const kind = extractUuidKind(uuid);
+        if (kind === ResourceKind.PROJECT || kind === ResourceKind.USER) {
+            dispatch<any>(navigateToProject(uuid));
+        } else if (kind === ResourceKind.COLLECTION) {
+            dispatch<any>(navigateToCollection(uuid));
+        }
+        if (uuid === SidePanelTreeCategory.FAVORITES) {
+            dispatch<any>(navigateToFavorites);
         }
     };
 
@@ -74,14 +80,16 @@ export const loadFavorites = () =>
     async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
         dispatch<any>(activateSidePanelTreeItem(SidePanelTreeCategory.FAVORITES));
         dispatch<any>(loadFavoritePanel());
+        dispatch<any>(setSidePanelBreadcrumbs(SidePanelTreeCategory.FAVORITES));
     };
 
 
 export const navigateToProject = compose(push, getProjectUrl);
 
 export const loadProject = (uuid: string) =>
-    (dispatch: Dispatch) => {
-        dispatch<any>(activateSidePanelTreeItem(uuid));
+    async (dispatch: Dispatch) => {
+        await dispatch<any>(activateSidePanelTreeItem(uuid));
+        dispatch<any>(setProjectBreadcrumbs(uuid));
         dispatch<any>(openProjectPanel(uuid));
         dispatch(loadDetailsPanel(uuid));
     };
@@ -91,7 +99,8 @@ export const navigateToCollection = compose(push, getCollectionUrl);
 export const loadCollection = (uuid: string) =>
     async (dispatch: Dispatch) => {
         const collection = await dispatch<any>(loadCollectionPanel(uuid));
-        dispatch<any>(activateSidePanelTreeItem(collection.ownerUuid));
+        await dispatch<any>(activateSidePanelTreeItem(collection.ownerUuid));
+        dispatch<any>(setCollectionBreadcrumbs(collection.uuid));
         dispatch(loadDetailsPanel(uuid));
     };
 
diff --git a/src/store/side-panel-tree/side-panel-tree-actions.ts b/src/store/side-panel-tree/side-panel-tree-actions.ts
index 268f1e6..2fe5aba 100644
--- a/src/store/side-panel-tree/side-panel-tree-actions.ts
+++ b/src/store/side-panel-tree/side-panel-tree-actions.ts
@@ -4,14 +4,15 @@
 
 import { Dispatch } from 'redux';
 import { treePickerActions } from "~/store/tree-picker/tree-picker-actions";
-import { createTreePickerNode } from '~/store/tree-picker/tree-picker';
+import { createTreePickerNode, TreePickerNode } from '~/store/tree-picker/tree-picker';
 import { RootState } from '../store';
 import { ServiceRepository } from '~/services/services';
 import { FilterBuilder } from '~/common/api/filter-builder';
 import { resourcesActions } from '../resources/resources-actions';
-import { getNodeValue } from '../../models/tree';
-import { getTreePicker } from '../tree-picker/tree-picker';
+import { getTreePicker, TreePicker } from '../tree-picker/tree-picker';
 import { TreeItemStatus } from "~/components/tree/tree";
+import { getNodeAncestors, getNodeValue } from '~/models/tree';
+import { ProjectResource } from '~/models/project';
 
 export enum SidePanelTreeCategory {
     PROJECTS = 'Projects',
@@ -24,6 +25,21 @@ export enum SidePanelTreeCategory {
 
 export const SIDE_PANEL_TREE = 'sidePanelTree';
 
+export const getSidePanelTree = (treePicker: TreePicker) =>
+    getTreePicker<ProjectResource | string>(SIDE_PANEL_TREE)(treePicker);
+
+export const getSidePanelTreeBranch = (uuid: string) => (treePicker: TreePicker): Array<TreePickerNode<ProjectResource | string>> => {
+    const tree = getSidePanelTree(treePicker);
+    if (tree) {
+        const ancestors = getNodeAncestors(uuid)(tree).map(node => node.value);
+        const node = getNodeValue(uuid)(tree);
+        if (node) {
+            return [...ancestors, node];
+        }
+    }
+    return [];
+};
+
 const SIDE_PANEL_CATEGORIES = [
     SidePanelTreeCategory.SHARED_WITH_ME,
     SidePanelTreeCategory.WORKFLOWS,
@@ -77,7 +93,7 @@ export const activateSidePanelTreeItem = (nodeId: string) =>
             dispatch(treePickerActions.TOGGLE_TREE_PICKER_NODE_SELECT({ nodeId, pickerId: SIDE_PANEL_TREE }));
         }
         if (!isSidePanelTreeCategory(nodeId)) {
-            dispatch<any>(activateSidePanelTreeProject(nodeId));
+            await dispatch<any>(activateSidePanelTreeProject(nodeId));
         }
     };
 
@@ -90,7 +106,7 @@ export const activateSidePanelTreeProject = (nodeId: string) =>
                 dispatch<any>(toggleSidePanelTreeItemCollapse(nodeId));
             }
         } else if (node === undefined) {
-            dispatch<any>(activateSidePanelTreeBranch(nodeId));
+            await dispatch<any>(activateSidePanelTreeBranch(nodeId));
         }
     };
 
diff --git a/src/store/side-panel/side-panel-action.ts b/src/store/side-panel/side-panel-action.ts
index 4fc745b..8c7ef4a 100644
--- a/src/store/side-panel/side-panel-action.ts
+++ b/src/store/side-panel/side-panel-action.ts
@@ -4,7 +4,7 @@
 
 import { Dispatch } from 'redux';
 import { isSidePanelTreeCategory, SidePanelTreeCategory } from '~/store/side-panel-tree/side-panel-tree-actions';
-import { navigateToFavorites, navigateToResource } from '../navigation/navigation-action';
+import { navigateToFavorites, navigateTo } from '../navigation/navigation-action';
 import { snackbarActions } from '~/store/snackbar/snackbar-actions';
 
 export const navigateFromSidePanel = (id: string) =>
@@ -12,7 +12,7 @@ export const navigateFromSidePanel = (id: string) =>
         if (isSidePanelTreeCategory(id)) {
             dispatch<any>(getSidePanelTreeCategoryAction(id));
         } else {
-            dispatch<any>(navigateToResource(id));
+            dispatch<any>(navigateTo(id));
         }
     };
 
diff --git a/src/store/tree-picker/tree-picker.ts b/src/store/tree-picker/tree-picker.ts
index fd104fe..259a4b8 100644
--- a/src/store/tree-picker/tree-picker.ts
+++ b/src/store/tree-picker/tree-picker.ts
@@ -4,13 +4,12 @@
 
 import { Tree } from "~/models/tree";
 import { TreeItemStatus } from "~/components/tree/tree";
-import { RootState } from '~/store/store';
 
 export type TreePicker = { [key: string]: Tree<TreePickerNode> };
 
-export interface TreePickerNode {
+export interface TreePickerNode<Value = any> {
     nodeId: string;
-    value: any;
+    value: Value;
     selected: boolean;
     collapsed: boolean;
     status: TreeItemStatus;
@@ -23,4 +22,4 @@ export const createTreePickerNode = (data: { nodeId: string, value: any }) => ({
     status: TreeItemStatus.INITIAL
 });
 
-export const getTreePicker = (id: string) => (state: TreePicker): Tree<TreePickerNode> | undefined => state[id];
\ No newline at end of file
+export const getTreePicker = <Value = {}>(id: string) => (state: TreePicker): Tree<TreePickerNode<Value>> | undefined => state[id];
\ No newline at end of file
diff --git a/src/views-components/breadcrumbs/breadcrumbs.ts b/src/views-components/breadcrumbs/breadcrumbs.ts
index 306b29e..69eb9e3 100644
--- a/src/views-components/breadcrumbs/breadcrumbs.ts
+++ b/src/views-components/breadcrumbs/breadcrumbs.ts
@@ -5,50 +5,25 @@
 import { connect } from "react-redux";
 import { Breadcrumbs as BreadcrumbsComponent, BreadcrumbsProps } from '~/components/breadcrumbs/breadcrumbs';
 import { RootState } from '~/store/store';
-import { Breadcrumb } from '~/components/breadcrumbs/breadcrumbs';
-import { matchProjectRoute } from '~/routes/routes';
-import { getTreePicker } from '~/store/tree-picker/tree-picker';
-import { SIDE_PANEL_TREE } from '~/store/side-panel-tree/side-panel-tree-actions';
-import { getNodeAncestors, getNode } from '~/models/tree';
 import { Dispatch } from 'redux';
-import { navigateToResource } from '~/store/navigation/navigation-action';
+import { navigateTo } from '~/store/navigation/navigation-action';
+import { getProperty } from '../../store/properties/properties';
+import { ResourceBreadcrumb, BREADCRUMBS } from '../../store/breadcrumbs/breadcrumbs-actions';
+
 
-interface ResourceBreadcrumb extends Breadcrumb {
-    uuid: string;
-}
 
 type BreadcrumbsDataProps = Pick<BreadcrumbsProps, 'items'>;
 type BreadcrumbsActionProps = Pick<BreadcrumbsProps, 'onClick' | 'onContextMenu'>;
 
-const memoizedMapStateToProps = () => {
-    let items: ResourceBreadcrumb[] = [];
-    return ({ router, treePicker }: RootState): BreadcrumbsDataProps => {
-        if (router.location) {
-            const projectMatch = matchProjectRoute(location.pathname);
-            const collectionMatch = matchProjectRoute(location.pathname);
-            const uuid = projectMatch && projectMatch.params.id
-                || collectionMatch && collectionMatch.params.id
-                || '';
-            const tree = getTreePicker(SIDE_PANEL_TREE)(treePicker);
-            if (tree) {
-                const ancestors = getNodeAncestors(uuid)(tree);
-                const node = getNode(uuid)(tree);
-                const nodes = node ? [...ancestors, node] : ancestors;
-                items = nodes.map(({ value }) =>
-                    typeof value.value === 'string'
-                        ? { label: value.value, uuid: value.nodeId }
-                        : { label: value.value.name, uuid: value.value.uuid });
-            }
-        }
-        return { items };
-    };
-};
+const mapStateToProps = () => ({ properties }: RootState): BreadcrumbsDataProps => ({
+    items: getProperty<ResourceBreadcrumb[]>(BREADCRUMBS)(properties) || []
+});
 
 const mapDispatchToProps = (dispatch: Dispatch): BreadcrumbsActionProps => ({
     onClick: ({ uuid }: ResourceBreadcrumb) => {
-        dispatch<any>(navigateToResource(uuid));
+        dispatch<any>(navigateTo(uuid));
     },
     onContextMenu: () => { return; }
 });
 
-export const Breadcrumbs = connect(memoizedMapStateToProps(), mapDispatchToProps)(BreadcrumbsComponent);
\ No newline at end of file
+export const Breadcrumbs = connect(mapStateToProps(), mapDispatchToProps)(BreadcrumbsComponent);
\ No newline at end of file
diff --git a/src/views-components/main-app-bar/main-app-bar.tsx b/src/views-components/main-app-bar/main-app-bar.tsx
index 54d6a5d..4c16754 100644
--- a/src/views-components/main-app-bar/main-app-bar.tsx
+++ b/src/views-components/main-app-bar/main-app-bar.tsx
@@ -23,7 +23,7 @@ export interface MainAppBarMenuItems {
 interface MainAppBarDataProps {
     searchText: string;
     searchDebounce?: number;
-    breadcrumbs: Breadcrumb[];
+    breadcrumbs: React.ComponentType<any>;
     user?: User;
     menuItems: MainAppBarMenuItems;
     buildInfo: string;
@@ -68,15 +68,10 @@ export const MainAppBar: React.SFC<MainAppBarProps> = (props) => {
             </Grid>
         </Toolbar>
         <Toolbar >
-            {
-                props.user && <Breadcrumbs
-                    items={props.breadcrumbs}
-                    onClick={props.onBreadcrumbClick}
-                    onContextMenu={props.onContextMenu} />
-            }
-            { props.user && <IconButton color="inherit" onClick={props.onDetailsPanelToggle}>
-                    <DetailsIcon />
-                </IconButton>
+            {props.user && <props.breadcrumbs />}
+            {props.user && <IconButton color="inherit" onClick={props.onDetailsPanelToggle}>
+                <DetailsIcon />
+            </IconButton>
             }
         </Toolbar>
     </AppBar>;
diff --git a/src/views/favorite-panel/favorite-panel.tsx b/src/views/favorite-panel/favorite-panel.tsx
index cdfe970..3fd33ae 100644
--- a/src/views/favorite-panel/favorite-panel.tsx
+++ b/src/views/favorite-panel/favorite-panel.tsx
@@ -22,7 +22,7 @@ import { Dispatch } from 'redux';
 import { contextMenuActions } from '~/store/context-menu/context-menu-actions';
 import { ContextMenuKind } from '~/views-components/context-menu/context-menu';
 import { loadDetailsPanel } from '../../store/details-panel/details-panel-action';
-import { navigateToResource } from '~/store/navigation/navigation-action';
+import { navigateTo } from '~/store/navigation/navigation-action';
 
 type CssRules = "toolbar" | "button";
 
@@ -164,7 +164,7 @@ const mapDispatchToProps = (dispatch: Dispatch): FavoritePanelActionProps => ({
         dispatch<any>(loadDetailsPanel(resourceUuid));
     },
     onItemDoubleClick: uuid => {
-        dispatch<any>(navigateToResource(uuid));
+        dispatch<any>(navigateTo(uuid));
     }
 });
 
diff --git a/src/views/project-panel/project-panel.tsx b/src/views/project-panel/project-panel.tsx
index e1c9d83..f9dcc39 100644
--- a/src/views/project-panel/project-panel.tsx
+++ b/src/views/project-panel/project-panel.tsx
@@ -28,10 +28,9 @@ import { openProjectCreator } from '~/store/project/project-action';
 import { reset } from 'redux-form';
 import { COLLECTION_CREATE_DIALOG } from '~/views-components/dialog-create/dialog-collection-create';
 import { collectionCreateActions } from '~/store/collections/creator/collection-creator-action';
-import { navigateToResource } from '~/store/navigation/navigation-action';
+import { navigateTo } from '~/store/navigation/navigation-action';
 import { getProperty } from '~/store/properties/properties';
 import { PROJECT_PANEL_CURRENT_UUID } from '~/store/project-panel/project-panel-action';
-import { Breadcrumbs } from '~/views-components/breadcrumbs/breadcrumbs';
 
 type CssRules = 'root' | "toolbar" | "button";
 
@@ -182,7 +181,6 @@ export const ProjectPanel = withStyles(styles)(
                             New project
                         </Button>
                     </div>
-                    <Breadcrumbs />
                     <DataExplorer
                         id={PROJECT_PANEL_ID}
                         onRowClick={this.handleRowClick}
@@ -232,7 +230,7 @@ export const ProjectPanel = withStyles(styles)(
             }
 
             handleRowDoubleClick = (uuid: string) => {
-                this.props.dispatch<any>(navigateToResource(uuid));
+                this.props.dispatch<any>(navigateTo(uuid));
             }
 
             handleRowClick = (uuid: string) => {
diff --git a/src/views/workbench/workbench.tsx b/src/views/workbench/workbench.tsx
index 8d8d937..a4defc1 100644
--- a/src/views/workbench/workbench.tsx
+++ b/src/views/workbench/workbench.tsx
@@ -5,7 +5,7 @@
 import * as React from 'react';
 import { StyleRulesCallback, WithStyles, withStyles } from '@material-ui/core/styles';
 import { connect, DispatchProp } from "react-redux";
-import { Route, Switch, Redirect } from "react-router";
+import { Route, Switch } from "react-router";
 import { login, logout } from "~/store/auth/auth-action";
 import { User } from "~/models/user";
 import { RootState } from "~/store/store";
@@ -18,9 +18,9 @@ import { ProjectPanel } from "~/views/project-panel/project-panel";
 import { DetailsPanel } from '~/views-components/details-panel/details-panel';
 import { ArvadosTheme } from '~/common/custom-theme';
 import { CreateProjectDialog } from "~/views-components/create-project-dialog/create-project-dialog";
-import { detailsPanelActions, loadDetailsPanel } from "~/store/details-panel/details-panel-action";
+import { detailsPanelActions } from "~/store/details-panel/details-panel-action";
 import { openContextMenu } from '~/store/context-menu/context-menu-actions';
-import { ProjectResource, getProjectUrl } from '~/models/project';
+import { ProjectResource } from '~/models/project';
 import { ContextMenu, ContextMenuKind } from "~/views-components/context-menu/context-menu";
 import { FavoritePanel } from "../favorite-panel/favorite-panel";
 import { CurrentTokenDialog } from '~/views-components/current-token-dialog/current-token-dialog';
@@ -41,7 +41,9 @@ import { MoveProjectDialog } from '~/views-components/move-project-dialog/move-p
 import { MoveCollectionDialog } from '~/views-components/move-collection-dialog/move-collection-dialog';
 import { SidePanel } from '~/views-components/side-panel/side-panel';
 import { Routes } from '~/routes/routes';
-import { navigateToResource } from '~/store/navigation/navigation-action';
+import { navigateTo } from '~/store/navigation/navigation-action';
+import { Breadcrumbs } from '~/views-components/breadcrumbs/breadcrumbs';
+
 
 const APP_BAR_HEIGHT = 100;
 
@@ -173,7 +175,7 @@ export const Workbench = withStyles(styles)(
                     <div className={classes.root}>
                         <div className={classes.appBar}>
                             <MainAppBar
-                                breadcrumbs={breadcrumbs}
+                                breadcrumbs={Breadcrumbs}
                                 searchText={this.state.searchText}
                                 user={this.props.user}
                                 menuItems={this.state.menuItems}
@@ -216,7 +218,7 @@ export const Workbench = withStyles(styles)(
 
             mainAppBarActions: MainAppBarActionProps = {
                 onBreadcrumbClick: ({ itemId }: NavBreadcrumb) => {
-                    this.props.dispatch(navigateToResource(itemId));
+                    this.props.dispatch(navigateTo(itemId));
                 },
                 onSearch: searchText => {
                     this.setState({ searchText });

-----------------------------------------------------------------------


hooks/post-receive
-- 




More information about the arvados-commits mailing list