X-Git-Url: https://git.arvados.org/arvados-workbench2.git/blobdiff_plain/ecd0b3c069eed7d411e3cec7d707b43ab6675081..7f160d39308e02e571543b2958320f387b5372ab:/src/views-components/auto-logout/auto-logout.tsx diff --git a/src/views-components/auto-logout/auto-logout.tsx b/src/views-components/auto-logout/auto-logout.tsx index f1464ce1..a2d71d08 100644 --- a/src/views-components/auto-logout/auto-logout.tsx +++ b/src/views-components/auto-logout/auto-logout.tsx @@ -6,11 +6,11 @@ import { connect } from "react-redux"; import { useIdleTimer } from "react-idle-timer"; import { Dispatch } from "redux"; -import { RootState } from "~/store/store"; -import { SnackbarKind, snackbarActions } from "~/store/snackbar/snackbar-actions"; -import { logout } from "~/store/auth/auth-action"; +import { RootState } from "store/store"; +import { SnackbarKind, snackbarActions } from "store/snackbar/snackbar-actions"; +import { logout } from "store/auth/auth-action"; import parse from "parse-duration"; -import * as React from "react"; +import React from "react"; import { min } from "lodash"; interface AutoLogoutDataProps { @@ -24,12 +24,10 @@ interface AutoLogoutActionProps { doCloseWarn: () => void; } -const mapStateToProps = (state: RootState, ownProps: any): AutoLogoutDataProps => { - return { - sessionIdleTimeout: parse(state.auth.config.clusterConfig.Workbench.IdleTimeout, 's') || 0, - lastWarningDuration: ownProps.lastWarningDuration || 60, - }; -}; +const mapStateToProps = (state: RootState, ownProps: any): AutoLogoutDataProps => ({ + sessionIdleTimeout: parse(state.auth.config.clusterConfig.Workbench.IdleTimeout, 's') || 0, + lastWarningDuration: ownProps.lastWarningDuration || 60, +}); const mapDispatchToProps = (dispatch: Dispatch): AutoLogoutActionProps => ({ doLogout: () => dispatch(logout(true)), @@ -41,9 +39,41 @@ const mapDispatchToProps = (dispatch: Dispatch): AutoLogoutActionProps => ({ export type AutoLogoutProps = AutoLogoutDataProps & AutoLogoutActionProps; +const debounce = (delay: number | undefined, fn: Function) => { + let timerId: NodeJS.Timer | null; + return (...args: any[]) => { + if (timerId) { clearTimeout(timerId); } + timerId = setTimeout(() => { + fn(...args); + timerId = null; + }, delay); + }; +}; + +export const LAST_ACTIVE_TIMESTAMP = 'lastActiveTimestamp'; + export const AutoLogoutComponent = (props: AutoLogoutProps) => { let logoutTimer: NodeJS.Timer; - const lastWarningDuration = min([props.lastWarningDuration, props.sessionIdleTimeout])! * 1000 ; + const lastWarningDuration = min([props.lastWarningDuration, props.sessionIdleTimeout])! * 1000; + + // Runs once after render + React.useEffect(() => { + window.addEventListener('storage', handleStorageEvents); + // Component cleanup + return () => { + window.removeEventListener('storage', handleStorageEvents); + }; + }); + + const handleStorageEvents = (e: StorageEvent) => { + if (e.key === LAST_ACTIVE_TIMESTAMP) { + // Other tab activity detected by a localStorage change event. + debounce(500, () => { + handleOnActive(); + reset(); + })(); + } + }; const handleOnIdle = () => { logoutTimer = setTimeout( @@ -54,16 +84,23 @@ export const AutoLogoutComponent = (props: AutoLogoutProps) => { }; const handleOnActive = () => { - clearTimeout(logoutTimer); + if (logoutTimer) { clearTimeout(logoutTimer); } props.doCloseWarn(); }; - useIdleTimer({ + const handleOnAction = () => { + // Notify the other tabs there was some activity. + const now = (new Date()).getTime(); + localStorage.setItem(LAST_ACTIVE_TIMESTAMP, now.toString()); + }; + + const { reset } = useIdleTimer({ timeout: (props.lastWarningDuration < props.sessionIdleTimeout) ? 1000 * (props.sessionIdleTimeout - props.lastWarningDuration) : 1, onIdle: handleOnIdle, onActive: handleOnActive, + onAction: handleOnAction, debounce: 500 });