mirror of
https://github.com/wassname/talk.git
synced 2026-08-09 12:30:42 +08:00
Auth Refactor part 1
This commit is contained in:
@@ -7,12 +7,12 @@ import t from 'coral-framework/services/i18n';
|
||||
import { can } from 'coral-framework/services/perms';
|
||||
import cn from 'classnames';
|
||||
|
||||
const CoralDrawer = ({ handleLogout, auth = {} }) => (
|
||||
const CoralDrawer = ({ handleLogout, currentUser }) => (
|
||||
<Drawer className={cn('talk-admin-drawer-nav', styles.drawer)}>
|
||||
{auth && auth.user && can(auth.user, 'ACCESS_ADMIN') ? (
|
||||
{currentUser && can(currentUser, 'ACCESS_ADMIN') ? (
|
||||
<div>
|
||||
<Navigation className={styles.nav}>
|
||||
{can(auth.user, 'MODERATE_COMMENTS') && (
|
||||
{can(currentUser, 'MODERATE_COMMENTS') && (
|
||||
<IndexLink
|
||||
className={cn('talk-admin-nav-moderate', styles.navLink)}
|
||||
to="/admin/moderate"
|
||||
@@ -35,7 +35,7 @@ const CoralDrawer = ({ handleLogout, auth = {} }) => (
|
||||
>
|
||||
{t('configure.community')}
|
||||
</Link>
|
||||
{can(auth.user, 'UPDATE_CONFIG') && (
|
||||
{can(currentUser, 'UPDATE_CONFIG') && (
|
||||
<Link
|
||||
className={cn('talk-admin-nav-configure', styles.navLink)}
|
||||
to="/admin/configure"
|
||||
@@ -55,7 +55,7 @@ const CoralDrawer = ({ handleLogout, auth = {} }) => (
|
||||
CoralDrawer.propTypes = {
|
||||
handleLogout: PropTypes.func.isRequired,
|
||||
restricted: PropTypes.bool, // hide app elements from a logged out user
|
||||
auth: PropTypes.object,
|
||||
currentUser: PropTypes.object,
|
||||
};
|
||||
|
||||
export default CoralDrawer;
|
||||
|
||||
@@ -13,7 +13,7 @@ import CommunityIndicator from '../routes/Community/containers/Indicator';
|
||||
const CoralHeader = ({
|
||||
handleLogout,
|
||||
showShortcuts = () => {},
|
||||
auth,
|
||||
currentUser,
|
||||
root,
|
||||
data,
|
||||
}) => {
|
||||
@@ -22,9 +22,9 @@ const CoralHeader = ({
|
||||
<Header className={styles.header}>
|
||||
<Logo className={styles.logo} />
|
||||
<div>
|
||||
{auth && auth.user && can(auth.user, 'ACCESS_ADMIN') ? (
|
||||
{currentUser && can(currentUser, 'ACCESS_ADMIN') ? (
|
||||
<Navigation className={styles.nav}>
|
||||
{can(auth.user, 'MODERATE_COMMENTS') && (
|
||||
{can(currentUser, 'MODERATE_COMMENTS') && (
|
||||
<IndexLink
|
||||
id="moderateNav"
|
||||
className={cn('talk-admin-nav-moderate', styles.navLink)}
|
||||
@@ -54,7 +54,7 @@ const CoralHeader = ({
|
||||
<CommunityIndicator root={root} data={data} />
|
||||
</Link>
|
||||
|
||||
{can(auth.user, 'UPDATE_CONFIG') && (
|
||||
{can(currentUser, 'UPDATE_CONFIG') && (
|
||||
<Link
|
||||
id="configureNav"
|
||||
className={cn('talk-admin-nav-configure', styles.navLink)}
|
||||
@@ -116,7 +116,7 @@ const CoralHeader = ({
|
||||
};
|
||||
|
||||
CoralHeader.propTypes = {
|
||||
auth: PropTypes.object,
|
||||
currentUser: PropTypes.object,
|
||||
showShortcuts: PropTypes.func,
|
||||
handleLogout: PropTypes.func.isRequired,
|
||||
root: PropTypes.object.isRequired,
|
||||
|
||||
@@ -10,22 +10,26 @@ const Layout = ({
|
||||
handleLogout = () => {},
|
||||
toggleShortcutModal = () => {},
|
||||
restricted = false,
|
||||
auth,
|
||||
currentUser,
|
||||
}) => (
|
||||
<LayoutMDL className={styles.layout} fixedDrawer>
|
||||
<Header
|
||||
handleLogout={handleLogout}
|
||||
showShortcuts={toggleShortcutModal}
|
||||
auth={auth}
|
||||
currentUser={currentUser}
|
||||
/>
|
||||
<Drawer
|
||||
handleLogout={handleLogout}
|
||||
restricted={restricted}
|
||||
currentUser={currentUser}
|
||||
/>
|
||||
<Drawer handleLogout={handleLogout} restricted={restricted} auth={auth} />
|
||||
<div className={styles.layout}>{children}</div>
|
||||
</LayoutMDL>
|
||||
);
|
||||
|
||||
Layout.propTypes = {
|
||||
children: PropTypes.node,
|
||||
auth: PropTypes.object,
|
||||
currentUser: PropTypes.object,
|
||||
handleLogout: PropTypes.func,
|
||||
toggleShortcutModal: PropTypes.func,
|
||||
restricted: PropTypes.bool, // hide elements from a user that's logged out
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
.layout {
|
||||
max-width: 800px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.loginLayout {
|
||||
max-width: 400px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.loginHeader, .loginCTA, .forgotPasswordCTA, .passwordRequestSuccess {
|
||||
text-align: center;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.forgotPasswordLink, .signInLink {
|
||||
color: blue;
|
||||
font-weight: normal;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.forgotPasswordLink:hover, .signInLink:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.layout h1 {
|
||||
font-size: 40px;
|
||||
}
|
||||
|
||||
.loginHeader {
|
||||
font-size: 30px;
|
||||
}
|
||||
|
||||
.passwordRequestSuccess {
|
||||
cursor: pointer;
|
||||
padding: 8px 14px;
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import Layout from 'coral-admin/src/components/Layout';
|
||||
import styles from './Login.css';
|
||||
import { Button, TextField, Alert } from 'coral-ui';
|
||||
import cn from 'classnames';
|
||||
|
||||
class AdminLogin extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
}
|
||||
|
||||
handleForgotPassword = e => {
|
||||
e.preventDefault();
|
||||
this.props.onForgotPassword();
|
||||
};
|
||||
handleEmailChange = e => this.props.onEmailChange(e.target.value);
|
||||
handlePasswordChange = e => this.props.onPasswordChange(e.target.value);
|
||||
|
||||
handleSubmit = e => {
|
||||
e.preventDefault();
|
||||
this.props.onSubmit();
|
||||
};
|
||||
|
||||
render() {
|
||||
const { email, password, errorMessage } = this.props;
|
||||
return (
|
||||
<Layout fixedDrawer restricted={true}>
|
||||
<div className={cn(styles.loginLayout, 'talk-admin-login')}>
|
||||
<h1 className={styles.loginHeader}>Team sign in</h1>
|
||||
<p className={styles.loginCTA}>
|
||||
Sign in to interact with your community.
|
||||
</p>
|
||||
<form
|
||||
className="talk-admin-login-sign-in"
|
||||
onSubmit={this.handleSubmit}
|
||||
>
|
||||
{errorMessage && <Alert>{errorMessage}</Alert>}
|
||||
<TextField
|
||||
id="email"
|
||||
label="Email Address"
|
||||
value={email}
|
||||
onChange={this.handleEmailChange}
|
||||
/>
|
||||
<TextField
|
||||
id="password"
|
||||
label="Password"
|
||||
value={password}
|
||||
onChange={this.handlePasswordChange}
|
||||
type="password"
|
||||
/>
|
||||
<div style={{ height: 10 }} />
|
||||
<Button
|
||||
className="talk-admin-login-sign-in-button"
|
||||
type="submit"
|
||||
cStyle="black"
|
||||
full
|
||||
>
|
||||
Sign In
|
||||
</Button>
|
||||
<p className={styles.forgotPasswordCTA}>
|
||||
Forgot your password?{' '}
|
||||
<a
|
||||
href="#"
|
||||
className={styles.forgotPasswordLink}
|
||||
onClick={this.handleForgotPassword}
|
||||
>
|
||||
Request a new one.
|
||||
</a>
|
||||
</p>
|
||||
</form>
|
||||
</div>
|
||||
</Layout>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
AdminLogin.propTypes = {
|
||||
email: PropTypes.string,
|
||||
password: PropTypes.string,
|
||||
onEmailChange: PropTypes.func,
|
||||
onPasswordChange: PropTypes.func,
|
||||
onForgotPassword: PropTypes.func,
|
||||
onSubmit: PropTypes.func,
|
||||
errorMessage: PropTypes.string,
|
||||
requireRecaptcha: PropTypes.string,
|
||||
};
|
||||
|
||||
export default AdminLogin;
|
||||
@@ -3,35 +3,3 @@
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.loginLayout {
|
||||
max-width: 400px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.loginHeader, .loginCTA, .forgotPasswordCTA, .passwordRequestSuccess {
|
||||
text-align: center;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.forgotPasswordLink, .signInLink {
|
||||
color: blue;
|
||||
font-weight: normal;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.forgotPasswordLink:hover, .signInLink:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.layout h1 {
|
||||
font-size: 40px;
|
||||
}
|
||||
|
||||
.loginHeader {
|
||||
font-size: 30px;
|
||||
}
|
||||
|
||||
.passwordRequestSuccess {
|
||||
cursor: pointer;
|
||||
padding: 8px 14px;
|
||||
}
|
||||
|
||||
@@ -3,85 +3,64 @@ import { connect } from 'react-redux';
|
||||
import { bindActionCreators } from 'redux';
|
||||
import Layout from '../components/Layout';
|
||||
import { fetchConfig } from '../actions/config';
|
||||
import AdminLogin from '../components/AdminLogin';
|
||||
import Login from '../containers/Login';
|
||||
import { FullLoading } from '../components/FullLoading';
|
||||
import BanUserDialog from './BanUserDialog';
|
||||
import SuspendUserDialog from './SuspendUserDialog';
|
||||
import { toggleModal as toggleShortcutModal } from '../actions/moderation';
|
||||
import {
|
||||
checkLogin,
|
||||
handleLogin,
|
||||
requestPasswordReset,
|
||||
logout,
|
||||
} from '../actions/auth';
|
||||
import { logout } from 'coral-framework/actions/auth';
|
||||
import { can } from 'coral-framework/services/perms';
|
||||
import UserDetail from 'coral-admin/src/containers/UserDetail';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
class LayoutContainer extends React.Component {
|
||||
componentWillMount() {
|
||||
const { checkLogin, fetchConfig } = this.props;
|
||||
const { fetchConfig } = this.props;
|
||||
|
||||
checkLogin();
|
||||
fetchConfig();
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
user,
|
||||
loggedIn,
|
||||
loadingUser,
|
||||
loginError,
|
||||
loginMaxExceeded,
|
||||
passwordRequestSuccess,
|
||||
} = this.props.auth;
|
||||
|
||||
const {
|
||||
currentUser,
|
||||
checkedInitialLogin,
|
||||
children,
|
||||
logout,
|
||||
toggleShortcutModal,
|
||||
TALK_RECAPTCHA_PUBLIC,
|
||||
} = this.props;
|
||||
|
||||
if (loadingUser) {
|
||||
if (!checkedInitialLogin) {
|
||||
return <FullLoading />;
|
||||
}
|
||||
|
||||
if (!loggedIn) {
|
||||
return (
|
||||
<AdminLogin
|
||||
loginMaxExceeded={loginMaxExceeded}
|
||||
handleLogin={this.props.handleLogin}
|
||||
requestPasswordReset={this.props.requestPasswordReset}
|
||||
passwordRequestSuccess={passwordRequestSuccess}
|
||||
recaptchaPublic={TALK_RECAPTCHA_PUBLIC}
|
||||
errorMessage={loginError}
|
||||
/>
|
||||
);
|
||||
if (!currentUser) {
|
||||
return <Login />;
|
||||
}
|
||||
|
||||
if (can(user, 'ACCESS_ADMIN') && loggedIn) {
|
||||
return (
|
||||
<Layout
|
||||
handleLogout={logout}
|
||||
toggleShortcutModal={toggleShortcutModal}
|
||||
auth={this.props.auth}
|
||||
>
|
||||
<BanUserDialog />
|
||||
<SuspendUserDialog />
|
||||
<UserDetail />
|
||||
{children}
|
||||
</Layout>
|
||||
);
|
||||
} else if (loggedIn) {
|
||||
return (
|
||||
<Layout {...this.props}>
|
||||
<p>
|
||||
This page is for team use only. Please contact an administrator if
|
||||
you want to join this team.
|
||||
</p>
|
||||
</Layout>
|
||||
);
|
||||
if (currentUser) {
|
||||
if (can(currentUser, 'ACCESS_ADMIN')) {
|
||||
return (
|
||||
<Layout
|
||||
handleLogout={logout}
|
||||
toggleShortcutModal={toggleShortcutModal}
|
||||
currentUser={this.props.currentUser}
|
||||
>
|
||||
<BanUserDialog />
|
||||
<SuspendUserDialog />
|
||||
<UserDetail />
|
||||
{children}
|
||||
</Layout>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<Layout {...this.props}>
|
||||
<p>
|
||||
This page is for team use only. Please contact an administrator if
|
||||
you want to join this team.
|
||||
</p>
|
||||
</Layout>
|
||||
);
|
||||
}
|
||||
}
|
||||
return <FullLoading />;
|
||||
}
|
||||
@@ -89,29 +68,23 @@ class LayoutContainer extends React.Component {
|
||||
|
||||
LayoutContainer.propTypes = {
|
||||
children: PropTypes.node,
|
||||
requestPasswordReset: PropTypes.func,
|
||||
handleLogin: PropTypes.func,
|
||||
auth: PropTypes.object,
|
||||
handleLogout: PropTypes.func,
|
||||
currentUser: PropTypes.object,
|
||||
checkedInitialLogin: PropTypes.bool,
|
||||
logout: PropTypes.func,
|
||||
toggleShortcutModal: PropTypes.func,
|
||||
TALK_RECAPTCHA_PUBLIC: PropTypes.string,
|
||||
checkLogin: PropTypes.func,
|
||||
fetchConfig: PropTypes.func,
|
||||
};
|
||||
|
||||
const mapStateToProps = state => ({
|
||||
auth: state.auth,
|
||||
TALK_RECAPTCHA_PUBLIC: state.config.data.TALK_RECAPTCHA_PUBLIC,
|
||||
currentUser: state.authCore.user,
|
||||
checkedInitialLogin: state.authCore.checkedInitialLogin,
|
||||
});
|
||||
|
||||
const mapDispatchToProps = dispatch =>
|
||||
bindActionCreators(
|
||||
{
|
||||
checkLogin,
|
||||
fetchConfig,
|
||||
handleLogin,
|
||||
requestPasswordReset,
|
||||
toggleShortcutModal,
|
||||
logout,
|
||||
},
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { withLogin } from 'coral-framework/hocs';
|
||||
import { compose } from 'recompose';
|
||||
import Login from '../components/Login';
|
||||
|
||||
class LoginContainer extends Component {
|
||||
state = {
|
||||
email: '',
|
||||
password: '',
|
||||
};
|
||||
|
||||
handleSubmit = () => {
|
||||
this.props.login(this.state.email, this.state.password);
|
||||
};
|
||||
|
||||
handleEmailChange = email => {
|
||||
this.setState({ email });
|
||||
};
|
||||
|
||||
handlePasswordChange = password => {
|
||||
this.setState({ password });
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Login
|
||||
onSubmit={this.handleSubmit}
|
||||
onEmailChange={this.handleEmailChange}
|
||||
onPasswordChange={this.handlePasswordChange}
|
||||
email={this.state.email}
|
||||
password={this.state.password}
|
||||
errorMessage={this.props.errorMessage}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
LoginContainer.propTypes = {
|
||||
login: PropTypes.func,
|
||||
errorMessage: PropTypes.string,
|
||||
};
|
||||
|
||||
export default compose(withLogin)(LoginContainer);
|
||||
@@ -71,7 +71,7 @@ const withConfigureQuery = withQuery(
|
||||
);
|
||||
|
||||
const mapStateToProps = state => ({
|
||||
auth: state.auth,
|
||||
auth: state.authCore,
|
||||
pending: state.configure.pending,
|
||||
canSave: state.configure.canSave,
|
||||
activeSection: state.configure.activeSection,
|
||||
|
||||
@@ -515,7 +515,7 @@ const withModQueueQuery = withQuery(
|
||||
|
||||
const mapStateToProps = state => ({
|
||||
moderation: state.moderation,
|
||||
auth: state.auth,
|
||||
auth: state.authCore,
|
||||
});
|
||||
|
||||
const mapDispatchToProps = dispatch => ({
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
import * as actions from '../constants/auth';
|
||||
import jwtDecode from 'jwt-decode';
|
||||
|
||||
function cleanAuthData(storage) {
|
||||
storage.removeItem('token');
|
||||
storage.removeItem('exp');
|
||||
}
|
||||
|
||||
/**
|
||||
* Check Login
|
||||
*/
|
||||
export const checkLogin = () => (
|
||||
dispatch,
|
||||
_,
|
||||
{ rest, client, pym, storage }
|
||||
) => {
|
||||
dispatch(checkLoginRequest());
|
||||
rest('/auth')
|
||||
.then(result => {
|
||||
if (!result.user) {
|
||||
if (storage) {
|
||||
cleanAuthData(storage);
|
||||
}
|
||||
dispatch(checkLoginSuccess(null));
|
||||
return;
|
||||
}
|
||||
|
||||
// Reset the websocket.
|
||||
client.resetWebsocket();
|
||||
|
||||
dispatch(checkLoginSuccess(result.user));
|
||||
pym.sendMessage('coral-auth-changed', JSON.stringify(result.user));
|
||||
})
|
||||
.catch(error => {
|
||||
if (error.status && error.status === 401 && storage) {
|
||||
// Unauthorized.
|
||||
cleanAuthData(storage);
|
||||
} else {
|
||||
console.error(error);
|
||||
}
|
||||
dispatch(checkLoginFailure(error));
|
||||
});
|
||||
};
|
||||
|
||||
const checkLoginRequest = () => ({ type: actions.CHECK_LOGIN_REQUEST });
|
||||
|
||||
const checkLoginFailure = error => ({
|
||||
type: actions.CHECK_LOGIN_FAILURE,
|
||||
error,
|
||||
});
|
||||
|
||||
const checkLoginSuccess = user => ({
|
||||
type: actions.CHECK_LOGIN_SUCCESS,
|
||||
user,
|
||||
});
|
||||
|
||||
/**
|
||||
* Login
|
||||
*/
|
||||
export const handleSuccessfulLogin = (user, token) => (
|
||||
dispatch,
|
||||
_,
|
||||
{ client, storage }
|
||||
) => {
|
||||
if (storage) {
|
||||
storage.setItem('exp', jwtDecode(token).exp);
|
||||
storage.setItem('token', token);
|
||||
}
|
||||
|
||||
client.resetWebsocket();
|
||||
|
||||
dispatch({
|
||||
type: actions.HANDLE_SUCCESSFUL_LOGIN,
|
||||
user,
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Logout
|
||||
*/
|
||||
export const logout = () => async (
|
||||
dispatch,
|
||||
_,
|
||||
{ rest, client, pym, storage }
|
||||
) => {
|
||||
await rest('/auth', { method: 'DELETE' });
|
||||
|
||||
if (storage) {
|
||||
cleanAuthData(storage);
|
||||
}
|
||||
|
||||
// Reset the websocket.
|
||||
client.resetWebsocket();
|
||||
|
||||
dispatch({ type: actions.LOGOUT });
|
||||
pym.sendMessage('coral-auth-changed');
|
||||
};
|
||||
@@ -0,0 +1,8 @@
|
||||
const prefix = `TALK_FRAMEWORK`;
|
||||
|
||||
export const CHECK_LOGIN_REQUEST = `${prefix}_CHECK_LOGIN_REQUEST`;
|
||||
export const CHECK_LOGIN_SUCCESS = `${prefix}_CHECK_LOGIN_SUCCESS`;
|
||||
export const CHECK_LOGIN_FAILURE = `${prefix}_CHECK_LOGIN_FAILURE`;
|
||||
|
||||
export const LOGOUT = `${prefix}_LOGOUT`;
|
||||
export const HANDLE_SUCCESSFUL_LOGIN = `${prefix}_HANDLE_SUCCESSFUL_LOGIN`;
|
||||
@@ -6,3 +6,4 @@ export { default as withEmit } from './withEmit';
|
||||
export { default as excludeIf } from './excludeIf';
|
||||
export { default as connect } from './connect';
|
||||
export { default as withMergedSettings } from './withMergedSettings';
|
||||
export { default as withLogin } from './withLogin';
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
import React from 'react';
|
||||
import hoistStatics from 'recompose/hoistStatics';
|
||||
import PropTypes from 'prop-types';
|
||||
import { handleSuccessfulLogin } from '../actions/auth';
|
||||
import { translateError } from '../utils';
|
||||
import { t } from '../services/i18n';
|
||||
|
||||
/**
|
||||
* WithLogin provides properties `login`, `loading` and `errorMessage`, `requireRecaptcha`.
|
||||
*/
|
||||
export default hoistStatics(WrappedComponent => {
|
||||
class WithLogin extends React.Component {
|
||||
static contextTypes = {
|
||||
store: PropTypes.object,
|
||||
rest: PropTypes.func,
|
||||
};
|
||||
|
||||
state = {
|
||||
error: null,
|
||||
loading: false,
|
||||
};
|
||||
|
||||
login = (email, password, recaptchaResponse) => {
|
||||
const { store, rest } = this.context;
|
||||
const params = {
|
||||
method: 'POST',
|
||||
body: {
|
||||
email,
|
||||
password,
|
||||
},
|
||||
};
|
||||
|
||||
if (recaptchaResponse) {
|
||||
params.headers = {
|
||||
'X-Recaptcha-Response': recaptchaResponse,
|
||||
};
|
||||
}
|
||||
|
||||
rest('/auth/local', params)
|
||||
.then(({ user, token }) => {
|
||||
this.setState({ loading: false, error: null });
|
||||
store.dispatch(handleSuccessfulLogin(user, token));
|
||||
})
|
||||
.catch(error => {
|
||||
if (!error.status || error.status !== 401) {
|
||||
console.error(error);
|
||||
}
|
||||
this.setState({ loading: false, error });
|
||||
});
|
||||
};
|
||||
|
||||
getErrorMessage() {
|
||||
if (!this.state.error) {
|
||||
return '';
|
||||
}
|
||||
return this.state.error.translation_key === 'NOT_AUTHORIZED'
|
||||
? t('error.email_password')
|
||||
: translateError(this.state.error);
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<WrappedComponent
|
||||
{...this.props}
|
||||
login={this.login}
|
||||
loading={this.state.loading}
|
||||
errorMessage={this.getErrorMessage()}
|
||||
requireRecaptcha={false}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return WithLogin;
|
||||
});
|
||||
@@ -0,0 +1,42 @@
|
||||
import * as actions from '../constants/auth';
|
||||
|
||||
const initialState = {
|
||||
checkedInitialLogin: false,
|
||||
initialLoginError: null,
|
||||
user: null,
|
||||
};
|
||||
|
||||
const purge = user => {
|
||||
const {settings, ...userData} = user; // eslint-disable-line
|
||||
return userData;
|
||||
};
|
||||
|
||||
export default function auth(state = initialState, action) {
|
||||
switch (action.type) {
|
||||
case actions.CHECK_LOGIN_FAILURE:
|
||||
return {
|
||||
...state,
|
||||
initialLoginError: action.error,
|
||||
checkedInitialLogin: true,
|
||||
user: null,
|
||||
};
|
||||
case actions.CHECK_LOGIN_SUCCESS:
|
||||
return {
|
||||
...state,
|
||||
checkedInitialLogin: true,
|
||||
user: action.user ? purge(action.user) : null,
|
||||
};
|
||||
case actions.HANDLE_SUCCESSFUL_LOGIN:
|
||||
return {
|
||||
...state,
|
||||
user: action.user ? purge(action.user) : null,
|
||||
};
|
||||
case actions.LOGOUT:
|
||||
return {
|
||||
...state,
|
||||
user: null,
|
||||
};
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
}
|
||||
@@ -22,6 +22,8 @@ import {
|
||||
import { createHistory } from 'coral-framework/services/history';
|
||||
import { createIntrospection } from 'coral-framework/services/introspection';
|
||||
import introspectionData from 'coral-framework/graphql/introspection.json';
|
||||
import auth from '../reducers/auth';
|
||||
import { checkLogin } from '../actions/auth';
|
||||
|
||||
/**
|
||||
* getAuthToken returns the active auth token or null
|
||||
@@ -143,6 +145,7 @@ export async function createContext({
|
||||
|
||||
// Create our redux store.
|
||||
const finalReducers = {
|
||||
authCore: auth,
|
||||
...reducers,
|
||||
...plugins.getReducers(),
|
||||
};
|
||||
@@ -162,6 +165,8 @@ export async function createContext({
|
||||
[client.middleware(), apolloErrorReporter, createReduxEmitter(eventEmitter)]
|
||||
);
|
||||
|
||||
store.dispatch(checkLogin());
|
||||
|
||||
// Run pre initialization.
|
||||
if (preInit) {
|
||||
await preInit(context);
|
||||
|
||||
@@ -252,3 +252,12 @@ export function mapLeaves(o, mapper) {
|
||||
return mapper(val);
|
||||
});
|
||||
}
|
||||
|
||||
export function translateError(error) {
|
||||
if (error.translation_key) {
|
||||
return t(`error.${error.translation_key}`);
|
||||
} else if (error.networkError) {
|
||||
return t('error.network_error');
|
||||
}
|
||||
return error.toString();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user