[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