// Copyright (C) The Arvados Authors. All rights reserved. // // SPDX-License-Identifier: AGPL-3.0 import * as React from 'react'; import { connect, DispatchProp } from 'react-redux'; import { RootState } from '~/store/store'; import { ArvadosTheme } from '~/common/custom-theme'; import { PopoverOrigin } from '@material-ui/core/Popover'; import { StyleRulesCallback, WithStyles, withStyles, Toolbar, Grid, Button, MenuItem, Menu } from '@material-ui/core'; import { AddIcon, CollectionIcon, ProcessIcon, ProjectIcon } from '~/components/icon/icon'; import { openProjectCreateDialog } from '~/store/projects/project-create-actions'; import { openCollectionCreateDialog } from '~/store/collections/collection-create-actions'; import { navigateToRunProcess } from '~/store/navigation/navigation-action'; import { runProcessPanelActions } from '~/store/run-process-panel/run-process-panel-actions'; import { getUserUuid } from '~/common/getuser'; import { matchProjectRoute } from '~/routes/routes'; import { GroupClass, GroupResource } from '~/models/group'; import { ResourcesState, getResource } from '~/store/resources/resources'; import { extractUuidKind, ResourceKind } from '~/models/resource'; import { pluginConfig } from '~/plugins'; import { ElementListReducer } from '~/common/plugintypes'; import { Location } from 'history'; type CssRules = 'button' | 'menuItem' | 'icon'; const styles: StyleRulesCallback = (theme: ArvadosTheme) => ({ button: { boxShadow: 'none', padding: '2px 10px 2px 5px', fontSize: '0.75rem' }, menuItem: { fontSize: '0.875rem', color: theme.palette.grey["700"] }, icon: { marginRight: theme.spacing.unit } }); interface SidePanelDataProps { location: Location; currentItemId: string; resources: ResourcesState; currentUserUUID: string | undefined; } interface SidePanelState { anchorEl: any; } type SidePanelProps = SidePanelDataProps & DispatchProp & WithStyles; const transformOrigin: PopoverOrigin = { vertical: -50, horizontal: 0 }; export const isProjectTrashed = (proj: GroupResource | undefined, resources: ResourcesState): boolean => { if (proj === undefined) { return false; } if (proj.isTrashed) { return true; } if (extractUuidKind(proj.ownerUuid) === ResourceKind.USER) { return false; } const parentProj = getResource(proj.ownerUuid)(resources); return isProjectTrashed(parentProj, resources); }; export const SidePanelButton = withStyles(styles)( connect((state: RootState) => ({ currentItemId: state.router.location ? state.router.location.pathname.split('/').slice(-1)[0] : null, location: state.router.location, resources: state.resources, currentUserUUID: getUserUuid(state), }))( class extends React.Component { state: SidePanelState = { anchorEl: undefined }; render() { const { classes, location, resources, currentUserUUID, currentItemId } = this.props; const { anchorEl } = this.state; let enabled = false; if (currentItemId === currentUserUUID) { enabled = true; } else if (matchProjectRoute(location ? location.pathname : '')) { const currentProject = getResource(currentItemId)(resources); if (currentProject && currentProject.writableBy.indexOf(currentUserUUID || '') >= 0 && !isProjectTrashed(currentProject, resources) && currentProject.groupClass !== GroupClass.FILTER) { enabled = true; } } for (const enableFn of pluginConfig.enableNewButtonMatchers) { if (enableFn(location, currentItemId, currentUserUUID, resources)) { enabled = true; } } let menuItems = <> New collection Run a process New project ; const reduceItemsFn: (a: React.ReactElement[], b: ElementListReducer) => React.ReactElement[] = (a, b) => b(a, classes.menuItem); menuItems = React.createElement(React.Fragment, null, pluginConfig.newButtonMenuList.reduce(reduceItemsFn, React.Children.toArray(menuItems.props.children))); return {menuItems} ; } handleNewProjectClick = () => { this.props.dispatch(openProjectCreateDialog(this.props.currentItemId)); } handleRunProcessClick = () => { const location = this.props.location; this.props.dispatch(runProcessPanelActions.RESET_RUN_PROCESS_PANEL()); this.props.dispatch(runProcessPanelActions.SET_PROCESS_PATHNAME(location.pathname)); this.props.dispatch(runProcessPanelActions.SET_PROCESS_OWNER_UUID(this.props.currentItemId)); this.props.dispatch(navigateToRunProcess); } handleNewCollectionClick = () => { this.props.dispatch(openCollectionCreateDialog(this.props.currentItemId)); } handleClose = () => { this.setState({ anchorEl: undefined }); } handleOpen = (event: React.MouseEvent) => { this.setState({ anchorEl: event.currentTarget }); } } ) );