From: Lucas Di Pentima Date: Fri, 12 Feb 2021 22:54:41 +0000 (-0300) Subject: 16848: Synchronizes auto-logout between different windows/tabs. X-Git-Url: https://git.arvados.org/arvados.git/commitdiff_plain/5f272aec410b2b1dce368c36570c6c786aefbd71?ds=inline 16848: Synchronizes auto-logout between different windows/tabs. On user action, a localStorage key is updated with the timestamp. The other windows/tabs of the same app receive the change event and reset the idle timer accordingly. There's some event debouncing going on so there's a 1 sec delay between the active window/tab and the rest. Arvados-DCO-1.1-Signed-off-by: Lucas Di Pentima --- diff --git a/src/views-components/auto-logout/auto-logout.tsx b/src/views-components/auto-logout/auto-logout.tsx index f1464ce184..9ccf79a57f 100644 --- a/src/views-components/auto-logout/auto-logout.tsx +++ b/src/views-components/auto-logout/auto-logout.tsx @@ -41,9 +41,31 @@ const mapDispatchToProps = (dispatch: Dispatch): AutoLogoutActionProps => ({ export type AutoLogoutProps = AutoLogoutDataProps & AutoLogoutActionProps; +const debounce = (delay: number | undefined, fn: Function) => { + let timerId: number | null; + return (...args: any[]) => { + if (timerId) { clearTimeout(timerId); } + timerId = setTimeout(() => { + fn(...args); + timerId = null; + }, delay); + }; +}; + +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; + const handleOtherTabActivity = debounce(500, () => { + handleOnActive(); + reset(); + }); + + window.addEventListener('storage', (e: StorageEvent) => { + // Other tab activity detected by a localStorage change event. + if (e.key === LAST_ACTIVE_TIMESTAMP) { handleOtherTabActivity(); } + }); const handleOnIdle = () => { logoutTimer = setTimeout( @@ -54,16 +76,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 });