mirror of
https://github.com/elyby/accounts-frontend.git
synced 2025-05-31 14:11:58 +05:30
#48: account switcher for oauth
This commit is contained in:
@@ -17,6 +17,7 @@ export class AccountSwitcher extends Component {
|
|||||||
switchAccount: PropTypes.func.isRequired,
|
switchAccount: PropTypes.func.isRequired,
|
||||||
removeAccount: PropTypes.func.isRequired,
|
removeAccount: PropTypes.func.isRequired,
|
||||||
onAfterAction: PropTypes.func, // called after each action performed
|
onAfterAction: PropTypes.func, // called after each action performed
|
||||||
|
onSwitch: PropTypes.func, // called after switching an account. The active account will be passed as arg
|
||||||
accounts: PropTypes.shape({ // TODO: accounts shape
|
accounts: PropTypes.shape({ // TODO: accounts shape
|
||||||
active: PropTypes.shape({
|
active: PropTypes.shape({
|
||||||
id: PropTypes.number
|
id: PropTypes.number
|
||||||
@@ -36,7 +37,8 @@ export class AccountSwitcher extends Component {
|
|||||||
highlightActiveAccount: true,
|
highlightActiveAccount: true,
|
||||||
allowLogout: true,
|
allowLogout: true,
|
||||||
allowAdd: true,
|
allowAdd: true,
|
||||||
onAfterAction() {}
|
onAfterAction() {},
|
||||||
|
onSwitch() {}
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
@@ -136,7 +138,8 @@ export class AccountSwitcher extends Component {
|
|||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
|
||||||
this.props.switchAccount(account)
|
this.props.switchAccount(account)
|
||||||
.then(() => this.props.onAfterAction());
|
.then(() => this.props.onAfterAction())
|
||||||
|
.then(() => this.props.onSwitch(account));
|
||||||
};
|
};
|
||||||
|
|
||||||
onRemove = (account) => (event) => {
|
onRemove = (account) => (event) => {
|
||||||
|
@@ -64,10 +64,7 @@ class PanelTransition extends Component {
|
|||||||
payload: PropTypes.object
|
payload: PropTypes.object
|
||||||
})]),
|
})]),
|
||||||
isLoading: PropTypes.bool,
|
isLoading: PropTypes.bool,
|
||||||
login: PropTypes.shape({
|
login: PropTypes.string
|
||||||
login: PropTypes.string,
|
|
||||||
password: PropTypes.string
|
|
||||||
})
|
|
||||||
}).isRequired,
|
}).isRequired,
|
||||||
user: userShape.isRequired,
|
user: userShape.isRequired,
|
||||||
setErrors: PropTypes.func.isRequired,
|
setErrors: PropTypes.func.isRequired,
|
||||||
@@ -89,10 +86,7 @@ class PanelTransition extends Component {
|
|||||||
type: PropTypes.string,
|
type: PropTypes.string,
|
||||||
payload: PropTypes.object
|
payload: PropTypes.object
|
||||||
})]),
|
})]),
|
||||||
login: PropTypes.shape({
|
login: PropTypes.string
|
||||||
login: PropTypes.string,
|
|
||||||
password: PropTypes.string
|
|
||||||
})
|
|
||||||
}),
|
}),
|
||||||
user: userShape,
|
user: userShape,
|
||||||
requestRedraw: PropTypes.func,
|
requestRedraw: PropTypes.func,
|
||||||
|
@@ -122,6 +122,14 @@ export function setLogin(login) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const SET_SWITCHER = 'auth:setAccountSwitcher';
|
||||||
|
export function setAccountSwitcher(isOn) {
|
||||||
|
return {
|
||||||
|
type: SET_SWITCHER,
|
||||||
|
payload: isOn
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export const ERROR = 'auth:error';
|
export const ERROR = 'auth:error';
|
||||||
export function setErrors(errors) {
|
export function setErrors(errors) {
|
||||||
return {
|
return {
|
||||||
|
@@ -13,8 +13,6 @@ export default class ChooseAccountBody extends BaseAuthBody {
|
|||||||
static panelId = 'chooseAccount';
|
static panelId = 'chooseAccount';
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {user} = this.context;
|
|
||||||
this.context.auth.client = {name: 'foo'}; // TODO: remove me
|
|
||||||
const {client} = this.context.auth;
|
const {client} = this.context.auth;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -28,9 +26,18 @@ export default class ChooseAccountBody extends BaseAuthBody {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className={styles.accountSwitcherContainer}>
|
<div className={styles.accountSwitcherContainer}>
|
||||||
<AccountSwitcher allowAdd={false} allowLogout={false} highlightActiveAccount={false} />
|
<AccountSwitcher
|
||||||
|
allowAdd={false}
|
||||||
|
allowLogout={false}
|
||||||
|
highlightActiveAccount={false}
|
||||||
|
onSwitch={this.onSwitch}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onSwitch = (account) => {
|
||||||
|
this.context.resolve(account);
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
@@ -8,13 +8,15 @@ import {
|
|||||||
SET_SCOPES,
|
SET_SCOPES,
|
||||||
SET_LOADING_STATE,
|
SET_LOADING_STATE,
|
||||||
REQUIRE_PERMISSIONS_ACCEPT,
|
REQUIRE_PERMISSIONS_ACCEPT,
|
||||||
SET_LOGIN
|
SET_LOGIN,
|
||||||
|
SET_SWITCHER
|
||||||
} from './actions';
|
} from './actions';
|
||||||
|
|
||||||
export default combineReducers({
|
export default combineReducers({
|
||||||
login,
|
login,
|
||||||
error,
|
error,
|
||||||
isLoading,
|
isLoading,
|
||||||
|
isSwitcherEnabled,
|
||||||
client,
|
client,
|
||||||
oauth,
|
oauth,
|
||||||
scopes
|
scopes
|
||||||
@@ -54,6 +56,22 @@ function login(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isSwitcherEnabled(
|
||||||
|
state = true,
|
||||||
|
{type, payload = false}
|
||||||
|
) {
|
||||||
|
switch (type) {
|
||||||
|
case SET_SWITCHER:
|
||||||
|
if (typeof payload !== 'boolean') {
|
||||||
|
throw new Error('Expected payload of boolean type');
|
||||||
|
}
|
||||||
|
|
||||||
|
return payload;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function isLoading(
|
function isLoading(
|
||||||
state = false,
|
state = false,
|
||||||
|
@@ -152,14 +152,13 @@ export default class AuthFlow {
|
|||||||
this.setState(new ResendActivationState());
|
this.setState(new ResendActivationState());
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case '/oauth/choose-account':
|
|
||||||
break;
|
|
||||||
case '/':
|
case '/':
|
||||||
case '/login':
|
case '/login':
|
||||||
case '/password':
|
case '/password':
|
||||||
case '/accept-rules':
|
case '/accept-rules':
|
||||||
case '/oauth/permissions':
|
case '/oauth/permissions':
|
||||||
case '/oauth/finish':
|
case '/oauth/finish':
|
||||||
|
case '/oauth/choose-account':
|
||||||
this.setState(new LoginState());
|
this.setState(new LoginState());
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
20
src/services/authFlow/ChooseAccountState.js
Normal file
20
src/services/authFlow/ChooseAccountState.js
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
import AbstractState from './AbstractState';
|
||||||
|
import LoginState from './LoginState';
|
||||||
|
import CompleteState from './CompleteState';
|
||||||
|
|
||||||
|
export default class ChooseAccountState extends AbstractState {
|
||||||
|
enter(context) {
|
||||||
|
context.navigate('/oauth/choose-account');
|
||||||
|
}
|
||||||
|
|
||||||
|
resolve(context, payload) {
|
||||||
|
context.run('setAccountSwitcher', false);
|
||||||
|
|
||||||
|
if (payload.id) {
|
||||||
|
context.setState(new CompleteState());
|
||||||
|
} else {
|
||||||
|
context.navigate('/login');
|
||||||
|
context.setState(new LoginState());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -1,6 +1,7 @@
|
|||||||
import AbstractState from './AbstractState';
|
import AbstractState from './AbstractState';
|
||||||
import LoginState from './LoginState';
|
import LoginState from './LoginState';
|
||||||
import PermissionsState from './PermissionsState';
|
import PermissionsState from './PermissionsState';
|
||||||
|
import ChooseAccountState from './ChooseAccountState';
|
||||||
import ActivationState from './ActivationState';
|
import ActivationState from './ActivationState';
|
||||||
import AcceptRulesState from './AcceptRulesState';
|
import AcceptRulesState from './AcceptRulesState';
|
||||||
import FinishState from './FinishState';
|
import FinishState from './FinishState';
|
||||||
@@ -22,7 +23,9 @@ export default class CompleteState extends AbstractState {
|
|||||||
} else if (user.shouldAcceptRules) {
|
} else if (user.shouldAcceptRules) {
|
||||||
context.setState(new AcceptRulesState());
|
context.setState(new AcceptRulesState());
|
||||||
} else if (auth.oauth && auth.oauth.clientId) {
|
} else if (auth.oauth && auth.oauth.clientId) {
|
||||||
if (auth.oauth.code) {
|
if (auth.isSwitcherEnabled) {
|
||||||
|
context.setState(new ChooseAccountState());
|
||||||
|
} else if (auth.oauth.code) {
|
||||||
context.setState(new FinishState());
|
context.setState(new FinishState());
|
||||||
} else {
|
} else {
|
||||||
const data = {};
|
const data = {};
|
||||||
|
@@ -13,6 +13,8 @@ export default class LoginState extends AbstractState {
|
|||||||
|| /login|password/.test(context.getRequest().path) // TODO: improve me
|
|| /login|password/.test(context.getRequest().path) // TODO: improve me
|
||||||
) {
|
) {
|
||||||
context.navigate('/login');
|
context.navigate('/login');
|
||||||
|
} else {
|
||||||
|
context.setState(new PasswordState());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1,9 +1,12 @@
|
|||||||
import expect from 'unexpected';
|
import expect from 'unexpected';
|
||||||
|
|
||||||
import auth from 'components/auth/reducer';
|
import auth from 'components/auth/reducer';
|
||||||
import { setLogin, SET_LOGIN } from 'components/auth/actions';
|
import {
|
||||||
|
setLogin, SET_LOGIN,
|
||||||
|
setAccountSwitcher, SET_SWITCHER
|
||||||
|
} from 'components/auth/actions';
|
||||||
|
|
||||||
describe('auth reducer', () => {
|
describe('components/auth/reducer', () => {
|
||||||
describe(SET_LOGIN, () => {
|
describe(SET_LOGIN, () => {
|
||||||
it('should set login', () => {
|
it('should set login', () => {
|
||||||
const expectedLogin = 'foo';
|
const expectedLogin = 'foo';
|
||||||
@@ -13,4 +16,28 @@ describe('auth reducer', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe(SET_SWITCHER, () => {
|
||||||
|
it('should be enabled by default', () =>
|
||||||
|
expect(auth(undefined, {}), 'to satisfy', {
|
||||||
|
isSwitcherEnabled: true
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
it('should enable switcher', () => {
|
||||||
|
const expectedValue = true;
|
||||||
|
|
||||||
|
expect(auth(undefined, setAccountSwitcher(expectedValue)), 'to satisfy', {
|
||||||
|
isSwitcherEnabled: expectedValue
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should disable switcher', () => {
|
||||||
|
const expectedValue = false;
|
||||||
|
|
||||||
|
expect(auth(undefined, setAccountSwitcher(expectedValue)), 'to satisfy', {
|
||||||
|
isSwitcherEnabled: expectedValue
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@@ -267,6 +267,7 @@ describe('AuthFlow', () => {
|
|||||||
'/password': LoginState,
|
'/password': LoginState,
|
||||||
'/accept-rules': LoginState,
|
'/accept-rules': LoginState,
|
||||||
'/oauth/permissions': LoginState,
|
'/oauth/permissions': LoginState,
|
||||||
|
'/oauth/choose-account': LoginState,
|
||||||
'/oauth/finish': LoginState,
|
'/oauth/finish': LoginState,
|
||||||
'/oauth2/v1/foo': OAuthState,
|
'/oauth2/v1/foo': OAuthState,
|
||||||
'/oauth2/v1': OAuthState,
|
'/oauth2/v1': OAuthState,
|
||||||
|
Reference in New Issue
Block a user