[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