Merge branch '18207-Workbench2-is-not-clearing-the-project-content-when-switching...
authorDaniel Kutyła <daniel.kutyla@contractors.roche.com>
Wed, 19 Jan 2022 15:55:21 +0000 (16:55 +0100)
committerDaniel Kutyła <daniel.kutyla@contractors.roche.com>
Wed, 19 Jan 2022 15:55:26 +0000 (16:55 +0100)
closes #18207

Arvados-DCO-1.1-Signed-off-by: Daniel Kutyła <daniel.kutyla@contractors.roche.com>

src/components/data-table/data-table.tsx
src/components/icon/icon.tsx
src/store/groups-panel/groups-panel-middleware-service.ts
src/store/project-panel/project-panel-middleware-service.ts
src/store/shared-with-me-panel/shared-with-me-middleware-service.ts
src/store/shared-with-me-panel/shared-with-me-panel-actions.ts
src/views-components/data-explorer/data-explorer.tsx
src/views/project-panel/project-panel.tsx
src/views/shared-with-me-panel/shared-with-me-panel.tsx

index de52d365030dfdc7a48a47bb2f6f52cd31fade51..2119e53d7df603a91de2da7dfa4f9772403963e4 100644 (file)
@@ -10,6 +10,7 @@ import { DataTableDefaultView } from '../data-table-default-view/data-table-defa
 import { DataTableFilters } from '../data-table-filters/data-table-filters-tree';
 import { DataTableFiltersPopover } from '../data-table-filters/data-table-filters-popover';
 import { countNodes } from 'models/tree';
+import { PendingIcon } from 'components/icon/icon';
 import { SvgIconProps } from '@material-ui/core/SvgIcon';
 import ArrowDownwardIcon from '@material-ui/icons/ArrowDownward';
 
@@ -35,7 +36,7 @@ export interface DataTableDataProps<T> {
     currentRoute?: string;
 }
 
-type CssRules = "tableBody" | "root" | "content" | "noItemsInfo" | 'tableCell' | 'arrow' | 'arrowButton' | 'tableCellWorkflows';
+type CssRules = "tableBody" | "root" | "content" | "noItemsInfo" | 'tableCell' | 'arrow' | 'arrowButton' | 'tableCellWorkflows' | 'loader';
 
 const styles: StyleRulesCallback<CssRules> = (theme: Theme) => ({
     root: {
@@ -48,6 +49,11 @@ const styles: StyleRulesCallback<CssRules> = (theme: Theme) => ({
     tableBody: {
         background: theme.palette.background.paper
     },
+    loader: {
+        left: '50%',
+        marginLeft: '-84px',
+        position: 'absolute'
+    },
     noItemsInfo: {
         textAlign: "center",
         padding: theme.spacing.unit
@@ -90,7 +96,14 @@ export const DataTable = withStyles(styles)(
                             </TableRow>
                         </TableHead>
                         <TableBody className={classes.tableBody}>
-                            {items.map(this.renderBodyRow)}
+                            {
+                                this.props.working ?
+                                <div className={classes.loader}>
+                                    <DataTableDefaultView
+                                        icon={PendingIcon}
+                                        messages={['Loading data, please wait.']} />
+                                </div> : items.map(this.renderBodyRow)
+                            }
                         </TableBody>
                     </Table>
                     {items.length === 0 && this.props.working !== undefined && !this.props.working && this.renderNoItemsPlaceholder()}
index 15a9f02d7339ab9c05411135d00d8190f06a0a92..4f7305f740e524b87745c1c0b82ef84e4f28305a 100644 (file)
@@ -68,14 +68,20 @@ import Computer from '@material-ui/icons/Computer';
 
 // Import FontAwesome icons
 import { library } from '@fortawesome/fontawesome-svg-core';
-import { faPencilAlt, faSlash, faUsers } from '@fortawesome/free-solid-svg-icons';
+import { faPencilAlt, faSlash, faUsers, faEllipsisH } from '@fortawesome/free-solid-svg-icons';
 import { CropFreeSharp } from '@material-ui/icons';
 library.add(
     faPencilAlt,
     faSlash,
     faUsers,
+    faEllipsisH,
 );
 
+export const PendingIcon = (props: any) =>
+    <span {...props}>
+        <span className='fas fa-ellipsis-h' />
+    </span>
+
 export const ReadOnlyIcon = (props: any) =>
     <span {...props}>
         <div className="fa-layers fa-1x fa-fw">
index 2841550686d61dc9d1f7e7652e05334cdbbff4f3..3997e33cef015547bd27ac74435833198248414a 100644 (file)
@@ -15,6 +15,7 @@ import { OrderBuilder, OrderDirection } from 'services/api/order-builder';
 import { GroupResource, GroupClass } from 'models/group';
 import { SortDirection } from 'components/data-table/data-column';
 import { GroupsPanelColumnNames } from 'views/groups-panel/groups-panel';
+import { progressIndicatorActions } from "store/progress-indicator/progress-indicator-actions";
 
 export class GroupsPanelMiddlewareService extends DataExplorerMiddlewareService {
     constructor(private services: ServiceRepository, id: string) {
@@ -26,6 +27,7 @@ export class GroupsPanelMiddlewareService extends DataExplorerMiddlewareService
             api.dispatch(groupsPanelDataExplorerIsNotSet());
         } else {
             try {
+                api.dispatch(progressIndicatorActions.START_WORKING(this.getId()));
                 const order = new OrderBuilder<GroupResource>();
                 const sortColumn = getSortColumn(dataExplorer);
                 if (sortColumn) {
@@ -58,6 +60,8 @@ export class GroupsPanelMiddlewareService extends DataExplorerMiddlewareService
                 api.dispatch(updateResources(permissions.items));
             } catch (e) {
                 api.dispatch(couldNotFetchFavoritesContents());
+            } finally {
+                api.dispatch(progressIndicatorActions.STOP_WORKING(this.getId()));
             }
         }
     }
index 659cd957a1bbaaac41b32e214927200ab0be89d9..456508437c9beb10bee2c04fa9cfebc6cdaba6d6 100644 (file)
@@ -58,7 +58,6 @@ export class ProjectPanelMiddlewareService extends DataExplorerMiddlewareService
                 await api.dispatch<any>(loadMissingProcessesInformation(response.items));
                 api.dispatch(setItems(response));
             } catch (e) {
-                api.dispatch(progressIndicatorActions.PERSIST_STOP_WORKING(this.getId()));
                 api.dispatch(projectPanelActions.SET_ITEMS({
                     items: [],
                     itemsAvailable: 0,
@@ -66,6 +65,8 @@ export class ProjectPanelMiddlewareService extends DataExplorerMiddlewareService
                     rowsPerPage: dataExplorer.rowsPerPage
                 }));
                 api.dispatch(couldNotFetchProjectContents());
+            } finally {
+                api.dispatch(progressIndicatorActions.PERSIST_STOP_WORKING(this.getId()));
             }
         }
     }
index c55a893de13a4baacf6d9190f79085a9625c447d..5f92637cbb3b7951874fe06218dac34acb7f9b2b 100644 (file)
@@ -21,6 +21,7 @@ import { ProjectPanelColumnNames } from 'views/project-panel/project-panel';
 import { getSortColumn } from "store/data-explorer/data-explorer-reducer";
 import { updatePublicFavorites } from 'store/public-favorites/public-favorites-actions';
 import { FilterBuilder } from 'services/api/filter-builder';
+import { progressIndicatorActions } from 'store/progress-indicator/progress-indicator-actions';
 
 export class SharedWithMeMiddlewareService extends DataExplorerMiddlewareService {
     constructor(private services: ServiceRepository, id: string) {
@@ -31,6 +32,7 @@ export class SharedWithMeMiddlewareService extends DataExplorerMiddlewareService
         const state = api.getState();
         const dataExplorer = getDataExplorer(state.dataExplorer, this.getId());
         try {
+            api.dispatch(progressIndicatorActions.START_WORKING(this.getId()));
             const response = await this.services.groupsService
                 .contents('', {
                     ...getParams(dataExplorer),
@@ -44,6 +46,8 @@ export class SharedWithMeMiddlewareService extends DataExplorerMiddlewareService
             api.dispatch(setItems(response));
         } catch (e) {
             api.dispatch(couldNotFetchSharedItems());
+        } finally {
+            api.dispatch(progressIndicatorActions.PERSIST_STOP_WORKING(this.getId()));
         }
     }
 }
index 1210edbbdf6406c653cb6f416b3a884a13af91b7..c8731ae68e55e9455eb00cbbd13915d464022eb3 100644 (file)
@@ -3,16 +3,7 @@
 // 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());
-    };
-
-
+export const loadSharedWithMePanel = () => sharedWithMePanelActions.REQUEST_ITEMS();
index f6f938a55b2de32391c58c0beab4265e17f23bea..900ab94e0cd00ee02e5566934ea95097feafaa73 100644 (file)
@@ -18,14 +18,30 @@ interface Props {
     onContextMenu?: (event: React.MouseEvent<HTMLElement>, item: any, isAdmin?: boolean) => void;
     onRowDoubleClick: (item: any) => void;
     extractKey?: (item: any) => React.Key;
+    working?: boolean;
 }
 
+let data: any[] = [];
+let href: string = '';
+
 const mapStateToProps = (state: RootState, { id }: Props) => {
     const progress = state.progressIndicator.find(p => p.id === id);
-    const working = progress && progress.working;
+    const dataExplorerState = getDataExplorer(state.dataExplorer, id);
     const currentRoute = state.router.location ? state.router.location.pathname : '';
     const currentItemUuid = currentRoute === '/workflows' ? state.properties.workflowPanelDetailsUuid : state.detailsPanel.resourceUuid;
-    return { ...getDataExplorer(state.dataExplorer, id), working, paperKey: currentRoute, currentItemUuid };
+
+    let loading = false;
+
+    if (dataExplorerState.items.length > 0 && data === dataExplorerState.items && href !== window.location.href) {
+        loading = true
+    } else {
+        href = window.location.href;
+        data = dataExplorerState.items;
+    }
+
+    const working = (progress && progress.working) || loading;
+
+    return { ...dataExplorerState, working, paperKey: currentRoute, currentItemUuid };
 };
 
 const mapDispatchToProps = () => {
index e08aea32217016ae6448f8cb55e87640e8d12860..178a96aaff674b5ba8ac90d7dab69309744a1f77 100644 (file)
@@ -131,20 +131,23 @@ interface ProjectPanelDataProps {
     resources: ResourcesState;
     isAdmin: boolean;
     userUuid: string;
+    dataExplorerItems: any;
 }
 
 type ProjectPanelProps = ProjectPanelDataProps & DispatchProp
     & WithStyles<CssRules> & RouteComponentProps<{ id: string }>;
 
+
 export const ProjectPanel = withStyles(styles)(
     connect((state: RootState) => ({
         currentItemId: getProperty(PROJECT_PANEL_CURRENT_UUID)(state.properties),
         resources: state.resources,
-        userUuid: state.auth.user!.uuid,
+        userUuid: state.auth.user!.uuid
     }))(
         class extends React.Component<ProjectPanelProps> {
             render() {
                 const { classes } = this.props;
+
                 return <div data-cy='project-panel' className={classes.root}>
                     <DataExplorer
                         id={PROJECT_PANEL_ID}
index 219410c54e3583655e90ca7f2e82e9a05ccfdf63..7ba9077cac8ec28e7e0bd839552844491ec63538 100644 (file)
@@ -55,7 +55,11 @@ export const SharedWithMePanel = withStyles(styles)(
                     onRowDoubleClick={this.handleRowDoubleClick}
                     onContextMenu={this.handleContextMenu}
                     contextMenuColumn={false}
-                    dataTableDefaultView={<DataTableDefaultView icon={ShareMeIcon} />} /></div>;
+                    dataTableDefaultView={
+                        <DataTableDefaultView
+                            icon={ShareMeIcon}
+                            messages={['No shared items']} />
+                    } /></div>;
             }
 
             handleContextMenu = (event: React.MouseEvent<HTMLElement>, resourceUuid: string) => {