diff --git a/src/containers/ContactDetailsPage/ContactDetailsPage.duck.js b/src/containers/ContactDetailsPage/ContactDetailsPage.duck.js index ddb1157d..91a32f78 100644 --- a/src/containers/ContactDetailsPage/ContactDetailsPage.duck.js +++ b/src/containers/ContactDetailsPage/ContactDetailsPage.duck.js @@ -1,3 +1,4 @@ +import merge from 'lodash/merge'; import { storableError } from '../../util/errors'; import { currentUserShowSuccess } from '../../ducks/user.duck'; @@ -5,14 +6,16 @@ import { currentUserShowSuccess } from '../../ducks/user.duck'; export const SAVE_CONTACT_DETAILS_REQUEST = 'app/ContactDetailsPage/SAVE_CONTACT_DETAILS_REQUEST'; export const SAVE_CONTACT_DETAILS_SUCCESS = 'app/ContactDetailsPage/SAVE_CONTACT_DETAILS_SUCCESS'; -export const SAVE_CONTACT_DETAILS_ERROR = 'app/ContactDetailsPage/SAVE_CONTACT_DETAILS_ERROR'; +export const SAVE_EMAIL_ERROR = 'app/ContactDetailsPage/SAVE_EMAIL_ERROR'; +export const SAVE_PHONE_NUMBER_ERROR = 'app/ContactDetailsPage/SAVE_PHONE_NUMBER_ERROR'; export const SAVE_CONTACT_DETAILS_CLEAR = 'app/ContactDetailsPage/SAVE_CONTACT_DETAILS_CLEAR'; // ================ Reducer ================ // const initialState = { - saveContactDetailsError: null, + saveEmailError: null, + savePhoneNumberError: null, saveContactDetailsInProgress: false, contactDetailsChanged: false, }; @@ -29,8 +32,10 @@ export default function reducer(state = initialState, action = {}) { }; case SAVE_CONTACT_DETAILS_SUCCESS: return { ...state, saveContactDetailsInProgress: false, contactDetailsChanged: true }; - case SAVE_CONTACT_DETAILS_ERROR: - return { ...state, saveContactDetailsInProgress: false, saveContactDetailsError: payload }; + case SAVE_EMAIL_ERROR: + return { ...state, saveContactDetailsInProgress: false, saveEmailError: payload }; + case SAVE_PHONE_NUMBER_ERROR: + return { ...state, saveContactDetailsInProgress: false, savePhoneNumberError: payload }; case SAVE_CONTACT_DETAILS_CLEAR: return { @@ -49,8 +54,13 @@ export default function reducer(state = initialState, action = {}) { export const saveContactDetailsRequest = () => ({ type: SAVE_CONTACT_DETAILS_REQUEST }); export const saveContactDetailsSuccess = () => ({ type: SAVE_CONTACT_DETAILS_SUCCESS }); -export const saveContactDetailsError = error => ({ - type: SAVE_CONTACT_DETAILS_ERROR, +export const saveEmailError = error => ({ + type: SAVE_EMAIL_ERROR, + payload: error, + error: true, +}); +export const savePhoneNumberError = error => ({ + type: SAVE_PHONE_NUMBER_ERROR, payload: error, error: true, }); @@ -59,16 +69,104 @@ export const saveContactDetailsClear = () => ({ type: SAVE_CONTACT_DETAILS_CLEAR // ================ Thunks ================ // -export const saveContactDetails = params => (dispatch, getState, sdk) => { - dispatch(saveContactDetailsRequest()); +/** + * Make a phone number update request to the API and return the current user. + */ +const requestSavePhoneNumber = params => (dispatch, getState, sdk) => { + const phoneNumber = params.phoneNumber; + + return sdk.currentUser + .updateProfile({ protectedData: { phoneNumber } }, { expand: true }) + .then(response => { + // return user + return response.data.data; + }) + .catch(e => { + dispatch(savePhoneNumberError(storableError(e))); + // pass the same error so that the SAVE_CONTACT_DETAILS_SUCCESS + // action will not be fired + throw e; + }); +}; + +/** + * Make a email update request to the API and return the current user. + */ +const requestSaveEmail = params => (dispatch, getState, sdk) => { const { email, currentPassword } = params; return sdk.currentUser .changeEmail({ email, currentPassword }, { expand: true }) .then(response => { - const currentUser = response.data.data; - dispatch(saveContactDetailsSuccess()); - dispatch(currentUserShowSuccess(currentUser)); + // return user + return response.data.data; }) - .catch(e => dispatch(saveContactDetailsError(storableError(e)))); + .catch(e => { + dispatch(saveEmailError(storableError(e))); + // pass the same error so that the SAVE_CONTACT_DETAILS_SUCCESS + // action will not be fired + throw e; + }); +}; + +/** + * Save email and update the current user. + */ +export const saveEmail = params => (dispatch, getState, sdk) => { + dispatch(saveContactDetailsRequest()); + + return dispatch(requestSaveEmail(params)) + .then(user => { + dispatch(currentUserShowSuccess(user)); + dispatch(saveContactDetailsSuccess()); + }) + .catch(e => null); +}; + +/** + * Save phone number and update the current user. + */ +export const savePhoneNumber = params => (dispatch, getState, sdk) => { + dispatch(saveContactDetailsRequest()); + + return dispatch(requestSavePhoneNumber(params)) + .then(user => { + dispatch(currentUserShowSuccess(user)); + dispatch(saveContactDetailsSuccess()); + }) + .catch(e => null); +}; + +/** + * Save email and phone number and update the current user. + */ +export const saveContactDetails = params => (dispatch, getState, sdk) => { + dispatch(saveContactDetailsRequest()); + const { email, phoneNumber, currentPassword } = params; + + // order of promises: 1. email, 2. phone number + const promises = [ + dispatch(requestSaveEmail({ email, currentPassword })), + dispatch(requestSavePhoneNumber({ phoneNumber })), + ]; + + return Promise.all(promises) + .then(values => { + // Array of two user objects is resolved + // the first one is from the email update + // the second one is from the phone number update + + const saveEmailUser = values[0]; + const savePhoneNumberUser = values[1]; + + // merge the protected data from the user object returned + // by the phone update operation + const protectedData = savePhoneNumberUser.attributes.profile.protectedData; + const phoneNumberMergeSource = { attributes: { profile: { protectedData } } }; + + const currentUser = merge(saveEmailUser, phoneNumberMergeSource); + dispatch(currentUserShowSuccess(currentUser)); + dispatch(saveContactDetailsSuccess()); + }) + .catch(e => null); }; diff --git a/src/containers/ContactDetailsPage/ContactDetailsPage.js b/src/containers/ContactDetailsPage/ContactDetailsPage.js index 08096109..11d49587 100644 --- a/src/containers/ContactDetailsPage/ContactDetailsPage.js +++ b/src/containers/ContactDetailsPage/ContactDetailsPage.js @@ -19,7 +19,12 @@ import { import { ContactDetailsForm, TopbarContainer } from '../../containers'; import { isScrollingDisabled } from '../../ducks/UI.duck'; -import { saveContactDetails, saveContactDetailsClear } from './ContactDetailsPage.duck'; +import { + saveContactDetails, + saveContactDetailsClear, + saveEmail, + savePhoneNumber, +} from './ContactDetailsPage.duck'; import css from './ContactDetailsPage.css'; export const ContactDetailsPageComponent = props => { @@ -34,9 +39,25 @@ export const ContactDetailsPageComponent = props => { sendVerificationEmailError, onResendVerificationEmail, onSubmitContactDetails, + onSubmitEmail, + onSubmitPhoneNumber, intl, } = props; + const handleSubmit = (values, currentEmail, currentPhoneNumber) => { + const { email, currentPassword, phoneNumber } = values; + const emailChanged = email !== currentEmail; + const phoneNumberChanged = phoneNumber !== currentPhoneNumber; + + if (emailChanged && phoneNumberChanged) { + onSubmitContactDetails({ email, currentPassword, phoneNumber }); + } else if (emailChanged) { + onSubmitEmail({ email, currentPassword }); + } else if (phoneNumberChanged) { + onSubmitPhoneNumber({ phoneNumber }); + } + }; + const tabs = [ { text: , @@ -72,7 +93,7 @@ export const ContactDetailsPageComponent = props => { saveContactDetailsError={saveContactDetailsError} currentUser={currentUser} onResendVerificationEmail={onResendVerificationEmail} - onSubmit={onSubmitContactDetails} + onSubmit={values => handleSubmit(values, email, phoneNumber)} onChange={onChange} inProgress={saveContactDetailsInProgress} ready={contactDetailsChanged} @@ -126,6 +147,8 @@ ContactDetailsPageComponent.propTypes = { contactDetailsChanged: bool.isRequired, onChange: func.isRequired, onSubmitContactDetails: func.isRequired, + onSubmitEmail: func.isRequired, + onSubmitPhoneNumber: func.isRequired, scrollingDisabled: bool.isRequired, sendVerificationEmailInProgress: bool.isRequired, sendVerificationEmailError: propTypes.error, @@ -158,6 +181,8 @@ const mapDispatchToProps = dispatch => ({ onChange: () => dispatch(saveContactDetailsClear()), onResendVerificationEmail: () => dispatch(sendVerificationEmail()), onSubmitContactDetails: values => dispatch(saveContactDetails(values)), + onSubmitEmail: values => dispatch(saveEmail(values)), + onSubmitPhoneNumber: values => dispatch(savePhoneNumber(values)), }); const ContactDetailsPage = compose(connect(mapStateToProps, mapDispatchToProps), injectIntl)( diff --git a/src/containers/ContactDetailsPage/ContactDetailsPage.test.js b/src/containers/ContactDetailsPage/ContactDetailsPage.test.js index 5e4e8a52..9230a8ef 100644 --- a/src/containers/ContactDetailsPage/ContactDetailsPage.test.js +++ b/src/containers/ContactDetailsPage/ContactDetailsPage.test.js @@ -22,6 +22,8 @@ describe('ContactDetailsPage', () => { sendVerificationEmailInProgress={false} onResendVerificationEmail={noop} onSubmitContactDetails={noop} + onSubmitEmail={noop} + onSubmitPhoneNumber={noop} saveContactDetailsInProgress={false} contactDetailsChanged={false} intl={fakeIntl}