Merge pull request #16796 from Bouncey/feat/completionModalProjects

fix(projectCompletion): Open completion modal for Project submission
pull/16831/merge
Berkeley Martinez 2018-03-05 14:15:31 -08:00 committed by GitHub
commit 23d9ceb399
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 91 additions and 40 deletions

View File

@ -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
);

View File

@ -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
);

View File

@ -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()
)
);
}

View File

@ -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

View File

@ -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 (
<form
name='NewFrontEndProject'
onSubmit={
handleSubmit((value) => {
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 (
<form
name='NewBackEndProject'
onSubmit={
handleSubmit((values) => {
submitChallenge(values);
resetForm('NewBackEndProject');
})
}
onSubmit={ handleSubmit(openChallengeModal) }
>
{
isSubmitting ?

View File

@ -13,7 +13,7 @@ import {
import { submittingSelector } from './redux';
import {
submitChallenge,
openChallengeModal,
openHelpModal,
chatRoomSelector,
@ -36,12 +36,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,
@ -66,7 +66,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";
@ -75,7 +75,7 @@ export class ToolPanel extends PureComponent {
block={ true }
bsStyle='primary'
className='btn-big'
onClick={ submitChallenge }
onClick={ openChallengeModal }
>
{ buttonCopy } (ctrl + enter)
</Button>
@ -91,7 +91,7 @@ export class ToolPanel extends PureComponent {
isSignedIn,
isSubmitting,
openHelpModal,
submitChallenge
openChallengeModal
} = this.props;
const FormElement = isFrontEnd ? FrontEndForm : BackEndForm;
@ -99,7 +99,7 @@ export class ToolPanel extends PureComponent {
<div>
{
isSimple ?
this.renderSubmitButton(isSignedIn, submitChallenge) :
this.renderSubmitButton(isSignedIn, openChallengeModal) :
<FormElement isSubmitting={ isSubmitting }/>
}
<ButtonSpacer />