2016-03-02 02:06:14 +05:30
|
|
|
import { routeActions } from 'react-router-redux';
|
|
|
|
|
|
|
|
import RegisterState from './RegisterState';
|
|
|
|
import LoginState from './LoginState';
|
|
|
|
import OAuthState from './OAuthState';
|
|
|
|
import ForgotPasswordState from './ForgotPasswordState';
|
2016-05-15 02:23:58 +05:30
|
|
|
import RecoverPasswordState from './RecoverPasswordState';
|
2016-06-05 17:36:14 +05:30
|
|
|
import ActivationState from './ActivationState';
|
2016-05-23 00:28:43 +05:30
|
|
|
import ResendActivationState from './ResendActivationState';
|
2016-03-02 02:06:14 +05:30
|
|
|
|
2016-04-12 09:19:58 +05:30
|
|
|
// TODO: a way to unload service (when we are on account page)
|
2016-03-02 02:06:14 +05:30
|
|
|
|
|
|
|
export default class AuthFlow {
|
2016-04-12 09:19:58 +05:30
|
|
|
constructor(actions) {
|
|
|
|
if (typeof actions !== 'object') {
|
|
|
|
throw new Error('AuthFlow requires an actions object');
|
|
|
|
}
|
|
|
|
|
|
|
|
this.actions = actions;
|
|
|
|
|
|
|
|
if (Object.freeze) {
|
|
|
|
Object.freeze(this.actions);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-03-02 02:06:14 +05:30
|
|
|
setStore(store) {
|
|
|
|
this.navigate = (route) => {
|
|
|
|
const {routing} = this.getState();
|
|
|
|
|
|
|
|
if (routing.location.pathname !== route) {
|
|
|
|
if (this.replace) {
|
|
|
|
this.replace(route);
|
|
|
|
}
|
2016-06-10 10:36:21 +05:30
|
|
|
store.dispatch(routeActions.push(route)); // TODO: may be deleted?
|
2016-03-02 02:06:14 +05:30
|
|
|
}
|
|
|
|
|
|
|
|
this.replace = null;
|
|
|
|
};
|
|
|
|
|
|
|
|
this.getState = store.getState.bind(store);
|
|
|
|
this.dispatch = store.dispatch.bind(store);
|
|
|
|
}
|
|
|
|
|
|
|
|
resolve(payload = {}) {
|
|
|
|
this.state.resolve(this, payload);
|
|
|
|
}
|
|
|
|
|
|
|
|
reject(payload = {}) {
|
|
|
|
this.state.reject(this, payload);
|
|
|
|
}
|
|
|
|
|
|
|
|
goBack() {
|
|
|
|
this.state.goBack(this);
|
|
|
|
}
|
|
|
|
|
|
|
|
run(actionId, payload) {
|
2016-05-30 09:53:27 +05:30
|
|
|
if (actionId === 'redirect') {
|
|
|
|
location.href = payload;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-04-12 09:19:58 +05:30
|
|
|
if (!this.actions[actionId]) {
|
2016-03-02 02:06:14 +05:30
|
|
|
throw new Error(`Action ${actionId} does not exists`);
|
|
|
|
}
|
|
|
|
|
2016-04-12 09:19:58 +05:30
|
|
|
return this.dispatch(this.actions[actionId](payload));
|
2016-03-02 02:06:14 +05:30
|
|
|
}
|
|
|
|
|
|
|
|
setState(state) {
|
|
|
|
if (!state) {
|
|
|
|
throw new Error('State is required');
|
|
|
|
}
|
|
|
|
|
2016-06-10 10:36:21 +05:30
|
|
|
// if (this.state instanceof state.constructor) {
|
|
|
|
// // already in this state
|
|
|
|
// return;
|
|
|
|
// }
|
|
|
|
|
|
|
|
if (this.state instanceof ResendActivationState && state instanceof ResendActivationState) {
|
|
|
|
// NOTE: a temporary workaround for resend-activation goBack to return to correct prevState
|
2016-06-05 17:36:14 +05:30
|
|
|
return;
|
|
|
|
}
|
2016-03-02 02:06:14 +05:30
|
|
|
|
|
|
|
this.state && this.state.leave(this);
|
2016-06-05 17:36:14 +05:30
|
|
|
this.prevState = this.state;
|
2016-03-02 02:06:14 +05:30
|
|
|
this.state = state;
|
2016-06-02 23:16:49 +05:30
|
|
|
const resp = this.state.enter(this);
|
|
|
|
|
|
|
|
if (resp && resp.then) {
|
|
|
|
// this is a state with an async enter phase
|
|
|
|
// block route components from mounting, till promise will be resolved
|
2016-06-04 00:40:47 +05:30
|
|
|
if (this.onReady) {
|
|
|
|
const callback = this.onReady;
|
|
|
|
this.onReady = () => {};
|
|
|
|
return resp.then(callback);
|
|
|
|
}
|
2016-06-04 14:47:06 +05:30
|
|
|
|
|
|
|
return resp;
|
2016-06-02 23:16:49 +05:30
|
|
|
}
|
2016-03-02 02:06:14 +05:30
|
|
|
}
|
|
|
|
|
2016-06-04 00:40:47 +05:30
|
|
|
/**
|
|
|
|
* This should be called from onEnter prop of react-router Route component
|
|
|
|
*
|
|
|
|
* @param {string} path
|
|
|
|
* @param {function} replace
|
|
|
|
* @param {function} [callback = function() {}] - an optional callback function to be called, when state will be stabilized
|
|
|
|
* (state's enter function's promise resolved)
|
|
|
|
*/
|
|
|
|
handleRequest(path, replace, callback = function() {}) {
|
2016-03-02 02:06:14 +05:30
|
|
|
this.replace = replace;
|
2016-06-02 23:16:49 +05:30
|
|
|
this.onReady = callback;
|
2016-03-02 02:06:14 +05:30
|
|
|
|
2016-03-16 10:33:23 +05:30
|
|
|
if (path === '/') {
|
|
|
|
// reset oauth data if user tried to navigate to index route
|
|
|
|
this.run('setOAuthRequest', {});
|
|
|
|
}
|
|
|
|
|
2016-05-28 03:54:22 +05:30
|
|
|
switch (path) { // use only first part of an url
|
2016-03-02 02:06:14 +05:30
|
|
|
case '/oauth':
|
|
|
|
this.setState(new OAuthState());
|
|
|
|
break;
|
|
|
|
|
|
|
|
case '/register':
|
|
|
|
this.setState(new RegisterState());
|
|
|
|
break;
|
|
|
|
|
|
|
|
case '/forgot-password':
|
|
|
|
this.setState(new ForgotPasswordState());
|
|
|
|
break;
|
|
|
|
|
2016-05-23 00:28:43 +05:30
|
|
|
case '/resend-activation':
|
|
|
|
this.setState(new ResendActivationState());
|
|
|
|
break;
|
|
|
|
|
2016-03-13 14:32:24 +05:30
|
|
|
case '/':
|
2016-03-02 02:06:14 +05:30
|
|
|
case '/login':
|
|
|
|
case '/password':
|
2016-03-16 11:04:18 +05:30
|
|
|
case '/change-password':
|
2016-03-02 02:06:14 +05:30
|
|
|
case '/oauth/permissions':
|
2016-03-15 12:06:13 +05:30
|
|
|
case '/oauth/finish':
|
2016-03-02 02:06:14 +05:30
|
|
|
this.setState(new LoginState());
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
2016-05-28 03:54:22 +05:30
|
|
|
switch (path.replace(/(.)\/.+/, '$1')) { // use only first part of an url
|
2016-06-05 17:36:14 +05:30
|
|
|
case '/activation':
|
|
|
|
this.setState(new ActivationState());
|
|
|
|
break;
|
2016-05-28 03:54:22 +05:30
|
|
|
case '/recover-password':
|
|
|
|
this.setState(new RecoverPasswordState());
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
throw new Error(`Unsupported request: ${path}`);
|
|
|
|
}
|
2016-03-02 02:06:14 +05:30
|
|
|
}
|
2016-06-02 23:16:49 +05:30
|
|
|
|
|
|
|
this.onReady();
|
2016-03-02 02:06:14 +05:30
|
|
|
}
|
|
|
|
}
|