16848: Synchronizes auto-logout between different windows/tabs.
authorLucas Di Pentima <lucas@di-pentima.com.ar>
Fri, 12 Feb 2021 22:54:41 +0000 (19:54 -0300)
committerLucas Di Pentima <lucas@di-pentima.com.ar>
Fri, 12 Feb 2021 22:54:41 +0000 (19:54 -0300)
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 <lucas@di-pentima.com.ar>

src/views-components/auto-logout/auto-logout.tsx

index f1464ce18497f3476017ec2075f430afb3bf339d..9ccf79a57f881f759e5c8af28d487682af4567dc 100644 (file)
@@ -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
     });