diff --git a/client/src/components/Donation/DonationModal.js b/client/src/components/Donation/DonationModal.js index e43c810fec..17f675708d 100644 --- a/client/src/components/Donation/DonationModal.js +++ b/client/src/components/Donation/DonationModal.js @@ -6,7 +6,6 @@ import { connect } from 'react-redux'; import { createSelector } from 'reselect'; import { Modal, Button, Col, Row } from '@freecodecamp/react-bootstrap'; import { Spacer } from '../helpers'; -import { blockNameify } from '../../../../utils/block-nameify'; import Heart from '../../assets/icons/Heart'; import Cup from '../../assets/icons/Cup'; import DonateForm from './DonateForm'; @@ -16,22 +15,18 @@ import { useTranslation } from 'react-i18next'; import { closeDonationModal, isDonationModalOpenSelector, - isBlockDonationModalSelector, + recentlyClaimedBlockSelector, executeGA } from '../../redux'; -import { challengeMetaSelector } from '../../templates/Challenges/redux'; - import './Donation.css'; const mapStateToProps = createSelector( isDonationModalOpenSelector, - challengeMetaSelector, - isBlockDonationModalSelector, - (show, { block }, isBlockDonation) => ({ + recentlyClaimedBlockSelector, + (show, recentlyClaimedBlock) => ({ show, - block, - isBlockDonation + recentlyClaimedBlock }) ); @@ -46,19 +41,17 @@ const mapDispatchToProps = dispatch => const propTypes = { activeDonors: PropTypes.number, - block: PropTypes.string, closeDonationModal: PropTypes.func.isRequired, executeGA: PropTypes.func, - isBlockDonation: PropTypes.bool, + recentlyClaimedBlock: PropTypes.string, show: PropTypes.bool }; function DonateModal({ show, - block, - isBlockDonation, closeDonationModal, - executeGA + executeGA, + recentlyClaimedBlock }) { const [closeLabel, setCloseLabel] = React.useState(false); const { t } = useTranslation(); @@ -87,13 +80,13 @@ function DonateModal({ data: { category: 'Donation View', action: `Displayed ${ - isBlockDonation ? 'block' : 'progress' + recentlyClaimedBlock ? 'block' : 'progress' } donation modal`, nonInteraction: true } }); } - }, [show, isBlockDonation, executeGA]); + }, [show, recentlyClaimedBlock, executeGA]); const getDonationText = () => { const donationDuration = modalDefaultDonation.donationDuration; @@ -117,7 +110,7 @@ function DonateModal({ {!closeLabel && ( - {t('donate.nicely-done', { block: blockNameify(block) })} + {t('donate.nicely-done', { block: recentlyClaimedBlock })}
{getDonationText()} @@ -144,7 +137,7 @@ function DonateModal({ return ( - {isBlockDonation ? blockDonationText : progressDonationText} + {recentlyClaimedBlock ? blockDonationText : progressDonationText} diff --git a/client/src/redux/donation-saga.js b/client/src/redux/donation-saga.js index 4d3fa981e4..e09a7634e0 100644 --- a/client/src/redux/donation-saga.js +++ b/client/src/redux/donation-saga.js @@ -4,7 +4,8 @@ import { takeEvery, takeLeading, delay, - call + call, + take } from 'redux-saga/effects'; import { @@ -12,11 +13,12 @@ import { preventBlockDonationRequests, shouldRequestDonationSelector, preventProgressDonationRequests, - canRequestBlockDonationSelector, + recentlyClaimedBlockSelector, addDonationComplete, addDonationError, postChargeStripeComplete, - postChargeStripeError + postChargeStripeError, + types as appTypes } from './'; import { addDonation, postChargeStripe } from '../utils/ajax'; @@ -27,9 +29,10 @@ function* showDonateModalSaga() { let shouldRequestDonation = yield select(shouldRequestDonationSelector); if (shouldRequestDonation) { yield delay(200); - const isBlockDonation = yield select(canRequestBlockDonationSelector); - yield put(openDonationModal(isBlockDonation)); - if (isBlockDonation) { + const recentlyClaimedBlock = yield select(recentlyClaimedBlockSelector); + yield put(openDonationModal()); + yield take(appTypes.closeDonationModal); + if (recentlyClaimedBlock) { yield put(preventBlockDonationRequests()); } else { yield put(preventProgressDonationRequests()); diff --git a/client/src/redux/index.js b/client/src/redux/index.js index 189cbf54a7..f77a2f2426 100644 --- a/client/src/redux/index.js +++ b/client/src/redux/index.js @@ -38,7 +38,7 @@ export const defaultDonationFormState = { const initialState = { appUsername: '', - canRequestBlockDonation: false, + recentlyClaimedBlock: null, canRequestProgressDonation: true, completionCount: 0, currentChallengeId: store.get(CURRENT_CHALLENGE_KEY), @@ -55,7 +55,6 @@ const initialState = { }, sessionMeta: { activeDonations: 0 }, showDonationModal: false, - isBlockDonationModal: false, isOnline: true, donationFormState: { ...defaultDonationFormState @@ -187,10 +186,8 @@ export const isDonatingSelector = state => userSelector(state).isDonating; export const isOnlineSelector = state => state[ns].isOnline; export const isSignedInSelector = state => !!state[ns].appUsername; export const isDonationModalOpenSelector = state => state[ns].showDonationModal; -export const canRequestBlockDonationSelector = state => - state[ns].canRequestBlockDonation; -export const isBlockDonationModalSelector = state => - state[ns].isBlockDonationModal; +export const recentlyClaimedBlockSelector = state => + state[ns].recentlyClaimedBlock; export const donationFormStateSelector = state => state[ns].donationFormState; export const signInLoadingSelector = state => userFetchStateSelector(state).pending; @@ -201,13 +198,13 @@ export const shouldRequestDonationSelector = state => { const completionCount = completionCountSelector(state); const canRequestProgressDonation = state[ns].canRequestProgressDonation; const isDonating = isDonatingSelector(state); - const canRequestBlockDonation = canRequestBlockDonationSelector(state); + const recentlyClaimedBlock = recentlyClaimedBlockSelector(state); // don't request donation if already donating if (isDonating) return false; // a block has been completed - if (canRequestBlockDonation) return true; + if (recentlyClaimedBlock) return true; // a donation has already been requested if (!canRequestProgressDonation) return false; @@ -393,10 +390,12 @@ export const reducer = handleActions( } }; }, - [types.allowBlockDonationRequests]: state => ({ - ...state, - canRequestBlockDonation: true - }), + [types.allowBlockDonationRequests]: (state, { payload }) => { + return { + ...state, + recentlyClaimedBlock: payload + }; + }, [types.updateDonationFormState]: (state, { payload }) => ({ ...state, donationFormState: { ...state.donationFormState, ...payload } @@ -522,14 +521,13 @@ export const reducer = handleActions( ...state, showDonationModal: false }), - [types.openDonationModal]: (state, { payload }) => ({ + [types.openDonationModal]: state => ({ ...state, - showDonationModal: true, - isBlockDonationModal: payload + showDonationModal: true }), [types.preventBlockDonationRequests]: state => ({ ...state, - canRequestBlockDonation: false + recentlyClaimedBlock: null }), [types.preventProgressDonationRequests]: state => ({ ...state, diff --git a/client/src/templates/Challenges/components/CompletionModal.js b/client/src/templates/Challenges/components/CompletionModal.js index fbcca04cda..717f93b1f0 100644 --- a/client/src/templates/Challenges/components/CompletionModal.js +++ b/client/src/templates/Challenges/components/CompletionModal.js @@ -20,11 +20,14 @@ import { isCompletionModalOpenSelector, successMessageSelector, challengeFilesSelector, - challengeMetaSelector, - lastBlockChalSubmitted + challengeMetaSelector } from '../redux'; -import { isSignedInSelector, executeGA } from '../../../redux'; +import { + isSignedInSelector, + executeGA, + allowBlockDonationRequests +} from '../../../redux'; const mapStateToProps = createSelector( challengeFilesSelector, @@ -57,8 +60,8 @@ const mapDispatchToProps = function(dispatch) { submitChallenge: () => { dispatch(submitChallenge()); }, - lastBlockChalSubmitted: () => { - dispatch(lastBlockChalSubmitted()); + allowBlockDonationRequests: block => { + dispatch(allowBlockDonationRequests(block)); }, executeGA }; @@ -66,6 +69,7 @@ const mapDispatchToProps = function(dispatch) { }; const propTypes = { + allowBlockDonationRequests: PropTypes.func, blockName: PropTypes.string, close: PropTypes.func.isRequired, completedChallengesIds: PropTypes.array, @@ -75,7 +79,6 @@ const propTypes = { id: PropTypes.string, isOpen: PropTypes.bool, isSignedIn: PropTypes.bool.isRequired, - lastBlockChalSubmitted: PropTypes.func, message: PropTypes.string, submitChallenge: PropTypes.func.isRequired, t: PropTypes.func.isRequired, @@ -168,7 +171,7 @@ export class CompletionModalInner extends Component { this.state.completedPercent === 100 && !this.props.completedChallengesIds.includes(this.props.id) ) { - this.props.lastBlockChalSubmitted(); + this.props.allowBlockDonationRequests(this.props.blockName); } } diff --git a/client/src/templates/Challenges/redux/current-challenge-saga.js b/client/src/templates/Challenges/redux/current-challenge-saga.js index 7c314136f8..603f548bed 100644 --- a/client/src/templates/Challenges/redux/current-challenge-saga.js +++ b/client/src/templates/Challenges/redux/current-challenge-saga.js @@ -4,8 +4,7 @@ import store from 'store'; import { isSignedInSelector, updateComplete, - updateFailed, - allowBlockDonationRequests + updateFailed } from '../../../redux'; import { post } from '../../../utils/ajax'; @@ -38,14 +37,9 @@ export function* updateSuccessMessageSaga() { yield put(updateSuccessMessage(randomCompliment())); } -export function* allowBlockDonationRequestsSaga() { - yield put(allowBlockDonationRequests()); -} - export function createCurrentChallengeSaga(types) { return [ takeEvery(types.challengeMounted, currentChallengeSaga), - takeEvery(types.challengeMounted, updateSuccessMessageSaga), - takeEvery(types.lastBlockChalSubmitted, allowBlockDonationRequestsSaga) + takeEvery(types.challengeMounted, updateSuccessMessageSaga) ]; } diff --git a/client/src/templates/Challenges/redux/current-challenge-saga.test.js b/client/src/templates/Challenges/redux/current-challenge-saga.test.js deleted file mode 100644 index 5431b08a5e..0000000000 --- a/client/src/templates/Challenges/redux/current-challenge-saga.test.js +++ /dev/null @@ -1,12 +0,0 @@ -/* global expect */ -import { allowBlockDonationRequestsSaga } from './current-challenge-saga'; -import { types as appTypes } from '../../../redux'; - -describe('allowBlockDonationRequestsSaga', () => { - it('should call allowBlockDonationRequests', () => { - const gen = allowBlockDonationRequestsSaga(); - expect(gen.next().value.payload.action.type).toEqual( - appTypes.allowBlockDonationRequests - ); - }); -}); diff --git a/client/src/templates/Challenges/redux/index.js b/client/src/templates/Challenges/redux/index.js index 129f639027..cd9e664b94 100644 --- a/client/src/templates/Challenges/redux/index.js +++ b/client/src/templates/Challenges/redux/index.js @@ -88,9 +88,7 @@ export const types = createTypes( 'setEditorFocusability', 'toggleVisibleEditor', - 'setAccessibilityMode', - - 'lastBlockChalSubmitted' + 'setAccessibilityMode' ], ns ); @@ -171,10 +169,6 @@ export const setEditorFocusability = createAction(types.setEditorFocusability); export const toggleVisibleEditor = createAction(types.toggleVisibleEditor); export const setAccessibilityMode = createAction(types.setAccessibilityMode); -export const lastBlockChalSubmitted = createAction( - types.lastBlockChalSubmitted -); - export const currentTabSelector = state => state[ns].currentTab; export const challengeFilesSelector = state => state[ns].challengeFiles; export const challengeMetaSelector = state => state[ns].challengeMeta; diff --git a/cypress/integration/learn/donate/donation-block-completion-modal.js b/cypress/integration/learn/donate/donation-block-completion-modal.js new file mode 100644 index 0000000000..5640c86379 --- /dev/null +++ b/cypress/integration/learn/donate/donation-block-completion-modal.js @@ -0,0 +1,45 @@ +/* global cy */ + +describe('Donate page', () => { + before(() => { + cy.clearCookies(); + cy.exec('npm run seed'); + cy.login(); + }); + + after(() => { + cy.exec('npm run seed'); + }); + + const projects = [ + 'tribute-page', + 'survey-form', + 'product-landing-page', + 'technical-documentation-page', + 'personal-portfolio-webpage' + ]; + + it('Should be able to submit projects', () => { + const submitProject = str => { + cy.visit( + `/learn/responsive-web-design/responsive-web-design-projects/build-a-${str}` + ); + cy.get('#dynamic-front-end-form') + .get('#solution') + .type('https://codepen.io/camperbot/full/oNvPqqo', { + force: true + }); + + cy.contains("I've completed this challenge").click(); + cy.contains('Submit and go to next challenge').click(); + }; + + projects.forEach(project => submitProject(project)); + }); + + it('Should have a pop up modal', () => { + cy.contains( + 'Nicely done. You just completed Responsive Web Design Projects.' + ); + }); +});