diff --git a/.circleci/config.yml b/.circleci/config.yml index fbe4c60282..8b9a41af6a 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -231,7 +231,7 @@ workflows: branches: only: - develop - - tco21 + - feature-contentful # This is alternate dev env for parallel testing - "build-test": context : org-global @@ -259,7 +259,7 @@ workflows: filters: branches: only: - - staging-env-setup + - feature-contentful # Production builds are exectuted # when PR is merged to the master # Don't change anything in this configuration diff --git a/__tests__/shared/components/Settings/Preferences/Email/__snapshots__/index.jsx.snap b/__tests__/shared/components/Settings/Preferences/Email/__snapshots__/index.jsx.snap new file mode 100644 index 0000000000..cde7b4e722 --- /dev/null +++ b/__tests__/shared/components/Settings/Preferences/Email/__snapshots__/index.jsx.snap @@ -0,0 +1,70 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`renders email preferences setting page correctly 1`] = ` +
+

+ E-Mail Preferences +

+
+ Your preferences +
+
+ Challenge Pipeline page." + value="Pipeline" + /> + Gig Work page." + value="Gig Work" + /> + + + + Topcoder Open you should definitely be subscribing to this one. Expect an update in your mailbox every Tuesday!" + value="TCO Tuesdays" + /> +
+
+`; diff --git a/__tests__/shared/components/Settings/Preferences/Email/index.jsx b/__tests__/shared/components/Settings/Preferences/Email/index.jsx new file mode 100644 index 0000000000..f49efaae8d --- /dev/null +++ b/__tests__/shared/components/Settings/Preferences/Email/index.jsx @@ -0,0 +1,11 @@ +import React from 'react'; +import Renderer from 'react-test-renderer/shallow'; + +import Email from 'components/Settings/Preferences/Email'; + +const rnd = new Renderer(); + +it('renders email preferences setting page correctly', () => { + rnd.render(()); + expect(rnd.getRenderOutput()).toMatchSnapshot(); +}); diff --git a/__tests__/shared/components/Settings/Preferences/__snapshots__/index.jsx.snap b/__tests__/shared/components/Settings/Preferences/__snapshots__/index.jsx.snap deleted file mode 100644 index c4e74aa901..0000000000 --- a/__tests__/shared/components/Settings/Preferences/__snapshots__/index.jsx.snap +++ /dev/null @@ -1,50 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`renders preferences setting page correctly 1`] = ` -
-
- , - "forum": , - "payment": , - } - } - names={ - Array [ - "e-mail", - "forum", - "payment", - ] - } - toggle={[Function]} - /> -
-
- - - -
-
-`; diff --git a/__tests__/shared/components/Settings/Preferences/index.jsx b/__tests__/shared/components/Settings/Preferences/index.jsx deleted file mode 100644 index c8c9042eed..0000000000 --- a/__tests__/shared/components/Settings/Preferences/index.jsx +++ /dev/null @@ -1,11 +0,0 @@ -import React from 'react'; -import Renderer from 'react-test-renderer/shallow'; - -import Preferences from 'components/Settings/Preferences'; - -const rnd = new Renderer(); - -it('renders preferences setting page correctly', () => { - rnd.render(()); - expect(rnd.getRenderOutput()).toMatchSnapshot(); -}); diff --git a/__tests__/shared/components/__snapshots__/TopcoderFooter.jsx.snap b/__tests__/shared/components/__snapshots__/TopcoderFooter.jsx.snap index 9fdfd6c76a..f95e017b49 100644 --- a/__tests__/shared/components/__snapshots__/TopcoderFooter.jsx.snap +++ b/__tests__/shared/components/__snapshots__/TopcoderFooter.jsx.snap @@ -149,18 +149,18 @@ exports[`Matches shallow shapshot 1`] = ` className="src-shared-components-TopcoderFooter-___style__link___3-nzm" > - TCO + Blog
  • - Programs + Events Calendar
  • +
  • + + Programs + +
  • @@ -185,9 +194,9 @@ exports[`Matches shallow shapshot 1`] = ` className="src-shared-components-TopcoderFooter-___style__link___3-nzm" > - Blog + TCO
  • ` diff --git a/src/shared/actions/newsletterPreferences.js b/src/shared/actions/newsletterPreferences.js new file mode 100644 index 0000000000..40a415c34a --- /dev/null +++ b/src/shared/actions/newsletterPreferences.js @@ -0,0 +1,104 @@ +/** + * Actions for the Newsletter preference container. + */ + +/* global fetch */ +import _ from 'lodash'; +import { createActions } from 'redux-actions'; +import { config } from 'topcoder-react-utils'; + +const PROXY_ENDPOINT = `${config.URL.COMMUNITY_APP}/api/mailchimp`; + +// Fetching member's newsletter preferences init +function fetchDataInit(email) { + return email; +} + +// Fetching member's newsletter preferences +async function fetchDataDone(emailHash, listId = config.NEWSLETTER_SIGNUP.DEFAUL_LIST_ID) { + /* NOTE: In the real life in most cases you don't want to use fetch() directly + * in an action. You want to create a service for your calls and use it here. + * However, in this example, to keep it a bit more compact, we use fetch() + * directly here. */ + try { + let error = false; + const subs = await fetch(`${PROXY_ENDPOINT}/${listId}/members/${emailHash}`, { + method: 'GET', + headers: { + 'Content-Type': 'application/json', + }, + }) + .then((result) => { + if (result.status !== 200) error = true; + return result.json(); + }); + + return { + email: emailHash, + preferences: subs.tags, + error, + }; + } catch (error) { + return { + email: emailHash, + error, + }; + } +} + +// Updates member newsletter subscription +async function updateSubscriptionsDone( + emailHash, tagId, status, listId = config.NEWSLETTER_SIGNUP.DEFAUL_LIST_ID, +) { + /* NOTE: In the real life in most cases you don't want to use fetch() directly + * in an action. You want to create a service for your calls and use it here. + * However, in this example, to keep it a bit more compact, we use fetch() + * directly here. */ + try { + let error = false; + const fetchUrl = `${PROXY_ENDPOINT}/${listId}/members/${emailHash}/tags`; + + const data = { + tags: [ + { name: tagId, status: status ? 'active' : 'inactive' }, + ], + }; + + const formData = JSON.stringify(data); + // use proxy for avoid 'Access-Control-Allow-Origin' bug + await fetch(fetchUrl, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: formData, + }) + .then((result) => { + if (!result.ok) error = true; + return result.json(); + }); + + return { + id: tagId, + checked: status, + email: emailHash, + error, + }; + } catch (error) { + return { + id: tagId, + checked: status, + email: emailHash, + error, + }; + } +} + +export default createActions({ + NEWSLETTER_PREFERENCES: { + FETCH_DATA_INIT: fetchDataInit, + FETCH_DATA_DONE: fetchDataDone, + UPDATE_TAG_INIT: _.identity, + UPDATE_TAG_DONE: updateSubscriptionsDone, + }, +}); diff --git a/src/shared/components/NewsletterSignupForMembers/ConfirmModal/index.jsx b/src/shared/components/NewsletterSignupForMembers/ConfirmModal/index.jsx index 1329dfcb53..68c49a979e 100644 --- a/src/shared/components/NewsletterSignupForMembers/ConfirmModal/index.jsx +++ b/src/shared/components/NewsletterSignupForMembers/ConfirmModal/index.jsx @@ -34,13 +34,15 @@ function ConfirmModal({ skipConfirmSignup, token, theme, + title, + desc, }) { let text; if (token) { text = (
    -

    Sign up for the Topcoder Newsletter

    -

    Do you want to subscribe to this newsletter?

    +

    {title}

    +

    {desc}

    ); if (skipConfirmSignup) { @@ -49,7 +51,7 @@ function ConfirmModal({ } else { text = customTcAuthModalText || (
    -

    Sign up for the Topcoder Newsletter

    +

    {title}

    You must be a Topcoder member before you can signup for Newsletter. To signup, login if you are already a member. If not, register first. @@ -84,7 +86,7 @@ function ConfirmModal({ button: buttonThemes.tc['primary-green-md'], }} > - SIGNUP + Ok

    ) : ( @@ -130,6 +132,8 @@ ConfirmModal.propTypes = { skipConfirmSignup: PT.bool.isRequired, theme: PT.shape().isRequired, token: PT.string, + title: PT.string.isRequired, + desc: PT.string.isRequired, }; export default themr('NewsletterSignupForMembers-Modal', defaultStyle)(ConfirmModal); diff --git a/src/shared/components/NewsletterSignupForMembers/index.jsx b/src/shared/components/NewsletterSignupForMembers/index.jsx index 8ea8712c0b..085f45d634 100644 --- a/src/shared/components/NewsletterSignupForMembers/index.jsx +++ b/src/shared/components/NewsletterSignupForMembers/index.jsx @@ -46,37 +46,44 @@ export default function NewsletterSignupForMembers({ state, theme, token, + buttonTheme, + title, + desc, }) { return (
    - { - switch (state) { - case STATE.SIGNEDUP: - case STATE.SIGNING: - return; - default: - } - showSignupConfirmModal(); - }} - className={state === STATE.SIGNING ? style.signing : ''} - theme={{ - button: buttonThemes.tc['primary-green-md'], - disabled: buttonThemes.tc.themedButtonDisabled, - }} - > - {state === STATE.SIGNING ? ( -
    - - Signing... - - -
    - ) : label} -
    + { + state !== STATE.HIDDEN ? ( + { + switch (state) { + case STATE.SIGNEDUP: + case STATE.SIGNING: + return; + default: + } + showSignupConfirmModal(); + }} + className={state === STATE.SIGNING ? style.signing : ''} + theme={{ + button: buttonThemes.tc[buttonTheme], + disabled: buttonThemes.tc.themedButtonDisabled, + }} + > + {state === STATE.SIGNING ? ( +
    + + Signing... + + +
    + ) : label} +
    + ) : null + } {state === STATE.SIGNEDUP ? (

    Congratulations!

    -

    +

    { customSignupConfirmationText - || 'You are subscribed to the newsletter.' + || 'You are now subscribed.' }

    @@ -110,6 +117,8 @@ export default function NewsletterSignupForMembers({ resetSignupButton={resetSignupButton} skipConfirmSignup={skipConfirmSignup} token={token} + title={title} + desc={desc} /> ) : null} {state === STATE.ERROR ? ( @@ -164,4 +173,7 @@ NewsletterSignupForMembers.propTypes = { state: PT.oneOf(_.values(STATE)).isRequired, theme: PT.shape(), token: PT.string, + buttonTheme: PT.string.isRequired, + title: PT.string.isRequired, + desc: PT.string.isRequired, }; diff --git a/src/shared/components/Settings/Preferences/Email/index.jsx b/src/shared/components/Settings/Preferences/Email/index.jsx index b8eae9fa7b..cea54fb30c 100644 --- a/src/shared/components/Settings/Preferences/Email/index.jsx +++ b/src/shared/components/Settings/Preferences/Email/index.jsx @@ -1,95 +1,141 @@ /** * Email Preferences component. */ -import { debounce, isEqual } from 'lodash'; +import { debounce, map } from 'lodash'; import React from 'react'; import PT from 'prop-types'; +import { toastr } from 'react-redux-toastr'; -import ConsentComponent from 'components/Settings/ConsentComponent'; +import ToggleableItem from 'components/Settings/ToggleableItem'; import './styles.scss'; +function toastrSuccess(title, message) { + setImmediate(() => { + toastr.success(title, message); + }); +} + +function toastrError(title, message) { + setImmediate(() => { + toastr.error(title, message); + }); +} + const SAVE_DELAY = 1000; -export default class EmailPreferences extends ConsentComponent { - saveEmailPreferences = debounce(() => { - const { - profile, - saveEmailPreferences, - tokenV3, - } = this.props; - const { emailPreferences } = this.state; +const newsletters = [ + { + id: 'Pipeline', + name: 'Challenge Pipeline', + desc: 'Subscribe to this newsletter if you want to get updates on the types of challenges coming up in the future. To view these challenges at your leisure you can always visit the Challenge Pipeline page.', + }, + { + id: 'Gig Work', + name: 'Gig Work', + desc: 'This newsletter gets sent out at various times, specifically when we have an opportunity of mass appeal. For more information you can visit the Gig Work page.', + }, + { + id: 'Monthly Newsletter', + name: 'Monthly Newsletter', + desc: 'This newsletter gets sent out at the end of every month and contains a variety of important information across all of our tracks.', + }, + { + id: 'Marathon Match Reminders', + name: 'Marathon Match Reminders', + desc: 'Receive updates whenever a new marathon match is scheduled.', + }, + { + id: 'Single Round Match Reminders', + name: 'Single Round Match (SRM) Reminders', + desc: 'Attention Competitive Programmers! If there is any newsletter you are subscribing too, it better be this one. Receive updates when a new SRM event is scheduled.', + }, + { + id: 'TCO Tuesdays', + name: 'TCO Newsletter', + desc: 'For all the latest updates surrounding the Topcoder Open you should definitely be subscribing to this one. Expect an update in your mailbox every Tuesday!', + }, +]; - saveEmailPreferences( - profile, - tokenV3, - emailPreferences, - ); +export default class EmailPreferences extends React.Component { + saveEmailPreferences = debounce((id, checked) => { + const { email, saveEmailPreferences } = this.props; + saveEmailPreferences(email, id, checked); }, SAVE_DELAY); constructor(props) { super(props); this.state = { - emailPreferences: {}, + emailPreferences: { ...props.preferences }, }; - this.onHandleChange = this.onHandleChange.bind(this); this.onChange = this.onChange.bind(this); - this.populate = this.populate.bind(this); - } - - componentDidMount() { - const { profileState: { emailPreferences } } = this.props; - if (emailPreferences) this.populate(emailPreferences); } - componentWillReceiveProps(nextProps) { - const { profileState: { emailPreferences } } = nextProps; - if (emailPreferences && !isEqual(this.state.emailPreferences, emailPreferences)) { - this.populate(emailPreferences); - } - } + componentDidUpdate(prevProps) { + const { updated } = this.props; + if (updated && updated !== prevProps.updated) { + if (updated.error) { + toastrError('Error! ', 'Failed to update Your email preferences :-('); + } + const { emailPreferences } = this.state; + const { id, checked } = updated; + if (!emailPreferences[id]) emailPreferences[id] = { id, checked }; + else emailPreferences[id].checked = checked; - shouldComponentUpdate(nextProps) { - const { profileState: { emailPreferences } } = nextProps; - if (emailPreferences && this.props.profileState.emailPreferences !== emailPreferences) { - return true; + // eslint-disable-next-line react/no-did-update-set-state + this.setState({ + emailPreferences, + }); + toastrSuccess('Success! ', 'Your email preferences were updated.'); } - return false; - } - - onHandleChange(id, checked) { - this.showConsent(this.onChange.bind(this, id, checked)); } onChange(id, checked) { document.querySelectorAll(`#pre-onoffswitch-${id}`).forEach((el) => { el.checked = checked; }); // eslint-disable-line no-param-reassign - const { emailPreferences } = this.state; - emailPreferences[id] = checked; - this.setState({ - emailPreferences, - }, () => this.saveEmailPreferences()); - } - - populate(data) { - this.setState({ - emailPreferences: { ...data }, - }); + this.saveEmailPreferences(id, checked); } render() { + const { emailPreferences } = this.state; return (

    E-Mail Preferences

    +
    + Your preferences +
    +
    + { + map(newsletters, (newsletter) => { + const checked = emailPreferences[newsletter.id] + ? emailPreferences[newsletter.id].checked : false; + return ( + this.onChange(newsletter.id, e.target.checked)} + /> + ); + }) + } +
    ); } } +EmailPreferences.defaultProps = { + updated: null, +}; + EmailPreferences.propTypes = { - tokenV3: PT.string.isRequired, - profile: PT.shape().isRequired, - profileState: PT.shape().isRequired, + email: PT.string.isRequired, + preferences: PT.shape().isRequired, saveEmailPreferences: PT.func.isRequired, + updated: PT.shape(), }; diff --git a/src/shared/components/Settings/Preferences/index.jsx b/src/shared/components/Settings/Preferences/index.jsx index 18ee9f971f..5db18e1be9 100644 --- a/src/shared/components/Settings/Preferences/index.jsx +++ b/src/shared/components/Settings/Preferences/index.jsx @@ -15,7 +15,7 @@ import Forum from 'assets/images/preferences/forum.svg'; import Payment from 'assets/images/preferences/payment.svg'; import SideBar from 'components/Settings/SideBar'; import ErrorWrapper from 'components/Settings/ErrorWrapper'; -import Email from './Email'; +import NewsletterPreferencesContainer from 'containers/NewsletterPreferences'; import { SCREEN_SIZE } from '../constants'; import './styles.scss'; @@ -83,9 +83,10 @@ export default class Preferences extends React.Component { } renderTabContent(tab) { + const { profile: { email } } = this.props; switch (tab) { case 'e-mail': - return ; + return ; case 'forum': return (window.location.href = `${config.URL.FORUMS}/?module=Settings`) && ; case 'payment': @@ -140,4 +141,5 @@ export default class Preferences extends React.Component { Preferences.propTypes = { clearToastrNotification: PT.func.isRequired, + profile: PT.shape().isRequired, }; diff --git a/src/shared/components/Settings/ToggleableItem/index.jsx b/src/shared/components/Settings/ToggleableItem/index.jsx index 1728b62d44..bbfe110863 100644 --- a/src/shared/components/Settings/ToggleableItem/index.jsx +++ b/src/shared/components/Settings/ToggleableItem/index.jsx @@ -7,6 +7,7 @@ */ import React from 'react'; import PT from 'prop-types'; +import ReactHtmlParser from 'react-html-parser'; import './styles.scss'; @@ -25,7 +26,7 @@ export default function ToggleableItem({ {primaryText}

    - {secondaryText} + {ReactHtmlParser(secondaryText)}

    diff --git a/src/shared/components/TopcoderFooter/index.jsx b/src/shared/components/TopcoderFooter/index.jsx index e079531f08..6f89707c7c 100644 --- a/src/shared/components/TopcoderFooter/index.jsx +++ b/src/shared/components/TopcoderFooter/index.jsx @@ -63,11 +63,12 @@ export default function TopcoderFooter() {

    COMMUNITY

      - TCO - Programs + Blog + Events Calendar Forums + Programs Statistics - Blog + TCO Thrive
    diff --git a/src/shared/containers/NewsletterPreferences.jsx b/src/shared/containers/NewsletterPreferences.jsx new file mode 100644 index 0000000000..acde23fc44 --- /dev/null +++ b/src/shared/containers/NewsletterPreferences.jsx @@ -0,0 +1,88 @@ + +import React from 'react'; +import PT from 'prop-types'; +import { connect } from 'react-redux'; +import actions from 'actions/newsletterPreferences'; +import LoadingIndicator from 'components/LoadingIndicator'; +import Email from 'components/Settings/Preferences/Email'; + +class NewsletterPreferencesContainer extends React.Component { + componentDidMount() { + const { + loading, + email, + getNewsletterPreferencesDone, + } = this.props; + if (!loading) { + getNewsletterPreferencesDone(email); + } + } + + render() { + const { + loading, error, preferences, saveEmailPreferences, email, updated, + } = this.props; + if (loading || !preferences) return ; + if (error) { + return Error loading Newsletter Preferences :-( {error.message}; + } + return ( + + ); + } +} + +NewsletterPreferencesContainer.defaultProps = { + loading: false, + error: null, + preferences: null, + updated: null, +}; + +NewsletterPreferencesContainer.propTypes = { + loading: PT.bool, + getNewsletterPreferencesDone: PT.func.isRequired, + saveEmailPreferences: PT.func.isRequired, + error: PT.bool, + preferences: PT.shape(), + email: PT.string.isRequired, + updated: PT.shape(), +}; + +function mapStateToProps(state) { + const { newsletterPreferences } = state; + if (newsletterPreferences) { + return { + loading: newsletterPreferences.loading, + error: newsletterPreferences.error, + preferences: newsletterPreferences.preferences, + updated: newsletterPreferences.updated, + }; + } + + return state; +} + +function mapDispatchToProps(dispatch) { + return { + getNewsletterPreferencesDone: (email) => { + dispatch(actions.newsletterPreferences.fetchDataDone(email)); + }, + saveEmailPreferences: (email, id, checked) => { + dispatch(actions.newsletterPreferences.updateTagInit()); + dispatch(actions.newsletterPreferences.updateTagDone(email, id, checked)); + }, + }; +} + +const Container = connect( + mapStateToProps, + mapDispatchToProps, +)(NewsletterPreferencesContainer); + +export default Container; diff --git a/src/shared/containers/NewsletterSignupForMembers.jsx b/src/shared/containers/NewsletterSignupForMembers.jsx index f656caa187..922eca698d 100644 --- a/src/shared/containers/NewsletterSignupForMembers.jsx +++ b/src/shared/containers/NewsletterSignupForMembers.jsx @@ -185,6 +185,9 @@ NewsletterSignupForMembersContainer.defaultProps = { label: 'Subscribe for Newsletter', tags: '', user: null, + buttonTheme: 'primary-green-md', + title: 'Sign up for the Topcoder Newsletter', + desc: 'Do you want to subscribe to this newsletter?', }; NewsletterSignupForMembersContainer.propTypes = { @@ -194,6 +197,9 @@ NewsletterSignupForMembersContainer.propTypes = { tags: PT.string, listId: PT.string.isRequired, user: PT.shape(), + buttonTheme: PT.string, + title: PT.string, + desc: PT.string, }; function mapStateToProps(state, ownProps) { diff --git a/src/shared/containers/Settings.jsx b/src/shared/containers/Settings.jsx index 75731d4247..40009cef92 100644 --- a/src/shared/containers/Settings.jsx +++ b/src/shared/containers/Settings.jsx @@ -103,7 +103,6 @@ SettingsContainer.propTypes = { deleteWebLink: PT.func.isRequired, linkExternalAccount: PT.func.isRequired, unlinkExternalAccount: PT.func.isRequired, - saveEmailPreferences: PT.func.isRequired, updatePassword: PT.func.isRequired, settingsTab: PT.string, authenticating: PT.bool.isRequired, @@ -155,7 +154,8 @@ function mapDispatchToProps(dispatch) { dispatch(lookupActions.getCountriesInit()); dispatch(lookupActions.getCountriesDone()); } else if (settingsTab === TABS.PREFERENCES) { - dispatch(profileActions.getEmailPreferencesDone(profile, tokenV3)); + // Deprecated. Leaving it here as reminder to update topcoder-react-lib as well + // dispatch(profileActions.getEmailPreferencesDone(profile, tokenV3)); } else if (settingsTab === TABS.ACCOUNT) { dispatch(profileActions.getLinkedAccountsDone(profile, tokenV3)); dispatch(profileActions.getExternalLinksDone(handle)); @@ -232,9 +232,6 @@ function mapDispatchToProps(dispatch) { dispatch(profileActions.unlinkExternalAccountInit({ providerType })); dispatch(profileActions.unlinkExternalAccountDone(profile, tokenV3, providerType)); }, - saveEmailPreferences: (profile, tokenV3, preferences) => { - dispatch(profileActions.saveEmailPreferencesDone(profile, tokenV3, preferences)); - }, updatePassword: (profile, tokenV3, newPassword, oldPassword) => { dispatch(profileActions.updatePasswordInit()); dispatch(profileActions.updatePasswordDone(profile, tokenV3, newPassword, oldPassword)); diff --git a/src/shared/reducers/index.js b/src/shared/reducers/index.js index 6b759c0c7c..8e417d6602 100644 --- a/src/shared/reducers/index.js +++ b/src/shared/reducers/index.js @@ -34,6 +34,7 @@ import { factory as tcCommunitiesFactory } from './tc-communities'; import { factory as leaderboardFactory } from './leaderboard'; import { factory as scoreboardFactory } from './tco/scoreboard'; import { factory as termsFactory } from './terms'; +import newsletterPreferences from './newsletterPreferences'; /** * Given HTTP request, generates options for SSR by topcoder-react-lib's reducer @@ -136,6 +137,7 @@ export function factory(req) { newsletterArchive, menuNavigation, challengesBlock, + newsletterPreferences, })); } diff --git a/src/shared/reducers/newsletterPreferences.js b/src/shared/reducers/newsletterPreferences.js new file mode 100644 index 0000000000..f1652090f2 --- /dev/null +++ b/src/shared/reducers/newsletterPreferences.js @@ -0,0 +1,74 @@ +/** + * Reducer for state.newsletterPreferences + */ + +import actions from 'actions/newsletterPreferences'; +import { handleActions } from 'redux-actions'; + +/** + * Handles newsletterPreferences.fetchDataInit action. + * @param {Object} state Previous state. + * @param {Object} name The action. + */ +function onInit(state, { payload }) { + return { + ...state, + email: payload, + preferences: [], + loading: true, + }; +} + +/** + * Handles newsletterPreferences.fetchDataDone action. + * @param {Object} state Previous state. + * @param {Object} action The action. + */ +function onDone(state, { payload }) { + const preferences = {}; + if (payload.preferences) { + payload.preferences.forEach((record) => { + preferences[record.name] = { ...record, checked: true }; + }); + } + return { + ...state, + preferences: payload.error ? null : preferences, + error: payload.error, + loading: false, + }; +} + +function onUpdateTagInit(state) { + return { + ...state, + updated: null, + }; +} + +function onUpdateTagDone(state, { payload }) { + // eslint-disable-next-line no-param-reassign + state.preferences[payload.id] = { name: payload.id, checked: payload.checked }; + return { + ...state, + updated: payload, + }; +} + +/** + * Creates newsletterPreferences reducer with the specified initial state. + * @param {Object} state Optional. If not given, the default one is + * generated automatically. + * @return {Function} Reducer. + */ +function create(state = {}) { + return handleActions({ + [actions.newsletterPreferences.fetchDataInit]: onInit, + [actions.newsletterPreferences.fetchDataDone]: onDone, + [actions.newsletterPreferences.updateTagInit]: onUpdateTagInit, + [actions.newsletterPreferences.updateTagDone]: onUpdateTagDone, + }, state); +} + +/* Reducer with the default initial state. */ +export default create();