From 6a163bae8890cce490ebce5fb2b51e27907a5c17 Mon Sep 17 00:00:00 2001 From: Stuart Taylor Date: Wed, 28 Feb 2018 10:06:50 +0000 Subject: [PATCH] fix(projectCopmletion): Open completion modal for Project submission --- common/app/reducer.js | 8 +--- common/app/redux-form-reducer.js | 37 +++++++++++++++++++ .../Challenges/redux/completion-epic.js | 33 +++++++++++++---- common/app/routes/Challenges/redux/index.js | 10 +++++ .../routes/Challenges/views/project/Forms.jsx | 25 ++++--------- .../Challenges/views/project/Tool-Panel.jsx | 18 ++++----- 6 files changed, 91 insertions(+), 40 deletions(-) create mode 100644 common/app/redux-form-reducer.js diff --git a/common/app/reducer.js b/common/app/reducer.js index 7c5d882ce7..6a8fd5d101 100644 --- a/common/app/reducer.js +++ b/common/app/reducer.js @@ -1,19 +1,15 @@ import { combineReducers } from 'berkeleys-redux-utils'; -import { reducer as formReducer } from 'redux-form'; import app from './redux'; import entities from './entities'; +import form from './redux-form-reducer'; import map from './Map/redux'; import nav from './Nav/redux'; import routes from './routes/redux'; import toasts from './Toasts/redux'; import files from './files'; import flash from './Flash/redux'; -// not ideal but should go away once we move to react-redux-form -import { projectNormalizer } from './routes/Challenges/redux'; -const _formReducer = formReducer.normalize({ ...projectNormalizer }); -_formReducer.toString = () => 'form'; export default combineReducers( app, @@ -24,5 +20,5 @@ export default combineReducers( toasts, files, flash, - _formReducer + form ); diff --git a/common/app/redux-form-reducer.js b/common/app/redux-form-reducer.js new file mode 100644 index 0000000000..18a037fdb1 --- /dev/null +++ b/common/app/redux-form-reducer.js @@ -0,0 +1,37 @@ +import { composeReducers } from 'berkeleys-redux-utils'; +import { reducer as formReducer } from 'redux-form'; + +import { + projectNormalizer, + types as challenge +} from './routes/Challenges/redux'; + +const normailizedFormReducer = formReducer.normalize({ ...projectNormalizer }); + +const pluggedInFormReducer = formReducer.plugin({ + NewFrontEndProject: (state, action) => { + if (action.type === challenge.moveToNextChallenge) { + return { + ...state, + solution: {} + }; + } + return state; + }, + NewBackEndProject: (state, action) => { + if (action.type === challenge.moveToNextChallenge) { + return { + ...state, + solution: {}, + githubLink: {} + }; + } + return state; + } +}); + +export default composeReducers( + 'form', + normailizedFormReducer, + pluggedInFormReducer +); diff --git a/common/app/routes/Challenges/redux/completion-epic.js b/common/app/routes/Challenges/redux/completion-epic.js index d8592ce4ed..6acc46950a 100644 --- a/common/app/routes/Challenges/redux/completion-epic.js +++ b/common/app/routes/Challenges/redux/completion-epic.js @@ -3,11 +3,14 @@ import { ofType } from 'redux-epic'; import { backendFormValuesSelector, + frontendProjectFormValuesSelector, + backendProjectFormValuesSelector, challengeMetaSelector, moveToNextChallenge, submitChallengeComplete, testsSelector, - types + types, + closeChallengeModal } from './'; import { @@ -69,7 +72,18 @@ function submitModern(type, state) { ); } -function submitProject(type, state, { solution, githubLink }) { +function submitProject(type, state) { + if (type === types.checkChallenge) { + return Observable.empty(); + } + const { + solution: frontEndSolution = '' + } = frontendProjectFormValuesSelector(state); + const { + solution: backendSolution = '', + githubLink = '' + } = backendProjectFormValuesSelector(state); + const solution = frontEndSolution ? frontEndSolution : backendSolution; const { id, challengeType } = challengeSelector(state); const { username } = userSelector(state); const csrfToken = csrfSelector(state); @@ -77,11 +91,16 @@ function submitProject(type, state, { solution, githubLink }) { if (challengeType === backEndProject) { challengeInfo.githubLink = githubLink; } - return postChallenge( - '/project-completed', - username, - csrfToken, - challengeInfo + return Observable.merge( + postChallenge( + '/project-completed', + username, + csrfToken, + challengeInfo + ), + Observable.of( + closeChallengeModal() + ) ); } diff --git a/common/app/routes/Challenges/redux/index.js b/common/app/routes/Challenges/redux/index.js index 86593feea9..b7c3d14135 100644 --- a/common/app/routes/Challenges/redux/index.js +++ b/common/app/routes/Challenges/redux/index.js @@ -65,6 +65,7 @@ export const types = createTypes([ 'updateHint', 'unlockUntrustedCode', 'closeChallengeModal', + 'openChallengeModal', 'updateSuccessMessage', // |- modern 'modernEditorUpdated', @@ -119,6 +120,7 @@ export const modernEditorUpdated = createAction( ); // challenges export const closeChallengeModal = createAction(types.closeChallengeModal); +export const openChallengeModal = createAction(types.openChallengeModal); export const updateHint = createAction(types.updateHint); export const unlockUntrustedCode = createAction( types.unlockUntrustedCode, @@ -258,6 +260,10 @@ export const challengeTemplateSelector = state => export const backendFormValuesSelector = state => getValues(state.form.BackEndChallenge); +export const frontendProjectFormValuesSelector = state => + getValues(state.form.NewFrontEndProject) || {}; +export const backendProjectFormValuesSelector = state => + getValues(state.form.NewBackEndProject) || {}; export default combineReducers( handleActions( @@ -286,6 +292,10 @@ export default combineReducers( tests.every(test => test.pass && !test.err) ) }), + [types.openChallengeModal]: state => ({ + ...state, + isChallengeModalOpen: true + }), [types.closeChallengeModal]: state => ({ ...state, isChallengeModalOpen: false diff --git a/common/app/routes/Challenges/views/project/Forms.jsx b/common/app/routes/Challenges/views/project/Forms.jsx index 1bc9535ff5..b563412dc6 100644 --- a/common/app/routes/Challenges/views/project/Forms.jsx +++ b/common/app/routes/Challenges/views/project/Forms.jsx @@ -9,7 +9,7 @@ import { import { showProjectSubmit } from './redux'; import SolutionInput from '../../Solution-Input.jsx'; -import { submitChallenge } from '../../redux'; +import { openChallengeModal } from '../../redux'; import { isValidURL, makeRequired, @@ -22,13 +22,14 @@ const propTypes = { handleSubmit: PropTypes.func, isSignedIn: PropTypes.bool, isSubmitting: PropTypes.bool, + openChallengeModal: PropTypes.func.isRequired, resetForm: PropTypes.func, showProjectSubmit: PropTypes.func, submitChallenge: PropTypes.func }; const bindableActions = { - submitChallenge, + openChallengeModal, showProjectSubmit }; const frontEndFields = [ 'solution' ]; @@ -49,8 +50,7 @@ const backEndFieldValidators = { export function _FrontEndForm({ fields, handleSubmit, - submitChallenge, - resetForm, + openChallengeModal, isSubmitting, showProjectSubmit }) { @@ -60,12 +60,7 @@ export function _FrontEndForm({ return (
{ - submitChallenge(value); - resetForm('NewFrontEndProject'); - }) - } + onSubmit={ handleSubmit(openChallengeModal) } > { isSubmitting ? @@ -103,8 +98,7 @@ export const FrontEndForm = reduxForm( export function _BackEndForm({ fields: { solution, githubLink }, handleSubmit, - submitChallenge, - resetForm, + openChallengeModal, isSubmitting, showProjectSubmit }) { @@ -114,12 +108,7 @@ export function _BackEndForm({ return ( { - submitChallenge(values); - resetForm('NewBackEndProject'); - }) - } + onSubmit={ handleSubmit(openChallengeModal) } > { isSubmitting ? diff --git a/common/app/routes/Challenges/views/project/Tool-Panel.jsx b/common/app/routes/Challenges/views/project/Tool-Panel.jsx index 44bb6e91a3..b341723ed0 100644 --- a/common/app/routes/Challenges/views/project/Tool-Panel.jsx +++ b/common/app/routes/Challenges/views/project/Tool-Panel.jsx @@ -12,7 +12,7 @@ import { import { submittingSelector } from './redux'; import { - submitChallenge, + openChallengeModal, openHelpModal, chatRoomSelector, @@ -35,12 +35,12 @@ const propTypes = { isSignedIn: PropTypes.bool, isSimple: PropTypes.bool, isSubmitting: PropTypes.bool, - openHelpModal: PropTypes.func.isRequired, - submitChallenge: PropTypes.func.isRequired + openChallengeModal: PropTypes.func.isRequired, + openHelpModal: PropTypes.func.isRequired }; const mapDispatchToProps = { - openHelpModal, - submitChallenge + openChallengeModal, + openHelpModal }; const mapStateToProps = createSelector( challengeSelector, @@ -65,7 +65,7 @@ const mapStateToProps = createSelector( ); export class ToolPanel extends PureComponent { - renderSubmitButton(isSignedIn, submitChallenge) { + renderSubmitButton(isSignedIn, openChallengeModal) { const buttonCopy = isSignedIn ? 'Submit and go to my next challenge' : "I've completed this challenge"; @@ -74,7 +74,7 @@ export class ToolPanel extends PureComponent { block={ true } bsStyle='primary' className='btn-big' - onClick={ submitChallenge } + onClick={ openChallengeModal } > { buttonCopy } (ctrl + enter) @@ -90,7 +90,7 @@ export class ToolPanel extends PureComponent { isSignedIn, isSubmitting, openHelpModal, - submitChallenge + openChallengeModal } = this.props; const FormElement = isFrontEnd ? FrontEndForm : BackEndForm; @@ -98,7 +98,7 @@ export class ToolPanel extends PureComponent {
{ isSimple ? - this.renderSubmitButton(isSignedIn, submitChallenge) : + this.renderSubmitButton(isSignedIn, openChallengeModal) : }