From 3c4424da163db13b80a2070eef1e73fe744fad9e Mon Sep 17 00:00:00 2001 From: Lucas Di Pentima Date: Fri, 13 Nov 2020 15:16:19 -0300 Subject: [PATCH] 13494: Adds content to the "versions" tab on collection's details panel. Old versions are requested only when the details panel is open. Arvados-DCO-1.1-Signed-off-by: Lucas Di Pentima --- .../collection-panel-files.tsx | 4 +- .../collection-panel-action.ts | 9 +- .../details-panel/details-panel-action.ts | 38 ++++++++- .../details-panel/details-panel-reducer.ts | 6 +- src/store/workbench/workbench-actions.ts | 2 +- .../collection-panel-files.ts | 2 +- .../details-panel/collection-details.tsx | 85 ++++++++++++++++++- .../all-processes-panel.tsx | 2 +- .../collection-panel/collection-panel.tsx | 2 +- src/views/favorite-panel/favorite-panel.tsx | 2 +- src/views/project-panel/project-panel.tsx | 2 +- .../shared-with-me-panel.tsx | 2 +- src/views/trash-panel/trash-panel.tsx | 2 +- 13 files changed, 138 insertions(+), 20 deletions(-) diff --git a/src/components/collection-panel-files/collection-panel-files.tsx b/src/components/collection-panel-files/collection-panel-files.tsx index bf551b9f..14c3c48c 100644 --- a/src/components/collection-panel-files/collection-panel-files.tsx +++ b/src/components/collection-panel-files/collection-panel-files.tsx @@ -119,10 +119,10 @@ export const CollectionPanelFilesComponent = ({ onItemMenuOpen, onSearchChange, Name - + File size - + {isLoading ?
diff --git a/src/store/collection-panel/collection-panel-action.ts b/src/store/collection-panel/collection-panel-action.ts index 15d5ef72..851ba84d 100644 --- a/src/store/collection-panel/collection-panel-action.ts +++ b/src/store/collection-panel/collection-panel-action.ts @@ -3,7 +3,10 @@ // SPDX-License-Identifier: AGPL-3.0 import { Dispatch } from "redux"; -import { loadCollectionFiles, COLLECTION_PANEL_LOAD_FILES_THRESHOLD } from "./collection-panel-files/collection-panel-files-actions"; +import { + loadCollectionFiles, + COLLECTION_PANEL_LOAD_FILES_THRESHOLD +} from "./collection-panel-files/collection-panel-files-actions"; import { CollectionResource } from '~/models/collection'; import { RootState } from "~/store/store"; import { ServiceRepository } from "~/services/services"; @@ -18,7 +21,6 @@ import { addProperty, deleteProperty } from "~/lib/resource-properties"; export const collectionPanelActions = unionize({ SET_COLLECTION: ofType(), - LOAD_COLLECTION: ofType<{ uuid: string }>(), LOAD_COLLECTION_SUCCESS: ofType<{ item: CollectionResource }>(), LOAD_BIG_COLLECTIONS: ofType(), }); @@ -30,9 +32,8 @@ export const COLLECTION_TAG_FORM_NAME = 'collectionTagForm'; export const loadCollectionPanel = (uuid: string) => async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => { const { collectionPanel: { item } } = getState(); - dispatch(collectionPanelActions.LOAD_COLLECTION({ uuid })); const collection = item ? item : await services.collectionService.get(uuid); - dispatch(loadDetailsPanel(collection.uuid)); + dispatch(loadDetailsPanel(collection.uuid)); dispatch(collectionPanelActions.LOAD_COLLECTION_SUCCESS({ item: collection })); dispatch(resourcesActions.SET_RESOURCES([collection])); if (collection.fileCount <= COLLECTION_PANEL_LOAD_FILES_THRESHOLD && diff --git a/src/store/details-panel/details-panel-action.ts b/src/store/details-panel/details-panel-action.ts index c5d472ad..cbce4210 100644 --- a/src/store/details-panel/details-panel-action.ts +++ b/src/store/details-panel/details-panel-action.ts @@ -14,12 +14,16 @@ import { startSubmit, stopSubmit } from 'redux-form'; import { resourcesActions } from '~/store/resources/resources-actions'; import {snackbarActions, SnackbarKind} from '~/store/snackbar/snackbar-actions'; import { addProperty, deleteProperty } from '~/lib/resource-properties'; +import { FilterBuilder } from '~/services/api/filter-builder'; +import { OrderBuilder } from '~/services/api/order-builder'; +import { CollectionResource } from '~/models/collection'; +import { extractUuidKind, ResourceKind } from '~/models/resource'; export const SLIDE_TIMEOUT = 500; export const detailsPanelActions = unionize({ TOGGLE_DETAILS_PANEL: ofType<{}>(), - OPEN_DETAILS_PANEL: ofType(), + OPEN_DETAILS_PANEL: ofType(), LOAD_DETAILS_PANEL: ofType() }); @@ -28,15 +32,43 @@ export type DetailsPanelAction = UnionOf; export const PROJECT_PROPERTIES_FORM_NAME = 'projectPropertiesFormName'; export const PROJECT_PROPERTIES_DIALOG_NAME = 'projectPropertiesDialogName'; -export const loadDetailsPanel = (uuid: string) => detailsPanelActions.LOAD_DETAILS_PANEL(uuid); +export const loadDetailsPanel = (uuid: string) => + (dispatch: Dispatch, getState: () => RootState) => { + if (getState().detailsPanel.isOpened) { + switch(extractUuidKind(uuid)) { + case ResourceKind.COLLECTION: + dispatch(refreshCollectionVersionsList(uuid)); + break; + default: + break; + } + } + dispatch(detailsPanelActions.LOAD_DETAILS_PANEL(uuid)); + }; -export const openDetailsPanel = (uuid: string) => detailsPanelActions.OPEN_DETAILS_PANEL(uuid); +export const openDetailsPanel = (uuid: string, tabNr: number = 0) => + (dispatch: Dispatch) => { + dispatch(loadDetailsPanel(uuid)); + dispatch(detailsPanelActions.OPEN_DETAILS_PANEL(tabNr)); + }; export const openProjectPropertiesDialog = () => (dispatch: Dispatch) => { dispatch(dialogActions.OPEN_DIALOG({ id: PROJECT_PROPERTIES_DIALOG_NAME, data: { } })); }; +export const refreshCollectionVersionsList = (uuid: string) => + async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => { + const versions = await services.collectionService.list({ + filters: new FilterBuilder() + .addEqual('current_version_uuid', uuid) + .getFilters(), + includeOldVersions: true, + order: new OrderBuilder().addDesc("version").getOrder() + }); + dispatch(resourcesActions.SET_RESOURCES(versions.items.slice(1))); + }; + export const deleteProjectProperty = (key: string, value: string) => async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => { const { detailsPanel, resources } = getState(); diff --git a/src/store/details-panel/details-panel-reducer.ts b/src/store/details-panel/details-panel-reducer.ts index 38c0edd5..6c32551c 100644 --- a/src/store/details-panel/details-panel-reducer.ts +++ b/src/store/details-panel/details-panel-reducer.ts @@ -7,17 +7,19 @@ import { detailsPanelActions, DetailsPanelAction } from "./details-panel-action" export interface DetailsPanelState { resourceUuid: string; isOpened: boolean; + tabNr: number; } const initialState = { resourceUuid: '', - isOpened: false + isOpened: false, + tabNr: 0 }; export const detailsPanelReducer = (state: DetailsPanelState = initialState, action: DetailsPanelAction) => detailsPanelActions.match(action, { default: () => state, LOAD_DETAILS_PANEL: resourceUuid => ({ ...state, resourceUuid }), - OPEN_DETAILS_PANEL: resourceUuid => ({ resourceUuid, isOpened: true }), + OPEN_DETAILS_PANEL: tabNr => ({ ...state, isOpened: true, tabNr }), TOGGLE_DETAILS_PANEL: () => ({ ...state, isOpened: !state.isOpened }), }); diff --git a/src/store/workbench/workbench-actions.ts b/src/store/workbench/workbench-actions.ts index 944c48cf..0416d815 100644 --- a/src/store/workbench/workbench-actions.ts +++ b/src/store/workbench/workbench-actions.ts @@ -379,7 +379,7 @@ export const loadProcess = (uuid: string) => const process = await dispatch(processesActions.loadProcess(uuid)); await dispatch(activateSidePanelTreeItem(process.containerRequest.ownerUuid)); dispatch(setProcessBreadcrumbs(uuid)); - dispatch(loadDetailsPanel(uuid)); + dispatch(loadDetailsPanel(uuid)); }); export const updateProcess = (data: processUpdateActions.ProcessUpdateFormDialogData) => diff --git a/src/views-components/collection-panel-files/collection-panel-files.ts b/src/views-components/collection-panel-files/collection-panel-files.ts index 9859f84b..91420edb 100644 --- a/src/views-components/collection-panel-files/collection-panel-files.ts +++ b/src/views-components/collection-panel-files/collection-panel-files.ts @@ -75,7 +75,7 @@ const mapDispatchToProps = (dispatch: Dispatch): Pick(openCollectionFilesContextMenu(event, isWritable)); }, onFileClick: (id) => { - dispatch(openDetailsPanel(id)); + dispatch(openDetailsPanel(id)); }, }); diff --git a/src/views-components/details-panel/collection-details.tsx b/src/views-components/details-panel/collection-details.tsx index d2457559..51b09b4f 100644 --- a/src/views-components/details-panel/collection-details.tsx +++ b/src/views-components/details-panel/collection-details.tsx @@ -7,6 +7,25 @@ import { CollectionIcon } from '~/components/icon/icon'; import { CollectionResource } from '~/models/collection'; import { DetailsData } from "./details-data"; import { CollectionDetailsAttributes } from '~/views/collection-panel/collection-panel'; +import { RootState } from '~/store/store'; +import { filterResources, getResource } from '~/store/resources/resources'; +import { connect } from 'react-redux'; +import { Grid, ListItem, StyleRulesCallback, Typography, withStyles, WithStyles } from '@material-ui/core'; +import { formatDate, formatFileSize } from '~/common/formatters'; +import { Dispatch } from 'redux'; +import { navigateTo } from '~/store/navigation/navigation-action'; + +export type CssRules = 'versionBrowserHeader' | 'selectedVersion'; + +const styles: StyleRulesCallback = theme => ({ + versionBrowserHeader: { + textAlign: 'center', + fontWeight: 'bold' + }, + selectedVersion: { + fontWeight: 'bold' + } +}); export class CollectionDetails extends DetailsData { @@ -34,6 +53,70 @@ export class CollectionDetails extends DetailsData { } private getVersionBrowser() { - return
; + return ; } } + +interface CollectionVersionBrowserProps { + currentCollection: CollectionResource | undefined; + versions: CollectionResource[]; +} + +interface CollectionVersionBrowserDispatchProps { + showVersion: (c: CollectionResource) => void; +} + +const mapStateToProps = (state: RootState): CollectionVersionBrowserProps => { + const currentCollection = getResource(state.detailsPanel.resourceUuid)(state.resources); + const versions = currentCollection + && filterResources(rsc => + (rsc as CollectionResource).currentVersionUuid === currentCollection.currentVersionUuid)(state.resources) + .sort((a: CollectionResource, b: CollectionResource) => b.version - a.version) as CollectionResource[] + || []; + return { currentCollection, versions }; +}; + +const mapDispatchToProps = () => + (dispatch: Dispatch): CollectionVersionBrowserDispatchProps => ({ + showVersion: (collection) => dispatch(navigateTo(collection.uuid)), + }); + +const CollectionVersionBrowser = withStyles(styles)( + connect(mapStateToProps, mapDispatchToProps)( + ({ currentCollection, versions, showVersion, classes }: CollectionVersionBrowserProps & CollectionVersionBrowserDispatchProps & WithStyles) => { + return <> + + + Version + + + Size + + + Date + + + { versions.map(item => { + const isSelectedVersion = !!(currentCollection && currentCollection.uuid === item.uuid); + return ( + showVersion(item)} + selected={isSelectedVersion}> + + + {item.version} + + + {formatFileSize(item.fileSizeTotal)} + + + {formatDate(item.modifiedAt)} + + + + ); + })} + ; + })); \ No newline at end of file diff --git a/src/views/all-processes-panel/all-processes-panel.tsx b/src/views/all-processes-panel/all-processes-panel.tsx index 650a0d95..d9a0fd90 100644 --- a/src/views/all-processes-panel/all-processes-panel.tsx +++ b/src/views/all-processes-panel/all-processes-panel.tsx @@ -138,7 +138,7 @@ export const AllProcessesPanel = withStyles(styles)( } handleRowClick = (uuid: string) => { - this.props.dispatch(loadDetailsPanel(uuid)); + this.props.dispatch(loadDetailsPanel(uuid)); } render() { diff --git a/src/views/collection-panel/collection-panel.tsx b/src/views/collection-panel/collection-panel.tsx index feade60c..f6df6212 100644 --- a/src/views/collection-panel/collection-panel.tsx +++ b/src/views/collection-panel/collection-panel.tsx @@ -266,7 +266,7 @@ export const CollectionPanel = withStyles(styles)( const { item } = this.props; if (item) { e.stopPropagation(); - this.props.dispatch(openDetailsPanel(item.uuid)); + this.props.dispatch(openDetailsPanel(item.uuid)); } } diff --git a/src/views/favorite-panel/favorite-panel.tsx b/src/views/favorite-panel/favorite-panel.tsx index 6b5cd4c3..cad2f9ba 100644 --- a/src/views/favorite-panel/favorite-panel.tsx +++ b/src/views/favorite-panel/favorite-panel.tsx @@ -153,7 +153,7 @@ export const FavoritePanel = withStyles(styles)( } handleRowClick = (uuid: string) => { - this.props.dispatch(loadDetailsPanel(uuid)); + this.props.dispatch(loadDetailsPanel(uuid)); } render() { diff --git a/src/views/project-panel/project-panel.tsx b/src/views/project-panel/project-panel.tsx index d79b98cf..11223f22 100644 --- a/src/views/project-panel/project-panel.tsx +++ b/src/views/project-panel/project-panel.tsx @@ -178,7 +178,7 @@ export const ProjectPanel = withStyles(styles)( } handleRowClick = (uuid: string) => { - this.props.dispatch(loadDetailsPanel(uuid)); + this.props.dispatch(loadDetailsPanel(uuid)); } } 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 index c9408752..9b4bcc85 100644 --- a/src/views/shared-with-me-panel/shared-with-me-panel.tsx +++ b/src/views/shared-with-me-panel/shared-with-me-panel.tsx @@ -77,7 +77,7 @@ export const SharedWithMePanel = withStyles(styles)( } handleRowClick = (uuid: string) => { - this.props.dispatch(loadDetailsPanel(uuid)); + this.props.dispatch(loadDetailsPanel(uuid)); } } ) diff --git a/src/views/trash-panel/trash-panel.tsx b/src/views/trash-panel/trash-panel.tsx index dec5af51..3173876b 100644 --- a/src/views/trash-panel/trash-panel.tsx +++ b/src/views/trash-panel/trash-panel.tsx @@ -179,7 +179,7 @@ export const TrashPanel = withStyles(styles)( } handleRowClick = (uuid: string) => { - this.props.dispatch(loadDetailsPanel(uuid)); + this.props.dispatch(loadDetailsPanel(uuid)); } } ) -- 2.30.2