import { WORKBENCH_LOADING_SCREEN } from '~/store/workbench/workbench-actions';
export const linkAccountPanelActions = unionize({
- LINK_INIT: ofType<{ targetUser: UserResource | undefined }>(),
+ LINK_INIT: ofType<{
+ targetUser: UserResource | undefined }>(),
LINK_LOAD: ofType<{
originatingUser: OriginatingUser | undefined,
targetUser: UserResource | undefined,
targetUser: UserResource | undefined,
userToLink: UserResource | undefined,
error: LinkAccountPanelError }>(),
+ SET_SELECTED_CLUSTER: ofType<{
+ selectedCluster: string }>(),
HAS_SESSION_DATA: {}
});
export const loadLinkAccountPanel = () =>
async (dispatch: Dispatch<any>, getState: () => RootState, services: ServiceRepository) => {
+ if (getState().linkAccountPanel.selectedCluster === undefined) {
+ dispatch(linkAccountPanelActions.SET_SELECTED_CLUSTER({ selectedCluster: getState().auth.localCluster }));
+ }
+
// First check if an account link operation has completed
dispatch(checkForLinkStatus());
const accountToLink = {type: t, userUuid: services.authService.getUuid(), token: services.authService.getApiToken()} as AccountToLink;
services.linkAccountService.saveAccountToLink(accountToLink);
const auth = getState().auth;
+ const selectedCluster = getState().linkAccountPanel.selectedCluster;
+ const homeCluster = selectedCluster ? selectedCluster : auth.homeCluster;
dispatch(logout());
- dispatch(login(auth.localCluster, auth.homeCluster, auth.remoteHosts));
+ dispatch(login(auth.localCluster, homeCluster, auth.remoteHosts));
};
export const getAccountLinkData = () =>
}
export interface LinkAccountPanelState {
+ selectedCluster: string | undefined;
originatingUser: OriginatingUser | undefined;
targetUser: UserResource | undefined;
targetUserToken: string | undefined;
}
const initialState = {
+ selectedCluster: undefined,
originatingUser: OriginatingUser.NONE,
targetUser: undefined,
targetUserToken: undefined,
linkAccountPanelActions.match(action, {
default: () => state,
LINK_INIT: ({ targetUser }) => ({
+ ...state,
targetUser, targetUserToken: undefined,
userToLink: undefined, userToLinkToken: undefined,
status: LinkAccountPanelStatus.INITIAL, error: LinkAccountPanelError.NONE, originatingUser: OriginatingUser.NONE
}),
LINK_LOAD: ({ originatingUser, userToLink, targetUser, targetUserToken, userToLinkToken}) => ({
+ ...state,
originatingUser,
targetUser, targetUserToken,
userToLink, userToLinkToken,
status: LinkAccountPanelStatus.LINKING, error: LinkAccountPanelError.NONE
}),
- LINK_INVALID: ({originatingUser, targetUser, userToLink, error}) => ({
+ LINK_INVALID: ({ originatingUser, targetUser, userToLink, error }) => ({
+ ...state,
originatingUser,
targetUser, targetUserToken: undefined,
userToLink, userToLinkToken: undefined,
error, status: LinkAccountPanelStatus.ERROR
}),
+ SET_SELECTED_CLUSTER: ({ selectedCluster }) => ({
+ ...state, selectedCluster
+ }),
HAS_SESSION_DATA: () => ({
...state, status: LinkAccountPanelStatus.HAS_SESSION_DATA
})
CardContent,
Button,
Grid,
+ Select
} from '@material-ui/core';
import { ArvadosTheme } from '~/common/custom-theme';
import { UserResource } from "~/models/user";
export interface LinkAccountPanelRootDataProps {
targetUser?: UserResource;
userToLink?: UserResource;
+ remoteHosts: { [key: string]: string };
+ hasRemoteHosts: boolean;
status : LinkAccountPanelStatus;
error: LinkAccountPanelError;
+ selectedCluster?: string;
}
export interface LinkAccountPanelRootActionProps {
startLinking: (type: LinkAccountType) => void;
cancelLinking: () => void;
linkAccount: () => void;
+ setSelectedCluster: (cluster: string) => void;
}
-function displayUser(user: UserResource, showCreatedAt: boolean = false) {
+function displayUser(user: UserResource, showCreatedAt: boolean = false, showCluster: boolean = false) {
const disp = [];
disp.push(<span><b>{user.email}</b> ({user.username}, {user.uuid})</span>);
+ if (showCluster) {
+ const homeCluster = user.uuid.substr(0,5);
+ disp.push(<span> hosted on cluster <b>{homeCluster}</b> and </span>);
+ }
if (showCreatedAt) {
disp.push(<span> created on <b>{formatDate(user.createdAt, true)}</b></span>);
}
type LinkAccountPanelRootProps = LinkAccountPanelRootDataProps & LinkAccountPanelRootActionProps & WithStyles<CssRules>;
export const LinkAccountPanelRoot = withStyles(styles) (
- ({classes, targetUser, userToLink, status, error, startLinking, cancelLinking, linkAccount}: LinkAccountPanelRootProps) => {
+ ({classes, targetUser, userToLink, status, error, startLinking, cancelLinking, linkAccount,
+ remoteHosts, hasRemoteHosts, selectedCluster, setSelectedCluster}: LinkAccountPanelRootProps) => {
return <Card className={classes.root}>
<CardContent>
{ status === LinkAccountPanelStatus.INITIAL && targetUser &&
<Grid container spacing={24}>
<Grid container item direction="column" spacing={24}>
<Grid item>
- You are currently logged in as {displayUser(targetUser, true)}
+ You are currently logged in as {displayUser(targetUser, true, hasRemoteHosts)}
</Grid>
<Grid item>
You can link Arvados accounts. After linking, either login will take you to the same account.
- </Grid>
+ </Grid >
+ {hasRemoteHosts && selectedCluster && <Grid item>
+ Please select the cluster that hosts the account you want to link with:
+ <Select id="remoteHostsDropdown" native defaultValue={selectedCluster} style={{ marginLeft: "1em" }}
+ onChange={(event) => setSelectedCluster(event.target.value)}>
+ {Object.keys(remoteHosts).map((k) => <option key={k} value={k}>{k}</option>)}
+ </Select>
+ </Grid> }
</Grid>
<Grid container item direction="row" spacing={24}>
<Grid item>
<Button disabled={!targetUser.isActive} color="primary" variant="contained" onClick={() => startLinking(LinkAccountType.ADD_OTHER_LOGIN)}>
- Add another login to this account
+ Add another login {hasRemoteHosts ? <label> from {selectedCluster} </label> : null} to this account
</Button>
</Grid>
<Grid item>
<Button color="primary" variant="contained" onClick={() => startLinking(LinkAccountType.ACCESS_OTHER_ACCOUNT)}>
- Use this login to access another account
+ Use this login to access another account {hasRemoteHosts ? <label> on {selectedCluster} </label> : null}
</Button>
</Grid>
</Grid>
<Grid container spacing={24}>
{ status === LinkAccountPanelStatus.LINKING && <Grid container item direction="column" spacing={24}>
<Grid item>
- Clicking 'Link accounts' will link {displayUser(userToLink, true)} to {displayUser(targetUser, true)}.
+ Clicking 'Link accounts' will link {displayUser(userToLink, true, hasRemoteHosts)} to {displayUser(targetUser, true, hasRemoteHosts)}.
</Grid>
<Grid item>
After linking, logging in as {displayUser(userToLink)} will log you into the same account as {displayUser(targetUser)}.
import { RootState } from '~/store/store';
import { Dispatch } from 'redux';
import { connect } from 'react-redux';
-import { startLinking, cancelLinking, linkAccount } from '~/store/link-account-panel/link-account-panel-actions';
+import { startLinking, cancelLinking, linkAccount, linkAccountPanelActions } from '~/store/link-account-panel/link-account-panel-actions';
import { LinkAccountType } from '~/models/link-account';
import {
LinkAccountPanelRoot,
const mapStateToProps = (state: RootState): LinkAccountPanelRootDataProps => {
return {
+ remoteHosts: state.auth.remoteHosts,
+ hasRemoteHosts: Object.keys(state.auth.remoteHosts).length > 1,
+ selectedCluster: state.linkAccountPanel.selectedCluster,
targetUser: state.linkAccountPanel.targetUser,
userToLink: state.linkAccountPanel.userToLink,
status: state.linkAccountPanel.status,
const mapDispatchToProps = (dispatch: Dispatch): LinkAccountPanelRootActionProps => ({
startLinking: (type: LinkAccountType) => dispatch<any>(startLinking(type)),
cancelLinking: () => dispatch<any>(cancelLinking()),
- linkAccount: () => dispatch<any>(linkAccount())
+ linkAccount: () => dispatch<any>(linkAccount()),
+ setSelectedCluster: (selectedCluster: string) => dispatch<any>(linkAccountPanelActions.SET_SELECTED_CLUSTER({selectedCluster}))
});
export const LinkAccountPanel = connect(mapStateToProps, mapDispatchToProps)(LinkAccountPanelRoot);