[ARVADOS-WORKBENCH2] created: 2.1.0-198-g5f272aec

Git user git at public.arvados.org
Fri Feb 12 22:59:26 UTC 2021


        at  5f272aec410b2b1dce368c36570c6c786aefbd71 (commit)


commit 5f272aec410b2b1dce368c36570c6c786aefbd71
Author: Lucas Di Pentima <lucas at di-pentima.com.ar>
Date:   Fri Feb 12 19:54:41 2021 -0300

    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 <lucas at di-pentima.com.ar>

diff --git a/src/views-components/auto-logout/auto-logout.tsx b/src/views-components/auto-logout/auto-logout.tsx
index f1464ce1..9ccf79a5 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
     });
 

commit abe391c9f3a2a7ad954ecf81198268076b233ef8
Author: Lucas Di Pentima <lucas at di-pentima.com.ar>
Date:   Fri Feb 12 13:23:06 2021 -0300

    16848: Renames 'current token dialog' code to 'token dialog'.
    
    Arvados-DCO-1.1-Signed-off-by: Lucas Di Pentima <lucas at di-pentima.com.ar>

diff --git a/src/index.tsx b/src/index.tsx
index 98281b67..b32066a4 100644
--- a/src/index.tsx
+++ b/src/index.tsx
@@ -36,7 +36,7 @@ import { ServiceRepository } from '~/services/services';
 import { initWebSocket } from '~/websocket/websocket';
 import { Config } from '~/common/config';
 import { addRouteChangeHandlers } from './routes/route-change-handlers';
-import { setCurrentTokenDialogApiHost } from '~/store/current-token-dialog/current-token-dialog-actions';
+import { setTokenDialogApiHost } from '~/store/token-dialog/token-dialog-actions';
 import { processResourceActionSet } from '~/views-components/context-menu/action-sets/process-resource-action-set';
 import { progressIndicatorActions } from '~/store/progress-indicator/progress-indicator-actions';
 import { trashedCollectionActionSet } from '~/views-components/context-menu/action-sets/trashed-collection-action-set';
@@ -130,7 +130,7 @@ fetchConfig()
         store.subscribe(initListener(history, store, services, config));
         store.dispatch(initAuth(config));
         store.dispatch(setBuildInfo());
-        store.dispatch(setCurrentTokenDialogApiHost(apiHost));
+        store.dispatch(setTokenDialogApiHost(apiHost));
         store.dispatch(loadVocabulary);
         store.dispatch(loadFileViewersConfig);
 
diff --git a/src/store/current-token-dialog/current-token-dialog-actions.tsx b/src/store/token-dialog/token-dialog-actions.tsx
similarity index 56%
rename from src/store/current-token-dialog/current-token-dialog-actions.tsx
rename to src/store/token-dialog/token-dialog-actions.tsx
index fe8186b7..08a45992 100644
--- a/src/store/current-token-dialog/current-token-dialog-actions.tsx
+++ b/src/store/token-dialog/token-dialog-actions.tsx
@@ -7,20 +7,20 @@ import { getProperty } from '~/store/properties/properties';
 import { propertiesActions } from '~/store/properties/properties-actions';
 import { RootState } from '~/store/store';
 
-export const CURRENT_TOKEN_DIALOG_NAME = 'currentTokenDialog';
+export const TOKEN_DIALOG_NAME = 'tokenDialog';
 const API_HOST_PROPERTY_NAME = 'apiHost';
 
-export interface CurrentTokenDialogData {
-    currentToken: string;
+export interface TokenDialogData {
+    token: string;
     apiHost: string;
 }
 
-export const setCurrentTokenDialogApiHost = (apiHost: string) =>
+export const setTokenDialogApiHost = (apiHost: string) =>
     propertiesActions.SET_PROPERTY({ key: API_HOST_PROPERTY_NAME, value: apiHost });
 
-export const getCurrentTokenDialogData = (state: RootState): CurrentTokenDialogData => ({
+export const getTokenDialogData = (state: RootState): TokenDialogData => ({
     apiHost: getProperty<string>(API_HOST_PROPERTY_NAME)(state.properties) || '',
-    currentToken: state.auth.apiToken || '',
+    token: state.auth.apiToken || '',
 });
 
-export const openCurrentTokenDialog = dialogActions.OPEN_DIALOG({ id: CURRENT_TOKEN_DIALOG_NAME, data: {} });
+export const openTokenDialog = dialogActions.OPEN_DIALOG({ id: TOKEN_DIALOG_NAME, data: {} });
diff --git a/src/views-components/main-app-bar/account-menu.tsx b/src/views-components/main-app-bar/account-menu.tsx
index 6e844cc8..58ed7b84 100644
--- a/src/views-components/main-app-bar/account-menu.tsx
+++ b/src/views-components/main-app-bar/account-menu.tsx
@@ -11,7 +11,7 @@ import { UserPanelIcon } from "~/components/icon/icon";
 import { DispatchProp, connect } from 'react-redux';
 import { authActions } from '~/store/auth/auth-action';
 import { RootState } from "~/store/store";
-import { openCurrentTokenDialog } from '~/store/current-token-dialog/current-token-dialog-actions';
+import { openTokenDialog } from '~/store/token-dialog/token-dialog-actions';
 import { openRepositoriesPanel } from "~/store/repositories/repositories-actions";
 import {
     navigateToSiteManager,
@@ -70,7 +70,7 @@ export const AccountMenuComponent =
             {user.isActive ? <>
                 <MenuItem onClick={() => dispatch(openUserVirtualMachines())}>Virtual Machines</MenuItem>
                 {!user.isAdmin && <MenuItem onClick={() => dispatch(openRepositoriesPanel())}>Repositories</MenuItem>}
-                <MenuItem onClick={() => dispatch(openCurrentTokenDialog)}>Current token</MenuItem>
+                <MenuItem onClick={() => dispatch(openTokenDialog)}>Get API token</MenuItem>
                 <MenuItem onClick={() => dispatch(navigateToSshKeysUser)}>Ssh Keys</MenuItem>
                 <MenuItem onClick={() => dispatch(navigateToSiteManager)}>Site Manager</MenuItem>
                 <MenuItem onClick={() => dispatch(navigateToMyAccount)}>My account</MenuItem>
diff --git a/src/views-components/current-token-dialog/current-token-dialog.test.tsx b/src/views-components/token-dialog/token-dialog.test.tsx
similarity index 89%
rename from src/views-components/current-token-dialog/current-token-dialog.test.tsx
rename to src/views-components/token-dialog/token-dialog.test.tsx
index eb405e94..e8df29b8 100644
--- a/src/views-components/current-token-dialog/current-token-dialog.test.tsx
+++ b/src/views-components/token-dialog/token-dialog.test.tsx
@@ -7,7 +7,7 @@ import { Button } from '@material-ui/core';
 import { mount, configure } from 'enzyme';
 import * as Adapter from 'enzyme-adapter-react-16';
 import * as CopyToClipboard from 'react-copy-to-clipboard';
-import { CurrentTokenDialogComponent } from './current-token-dialog';
+import { TokenDialogComponent } from './token-dialog';
 
 configure({ adapter: new Adapter() });
 
@@ -30,7 +30,7 @@ describe('<CurrentTokenDialog />', () => {
 
   describe('copy to clipboard', () => {
     beforeEach(() => {
-      wrapper = mount(<CurrentTokenDialogComponent {...props} />);
+      wrapper = mount(<TokenDialogComponent {...props} />);
     });
 
     it('should copy API TOKEN to the clipboard', () => {
diff --git a/src/views-components/current-token-dialog/current-token-dialog.tsx b/src/views-components/token-dialog/token-dialog.tsx
similarity index 85%
rename from src/views-components/current-token-dialog/current-token-dialog.tsx
rename to src/views-components/token-dialog/token-dialog.tsx
index 9cb08f8b..b0d5c67e 100644
--- a/src/views-components/current-token-dialog/current-token-dialog.tsx
+++ b/src/views-components/token-dialog/token-dialog.tsx
@@ -9,7 +9,7 @@ import { ArvadosTheme } from '~/common/custom-theme';
 import { withDialog } from '~/store/dialog/with-dialog';
 import { WithDialogProps } from '~/store/dialog/with-dialog';
 import { connect, DispatchProp } from 'react-redux';
-import { CurrentTokenDialogData, getCurrentTokenDialogData, CURRENT_TOKEN_DIALOG_NAME } from '~/store/current-token-dialog/current-token-dialog-actions';
+import { TokenDialogData, getTokenDialogData, TOKEN_DIALOG_NAME } from '~/store/token-dialog/token-dialog-actions';
 import { DefaultCodeSnippet } from '~/components/default-code-snippet/default-code-snippet';
 import { snackbarActions, SnackbarKind } from '~/store/snackbar/snackbar-actions';
 
@@ -38,9 +38,9 @@ const styles: StyleRulesCallback<CssRules> = (theme: ArvadosTheme) => ({
     }
 });
 
-type CurrentTokenProps = CurrentTokenDialogData & WithDialogProps<{}> & WithStyles<CssRules> & DispatchProp;
+type TokenDialogProps = TokenDialogData & WithDialogProps<{}> & WithStyles<CssRules> & DispatchProp;
 
-export class CurrentTokenDialogComponent extends React.Component<CurrentTokenProps> {
+export class TokenDialogComponent extends React.Component<TokenDialogProps> {
     onCopy = (message: string) => {
         this.props.dispatch(snackbarActions.OPEN_SNACKBAR({
             message,
@@ -49,9 +49,9 @@ export class CurrentTokenDialogComponent extends React.Component<CurrentTokenPro
         }));
     }
 
-    getSnippet = ({ apiHost, currentToken }: CurrentTokenDialogData) =>
+    getSnippet = ({ apiHost, token }: TokenDialogData) =>
         `HISTIGNORE=$HISTIGNORE:'export ARVADOS_API_TOKEN=*'
-export ARVADOS_API_TOKEN=${currentToken}
+export ARVADOS_API_TOKEN=${token}
 export ARVADOS_API_HOST=${apiHost}
 unset ARVADOS_API_HOST_INSECURE`
 
@@ -63,7 +63,7 @@ unset ARVADOS_API_HOST_INSECURE`
             onClose={closeDialog}
             fullWidth={true}
             maxWidth='md'>
-            <DialogTitle>Current Token</DialogTitle>
+            <DialogTitle>Get API Token</DialogTitle>
             <DialogContent>
                 <Typography paragraph={true}>
                     The Arvados API token is a secret key that enables the Arvados SDKs to access Arvados with the proper permissions.
@@ -101,8 +101,8 @@ unset ARVADOS_API_HOST_INSECURE`
     }
 }
 
-export const CurrentTokenDialog =
+export const TokenDialog =
     withStyles(styles)(
-        connect(getCurrentTokenDialogData)(
-            withDialog(CURRENT_TOKEN_DIALOG_NAME)(CurrentTokenDialogComponent)));
+        connect(getTokenDialogData)(
+            withDialog(TOKEN_DIALOG_NAME)(TokenDialogComponent)));
 
diff --git a/src/views/workbench/workbench.tsx b/src/views/workbench/workbench.tsx
index f5cfda89..9c2a7df8 100644
--- a/src/views/workbench/workbench.tsx
+++ b/src/views/workbench/workbench.tsx
@@ -10,7 +10,7 @@ import { DetailsPanel } from '~/views-components/details-panel/details-panel';
 import { ArvadosTheme } from '~/common/custom-theme';
 import { ContextMenu } from "~/views-components/context-menu/context-menu";
 import { FavoritePanel } from "../favorite-panel/favorite-panel";
-import { CurrentTokenDialog } from '~/views-components/current-token-dialog/current-token-dialog';
+import { TokenDialog } from '~/views-components/token-dialog/token-dialog';
 import { RichTextEditorDialog } from '~/views-components/rich-text-editor-dialog/rich-text-editor-dialog';
 import { Snackbar } from '~/views-components/snackbar/snackbar';
 import { CollectionPanel } from '../collection-panel/collection-panel';
@@ -221,7 +221,7 @@ export const WorkbenchPanel =
             <CreateRepositoryDialog />
             <CreateSshKeyDialog />
             <CreateUserDialog />
-            <CurrentTokenDialog />
+            <TokenDialog />
             <FileRemoveDialog />
             <FilesUploadCollectionDialog />
             <GroupAttributesDialog />

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


hooks/post-receive
-- 




More information about the arvados-commits mailing list