18972: Records the last refresh click on localStorage for others to act on.
authorLucas Di Pentima <lucas.dipentima@curii.com>
Tue, 12 Apr 2022 14:32:41 +0000 (11:32 -0300)
committerLucas Di Pentima <lucas.dipentima@curii.com>
Tue, 12 Apr 2022 19:30:06 +0000 (16:30 -0300)
Adding timestamps on the store isn't recommended as the reducers should be
only pure functions so I opted to use the localStorage for this application.
The DataExplorer code now also checks if the last Refresh button click
timestamp changed to decide if it should show the "loading, please wait"
message.

Arvados-DCO-1.1-Signed-off-by: Lucas Di Pentima <lucas.dipentima@curii.com>

src/components/refresh-button/refresh-button.test.tsx
src/components/refresh-button/refresh-button.tsx
src/views-components/data-explorer/data-explorer.tsx
src/views-components/main-content-bar/main-content-bar.tsx

index f9fa32d21fdc9cfc01e6fbaa1578f873cf601332..3a9292e6ff703c9130465c7578af6ef00b183c13 100644 (file)
@@ -6,7 +6,7 @@ import React from "react";
 import { Button } from "@material-ui/core";
 import { shallow, configure } from "enzyme";
 import Adapter from "enzyme-adapter-react-16";
-import { RefreshButton } from './refresh-button';
+import { LAST_REFRESH_TIMESTAMP, RefreshButton } from './refresh-button';
 
 configure({ adapter: new Adapter() });
 
@@ -31,6 +31,7 @@ describe('<RefreshButton />', () => {
     });
 
     it('should pass window location to router', () => {
+        expect(localStorage.getItem(LAST_REFRESH_TIMESTAMP)).toBeFalsy();
         // setup
         const wrapper = shallow(<RefreshButton {...props} />);
 
@@ -39,5 +40,6 @@ describe('<RefreshButton />', () => {
 
         // then
         expect(props.history.replace).toHaveBeenCalledWith('/');
+        expect(localStorage.getItem(LAST_REFRESH_TIMESTAMP)).not.toBeFalsy();
     });
 });
index 9971547bf092a5462b90f4db0e9ee66bb32f43a7..e2fe54846820d3efbaf744ce20a1f75d57f0cfec 100644 (file)
@@ -26,12 +26,17 @@ interface RefreshButtonProps {
     onClick?: () => void;
 }
 
+export const LAST_REFRESH_TIMESTAMP = 'lastRefreshTimestamp';
+
 export const RefreshButton = ({ history, classes, onClick }: RouteComponentProps & WithStyles<CssRules> & RefreshButtonProps) =>
     <Button
         color="primary"
         size="small"
         variant="contained"
         onClick={() => {
+            // Notify interested parties that the refresh button was clicked.
+            const now = (new Date()).getTime();
+            localStorage.setItem(LAST_REFRESH_TIMESTAMP, now.toString());
             history.replace(window.location.pathname);
             if (onClick) {
                 onClick();
index 8db551e8b48a75b300325a2e969e1ddd2e6e2406..97e20b0d82e25820136187d6286afcfb748561ae 100644 (file)
@@ -11,6 +11,7 @@ import { dataExplorerActions } from "store/data-explorer/data-explorer-action";
 import { DataColumn } from "components/data-table/data-column";
 import { DataColumns } from "components/data-table/data-table";
 import { DataTableFilters } from 'components/data-table-filters/data-table-filters-tree';
+import { LAST_REFRESH_TIMESTAMP } from "components/refresh-button/refresh-button";
 
 interface Props {
     id: string;
@@ -18,10 +19,10 @@ interface Props {
     onContextMenu?: (event: React.MouseEvent<HTMLElement>, item: any, isAdmin?: boolean) => void;
     onRowDoubleClick: (item: any) => void;
     extractKey?: (item: any) => React.Key;
-    working?: boolean;
 }
 
 let prevRoute = '';
+let prevRefresh = '';
 let routeChanged = false;
 let isWorking = false;
 
@@ -29,11 +30,13 @@ const mapStateToProps = (state: RootState, { id }: Props) => {
     const progress = state.progressIndicator.find(p => p.id === id);
     const dataExplorerState = getDataExplorer(state.dataExplorer, id);
     const currentRoute = state.router.location ? state.router.location.pathname : '';
+    const currentRefresh = localStorage.getItem(LAST_REFRESH_TIMESTAMP) || '';
     const currentItemUuid = currentRoute === '/workflows' ? state.properties.workflowPanelDetailsUuid : state.detailsPanel.resourceUuid;
 
-    if (currentRoute !== prevRoute) {
+    if (currentRoute !== prevRoute || currentRefresh !== prevRefresh) {
         routeChanged = true;
         prevRoute = currentRoute;
+        prevRefresh = currentRefresh;
     }
     if (progress?.working) {
         isWorking = true;
index a460a51800c7d00ee929a82258a6e9a8eea2c3fa..271d77c1085319854c8edec9d887ff0968bc233c 100644 (file)
@@ -13,6 +13,7 @@ import * as Routes from 'routes/routes';
 import { toggleDetailsPanel } from 'store/details-panel/details-panel-action';
 import RefreshButton from "components/refresh-button/refresh-button";
 import { loadSidePanelTreeProjects } from "store/side-panel-tree/side-panel-tree-actions";
+import { Dispatch } from "redux";
 
 type CssRules = "infoTooltip";
 
@@ -44,46 +45,40 @@ const isButtonVisible = ({ router }: RootState) => {
         Routes.matchAllProcessesRoute(pathname) ||
         Routes.matchTrashRoute(pathname) ||
         Routes.matchFavoritesRoute(pathname);
-
-    /* return !Routes.matchWorkflowRoute(pathname) && !Routes.matchUserVirtualMachineRoute(pathname) &&
-     *     !Routes.matchAdminVirtualMachineRoute(pathname) && !Routes.matchRepositoriesRoute(pathname) &&
-     *     !Routes.matchSshKeysAdminRoute(pathname) && !Routes.matchSshKeysUserRoute(pathname) &&
-     *     !Routes.matchSiteManagerRoute(pathname) &&
-     *     !Routes.matchKeepServicesRoute(pathname) && !Routes.matchComputeNodesRoute(pathname) &&
-     *     !Routes.matchApiClientAuthorizationsRoute(pathname) && !Routes.matchUsersRoute(pathname) &&
-     *     !Routes.matchMyAccountRoute(pathname) && !Routes.matchLinksRoute(pathname); */
 };
 
-export const MainContentBar =
-    connect((state: RootState) => ({
-        buttonVisible: isButtonVisible(state),
-        projectUuid: state.detailsPanel.resourceUuid,
-    }), (dispatch) => ({
-            onDetailsPanelToggle: () => dispatch<any>(toggleDetailsPanel()),
-            onRefreshButtonClick: (id) => {
-                dispatch<any>(loadSidePanelTreeProjects(id));
-            }
-        }))(
-            withStyles(styles)(
-                (props: MainContentBarProps & WithStyles<CssRules> & any) =>
-                    <Toolbar>
-                        <Grid container>
-                            <Grid container item xs alignItems="center">
-                                <Breadcrumbs />
-                            </Grid>
-                            <Grid item>
-                                <RefreshButton onClick={() => {
-                                    props.onRefreshButtonClick(props.projectUuid);
-                                }} />
-                            </Grid>
-                            <Grid item>
-                                {props.buttonVisible && <Tooltip title="Additional Info">
-                                    <IconButton data-cy="additional-info-icon" color="inherit" className={props.classes.infoTooltip} onClick={props.onDetailsPanelToggle}>
-                                        <DetailsIcon />
-                                    </IconButton>
-                                </Tooltip>}
-                            </Grid>
-                        </Grid>
-                    </Toolbar>
-            )
-        );
+const mapStateToProps = (state: RootState) => ({
+    buttonVisible: isButtonVisible(state),
+    projectUuid: state.detailsPanel.resourceUuid,
+});
+
+const mapDispatchToProps = () => (dispatch: Dispatch) => ({
+    onDetailsPanelToggle: () => dispatch<any>(toggleDetailsPanel()),
+    onRefreshButtonClick: (id) => {
+        dispatch<any>(loadSidePanelTreeProjects(id));
+    }
+});
+
+export const MainContentBar = connect(mapStateToProps, mapDispatchToProps)(withStyles(styles)(
+    (props: MainContentBarProps & WithStyles<CssRules> & any) =>
+        <Toolbar><Grid container>
+            <Grid container item xs alignItems="center">
+                <Breadcrumbs />
+            </Grid>
+            <Grid item>
+                <RefreshButton onClick={() => {
+                    props.onRefreshButtonClick(props.projectUuid);
+                }} />
+            </Grid>
+            <Grid item>
+                {props.buttonVisible && <Tooltip title="Additional Info">
+                    <IconButton data-cy="additional-info-icon"
+                        color="inherit"
+                        className={props.classes.infoTooltip}
+                        onClick={props.onDetailsPanelToggle}>
+                        <DetailsIcon />
+                    </IconButton>
+                </Tooltip>}
+            </Grid>
+        </Grid></Toolbar>
+));