From 23575ae95aab42c4c4d9c2b5ee0236fab004dccb Mon Sep 17 00:00:00 2001 From: Lisa Knox Date: Wed, 6 Sep 2023 09:32:10 -0400 Subject: [PATCH] 15768: fixed navigateTo bug Arvados-DCO-1.1-Signed-off-by: Lisa Knox --- .../collection-panel-files.tsx | 951 ++++++++++-------- src/store/navigation/navigation-action.ts | 138 ++- .../action-sets/collection-action-set.ts | 6 +- 3 files changed, 594 insertions(+), 501 deletions(-) diff --git a/src/components/collection-panel-files/collection-panel-files.tsx b/src/components/collection-panel-files/collection-panel-files.tsx index fb36ebce54..95dd4d4c43 100644 --- a/src/components/collection-panel-files/collection-panel-files.tsx +++ b/src/components/collection-panel-files/collection-panel-files.tsx @@ -2,14 +2,14 @@ // // SPDX-License-Identifier: AGPL-3.0 -import React from 'react'; -import classNames from 'classnames'; -import { connect } from 'react-redux'; +import React from "react"; +import classNames from "classnames"; +import { connect } from "react-redux"; import { FixedSizeList } from "react-window"; import AutoSizer from "react-virtualized-auto-sizer"; -import servicesProvider from 'common/service-provider'; -import { CustomizeTableIcon, DownloadIcon, MoreOptionsIcon } from 'components/icon/icon'; -import { SearchInput } from 'components/search-input/search-input'; +import servicesProvider from "common/service-provider"; +import { CustomizeTableIcon, DownloadIcon, MoreOptionsIcon } from "components/icon/icon"; +import { SearchInput } from "components/search-input/search-input"; import { ListItemIcon, StyleRulesCallback, @@ -21,24 +21,18 @@ import { Checkbox, CircularProgress, Button, -} from '@material-ui/core'; -import { FileTreeData } from '../file-tree/file-tree-data'; -import { TreeItem, TreeItemStatus } from '../tree/tree'; -import { RootState } from 'store/store'; -import { WebDAV, WebDAVRequestConfig } from 'common/webdav'; -import { AuthState } from 'store/auth/auth-reducer'; -import { extractFilesData } from 'services/collection-service/collection-service-files-response'; -import { - DefaultIcon, - DirectoryIcon, - FileIcon, - BackIcon, - SidePanelRightArrowIcon -} from 'components/icon/icon'; -import { setCollectionFiles } from 'store/collection-panel/collection-panel-files/collection-panel-files-actions'; -import { sortBy } from 'lodash'; -import { formatFileSize } from 'common/formatters'; -import { getInlineFileUrl, sanitizeToken } from 'views-components/context-menu/actions/helpers'; +} from "@material-ui/core"; +import { FileTreeData } from "../file-tree/file-tree-data"; +import { TreeItem, TreeItemStatus } from "../tree/tree"; +import { RootState } from "store/store"; +import { WebDAV, WebDAVRequestConfig } from "common/webdav"; +import { AuthState } from "store/auth/auth-reducer"; +import { extractFilesData } from "services/collection-service/collection-service-files-response"; +import { DefaultIcon, DirectoryIcon, FileIcon, BackIcon, SidePanelRightArrowIcon } from "components/icon/icon"; +import { setCollectionFiles } from "store/collection-panel/collection-panel-files/collection-panel-files-actions"; +import { sortBy } from "lodash"; +import { formatFileSize } from "common/formatters"; +import { getInlineFileUrl, sanitizeToken } from "views-components/context-menu/actions/helpers"; export interface CollectionPanelFilesProps { isWritable: boolean; @@ -55,7 +49,8 @@ export interface CollectionPanelFilesProps { collectionPanel: any; } -type CssRules = "backButton" +type CssRules = + | "backButton" | "backButtonHidden" | "pathPanelPathWrapper" | "uploadButton" @@ -83,513 +78,613 @@ type CssRules = "backButton" const styles: StyleRulesCallback = (theme: Theme) => ({ wrapper: { - display: 'flex', - minHeight: '600px', - color: 'rgba(0,0,0,0.87)', - fontSize: '0.875rem', + display: "flex", + minHeight: "600px", + color: "rgba(0,0,0,0.87)", + fontSize: "0.875rem", fontFamily: '"Roboto", "Helvetica", "Arial", sans-serif', fontWeight: 400, - lineHeight: '1.5', - letterSpacing: '0.01071em' + lineHeight: "1.5", + letterSpacing: "0.01071em", }, backButton: { - color: '#00bfa5', - cursor: 'pointer', - float: 'left', + color: "#00bfa5", + cursor: "pointer", + float: "left", }, backButtonHidden: { - display: 'none', + display: "none", }, dataWrapper: { - minHeight: '500px' + minHeight: "500px", }, row: { - display: 'flex', - marginTop: '0.5rem', - marginBottom: '0.5rem', - cursor: 'pointer', + display: "flex", + marginTop: "0.5rem", + marginBottom: "0.5rem", + cursor: "pointer", "&:hover": { - backgroundColor: 'rgba(0, 0, 0, 0.08)', - } + backgroundColor: "rgba(0, 0, 0, 0.08)", + }, }, rowEmpty: { - top: '40%', - width: '100%', - textAlign: 'center', - position: 'absolute' + top: "40%", + width: "100%", + textAlign: "center", + position: "absolute", }, loader: { - top: '50%', - left: '50%', - marginTop: '-15px', - marginLeft: '-15px', - position: 'absolute' + top: "50%", + left: "50%", + marginTop: "-15px", + marginLeft: "-15px", + position: "absolute", }, rowName: { - display: 'inline-flex', - flexDirection: 'column', - justifyContent: 'center' + display: "inline-flex", + flexDirection: "column", + justifyContent: "center", }, searchWrapper: { - display: 'inline-block', - marginBottom: '1rem', - marginLeft: '1rem', + display: "inline-block", + marginBottom: "1rem", + marginLeft: "1rem", }, searchWrapperHidden: { - width: '0px' + width: "0px", }, rowSelection: { - padding: '0px', + padding: "0px", }, rowActive: { color: `${theme.palette.primary.main} !important`, }, listItemIcon: { - display: 'inline-flex', - flexDirection: 'column', - justifyContent: 'center' + display: "inline-flex", + flexDirection: "column", + justifyContent: "center", }, pathPanelMenu: { - float: 'right', - marginTop: '-15px', + float: "right", + marginTop: "-15px", }, pathPanel: { - padding: '0.5rem', - marginBottom: '0.5rem', - backgroundColor: '#fff', - boxShadow: '0px 1px 3px 0px rgb(0 0 0 / 20%), 0px 1px 1px 0px rgb(0 0 0 / 14%), 0px 2px 1px -1px rgb(0 0 0 / 12%)', + padding: "0.5rem", + marginBottom: "0.5rem", + backgroundColor: "#fff", + boxShadow: "0px 1px 3px 0px rgb(0 0 0 / 20%), 0px 1px 1px 0px rgb(0 0 0 / 14%), 0px 2px 1px -1px rgb(0 0 0 / 12%)", }, pathPanelPathWrapper: { - display: 'inline-block', + display: "inline-block", }, leftPanel: { flex: 0, - padding: '0 1rem 1rem', - marginRight: '1rem', - whiteSpace: 'nowrap', - position: 'relative', - backgroundColor: '#fff', - boxShadow: '0px 3px 3px 0px rgb(0 0 0 / 20%), 0px 3px 1px 0px rgb(0 0 0 / 14%), 0px 3px 1px -1px rgb(0 0 0 / 12%)', + padding: "0 1rem 1rem", + marginRight: "1rem", + whiteSpace: "nowrap", + position: "relative", + backgroundColor: "#fff", + boxShadow: "0px 3px 3px 0px rgb(0 0 0 / 20%), 0px 3px 1px 0px rgb(0 0 0 / 14%), 0px 3px 1px -1px rgb(0 0 0 / 12%)", }, leftPanelVisible: { opacity: 1, - flex: '50%', - animation: `animateVisible 1000ms ${theme.transitions.easing.easeOut}` + flex: "50%", + animation: `animateVisible 1000ms ${theme.transitions.easing.easeOut}`, }, leftPanelHidden: { opacity: 0, - flex: 'initial', - padding: '0', - marginRight: '0', + flex: "initial", + padding: "0", + marginRight: "0", }, "@keyframes animateVisible": { "0%": { opacity: 0, - flex: 'initial', + flex: "initial", }, "100%": { opacity: 1, - flex: '50%', - } + flex: "50%", + }, }, rightPanel: { - flex: '50%', - padding: '1rem', - paddingTop: '0.5rem', - marginTop: '-0.5rem', - position: 'relative', - backgroundColor: '#fff', - boxShadow: '0px 3px 3px 0px rgb(0 0 0 / 20%), 0px 3px 1px 0px rgb(0 0 0 / 14%), 0px 3px 1px -1px rgb(0 0 0 / 12%)', + flex: "50%", + padding: "1rem", + paddingTop: "0.5rem", + marginTop: "-0.5rem", + position: "relative", + backgroundColor: "#fff", + boxShadow: "0px 3px 3px 0px rgb(0 0 0 / 20%), 0px 3px 1px 0px rgb(0 0 0 / 14%), 0px 3px 1px -1px rgb(0 0 0 / 12%)", }, pathPanelItem: { - cursor: 'pointer', + cursor: "pointer", }, uploadIcon: { - transform: 'rotate(180deg)' + transform: "rotate(180deg)", }, uploadButton: { - float: 'right', + float: "right", }, moreOptionsButton: { width: theme.spacing.unit * 3, height: theme.spacing.unit * 3, marginRight: theme.spacing.unit, - marginTop: 'auto', - marginBottom: 'auto', - justifyContent: 'center', + marginTop: "auto", + marginBottom: "auto", + justifyContent: "center", }, moreOptions: { - position: 'absolute' + position: "absolute", }, }); const pathPromise = {}; -export const CollectionPanelFiles = withStyles(styles)(connect((state: RootState) => ({ - auth: state.auth, - collectionPanel: state.collectionPanel, - collectionPanelFiles: state.collectionPanelFiles, -}))((props: CollectionPanelFilesProps & WithStyles & { auth: AuthState }) => { - const { classes, onItemMenuOpen, onUploadDataClick, isWritable, dispatch, collectionPanelFiles, collectionPanel } = props; - const { apiToken, config } = props.auth; - - const webdavClient = new WebDAV({ - baseURL: config.keepWebServiceUrl, - headers: { - Authorization: `Bearer ${apiToken}` - }, - }); - - const webDAVRequestConfig: WebDAVRequestConfig = { - headers: { - Depth: '1', - }, - }; - - const parentRef = React.useRef(null); - const [path, setPath] = React.useState([]); - const [pathData, setPathData] = React.useState({}); - const [isLoading, setIsLoading] = React.useState(false); - const [leftSearch, setLeftSearch] = React.useState(''); - const [rightSearch, setRightSearch] = React.useState(''); - - const leftKey = (path.length > 1 ? path.slice(0, path.length - 1) : path).join('/'); - const rightKey = path.join('/'); - - const leftData = pathData[leftKey] || []; - const rightData = pathData[rightKey]; - - React.useEffect(() => { - if (props.currentItemUuid) { - setPathData({}); - setPath([props.currentItemUuid]); - } - }, [props.currentItemUuid]); - - const fetchData = (keys, ignoreCache = false) => { - const keyArray = Array.isArray(keys) ? keys : [keys]; - - Promise.all(keyArray.filter(key => !!key) - .map((key) => { - const dataExists = !!pathData[key]; - const runningRequest = pathPromise[key]; - - if (ignoreCache || (!dataExists && !runningRequest)) { - if (!isLoading) { - setIsLoading(true); - } +export const CollectionPanelFiles = withStyles(styles)( + connect((state: RootState) => ({ + auth: state.auth, + collectionPanel: state.collectionPanel, + collectionPanelFiles: state.collectionPanelFiles, + }))((props: CollectionPanelFilesProps & WithStyles & { auth: AuthState }) => { + const { classes, onItemMenuOpen, onUploadDataClick, isWritable, dispatch, collectionPanelFiles, collectionPanel } = props; + const { apiToken, config } = props.auth; + + const webdavClient = new WebDAV({ + baseURL: config.keepWebServiceUrl, + headers: { + Authorization: `Bearer ${apiToken}`, + }, + }); - pathPromise[key] = true; + const webDAVRequestConfig: WebDAVRequestConfig = { + headers: { + Depth: "1", + }, + }; - return webdavClient.propfind(`c=${key}`, webDAVRequestConfig); - } + const parentRef = React.useRef(null); + const [path, setPath] = React.useState([]); + const [pathData, setPathData] = React.useState({}); + const [isLoading, setIsLoading] = React.useState(false); + const [leftSearch, setLeftSearch] = React.useState(""); + const [rightSearch, setRightSearch] = React.useState(""); - return Promise.resolve(null); - }) - .filter((promise) => !!promise) - ) - .then((requests) => { - const newState = requests.map((request, index) => { - if (request && request.responseXML != null) { - const key = keyArray[index]; - const result: any = extractFilesData(request.responseXML); - const sortedResult = sortBy(result, (n) => n.name).sort((n1, n2) => { - if (n1.type === 'directory' && n2.type !== 'directory') { - return -1; - } - if (n1.type !== 'directory' && n2.type === 'directory') { - return 1; - } - return 0; - }); + const leftKey = (path.length > 1 ? path.slice(0, path.length - 1) : path).join("/"); + const rightKey = path.join("/"); - return { [key]: sortedResult }; - } - return {}; - }).reduce((prev, next) => { - return { ...next, ...prev }; - }, {}); - setPathData((state) => ({ ...state, ...newState })); - }) - .finally(() => { - setIsLoading(false); - keyArray.forEach(key => delete pathPromise[key]); - }); - }; - - React.useEffect(() => { - if (rightKey) { - fetchData(rightKey); - setLeftSearch(''); - setRightSearch(''); - } - }, [rightKey]); // eslint-disable-line react-hooks/exhaustive-deps - - const currentPDH = (collectionPanel.item || {}).portableDataHash; - React.useEffect(() => { - if (currentPDH) { - fetchData([leftKey, rightKey], true); - } - }, [currentPDH]); // eslint-disable-line react-hooks/exhaustive-deps - - React.useEffect(() => { - if (rightData) { - const filtered = rightData.filter(({ name }) => name.indexOf(rightSearch) > -1); - setCollectionFiles(filtered, false)(dispatch); - } - }, [rightData, dispatch, rightSearch]); - - const handleRightClick = React.useCallback( - (event) => { - event.preventDefault(); - let elem = event.target; - - while (elem && elem.dataset && !elem.dataset.item) { - elem = elem.parentNode; - } + const leftData = pathData[leftKey] || []; + const rightData = pathData[rightKey]; - if (!elem || !elem.dataset) { - return; + React.useEffect(() => { + if (props.currentItemUuid) { + setPathData({}); + setPath([props.currentItemUuid]); } + }, [props.currentItemUuid]); - const { id } = elem.dataset; + const fetchData = (keys, ignoreCache = false) => { + const keyArray = Array.isArray(keys) ? keys : [keys]; - const item: any = { - id, - data: rightData.find((elem) => elem.id === id), - }; + Promise.all( + keyArray + .filter(key => !!key) + .map(key => { + const dataExists = !!pathData[key]; + const runningRequest = pathPromise[key]; - if (id) { - onItemMenuOpen(event, item, isWritable); - } - }, - [onItemMenuOpen, isWritable, rightData]); + if (ignoreCache || (!dataExists && !runningRequest)) { + if (!isLoading) { + setIsLoading(true); + } - React.useEffect(() => { - let node = null; + pathPromise[key] = true; + console.log(key); - if (parentRef?.current) { - node = parentRef.current; - (node as any).addEventListener('contextmenu', handleRightClick); - } + return webdavClient.propfind(`c=${key}`, webDAVRequestConfig); + } - return () => { - if (node) { - (node as any).removeEventListener('contextmenu', handleRightClick); - } + return Promise.resolve(null); + }) + .filter(promise => !!promise) + ) + .then(requests => { + const newState = requests + .map((request, index) => { + if (request && request.responseXML != null) { + const key = keyArray[index]; + const result: any = extractFilesData(request.responseXML); + const sortedResult = sortBy(result, n => n.name).sort((n1, n2) => { + if (n1.type === "directory" && n2.type !== "directory") { + return -1; + } + if (n1.type !== "directory" && n2.type === "directory") { + return 1; + } + return 0; + }); + + return { [key]: sortedResult }; + } + return {}; + }) + .reduce((prev, next) => { + return { ...next, ...prev }; + }, {}); + setPathData(state => ({ ...state, ...newState })); + }) + .finally(() => { + setIsLoading(false); + keyArray.forEach(key => delete pathPromise[key]); + }); }; - }, [parentRef, handleRightClick]); - const handleClick = React.useCallback( - (event: any) => { - let isCheckbox = false; - let isMoreButton = false; - let elem = event.target; - - if (elem.type === 'checkbox') { - isCheckbox = true; - } - // The "More options" button click event could be triggered on its - // internal graphic element. - else if ((elem.dataset && elem.dataset.id === 'moreOptions') || (elem.parentNode && elem.parentNode.dataset && elem.parentNode.dataset.id === 'moreOptions')) { - isMoreButton = true; + React.useEffect(() => { + if (rightKey) { + fetchData(rightKey); + setLeftSearch(""); + setRightSearch(""); } + }, [rightKey]); // eslint-disable-line react-hooks/exhaustive-deps - while (elem && elem.dataset && !elem.dataset.item) { - elem = elem.parentNode; + const currentPDH = (collectionPanel.item || {}).portableDataHash; + React.useEffect(() => { + if (currentPDH) { + fetchData([leftKey, rightKey], true); } + }, [currentPDH]); // eslint-disable-line react-hooks/exhaustive-deps - if (elem && elem.dataset && !isCheckbox && !isMoreButton) { - const { parentPath, subfolderPath, breadcrumbPath, type } = elem.dataset; - - if (breadcrumbPath) { - const index = path.indexOf(breadcrumbPath); - setPath((state) => ([...state.slice(0, index + 1)])); - } - - if (parentPath && type === 'directory') { - if (path.length > 1) { - path.pop() - } + React.useEffect(() => { + if (rightData) { + const filtered = rightData.filter(({ name }) => name.indexOf(rightSearch) > -1); + setCollectionFiles(filtered, false)(dispatch); + } + }, [rightData, dispatch, rightSearch]); - setPath((state) => ([...state, parentPath])); - } + const handleRightClick = React.useCallback( + event => { + event.preventDefault(); + let elem = event.target; - if (subfolderPath && type === 'directory') { - setPath((state) => ([...state, subfolderPath])); + while (elem && elem.dataset && !elem.dataset.item) { + elem = elem.parentNode; } - if (elem.dataset.id && type === 'file') { - const item = rightData.find(({id}) => id === elem.dataset.id) || leftData.find(({ id }) => id === elem.dataset.id); - const enhancedItem = servicesProvider.getServices().collectionService.extendFileURL(item); - const fileUrl = sanitizeToken(getInlineFileUrl(enhancedItem.url, config.keepWebServiceUrl, config.keepWebInlineServiceUrl), true); - window.open(fileUrl, '_blank'); + if (!elem || !elem.dataset) { + return; } - } - if (isCheckbox) { - const { id } = elem.dataset; - const item = collectionPanelFiles[id]; - props.onSelectionToggle(event, item); - } - if (isMoreButton) { const { id } = elem.dataset; + const item: any = { id, - data: rightData.find((elem) => elem.id === id), + data: rightData.find(elem => elem.id === id), }; - onItemMenuOpen(event, item, isWritable); - } - }, - [path, setPath, collectionPanelFiles] // eslint-disable-line react-hooks/exhaustive-deps - ); - - const getItemIcon = React.useCallback( - (type: string, activeClass: string | null) => { - let Icon = DefaultIcon; - - switch (type) { - case 'directory': - Icon = DirectoryIcon; - break; - case 'file': - Icon = FileIcon; - break; - } - return ( - - - - ) - }, - [classes] - ); + if (id) { + onItemMenuOpen(event, item, isWritable); + } + }, + [onItemMenuOpen, isWritable, rightData] + ); - const getActiveClass = React.useCallback( - (name) => { - return path[path.length - 1] === name ? classes.rowActive : null; - }, - [path, classes] - ); + React.useEffect(() => { + let node = null; - const onOptionsMenuOpen = React.useCallback( - (ev, isWritable) => { - props.onOptionsMenuOpen(ev, isWritable); - }, - [props.onOptionsMenuOpen] // eslint-disable-line react-hooks/exhaustive-deps - ); - - return
-
-
- { path.map( (p: string, index: number) => - - {index === 0 ? 'Home' : p} /  - ) + if (parentRef?.current) { + node = parentRef.current; + (node as any).addEventListener("contextmenu", handleRightClick); } -
- - { - onOptionsMenuOpen(ev, isWritable); - }}> - - - -
-
-
1 ? classes.leftPanelVisible : classes.leftPanelHidden)} data-cy="collection-files-left-panel"> - 1 ? classes.backButton : classes.backButtonHidden}> - setPath((state) => ([...state.slice(0, state.length -1)]))}> - - - -
1 ? classes.searchWrapper : classes.searchWrapperHidden}> - + + return () => { + if (node) { + (node as any).removeEventListener("contextmenu", handleRightClick); + } + }; + }, [parentRef, handleRightClick]); + + const handleClick = React.useCallback( + (event: any) => { + let isCheckbox = false; + let isMoreButton = false; + let elem = event.target; + + if (elem.type === "checkbox") { + isCheckbox = true; + } + // The "More options" button click event could be triggered on its + // internal graphic element. + else if ( + (elem.dataset && elem.dataset.id === "moreOptions") || + (elem.parentNode && elem.parentNode.dataset && elem.parentNode.dataset.id === "moreOptions") + ) { + isMoreButton = true; + } + + while (elem && elem.dataset && !elem.dataset.item) { + elem = elem.parentNode; + } + + if (elem && elem.dataset && !isCheckbox && !isMoreButton) { + const { parentPath, subfolderPath, breadcrumbPath, type } = elem.dataset; + + if (breadcrumbPath) { + const index = path.indexOf(breadcrumbPath); + setPath(state => [...state.slice(0, index + 1)]); + } + + if (parentPath && type === "directory") { + if (path.length > 1) { + path.pop(); + } + + setPath(state => [...state, parentPath]); + } + + if (subfolderPath && type === "directory") { + setPath(state => [...state, subfolderPath]); + } + + if (elem.dataset.id && type === "file") { + const item = rightData.find(({ id }) => id === elem.dataset.id) || leftData.find(({ id }) => id === elem.dataset.id); + const enhancedItem = servicesProvider.getServices().collectionService.extendFileURL(item); + const fileUrl = sanitizeToken( + getInlineFileUrl(enhancedItem.url, config.keepWebServiceUrl, config.keepWebInlineServiceUrl), + true + ); + window.open(fileUrl, "_blank"); + } + } + + if (isCheckbox) { + const { id } = elem.dataset; + const item = collectionPanelFiles[id]; + props.onSelectionToggle(event, item); + } + if (isMoreButton) { + const { id } = elem.dataset; + const item: any = { + id, + data: rightData.find(elem => elem.id === id), + }; + onItemMenuOpen(event, item, isWritable); + } + }, + [path, setPath, collectionPanelFiles] // eslint-disable-line react-hooks/exhaustive-deps + ); + + const getItemIcon = React.useCallback( + (type: string, activeClass: string | null) => { + let Icon = DefaultIcon; + + switch (type) { + case "directory": + Icon = DirectoryIcon; + break; + case "file": + Icon = FileIcon; + break; + } + + return ( + + + + ); + }, + [classes] + ); + + const getActiveClass = React.useCallback( + name => { + return path[path.length - 1] === name ? classes.rowActive : null; + }, + [path, classes] + ); + + const onOptionsMenuOpen = React.useCallback( + (ev, isWritable) => { + props.onOptionsMenuOpen(ev, isWritable); + }, + [props.onOptionsMenuOpen] // eslint-disable-line react-hooks/exhaustive-deps + ); + + return ( +
+
+
+ {path.map((p: string, index: number) => ( + + {index === 0 ? "Home" : p} /  + + ))} +
+ + { + onOptionsMenuOpen(ev, isWritable); + }}> + + +
-
{ leftData - ? {({ height, width }) => { - const filtered = leftData.filter(({ name }) => name.indexOf(leftSearch) > -1); - return !!filtered.length - ? { ({ index, style }) => { - const { id, type, name } = filtered[index]; - return
- { getItemIcon(type, getActiveClass(name)) } -
- {name} +
+
1 ? classes.leftPanelVisible : classes.leftPanelHidden)} + data-cy="collection-files-left-panel"> + 1 ? classes.backButton : classes.backButtonHidden}> + setPath(state => [...state.slice(0, state.length - 1)])}> + + + +
1 ? classes.searchWrapper : classes.searchWrapperHidden}> + +
+
+ {leftData ? ( + + {({ height, width }) => { + const filtered = leftData.filter(({ name }) => name.indexOf(leftSearch) > -1); + return !!filtered.length ? ( + + {({ index, style }) => { + const { id, type, name } = filtered[index]; + return ( +
+ {getItemIcon(type, getActiveClass(name))} +
{name}
+ {getActiveClass(name) ? ( + + ) : null} +
+ ); + }} +
+ ) : ( +
No directories available
+ ); + }} +
+ ) : ( +
+
- { getActiveClass(name) - ? - : null - } -
; - }} - :
No directories available
- }} - - :
} -
-
-
-
- -
- { isWritable && - } -
{ rightData && !isLoading - ? {({ height, width }) => { - const filtered = rightData.filter(({ name }) => name.indexOf(rightSearch) > -1); - return !!filtered.length - ? { ({ index, style }) => { - const { id, type, name, size } = filtered[index]; - - return
-   - {getItemIcon(type, null)} -
- {name} -
- - { formatFileSize(size) } - - - - - - + )} +
+
+
+
+ +
+ {isWritable && ( + + )} +
+ {rightData && !isLoading ? ( + + {({ height, width }) => { + const filtered = rightData.filter(({ name }) => name.indexOf(rightSearch) > -1); + return !!filtered.length ? ( + + {({ index, style }) => { + const { id, type, name, size } = filtered[index]; + + return ( +
+ +   + {getItemIcon(type, null)} +
{name}
+ + {formatFileSize(size)} + + + + + + +
+ ); + }} +
+ ) : ( +
This collection is empty
+ ); + }} +
+ ) : ( +
+
- } } - :
This collection is empty
- }} - :
- -
} + )} +
+
-
-
})); + ); + }) +); diff --git a/src/store/navigation/navigation-action.ts b/src/store/navigation/navigation-action.ts index 55479188c1..b3bd32909d 100644 --- a/src/store/navigation/navigation-action.ts +++ b/src/store/navigation/navigation-action.ts @@ -2,86 +2,85 @@ // // SPDX-License-Identifier: AGPL-3.0 -import { Dispatch, compose, AnyAction } from 'redux'; +import { Dispatch, compose, AnyAction } from "redux"; import { push } from "react-router-redux"; -import { ResourceKind, extractUuidKind } from 'models/resource'; -import { SidePanelTreeCategory } from '../side-panel-tree/side-panel-tree-actions'; -import { Routes, getGroupUrl, getNavUrl, getUserProfileUrl } from 'routes/routes'; -import { RootState } from 'store/store'; -import { ServiceRepository } from 'services/services'; -import { pluginConfig } from 'plugins'; -import { snackbarActions, SnackbarKind } from 'store/snackbar/snackbar-actions'; -import { USERS_PANEL_LABEL, MY_ACCOUNT_PANEL_LABEL } from 'store/breadcrumbs/breadcrumbs-actions'; +import { ResourceKind, extractUuidKind } from "models/resource"; +import { SidePanelTreeCategory } from "../side-panel-tree/side-panel-tree-actions"; +import { Routes, getGroupUrl, getNavUrl, getUserProfileUrl } from "routes/routes"; +import { RootState } from "store/store"; +import { ServiceRepository } from "services/services"; +import { pluginConfig } from "plugins"; +import { snackbarActions, SnackbarKind } from "store/snackbar/snackbar-actions"; +import { USERS_PANEL_LABEL, MY_ACCOUNT_PANEL_LABEL } from "store/breadcrumbs/breadcrumbs-actions"; export const navigationNotAvailable = (id: string) => snackbarActions.OPEN_SNACKBAR({ message: `${id} not available`, hideDuration: 3000, - kind: SnackbarKind.ERROR + kind: SnackbarKind.ERROR, }); -export const navigateTo = (uuid: string) => - async (dispatch: Dispatch, getState: () => RootState) => { +export const navigateTo = (inputUuid: any) => async (dispatch: Dispatch, getState: () => RootState) => { + const uuid = typeof inputUuid === "string" ? inputUuid : inputUuid[0]; - for (const navToFn of pluginConfig.navigateToHandlers) { - if (navToFn(dispatch, getState, uuid)) { - return; - } - } - - const kind = extractUuidKind(uuid); - switch (kind) { - case ResourceKind.PROJECT: - case ResourceKind.USER: - case ResourceKind.COLLECTION: - case ResourceKind.CONTAINER_REQUEST: - dispatch(pushOrGoto(getNavUrl(uuid, getState().auth))); - return; - case ResourceKind.VIRTUAL_MACHINE: - dispatch(navigateToAdminVirtualMachines); - return; - case ResourceKind.WORKFLOW: - dispatch(pushOrGoto(getNavUrl(uuid, getState().auth))); - // dispatch(openDetailsPanel(uuid)); - return; + for (const navToFn of pluginConfig.navigateToHandlers) { + if (navToFn(dispatch, getState, uuid)) { + return; } + } - switch (uuid) { - case SidePanelTreeCategory.PROJECTS: - const usr = getState().auth.user; - if (usr) { - dispatch(pushOrGoto(getNavUrl(usr.uuid, getState().auth))); - } - return; - case SidePanelTreeCategory.FAVORITES: - dispatch(navigateToFavorites); - return; - case SidePanelTreeCategory.PUBLIC_FAVORITES: - dispatch(navigateToPublicFavorites); - return; - case SidePanelTreeCategory.SHARED_WITH_ME: - dispatch(navigateToSharedWithMe); - return; - case SidePanelTreeCategory.TRASH: - dispatch(navigateToTrash); - return; - case SidePanelTreeCategory.GROUPS: - dispatch(navigateToGroups); - return; - case SidePanelTreeCategory.ALL_PROCESSES: - dispatch(navigateToAllProcesses); - return; - case USERS_PANEL_LABEL: - dispatch(navigateToUsers); - return; - case MY_ACCOUNT_PANEL_LABEL: - dispatch(navigateToMyAccount); - return; - } + const kind = extractUuidKind(uuid); + switch (kind) { + case ResourceKind.PROJECT: + case ResourceKind.USER: + case ResourceKind.COLLECTION: + case ResourceKind.CONTAINER_REQUEST: + dispatch(pushOrGoto(getNavUrl(uuid, getState().auth))); + return; + case ResourceKind.VIRTUAL_MACHINE: + dispatch(navigateToAdminVirtualMachines); + return; + case ResourceKind.WORKFLOW: + dispatch(pushOrGoto(getNavUrl(uuid, getState().auth))); + // dispatch(openDetailsPanel(uuid)); + return; + } - dispatch(navigationNotAvailable(uuid)); - }; + switch (uuid) { + case SidePanelTreeCategory.PROJECTS: + const usr = getState().auth.user; + if (usr) { + dispatch(pushOrGoto(getNavUrl(usr.uuid, getState().auth))); + } + return; + case SidePanelTreeCategory.FAVORITES: + dispatch(navigateToFavorites); + return; + case SidePanelTreeCategory.PUBLIC_FAVORITES: + dispatch(navigateToPublicFavorites); + return; + case SidePanelTreeCategory.SHARED_WITH_ME: + dispatch(navigateToSharedWithMe); + return; + case SidePanelTreeCategory.TRASH: + dispatch(navigateToTrash); + return; + case SidePanelTreeCategory.GROUPS: + dispatch(navigateToGroups); + return; + case SidePanelTreeCategory.ALL_PROCESSES: + dispatch(navigateToAllProcesses); + return; + case USERS_PANEL_LABEL: + dispatch(navigateToUsers); + return; + case MY_ACCOUNT_PANEL_LABEL: + dispatch(navigateToMyAccount); + return; + } + dispatch(navigationNotAvailable(uuid)); +}; export const navigateToNotFound = push(Routes.NO_MATCH); @@ -98,7 +97,7 @@ export const navigateToWorkflows = push(Routes.WORKFLOWS); export const pushOrGoto = (url: string): AnyAction => { if (url === "") { return { type: "noop" }; - } else if (url[0] === '/') { + } else if (url[0] === "/") { return push(url); } else { window.location.href = url; @@ -106,7 +105,6 @@ export const pushOrGoto = (url: string): AnyAction => { } }; - export const navigateToRootProject = (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => { navigateTo(SidePanelTreeCategory.PROJECTS)(dispatch, getState); }; @@ -117,7 +115,7 @@ export const navigateToRunProcess = push(Routes.RUN_PROCESS); export const navigateToSearchResults = (searchValue: string) => { if (searchValue !== "") { - return push({ pathname: Routes.SEARCH_RESULTS, search: '?q=' + encodeURIComponent(searchValue) }); + return push({ pathname: Routes.SEARCH_RESULTS, search: "?q=" + encodeURIComponent(searchValue) }); } else { return push({ pathname: Routes.SEARCH_RESULTS }); } diff --git a/src/views-components/context-menu/action-sets/collection-action-set.ts b/src/views-components/context-menu/action-sets/collection-action-set.ts index c93d585990..f960571e8c 100644 --- a/src/views-components/context-menu/action-sets/collection-action-set.ts +++ b/src/views-components/context-menu/action-sets/collection-action-set.ts @@ -51,8 +51,8 @@ const commonActionSet: ContextMenuActionSet = [ { icon: OpenIcon, name: "Open in new tab", - execute: (dispatch, resource) => { - dispatch(openInNewTabAction(resource)); + execute: (dispatch, resources) => { + dispatch(openInNewTabAction(resources[0])); }, }, { @@ -66,7 +66,7 @@ const commonActionSet: ContextMenuActionSet = [ icon: CopyIcon, name: "Make a copy", execute: (dispatch, resources) => { - resources.forEach(resource => dispatch(openCollectionCopyDialog(resource))); + resources.forEach(resource => dispatch(openCollectionCopyDialog(resource))); //here }, }, { -- 2.30.2