diff --git a/client/coral-framework/actions/auth.js b/client/coral-framework/actions/auth.js index e71635ae1..d06abaee0 100644 --- a/client/coral-framework/actions/auth.js +++ b/client/coral-framework/actions/auth.js @@ -22,6 +22,7 @@ export const cleanState = () => ({type: actions.CLEAN_STATE}); const signInRequest = () => ({type: actions.FETCH_SIGNIN_REQUEST}); const signInSuccess = (user, isAdmin) => ({type: actions.FETCH_SIGNIN_SUCCESS, user, isAdmin}); const signInFailure = error => ({type: actions.FETCH_SIGNIN_FAILURE, error}); +const emailConfirmError = () => ({type: actions.EMAIL_CONFIRM_ERROR}); export const fetchSignIn = (formData) => (dispatch) => { dispatch(signInRequest()); @@ -32,7 +33,18 @@ export const fetchSignIn = (formData) => (dispatch) => { dispatch(hideSignInDialog()); dispatch(addItem(user, 'users')); }) - .catch(() => dispatch(signInFailure(lang.t('error.emailPasswordError')))); + .catch(error => { + if (error.metadata) { + + // the user might not have a valid email. prompt the user user re-request the confirmation email + dispatch(signInFailure(lang.t('error.emailNotVerified', error.metadata))); + dispatch(emailConfirmError()); + } else { + + // invalid credentials + dispatch(signInFailure(lang.t('error.emailPasswordError'))); + } + }); }; // Sign In - Facebook @@ -138,7 +150,20 @@ export const checkLogin = () => dispatch => { .catch(error => dispatch(checkLoginFailure(error))); }; -export const requestConfirmEmail = () => dispatch => { +const confirmEmailRequest = () => ({type: actions.CONFIRM_EMAIL_REQUEST}); +const confirmEmailSuccess = () => ({type: actions.CONFIRM_EMAIL_SUCCESS}); +const confirmEmailFailure = () => ({type: actions.CONFIRM_EMAIL_FAILURE}); +export const requestConfirmEmail = email => dispatch => { + dispatch(confirmEmailRequest()); + coralApi('/users/resend-confirm', {method: 'POST', body: {email}}) + .then(() => { + dispatch(confirmEmailSuccess()); + }) + .catch(err => { + console.log('failed to send email confirmation', err); + + // email might have already been confirmed + dispatch(confirmEmailFailure()); + }); }; - diff --git a/client/coral-framework/constants/auth.js b/client/coral-framework/constants/auth.js index 5742adf75..1dae348df 100644 --- a/client/coral-framework/constants/auth.js +++ b/client/coral-framework/constants/auth.js @@ -32,3 +32,8 @@ export const CHECK_LOGIN_SUCCESS = 'CHECK_LOGIN_SUCCESS'; export const CHECK_LOGIN_FAILURE = 'CHECK_LOGIN_FAILURE'; export const CHECK_CSRF_TOKEN = 'CHECK_CSRF_TOKEN'; + +export const EMAIL_CONFIRM_ERROR = 'EMAIL_CONFIRM_ERROR'; +export const CONFIRM_EMAIL_REQUEST = 'CONFIRM_EMAIL_REQUEST'; +export const CONFIRM_EMAIL_SUCCESS = 'CONFIRM_EMAIL_SUCCESS'; +export const CONFIRM_EMAIL_FAILURE = 'CONFIRM_EMAIL_FAILURE'; diff --git a/client/coral-framework/helpers/response.js b/client/coral-framework/helpers/response.js index e4e6e7c37..d95773c04 100644 --- a/client/coral-framework/helpers/response.js +++ b/client/coral-framework/helpers/response.js @@ -34,15 +34,21 @@ const buildOptions = (inputOptions = {}) => { }; const handleResp = res => { - if (res.status === 401) { - throw new Error('Not Authorized to make this request'); - } else if (res.status > 399) { + if (res.status > 399) { return res.json().then(err => { let message = err.message || res.status; + const error = new Error(); + + if (err.error && err.error.metadata && err.error.metadata.message) { + error.metadata = err.error.metadata.message; + } + if (err.error && err.error.translation_key) { message = err.error.translation_key; } - throw new Error(message); + + error.message = message; + throw error; }); } else if (res.status === 204) { return res.text(); diff --git a/client/coral-framework/reducers/auth.js b/client/coral-framework/reducers/auth.js index 36f8e0764..54ffa37a6 100644 --- a/client/coral-framework/reducers/auth.js +++ b/client/coral-framework/reducers/auth.js @@ -11,6 +11,7 @@ const initialState = Map({ error: '', passwordRequestSuccess: null, passwordRequestFailure: null, + emailConfirmationFailure: false, successSignUp: false }); @@ -33,6 +34,7 @@ export default function auth (state = initialState, action) { error: '', passwordRequestFailure: null, passwordRequestSuccess: null, + emailConfirmationFailure: false, successSignUp: false })); case actions.CHANGE_VIEW : @@ -101,6 +103,8 @@ export default function auth (state = initialState, action) { return state .set('passwordRequestFailure', 'There was an error sending your password reset email. Please try again soon!') .set('passwordRequestSuccess', null); + case actions.EMAIL_CONFIRM_ERROR: + return state.set('emailConfirmationFailure', true); default : return state; } diff --git a/client/coral-framework/translations.json b/client/coral-framework/translations.json index 5bf9b40a9..07b719f6b 100644 --- a/client/coral-framework/translations.json +++ b/client/coral-framework/translations.json @@ -5,6 +5,7 @@ "contentNotAvailable": "This content is not available", "suspendedAccountMsg": "Your account is currently suspended. This means that you cannot Like, Flag, or write comments. Please contact moderator@fakeurl.com for more information", "error": { + "emailNotVerified": "Email address {0} not verified.", "email": "Not a valid E-Mail", "password": "Password must be at least 8 characters", "displayName": "Display names can contain letters, numbers and _ only", @@ -27,6 +28,7 @@ "contentNotAvailable": "El contenido no se encuentra disponible", "suspendedAccountMsg": "Tu cuenta se encuentra suspendida. Esto significa que no puedes dar Like, Marcar o escribir commentarios. Por favor, contacta moderator@fakeurl for more information", "error": { + "emailNotVerified": "Dirección de correo electrónico {0} no verificada.", "email": "No es un email válido", "password": "La contraseña debe tener por lo menos 8 caracteres", "displayName": "Los nombres pueden contener letras, números y _", diff --git a/client/coral-sign-in/components/SignInContent.js b/client/coral-sign-in/components/SignInContent.js index efd6ad5c2..d3d064c06 100644 --- a/client/coral-sign-in/components/SignInContent.js +++ b/client/coral-sign-in/components/SignInContent.js @@ -1,4 +1,4 @@ -import React from 'react'; +import React, {PropTypes} from 'react'; import Alert from './Alert'; import {Button, FormField, Spinner} from 'coral-ui'; import styles from './styles.css'; @@ -6,60 +6,89 @@ import I18n from 'coral-framework/modules/i18n/i18n'; import translations from '../translations'; const lang = new I18n(translations); -const SignInContent = ({handleChange, formData, ...props}) => ( -