Replace go back item with browser back button support
authorMichal Klobukowski <michal.klobukowski@contractors.roche.com>
Fri, 29 Jun 2018 14:41:35 +0000 (16:41 +0200)
committerMichal Klobukowski <michal.klobukowski@contractors.roche.com>
Fri, 29 Jun 2018 14:41:35 +0000 (16:41 +0200)
Feature #13678

Arvados-DCO-1.1-Signed-off-by: Michal Klobukowski <michal.klobukowski@contractors.roche.com>

src/models/resource.ts
src/store/navigation/navigation-action.ts
src/store/project/project-action.ts
src/store/project/project-reducer.ts
src/store/side-panel/side-panel-action.ts
src/views/project-panel/project-panel-selectors.ts
src/views/project-panel/project-panel.tsx
src/views/workbench/workbench.tsx

index 0f5fbc28f68cbd151857c31a59d9b634626a59a2..1dd5979b521a005af1ff94fbf219f6dee3138de8 100644 (file)
@@ -16,15 +16,18 @@ export enum ResourceKind {
     PROJECT = "project",
     COLLECTION = "collection",
     PIPELINE = "pipeline",
     PROJECT = "project",
     COLLECTION = "collection",
     PIPELINE = "pipeline",
-    LEVEL_UP = "",
     UNKNOWN = "unknown"
 }
 
 export function getResourceKind(itemKind: string) {
     switch (itemKind) {
     UNKNOWN = "unknown"
 }
 
 export function getResourceKind(itemKind: string) {
     switch (itemKind) {
-        case "arvados#project": return ResourceKind.PROJECT;
-        case "arvados#collection": return ResourceKind.COLLECTION;
-        case "arvados#pipeline": return ResourceKind.PIPELINE;
+        case "arvados#project":
+        case "arvados#group":
+            return ResourceKind.PROJECT;
+        case "arvados#collection":
+            return ResourceKind.COLLECTION;
+        case "arvados#pipeline":
+            return ResourceKind.PIPELINE;
         default:
             return ResourceKind.UNKNOWN;
     }
         default:
             return ResourceKind.UNKNOWN;
     }
index b811f9a292b8b42bf9cfb6ba8fcdf93db017351d..a4b9a8f6ede99f23d6ef58ae35710a04ac08912e 100644 (file)
@@ -17,11 +17,9 @@ import { RootState } from "../store";
 
 export const getResourceUrl = (resource: Resource): string => {
     switch (resource.kind) {
 
 export const getResourceUrl = (resource: Resource): string => {
     switch (resource.kind) {
-        case ResourceKind.LEVEL_UP: return `/projects/${resource.ownerUuid}`;
         case ResourceKind.PROJECT: return `/projects/${resource.uuid}`;
         case ResourceKind.COLLECTION: return `/collections/${resource.uuid}`;
         case ResourceKind.PROJECT: return `/projects/${resource.uuid}`;
         case ResourceKind.COLLECTION: return `/collections/${resource.uuid}`;
-        default:
-            return "#";
+        default: return "";
     }
 };
 
     }
 };
 
@@ -31,50 +29,45 @@ export enum ItemMode {
     ACTIVE
 }
 
     ACTIVE
 }
 
-export const setProjectItem = (itemId: string, itemKind = ResourceKind.PROJECT, itemMode = ItemMode.OPEN) =>
+export const setProjectItem = (itemId: string, itemMode: ItemMode) =>
     (dispatch: Dispatch, getState: () => RootState) => {
     (dispatch: Dispatch, getState: () => RootState) => {
-        const { projects } = getState();
-
-        let treeItem = findTreeItem(projects.items, itemId);
-        if (treeItem && itemKind === ResourceKind.LEVEL_UP) {
-            treeItem = findTreeItem(projects.items, treeItem.data.ownerUuid);
-        }
+        const { projects, router } = getState();
+        const treeItem = findTreeItem(projects.items, itemId);
 
         if (treeItem) {
 
         if (treeItem) {
-            dispatch(projectActions.TOGGLE_PROJECT_TREE_ITEM_ACTIVE(treeItem.data.uuid));
 
 
-            if (treeItem.status === TreeItemStatus.Loaded) {
-                dispatch<any>(openProjectItem(treeItem.data, itemKind, itemMode));
-            } else {
-                dispatch<any>(getProjectList(itemId))
-                    .then(() => dispatch<any>(openProjectItem(treeItem!.data, itemKind, itemMode)));
+            dispatch(sidePanelActions.RESET_SIDE_PANEL_ACTIVITY());
+
+            if (itemMode === ItemMode.OPEN || itemMode === ItemMode.BOTH) {
+                dispatch(projectActions.TOGGLE_PROJECT_TREE_ITEM_OPEN(treeItem.data.uuid));
             }
             }
+
+            const resourceUrl = getResourceUrl({ ...treeItem.data });
+
             if (itemMode === ItemMode.ACTIVE || itemMode === ItemMode.BOTH) {
             if (itemMode === ItemMode.ACTIVE || itemMode === ItemMode.BOTH) {
-                dispatch<any>(getCollectionList(itemId));
+                if (router.location && !router.location.pathname.includes(resourceUrl)) {
+                    dispatch(push(resourceUrl));
+                }
+                dispatch(projectActions.TOGGLE_PROJECT_TREE_ITEM_ACTIVE(treeItem.data.uuid));
             }
             }
-        }
-    };
 
 
-const openProjectItem = (resource: Resource, itemKind: ResourceKind, itemMode: ItemMode) =>
-    (dispatch: Dispatch, getState: () => RootState) => {
+            const promise = treeItem.status === TreeItemStatus.Loaded
+                ? Promise.resolve()
+                : dispatch<any>(getProjectList(itemId));
 
 
-        const { collections, projects } = getState();
+            promise
+                .then(() => dispatch<any>(getCollectionList(itemId)))
+                .then(() => dispatch<any>(() => {
+                    const { projects, collections } = getState();
+                    dispatch(dataExplorerActions.SET_ITEMS({
+                        id: PROJECT_PANEL_ID,
+                        items: projectPanelItems(
+                            projects.items,
+                            treeItem.data.uuid,
+                            collections
+                        )
+                    }));
+                }));
 
 
-        if (itemMode === ItemMode.OPEN || itemMode === ItemMode.BOTH) {
-            dispatch(projectActions.TOGGLE_PROJECT_TREE_ITEM_OPEN(resource.uuid));
         }
         }
-
-        if (itemMode === ItemMode.ACTIVE || itemMode === ItemMode.BOTH) {
-            dispatch(sidePanelActions.RESET_SIDE_PANEL_ACTIVITY(resource.uuid));
-        }
-
-        dispatch(push(getResourceUrl({ ...resource, kind: itemKind })));
-        dispatch(dataExplorerActions.SET_ITEMS({
-            id: PROJECT_PANEL_ID,
-            items: projectPanelItems(
-                projects.items,
-                resource.uuid,
-                collections
-            )
-        }));
     };
     };
index 3c264d3ef9fcbba5089a9294a65b3aec0d863c5b..35ff445e43d0d09c660c8ea22f68e2d3cc9d6030 100644 (file)
@@ -14,7 +14,7 @@ const actions = unionize({
     PROJECTS_SUCCESS: ofType<{ projects: Project[], parentItemId?: string }>(),
     TOGGLE_PROJECT_TREE_ITEM_OPEN: ofType<string>(),
     TOGGLE_PROJECT_TREE_ITEM_ACTIVE: ofType<string>(),
     PROJECTS_SUCCESS: ofType<{ projects: Project[], parentItemId?: string }>(),
     TOGGLE_PROJECT_TREE_ITEM_OPEN: ofType<string>(),
     TOGGLE_PROJECT_TREE_ITEM_ACTIVE: ofType<string>(),
-    RESET_PROJECT_TREE_ACTIVITY: ofType<string>(),
+    RESET_PROJECT_TREE_ACTIVITY: ofType<string>()
 }, {
         tag: 'type',
         value: 'payload'
 }, {
         tag: 'type',
         value: 'payload'
index 0e2018b41c3830784c24b9615ad8b08db40cf70c..efef80992189c39a2d748a477cc972f3b1bf696b 100644 (file)
@@ -132,6 +132,7 @@ const projectsReducer = (state: ProjectState = { items: [], currentItemId: "" },
             resetTreeActivity(items);
             const item = findTreeItem(items, itemId);
             if (item) {
             resetTreeActivity(items);
             const item = findTreeItem(items, itemId);
             if (item) {
+                item.toggled = true;
                 item.active = true;
             }
             return {
                 item.active = true;
             }
             return {
index 32fa653bfe0427638fa03ac63ddb46a68c77b54d..6a83946cc8fcdf75e1ebbd7bb4abcce3951a3d41 100644 (file)
@@ -7,7 +7,7 @@ import { default as unionize, ofType, UnionOf } from "unionize";
 const actions = unionize({
     TOGGLE_SIDE_PANEL_ITEM_OPEN: ofType<string>(),
     TOGGLE_SIDE_PANEL_ITEM_ACTIVE: ofType<string>(),
 const actions = unionize({
     TOGGLE_SIDE_PANEL_ITEM_OPEN: ofType<string>(),
     TOGGLE_SIDE_PANEL_ITEM_ACTIVE: ofType<string>(),
-    RESET_SIDE_PANEL_ACTIVITY: ofType<string>(),
+    RESET_SIDE_PANEL_ACTIVITY: ofType<{}>(),
 }, {
     tag: 'type',
     value: 'payload'
 }, {
     tag: 'type',
     value: 'payload'
index 5571e91257653a9f3bfb297fa0601eefa931d262..ee039a807a388c087584398c1092b851de07dfe6 100644 (file)
@@ -15,15 +15,6 @@ export const projectPanelItems = (projects: Array<TreeItem<Project>>, treeItemId
 
     const treeItem = findTreeItem(projects, treeItemId);
     if (treeItem) {
 
     const treeItem = findTreeItem(projects, treeItemId);
     if (treeItem) {
-        dataItems.push({
-            name: "..",
-            url: getResourceUrl(treeItem.data),
-            kind: ResourceKind.LEVEL_UP,
-            owner: "",
-            uuid: treeItem.data.uuid,
-            lastModified: ""
-        });
-
         if (treeItem.items) {
             treeItem.items.forEach(p => {
                 const item = {
         if (treeItem.items) {
             treeItem.items.forEach(p => {
                 const item = {
index dbff20e18717d227909f524ec5748225cbe484eb..0cd74ff91388b3287f09de80ecac745a3ea6b798 100644 (file)
@@ -12,12 +12,18 @@ import { DataTableFilterItem } from '../../components/data-table-filters/data-ta
 import { ContextMenuAction } from '../../components/context-menu/context-menu';
 import { DispatchProp, connect } from 'react-redux';
 import actions from "../../store/data-explorer/data-explorer-action";
 import { ContextMenuAction } from '../../components/context-menu/context-menu';
 import { DispatchProp, connect } from 'react-redux';
 import actions from "../../store/data-explorer/data-explorer-action";
-import { setProjectItem } from "../../store/navigation/navigation-action";
+import { setProjectItem, ItemMode } from "../../store/navigation/navigation-action";
 import { DataColumns } from '../../components/data-table/data-table';
 import { ResourceKind } from "../../models/resource";
 import { DataColumns } from '../../components/data-table/data-table';
 import { ResourceKind } from "../../models/resource";
+import { RouteComponentProps } from 'react-router';
 
 export const PROJECT_PANEL_ID = "projectPanel";
 
 export const PROJECT_PANEL_ID = "projectPanel";
-class ProjectPanel extends React.Component<DispatchProp & WithStyles<CssRules>> {
+
+type ProjectPanelProps = { onItemOpen: (itemId: string) => void }
+    & DispatchProp
+    & WithStyles<CssRules>
+    & RouteComponentProps<{ id: string }>;
+class ProjectPanel extends React.Component<ProjectPanelProps> {
     render() {
         return <div>
             <div className={this.props.classes.toolbar}>
     render() {
         return <div>
             <div className={this.props.classes.toolbar}>
@@ -49,6 +55,12 @@ class ProjectPanel extends React.Component<DispatchProp & WithStyles<CssRules>>
         this.props.dispatch(actions.SET_COLUMNS({ id: PROJECT_PANEL_ID, columns }));
     }
 
         this.props.dispatch(actions.SET_COLUMNS({ id: PROJECT_PANEL_ID, columns }));
     }
 
+    componentWillReceiveProps(nextProps: ProjectPanelProps) {
+        if (this.props.match.params.id !== nextProps.match.params.id) {
+            this.props.onItemOpen(nextProps.match.params.id);
+        }
+    }
+
     toggleColumn = (toggledColumn: DataColumn<ProjectPanelItem>) => {
         this.props.dispatch(actions.TOGGLE_COLUMN({ id: PROJECT_PANEL_ID, columnName: toggledColumn.name }));
     }
     toggleColumn = (toggledColumn: DataColumn<ProjectPanelItem>) => {
         this.props.dispatch(actions.TOGGLE_COLUMN({ id: PROJECT_PANEL_ID, columnName: toggledColumn.name }));
     }
@@ -78,7 +90,7 @@ class ProjectPanel extends React.Component<DispatchProp & WithStyles<CssRules>>
     }
 
     openProject = (item: ProjectPanelItem) => {
     }
 
     openProject = (item: ProjectPanelItem) => {
-        this.props.dispatch<any>(setProjectItem(item.uuid));
+        this.props.onItemOpen(item.uuid);
     }
 }
 
     }
 }
 
@@ -113,8 +125,6 @@ const renderName = (item: ProjectPanelItem) =>
 
 const renderIcon = (item: ProjectPanelItem) => {
     switch (item.kind) {
 
 const renderIcon = (item: ProjectPanelItem) => {
     switch (item.kind) {
-        case ResourceKind.LEVEL_UP:
-            return <i className="icon-level-up" style={{ fontSize: "1rem" }} />;
         case ResourceKind.PROJECT:
             return <i className="fas fa-folder fa-lg" />;
         case ResourceKind.COLLECTION:
         case ResourceKind.PROJECT:
             return <i className="fas fa-folder fa-lg" />;
         case ResourceKind.COLLECTION:
index 72eb0ddcfefe759677f017667532cf43ce075d22..c095868f8c58f0eee3c5fb26ac475f3754651979 100644 (file)
@@ -6,7 +6,7 @@ import * as React from 'react';
 import { StyleRulesCallback, Theme, WithStyles, withStyles } from '@material-ui/core/styles';
 import Drawer from '@material-ui/core/Drawer';
 import { connect, DispatchProp } from "react-redux";
 import { StyleRulesCallback, Theme, WithStyles, withStyles } from '@material-ui/core/styles';
 import Drawer from '@material-ui/core/Drawer';
 import { connect, DispatchProp } from "react-redux";
-import { Route, Switch } from "react-router";
+import { Route, Switch, RouteComponentProps } from "react-router";
 import authActions from "../../store/auth/auth-action";
 import dataExplorerActions from "../../store/data-explorer/data-explorer-action";
 import { User } from "../../models/user";
 import authActions from "../../store/auth/auth-action";
 import dataExplorerActions from "../../store/data-explorer/data-explorer-action";
 import { User } from "../../models/user";
@@ -133,7 +133,7 @@ class Workbench extends React.Component<WorkbenchProps, WorkbenchState> {
 
     mainAppBarActions: MainAppBarActionProps = {
         onBreadcrumbClick: ({ itemId }: NavBreadcrumb) => {
 
     mainAppBarActions: MainAppBarActionProps = {
         onBreadcrumbClick: ({ itemId }: NavBreadcrumb) => {
-            this.props.dispatch<any>(setProjectItem(itemId, ResourceKind.PROJECT, ItemMode.BOTH));
+            this.props.dispatch<any>(setProjectItem(itemId, ItemMode.BOTH));
         },
         onSearch: searchText => {
             this.setState({ searchText });
         },
         onSearch: searchText => {
             this.setState({ searchText });
@@ -152,12 +152,12 @@ class Workbench extends React.Component<WorkbenchProps, WorkbenchState> {
     }
 
     render() {
     }
 
     render() {
-        const branch = getTreePath(this.props.projects, this.props.currentProjectId);
-        const breadcrumbs = branch.map(item => ({
+        const path = getTreePath(this.props.projects, this.props.currentProjectId);
+        const breadcrumbs = path.map(item => ({
             label: item.data.name,
             itemId: item.data.uuid,
             status: item.status
             label: item.data.name,
             itemId: item.data.uuid,
             status: item.status
-        }));
+        }));  
 
         const { classes, user } = this.props;
         return (
 
         const { classes, user } = this.props;
         return (
@@ -186,11 +186,11 @@ class Workbench extends React.Component<WorkbenchProps, WorkbenchState> {
                                 projects={this.props.projects}
                                 toggleOpen={itemId =>
                                     this.props.dispatch<any>(
                                 projects={this.props.projects}
                                 toggleOpen={itemId =>
                                     this.props.dispatch<any>(
-                                        setProjectItem(itemId, ResourceKind.PROJECT, ItemMode.OPEN)
+                                        setProjectItem(itemId, ItemMode.OPEN)
                                     )}
                                 toggleActive={itemId =>
                                     this.props.dispatch<any>(
                                     )}
                                 toggleActive={itemId =>
                                     this.props.dispatch<any>(
-                                        setProjectItem(itemId, ResourceKind.PROJECT, ItemMode.ACTIVE)
+                                        setProjectItem(itemId, ItemMode.ACTIVE)
                                     )}
                             />
                         </SidePanel>
                                     )}
                             />
                         </SidePanel>
@@ -198,13 +198,20 @@ class Workbench extends React.Component<WorkbenchProps, WorkbenchState> {
                 <main className={classes.contentWrapper}>
                     <div className={classes.content}>
                         <Switch>
                 <main className={classes.contentWrapper}>
                     <div className={classes.content}>
                         <Switch>
-                            <Route path="/projects/:name" component={ProjectPanel} />
+                            <Route path="/projects/:id" render={this.renderProjectPanel} />
                         </Switch>
                     </div>
                 </main>
             </div>
         );
     }
                         </Switch>
                     </div>
                 </main>
             </div>
         );
     }
+
+    renderProjectPanel = (props: RouteComponentProps<any>) =>
+        <ProjectPanel
+            onItemOpen={itemId => this.props.dispatch<any>(
+                setProjectItem(itemId, ItemMode.ACTIVE)
+            )}
+            {...props} />
 }
 
 export default connect<WorkbenchDataProps>(
 }
 
 export default connect<WorkbenchDataProps>(