From 5f27c58e1c34d147786de6104ead8a57c48f7d2e Mon Sep 17 00:00:00 2001 From: Michal Klobukowski Date: Fri, 7 Sep 2018 13:29:26 +0200 Subject: [PATCH] Create shared-with-me panel skeleton Feature #13751 Arvados-DCO-1.1-Signed-off-by: Michal Klobukowski --- src/routes/route-change-handlers.ts | 9 +- src/routes/routes.ts | 6 +- src/store/navigation/navigation-action.ts | 4 +- .../shared-with-me-middleware-service.ts | 18 ++++ .../shared-with-me-panel-actions.ts | 18 ++++ src/store/side-panel/side-panel-action.ts | 4 +- src/store/store.ts | 8 +- src/store/workbench/workbench-actions.ts | 11 ++- .../shared-with-me-panel.tsx | 85 +++++++++++++++++++ src/views/workbench/workbench.tsx | 2 + 10 files changed, 158 insertions(+), 7 deletions(-) create mode 100644 src/store/shared-with-me-panel/shared-with-me-middleware-service.ts create mode 100644 src/store/shared-with-me-panel/shared-with-me-panel-actions.ts create mode 100644 src/views/shared-with-me-panel/shared-with-me-panel.tsx diff --git a/src/routes/route-change-handlers.ts b/src/routes/route-change-handlers.ts index 00fb4bc0..33e0bef7 100644 --- a/src/routes/route-change-handlers.ts +++ b/src/routes/route-change-handlers.ts @@ -4,9 +4,11 @@ import { History, Location } from 'history'; import { RootStore } from '~/store/store'; -import { matchProcessRoute, matchProcessLogRoute, matchProjectRoute, matchCollectionRoute, matchFavoritesRoute, matchTrashRoute, matchRootRoute } from './routes'; +import { matchProcessRoute, matchProcessLogRoute, matchProjectRoute, matchCollectionRoute, matchFavoritesRoute, matchTrashRoute, matchRootRoute, matchSharedWithMeRoute } from './routes'; import { loadProject, loadCollection, loadFavorites, loadTrash, loadProcess, loadProcessLog } from '~/store/workbench/workbench-actions'; import { navigateToRootProject } from '~/store/navigation/navigation-action'; +import { navigateToSharedWithMe } from '../store/navigation/navigation-action'; +import { loadSharedWithMe } from '../store/workbench/workbench-actions'; export const addRouteChangeHandlers = (history: History, store: RootStore) => { const handler = handleLocationChange(store); @@ -22,7 +24,8 @@ const handleLocationChange = (store: RootStore) => ({ pathname }: Location) => { const trashMatch = matchTrashRoute(pathname); const processMatch = matchProcessRoute(pathname); const processLogMatch = matchProcessLogRoute(pathname); - + const sharedWithMeMatch = matchSharedWithMeRoute(pathname); + if (projectMatch) { store.dispatch(loadProject(projectMatch.params.id)); } else if (collectionMatch) { @@ -37,5 +40,7 @@ const handleLocationChange = (store: RootStore) => ({ pathname }: Location) => { store.dispatch(loadProcessLog(processLogMatch.params.id)); } else if (rootMatch) { store.dispatch(navigateToRootProject); + } else if (sharedWithMeMatch) { + store.dispatch(loadSharedWithMe); } }; diff --git a/src/routes/routes.ts b/src/routes/routes.ts index f108e0b8..107b9e6f 100644 --- a/src/routes/routes.ts +++ b/src/routes/routes.ts @@ -15,7 +15,8 @@ export const Routes = { PROCESSES: `/processes/:id(${RESOURCE_UUID_PATTERN})`, FAVORITES: '/favorites', TRASH: '/trash', - PROCESS_LOGS: `/process-logs/:id(${RESOURCE_UUID_PATTERN})` + PROCESS_LOGS: `/process-logs/:id(${RESOURCE_UUID_PATTERN})`, + SHARED_WITH_ME: '/shared-with-me', }; export const getResourceUrl = (uuid: string) => { @@ -58,3 +59,6 @@ export const matchProcessRoute = (route: string) => export const matchProcessLogRoute = (route: string) => matchPath(route, { path: Routes.PROCESS_LOGS }); + +export const matchSharedWithMeRoute = (route: string) => + matchPath(route, { path: Routes.SHARED_WITH_ME }); diff --git a/src/store/navigation/navigation-action.ts b/src/store/navigation/navigation-action.ts index 0e87769e..b65f6875 100644 --- a/src/store/navigation/navigation-action.ts +++ b/src/store/navigation/navigation-action.ts @@ -44,4 +44,6 @@ export const navigateToRootProject = (dispatch: Dispatch, getState: () => RootSt if (rootProjectUuid) { dispatch(navigateToProject(rootProjectUuid)); } -}; \ No newline at end of file +}; + +export const navigateToSharedWithMe = push(Routes.SHARED_WITH_ME); diff --git a/src/store/shared-with-me-panel/shared-with-me-middleware-service.ts b/src/store/shared-with-me-panel/shared-with-me-middleware-service.ts new file mode 100644 index 00000000..12c68501 --- /dev/null +++ b/src/store/shared-with-me-panel/shared-with-me-middleware-service.ts @@ -0,0 +1,18 @@ +// Copyright (C) The Arvados Authors. All rights reserved. +// +// SPDX-License-Identifier: AGPL-3.0 + +import { DataExplorerMiddlewareService } from '../data-explorer/data-explorer-middleware-service'; +import { RootState } from "../store"; +import { ServiceRepository } from "~/services/services"; +import { Dispatch, MiddlewareAPI } from "redux"; + +export class SharedWithMeMiddlewareService extends DataExplorerMiddlewareService { + constructor(private services: ServiceRepository, id: string) { + super(id); + } + + async requestItems(api: MiddlewareAPI) { + return; + } +} diff --git a/src/store/shared-with-me-panel/shared-with-me-panel-actions.ts b/src/store/shared-with-me-panel/shared-with-me-panel-actions.ts new file mode 100644 index 00000000..2553086a --- /dev/null +++ b/src/store/shared-with-me-panel/shared-with-me-panel-actions.ts @@ -0,0 +1,18 @@ +// Copyright (C) The Arvados Authors. All rights reserved. +// +// SPDX-License-Identifier: AGPL-3.0 + +import { bindDataExplorerActions } from "../data-explorer/data-explorer-action"; +import { Dispatch } from 'redux'; +import { ServiceRepository } from "~/services/services"; +import { RootState } from '~/store/store'; + +export const SHARED_WITH_ME_PANEL_ID = "sharedWithMePanel"; +export const sharedWithMePanelActions = bindDataExplorerActions(SHARED_WITH_ME_PANEL_ID); + +export const loadSharedWithMePanel = () => + (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => { + dispatch(sharedWithMePanelActions.REQUEST_ITEMS()); + }; + + diff --git a/src/store/side-panel/side-panel-action.ts b/src/store/side-panel/side-panel-action.ts index 3b66157d..2a5fdd0c 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, navigateTo, navigateToTrash } from '../navigation/navigation-action'; +import { navigateToFavorites, navigateTo, navigateToTrash, navigateToSharedWithMe } from '../navigation/navigation-action'; import { snackbarActions } from '~/store/snackbar/snackbar-actions'; export const navigateFromSidePanel = (id: string) => @@ -22,6 +22,8 @@ const getSidePanelTreeCategoryAction = (id: string) => { return navigateToFavorites; case SidePanelTreeCategory.TRASH: return navigateToTrash; + case SidePanelTreeCategory.SHARED_WITH_ME: + return navigateToSharedWithMe; default: return sidePanelTreeCategoryNotAvailable(id); } diff --git a/src/store/store.ts b/src/store/store.ts index 9b4f42b8..43ab2310 100644 --- a/src/store/store.ts +++ b/src/store/store.ts @@ -32,6 +32,8 @@ import { TrashPanelMiddlewareService } from "~/store/trash-panel/trash-panel-mid import { TRASH_PANEL_ID } from "~/store/trash-panel/trash-panel-action"; import { processLogsPanelReducer } from './process-logs-panel/process-logs-panel-reducer'; import { processPanelReducer } from '~/store/process-panel/process-panel-reducer'; +import { SHARED_WITH_ME_PANEL_ID } from '~/store/shared-with-me-panel/shared-with-me-panel-actions'; +import { SharedWithMeMiddlewareService } from './shared-with-me-panel/shared-with-me-middleware-service'; const composeEnhancers = (process.env.NODE_ENV === 'development' && @@ -54,13 +56,17 @@ export function configureStore(history: History, services: ServiceRepository): R const trashPanelMiddleware = dataExplorerMiddleware( new TrashPanelMiddlewareService(services, TRASH_PANEL_ID) ); + const sharedWithMePanelMiddleware = dataExplorerMiddleware( + new SharedWithMeMiddlewareService(services, SHARED_WITH_ME_PANEL_ID) + ); const middlewares: Middleware[] = [ routerMiddleware(history), thunkMiddleware.withExtraArgument(services), projectPanelMiddleware, favoritePanelMiddleware, - trashPanelMiddleware + trashPanelMiddleware, + sharedWithMePanelMiddleware, ]; const enhancer = composeEnhancers(applyMiddleware(...middlewares)); return createStore(rootReducer, enhancer); diff --git a/src/store/workbench/workbench-actions.ts b/src/store/workbench/workbench-actions.ts index 036f4a34..c6798ea3 100644 --- a/src/store/workbench/workbench-actions.ts +++ b/src/store/workbench/workbench-actions.ts @@ -33,6 +33,8 @@ import { trashPanelColumns } from "~/views/trash-panel/trash-panel"; import { loadTrashPanel, trashPanelActions } from "~/store/trash-panel/trash-panel-action"; import { initProcessLogsPanel } from '../process-logs-panel/process-logs-panel-actions'; import { loadProcessPanel } from '~/store/process-panel/process-panel-actions'; +import { sharedWithMePanelActions } from '~/store/shared-with-me-panel/shared-with-me-panel-actions'; +import { loadSharedWithMePanel } from '../shared-with-me-panel/shared-with-me-panel-actions'; export const loadWorkbench = () => @@ -45,6 +47,7 @@ export const loadWorkbench = () => dispatch(projectPanelActions.SET_COLUMNS({ columns: projectPanelColumns })); dispatch(favoritePanelActions.SET_COLUMNS({ columns: favoritePanelColumns })); dispatch(trashPanelActions.SET_COLUMNS({ columns: trashPanelColumns })); + dispatch(sharedWithMePanelActions.SET_COLUMNS({ columns: projectPanelColumns })); dispatch(initSidePanelTree()); if (router.location) { const match = matchRootRoute(router.location.pathname); @@ -191,7 +194,7 @@ export const loadProcess = (uuid: string) => await dispatch(activateSidePanelTreeItem(process.containerRequest.ownerUuid)); dispatch(setProcessBreadcrumbs(uuid)); dispatch(loadDetailsPanel(uuid)); - + }; export const loadProcessLog = (uuid: string) => @@ -222,3 +225,9 @@ export const reloadProjectMatchingUuid = (matchingUuids: string[]) => dispatch(loadProject(currentProjectPanelUuid)); } }; + +export const loadSharedWithMe = (dispatch: Dispatch) => { + dispatch(activateSidePanelTreeItem(SidePanelTreeCategory.SHARED_WITH_ME)); + dispatch(loadSharedWithMePanel()); + dispatch(setSidePanelBreadcrumbs(SidePanelTreeCategory.SHARED_WITH_ME)); +}; diff --git a/src/views/shared-with-me-panel/shared-with-me-panel.tsx b/src/views/shared-with-me-panel/shared-with-me-panel.tsx new file mode 100644 index 00000000..df668035 --- /dev/null +++ b/src/views/shared-with-me-panel/shared-with-me-panel.tsx @@ -0,0 +1,85 @@ +// Copyright (C) The Arvados Authors. All rights reserved. +// +// SPDX-License-Identifier: AGPL-3.0 + +import * as React from 'react'; +import { StyleRulesCallback, WithStyles, withStyles } from '@material-ui/core'; +import { DataExplorer } from "~/views-components/data-explorer/data-explorer"; +import { connect, DispatchProp } from 'react-redux'; +import { RootState } from '~/store/store'; +import { ArvadosTheme } from '~/common/custom-theme'; +import { ShareMeIcon } from '~/components/icon/icon'; +import { ResourcesState } from "~/store/resources/resources"; +import { navigateTo } from "~/store/navigation/navigation-action"; +import { loadDetailsPanel } from "~/store/details-panel/details-panel-action"; +import { PanelDefaultView } from '~/components/panel-default-view/panel-default-view'; +import { DataTableDefaultView } from '~/components/data-table-default-view/data-table-default-view'; +import { SHARED_WITH_ME_PANEL_ID } from '~/store/shared-with-me-panel/shared-with-me-panel-actions'; + +type CssRules = "toolbar" | "button"; + +const styles: StyleRulesCallback = (theme: ArvadosTheme) => ({ + toolbar: { + paddingBottom: theme.spacing.unit * 3, + textAlign: "right" + }, + button: { + marginLeft: theme.spacing.unit + }, +}); + +interface SharedWithMePanelDataProps { + resources: ResourcesState; +} + +type SharedWithMePanelProps = SharedWithMePanelDataProps & DispatchProp & WithStyles; + +export const SharedWithMePanel = withStyles(styles)( + connect((state: RootState) => ({ + resources: state.resources + }))( + class extends React.Component { + render() { + return this.hasAnyTrashedResources() + ? } /> + : ; + } + + hasAnyTrashedResources = () => { + // TODO: implement check if there is anything in the trash, + // without taking pagination into the account + return false; + } + + handleContextMenu = (event: React.MouseEvent, resourceUuid: string) => { + // const resource = getResource(resourceUuid)(this.props.resources); + // if (resource) { + // this.props.dispatch(openContextMenu(event, { + // name: '', + // uuid: resource.uuid, + // ownerUuid: resource.ownerUuid, + // isTrashed: resource.isTrashed, + // kind: resource.kind, + // menuKind: ContextMenuKind.TRASH + // })); + // } + } + + handleRowDoubleClick = (uuid: string) => { + this.props.dispatch(navigateTo(uuid)); + } + + handleRowClick = (uuid: string) => { + this.props.dispatch(loadDetailsPanel(uuid)); + } + } + ) +); diff --git a/src/views/workbench/workbench.tsx b/src/views/workbench/workbench.tsx index a166473e..e075680d 100644 --- a/src/views/workbench/workbench.tsx +++ b/src/views/workbench/workbench.tsx @@ -40,6 +40,7 @@ import { PartialCopyCollectionDialog } from '~/views-components/dialog-forms/par import { TrashPanel } from "~/views/trash-panel/trash-panel"; import { MainContentBar } from '../../views-components/main-content-bar/main-content-bar'; import { Grid } from '@material-ui/core'; +import { SharedWithMePanel } from '../shared-with-me-panel/shared-with-me-panel'; type CssRules = 'root' | 'contentWrapper' | 'content' | 'appBar'; @@ -133,6 +134,7 @@ export const Workbench = withStyles(styles)( + -- 2.30.2