[ARVADOS-WORKBENCH2] created: 2.1.0-435-g7f80f223

Git user git at public.arvados.org
Tue Sep 21 20:57:37 UTC 2021


        at  7f80f22374711ce0c55d7a1657b9039fe84a2c1f (commit)


commit 7f80f22374711ce0c55d7a1657b9039fe84a2c1f
Author: Daniel Kutyła <daniel.kutyla at contractors.roche.com>
Date:   Tue Sep 21 22:54:52 2021 +0200

    16951: Added overlay to disable page modifications when refreshing
    
    Arvados-DCO-1.1-Signed-off-by: Daniel Kutyła <daniel.kutyla at contractors.roche.com>

diff --git a/cypress/integration/favorites.spec.js b/cypress/integration/favorites.spec.js
index 9f4e2b84..747539b3 100644
--- a/cypress/integration/favorites.spec.js
+++ b/cypress/integration/favorites.spec.js
@@ -34,6 +34,7 @@ describe('Favorites tests', function () {
             group_class: 'project',
         }).as('myFavoriteProject').then(function () {
             cy.contains('Refresh').click();
+            cy.get('[data-cy=loading-overlay]').should('not.exist');
             cy.get('main').contains('my-favorite-project').rightclick();
             cy.contains('Add to public favorites').click();
             cy.contains('Public Favorites').click();
diff --git a/cypress/integration/sharing.spec.js b/cypress/integration/sharing.spec.js
index c5664e3b..b91ef257 100644
--- a/cypress/integration/sharing.spec.js
+++ b/cypress/integration/sharing.spec.js
@@ -36,6 +36,7 @@ describe('Sharing tests', function () {
             group_class: 'project',
         }).as('mySharedWritableProject').then(function (mySharedWritableProject) {
             cy.contains('Refresh').click();
+            cy.get('[data-cy=loading-overlay]').should('not.exist');
             cy.get('main').contains(mySharedWritableProject.name).rightclick();
             cy.get('[data-cy=context-menu]').within(() => {
                 cy.contains('Share').click();
@@ -54,6 +55,7 @@ describe('Sharing tests', function () {
             group_class: 'project',
         }).as('mySharedReadonlyProject').then(function (mySharedReadonlyProject) {
             cy.contains('Refresh').click();
+            cy.get('[data-cy=loading-overlay]').should('not.exist');
             cy.get('main').contains(mySharedReadonlyProject.name).rightclick();
             cy.get('[data-cy=context-menu]').within(() => {
                 cy.contains('Share').click();
diff --git a/cypress/integration/side-panel.spec.js b/cypress/integration/side-panel.spec.js
index 912e68eb..3166f29d 100644
--- a/cypress/integration/side-panel.spec.js
+++ b/cypress/integration/side-panel.spec.js
@@ -83,6 +83,7 @@ describe('Side panel tests', function() {
             properties: {filters: []},
         }).as('myFavoriteFilterGroup').then(function (myFavoriteFilterGroup) {
             cy.contains('Refresh').click();
+            cy.get('[data-cy=loading-overlay]').should('not.exist');
             cy.goToPath(`/projects/${myFavoriteFilterGroup.uuid}`);
             cy.get('[data-cy=breadcrumb-last]').should('contain', 'my-favorite-filter-group');
 
diff --git a/src/components/refresh-button/refresh-button.tsx b/src/components/refresh-button/refresh-button.tsx
index 66bda99e..e6b7d10e 100644
--- a/src/components/refresh-button/refresh-button.tsx
+++ b/src/components/refresh-button/refresh-button.tsx
@@ -4,14 +4,17 @@
 // SPDX-License-Identifier: AGPL-3.0
 
 import * as React from 'react';
+import { Dispatch } from 'redux';
+import { connect } from 'react-redux';
 import * as classNames from 'classnames';
 import { withRouter, RouteComponentProps } from 'react-router';
 import { StyleRulesCallback, Button, WithStyles, withStyles } from "@material-ui/core";
 import { ReRunProcessIcon } from '~/components/icon/icon';
+import { refreshButtonActions } from '~/store/refresh-button/refresh-button-actions';
 
 type CssRules = 'button' | 'buttonRight';
 
-const styles: StyleRulesCallback<CssRules> = theme => ({
+const styles: StyleRulesCallback<CssRules> = () => ({
     button: {
         boxShadow: 'none',
         padding: '2px 10px 2px 5px',
@@ -22,17 +25,26 @@ const styles: StyleRulesCallback<CssRules> = theme => ({
     },
 });
 
-export const RefreshButton = ({ history, classes }: RouteComponentProps & WithStyles<CssRules>) =>
+interface RefreshButtonActions {
+    startRefresh: () => void;
+}
+
+export const RefreshButton = ({ history, classes, startRefresh }: RouteComponentProps & WithStyles<CssRules> & RefreshButtonActions) =>
     <Button
         color="primary"
         size="small"
         variant="contained"
         onClick={() => {
             history.replace(window.location.pathname);
+            startRefresh();
         }}
         className={classNames(classes.buttonRight, classes.button)}>
         <ReRunProcessIcon />
         Refresh
     </Button>;
 
-export default withStyles(styles)(withRouter(RefreshButton));
\ No newline at end of file
+const mapDispatchToProps = (dispatch: Dispatch): RefreshButtonActions => ({
+    startRefresh: () => dispatch<any>(refreshButtonActions.START_REFRESH())
+});
+
+export default withStyles(styles)(withRouter(connect(null, mapDispatchToProps)(RefreshButton)));
\ No newline at end of file
diff --git a/src/store/refresh-button/refresh-button-actions.ts b/src/store/refresh-button/refresh-button-actions.ts
new file mode 100644
index 00000000..f63f5824
--- /dev/null
+++ b/src/store/refresh-button/refresh-button-actions.ts
@@ -0,0 +1,12 @@
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+import { unionize, ofType, UnionOf } from "~/common/unionize";
+
+export const refreshButtonActions = unionize({
+    START_REFRESH: ofType<{}>(),
+    STOP_REFRESH: ofType<{}>(),
+});
+
+export type RefreshButtonAction = UnionOf<typeof refreshButtonActions>;
diff --git a/src/store/refresh-button/refresh-button-reducer.ts b/src/store/refresh-button/refresh-button-reducer.ts
new file mode 100644
index 00000000..e5f963b4
--- /dev/null
+++ b/src/store/refresh-button/refresh-button-reducer.ts
@@ -0,0 +1,19 @@
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+import { RefreshButtonAction, refreshButtonActions } from "~/store/refresh-button/refresh-button-actions";
+
+export type RefreshButtonState = { working: boolean };
+
+const initialState: RefreshButtonState = {
+    working: false,
+};
+
+export const refreshButtonReducer = (state: RefreshButtonState = initialState, action: RefreshButtonAction) => {
+    return refreshButtonActions.match(action, {
+        START_REFRESH: () => ({ working: true }),
+        STOP_REFRESH: () =>  ({ working: false }),
+        default: () => state,
+    });
+};
\ No newline at end of file
diff --git a/src/store/store.ts b/src/store/store.ts
index f236d029..2487eb35 100644
--- a/src/store/store.ts
+++ b/src/store/store.ts
@@ -72,6 +72,7 @@ import { ALL_PROCESSES_PANEL_ID } from './all-processes-panel/all-processes-pane
 import { Config } from '~/common/config';
 import { pluginConfig } from '~/plugins';
 import { MiddlewareListReducer } from '~/common/plugintypes';
+import { refreshButtonReducer } from './refresh-button/refresh-button-reducer';
 
 const composeEnhancers =
     (process.env.NODE_ENV === 'development' &&
@@ -202,5 +203,6 @@ const createRootReducer = (services: ServiceRepository) => combineReducers({
     virtualMachines: virtualMachinesReducer,
     repositories: repositoriesReducer,
     keepServices: keepServicesReducer,
-    linkAccountPanel: linkAccountPanelReducer
+    linkAccountPanel: linkAccountPanelReducer,
+    refreshButton: refreshButtonReducer,
 });
diff --git a/src/views-components/loading-overlay/loading-overlay.tsx b/src/views-components/loading-overlay/loading-overlay.tsx
new file mode 100644
index 00000000..cf3e8d1c
--- /dev/null
+++ b/src/views-components/loading-overlay/loading-overlay.tsx
@@ -0,0 +1,30 @@
+// Copyright (C) The Arvados Authors. All rights reserved.
+//
+// SPDX-License-Identifier: AGPL-3.0
+
+import * as React from 'react';
+import { StyleRulesCallback, withStyles, WithStyles } from '@material-ui/core';
+
+type CssRules = 'overlay';
+
+const styles: StyleRulesCallback<CssRules> = () => ({
+    overlay: {
+        opacity: '0.4',
+        pointerEvents: 'none',
+        backgroundColor: '#fff',
+    },
+});
+
+type LoginFormProps = {
+    loading: Boolean,
+    children?: any,
+};
+
+const LoadingOverlay = ({ loading, children, classes }: LoginFormProps & WithStyles<CssRules>) => 
+    loading ? 
+        <div data-cy="loading-overlay" className={classes.overlay}>
+            {children}
+        </div> :
+        children;
+
+export default withStyles(styles)(LoadingOverlay);
\ No newline at end of file
diff --git a/src/views/main-panel/main-panel-root.tsx b/src/views/main-panel/main-panel-root.tsx
index 8f41ff3d..72b76e37 100644
--- a/src/views/main-panel/main-panel-root.tsx
+++ b/src/views/main-panel/main-panel-root.tsx
@@ -11,6 +11,7 @@ import { LoginPanel } from '~/views/login-panel/login-panel';
 import { InactivePanel } from '~/views/inactive-panel/inactive-panel';
 import { WorkbenchLoadingScreen } from '~/views/workbench/workbench-loading-screen';
 import { MainAppBar } from '~/views-components/main-app-bar/main-app-bar';
+import LoadingOverlay from '~/views-components/loading-overlay/loading-overlay';
 
 type CssRules = 'root';
 
@@ -22,6 +23,10 @@ const styles: StyleRulesCallback<CssRules> = (theme: ArvadosTheme) => ({
     }
 });
 
+export interface MainPanelActions {
+    stopRefresh: () => void;
+}
+
 export interface MainPanelRootDataProps {
     user?: User;
     working: boolean;
@@ -32,14 +37,26 @@ export interface MainPanelRootDataProps {
     isLinkingPath: boolean;
     siteBanner: string;
     sessionIdleTimeout: number;
+    refreshing: boolean;
 }
 
-type MainPanelRootProps = MainPanelRootDataProps & WithStyles<CssRules>;
+type MainPanelRootProps = MainPanelRootDataProps & MainPanelActions & WithStyles<CssRules>;
+
+let timeoutId: any = null;
 
 export const MainPanelRoot = withStyles(styles)(
     ({ classes, loading, working, user, buildInfo, uuidPrefix,
-        isNotLinking, isLinkingPath, siteBanner, sessionIdleTimeout }: MainPanelRootProps) =>
-        loading
+        isNotLinking, isLinkingPath, siteBanner, sessionIdleTimeout, refreshing, stopRefresh }: MainPanelRootProps) => {
+
+        if (refreshing && !working) {
+            timeoutId = setTimeout(() => {
+                stopRefresh();
+            }, 2000);
+        } else if (refreshing && working && timeoutId) {
+            clearTimeout(timeoutId);
+        }
+
+        return loading
             ? <WorkbenchLoadingScreen />
             : <>
                 {isNotLinking && <MainAppBar
@@ -51,12 +68,14 @@ export const MainPanelRoot = withStyles(styles)(
                         ? <LinearProgress color="secondary" data-cy="linear-progress" />
                         : null}
                 </MainAppBar>}
-                <Grid container direction="column" className={classes.root}>
-                    {user
-                        ? (user.isActive || (!user.isActive && isLinkingPath)
-                            ? <WorkbenchPanel isNotLinking={isNotLinking} isUserActive={user.isActive} sessionIdleTimeout={sessionIdleTimeout} />
-                            : <InactivePanel />)
-                        : <LoginPanel />}
-                </Grid>
-            </>
-);
+                <LoadingOverlay loading={refreshing}>
+                    <Grid container direction="column" className={classes.root}>
+                        {user
+                            ? (user.isActive || (!user.isActive && isLinkingPath)
+                                ? <WorkbenchPanel isNotLinking={isNotLinking} isUserActive={user.isActive} sessionIdleTimeout={sessionIdleTimeout} />
+                                : <InactivePanel />)
+                            : <LoginPanel />}
+                    </Grid>
+                </LoadingOverlay>
+            </>;
+});
diff --git a/src/views/main-panel/main-panel.tsx b/src/views/main-panel/main-panel.tsx
index edbf5cc4..5cb67e96 100644
--- a/src/views/main-panel/main-panel.tsx
+++ b/src/views/main-panel/main-panel.tsx
@@ -3,13 +3,15 @@
 // SPDX-License-Identifier: AGPL-3.0
 
 import { RootState } from '~/store/store';
+import { Dispatch } from 'redux';
 import { connect } from 'react-redux';
 import parse from 'parse-duration';
-import { MainPanelRoot, MainPanelRootDataProps } from '~/views/main-panel/main-panel-root';
+import { MainPanelActions, MainPanelRoot, MainPanelRootDataProps } from '~/views/main-panel/main-panel-root';
 import { isSystemWorking } from '~/store/progress-indicator/progress-indicator-reducer';
 import { isWorkbenchLoading } from '~/store/workbench/workbench-actions';
 import { LinkAccountPanelStatus } from '~/store/link-account-panel/link-account-panel-reducer';
 import { matchLinkAccountRoute } from '~/routes/routes';
+import { refreshButtonActions } from '~/store/refresh-button/refresh-button-actions';
 
 const mapStateToProps = (state: RootState): MainPanelRootDataProps => {
     return {
@@ -18,6 +20,7 @@ const mapStateToProps = (state: RootState): MainPanelRootDataProps => {
         loading: isWorkbenchLoading(state),
         buildInfo: state.appInfo.buildInfo,
         uuidPrefix: state.auth.localCluster,
+        refreshing: state.refreshButton.working,
         isNotLinking: state.linkAccountPanel.status === LinkAccountPanelStatus.NONE || state.linkAccountPanel.status === LinkAccountPanelStatus.INITIAL,
         isLinkingPath: state.router.location ? matchLinkAccountRoute(state.router.location.pathname) !== null : false,
         siteBanner: state.auth.config.clusterConfig.Workbench.SiteName,
@@ -25,6 +28,8 @@ const mapStateToProps = (state: RootState): MainPanelRootDataProps => {
     };
 };
 
-const mapDispatchToProps = null;
+const mapDispatchToProps = (dispatch: Dispatch): MainPanelActions => ({
+    stopRefresh: () => dispatch<any>(refreshButtonActions.STOP_REFRESH())
+});
 
 export const MainPanel = connect(mapStateToProps, mapDispatchToProps)(MainPanelRoot);

commit 56b5ab0de8b07ba2115bee13235cd574f1b9d69f
Merge: 5b2cd670 49708fe5
Author: Daniel Kutyła <daniel.kutyla at contractors.roche.com>
Date:   Wed Jul 7 17:32:31 2021 +0200

    Merge branch '16971-Issues-with-Project-and-Collection-name-description-not-being-saved-shown'
    closes #16971
    
    Arvados-DCO-1.1-Signed-off-by: Daniel Kutyła <daniel.kutyla at contractors.roche.com>


-----------------------------------------------------------------------


hooks/post-receive
-- 




More information about the arvados-commits mailing list