fix(client): update current challenge for all challenges
This commit is contained in:
@ -7,6 +7,7 @@ import { graphql } from 'gatsby';
|
|||||||
|
|
||||||
import {
|
import {
|
||||||
executeChallenge,
|
executeChallenge,
|
||||||
|
challengeMounted,
|
||||||
challengeTestsSelector,
|
challengeTestsSelector,
|
||||||
consoleOutputSelector,
|
consoleOutputSelector,
|
||||||
initTests,
|
initTests,
|
||||||
@ -42,12 +43,15 @@ const reduxFormPropTypes = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const propTypes = {
|
const propTypes = {
|
||||||
|
challengeMounted: PropTypes.func.isRequired,
|
||||||
description: PropTypes.string,
|
description: PropTypes.string,
|
||||||
executeChallenge: PropTypes.func.isRequired,
|
executeChallenge: PropTypes.func.isRequired,
|
||||||
id: PropTypes.string,
|
id: PropTypes.string,
|
||||||
|
initTests: PropTypes.func.isRequired,
|
||||||
output: PropTypes.string,
|
output: PropTypes.string,
|
||||||
tests: PropTypes.array,
|
tests: PropTypes.array,
|
||||||
title: PropTypes.string,
|
title: PropTypes.string,
|
||||||
|
updateChallengeMeta: PropTypes.func.isRequired,
|
||||||
...reduxFormPropTypes
|
...reduxFormPropTypes
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -67,6 +71,7 @@ const mapStateToProps = createSelector(
|
|||||||
);
|
);
|
||||||
|
|
||||||
const mapDispatchToActions = {
|
const mapDispatchToActions = {
|
||||||
|
challengeMounted,
|
||||||
executeChallenge,
|
executeChallenge,
|
||||||
initTests,
|
initTests,
|
||||||
updateChallengeMeta
|
updateChallengeMeta
|
||||||
@ -89,6 +94,7 @@ export class BackEnd extends Component {
|
|||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
const {
|
const {
|
||||||
|
challengeMounted,
|
||||||
initTests,
|
initTests,
|
||||||
updateChallengeMeta,
|
updateChallengeMeta,
|
||||||
data: {
|
data: {
|
||||||
@ -101,6 +107,7 @@ export class BackEnd extends Component {
|
|||||||
} = this.props;
|
} = this.props;
|
||||||
initTests(tests);
|
initTests(tests);
|
||||||
updateChallengeMeta({ ...challengeMeta, challengeType });
|
updateChallengeMeta({ ...challengeMeta, challengeType });
|
||||||
|
challengeMounted(challengeMeta.id);
|
||||||
window.addEventListener('resize', this.updateDimensions);
|
window.addEventListener('resize', this.updateDimensions);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -119,6 +126,7 @@ export class BackEnd extends Component {
|
|||||||
}
|
}
|
||||||
} = prevProps;
|
} = prevProps;
|
||||||
const {
|
const {
|
||||||
|
challengeMounted,
|
||||||
initTests,
|
initTests,
|
||||||
updateChallengeMeta,
|
updateChallengeMeta,
|
||||||
data: {
|
data: {
|
||||||
@ -133,6 +141,7 @@ export class BackEnd extends Component {
|
|||||||
if (prevTitle !== currentTitle) {
|
if (prevTitle !== currentTitle) {
|
||||||
initTests(tests);
|
initTests(tests);
|
||||||
updateChallengeMeta({ ...challengeMeta, challengeType });
|
updateChallengeMeta({ ...challengeMeta, challengeType });
|
||||||
|
challengeMounted(challengeMeta.id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -166,6 +166,11 @@ class ShowClassic extends Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
componentWillUnmount() {
|
||||||
|
const { createFiles } = this.props;
|
||||||
|
createFiles({});
|
||||||
|
}
|
||||||
|
|
||||||
getChallenge = () => this.props.data.challengeNode;
|
getChallenge = () => this.props.data.challengeNode;
|
||||||
|
|
||||||
getBlockNameTitle() {
|
getBlockNameTitle() {
|
||||||
|
@ -8,8 +8,8 @@ import Helmet from 'react-helmet';
|
|||||||
import { randomCompliment } from '../utils/get-words';
|
import { randomCompliment } from '../utils/get-words';
|
||||||
import { ChallengeNode } from '../../../redux/propTypes';
|
import { ChallengeNode } from '../../../redux/propTypes';
|
||||||
import {
|
import {
|
||||||
|
challengeMounted,
|
||||||
updateChallengeMeta,
|
updateChallengeMeta,
|
||||||
createFiles,
|
|
||||||
updateSuccessMessage,
|
updateSuccessMessage,
|
||||||
openModal,
|
openModal,
|
||||||
updateProjectFormValues
|
updateProjectFormValues
|
||||||
@ -32,7 +32,7 @@ const mapDispatchToProps = dispatch =>
|
|||||||
bindActionCreators(
|
bindActionCreators(
|
||||||
{
|
{
|
||||||
updateChallengeMeta,
|
updateChallengeMeta,
|
||||||
createFiles,
|
challengeMounted,
|
||||||
updateProjectFormValues,
|
updateProjectFormValues,
|
||||||
updateSuccessMessage,
|
updateSuccessMessage,
|
||||||
openCompletionModal: () => openModal('completion')
|
openCompletionModal: () => openModal('completion')
|
||||||
@ -41,7 +41,7 @@ const mapDispatchToProps = dispatch =>
|
|||||||
);
|
);
|
||||||
|
|
||||||
const propTypes = {
|
const propTypes = {
|
||||||
createFiles: PropTypes.func.isRequired,
|
challengeMounted: PropTypes.func.isRequired,
|
||||||
data: PropTypes.shape({
|
data: PropTypes.shape({
|
||||||
challengeNode: ChallengeNode
|
challengeNode: ChallengeNode
|
||||||
}),
|
}),
|
||||||
@ -57,34 +57,42 @@ const propTypes = {
|
|||||||
export class Project extends Component {
|
export class Project extends Component {
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
const {
|
const {
|
||||||
createFiles,
|
challengeMounted,
|
||||||
data: { challengeNode: { title, challengeType } },
|
data: {
|
||||||
|
challengeNode: { title, challengeType }
|
||||||
|
},
|
||||||
pageContext: { challengeMeta },
|
pageContext: { challengeMeta },
|
||||||
updateChallengeMeta,
|
updateChallengeMeta,
|
||||||
updateSuccessMessage
|
updateSuccessMessage
|
||||||
} = this.props;
|
} = this.props;
|
||||||
createFiles({});
|
|
||||||
updateSuccessMessage(randomCompliment());
|
updateSuccessMessage(randomCompliment());
|
||||||
return updateChallengeMeta({ ...challengeMeta, title, challengeType });
|
updateChallengeMeta({ ...challengeMeta, title, challengeType });
|
||||||
|
challengeMounted(challengeMeta.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidUpdate(prevProps) {
|
componentDidUpdate(prevProps) {
|
||||||
const { data: { challengeNode: { title: prevTitle } } } = prevProps;
|
|
||||||
const {
|
const {
|
||||||
createFiles,
|
data: {
|
||||||
data: { challengeNode: { title: currentTitle, challengeType } },
|
challengeNode: { title: prevTitle }
|
||||||
|
}
|
||||||
|
} = prevProps;
|
||||||
|
const {
|
||||||
|
challengeMounted,
|
||||||
|
data: {
|
||||||
|
challengeNode: { title: currentTitle, challengeType }
|
||||||
|
},
|
||||||
pageContext: { challengeMeta },
|
pageContext: { challengeMeta },
|
||||||
updateChallengeMeta,
|
updateChallengeMeta,
|
||||||
updateSuccessMessage
|
updateSuccessMessage
|
||||||
} = this.props;
|
} = this.props;
|
||||||
updateSuccessMessage(randomCompliment());
|
updateSuccessMessage(randomCompliment());
|
||||||
if (prevTitle !== currentTitle) {
|
if (prevTitle !== currentTitle) {
|
||||||
createFiles({});
|
|
||||||
updateChallengeMeta({
|
updateChallengeMeta({
|
||||||
...challengeMeta,
|
...challengeMeta,
|
||||||
title: currentTitle,
|
title: currentTitle,
|
||||||
challengeType
|
challengeType
|
||||||
});
|
});
|
||||||
|
challengeMounted(challengeMeta.id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -133,7 +141,10 @@ export class Project extends Component {
|
|||||||
Project.displayName = 'Project';
|
Project.displayName = 'Project';
|
||||||
Project.propTypes = propTypes;
|
Project.propTypes = propTypes;
|
||||||
|
|
||||||
export default connect(mapStateToProps, mapDispatchToProps)(Project);
|
export default connect(
|
||||||
|
mapStateToProps,
|
||||||
|
mapDispatchToProps
|
||||||
|
)(Project);
|
||||||
|
|
||||||
export const query = graphql`
|
export const query = graphql`
|
||||||
query ProjectChallenge($slug: String!) {
|
query ProjectChallenge($slug: String!) {
|
||||||
|
@ -89,6 +89,10 @@ function saveCodeEpic(action$, state$) {
|
|||||||
function loadCodeEpic(action$, state$) {
|
function loadCodeEpic(action$, state$) {
|
||||||
return action$.pipe(
|
return action$.pipe(
|
||||||
ofType(types.challengeMounted),
|
ofType(types.challengeMounted),
|
||||||
|
filter(() => {
|
||||||
|
const files = challengeFilesSelector(state$.value);
|
||||||
|
return Object.keys(files).length > 0;
|
||||||
|
}),
|
||||||
switchMap(({ payload: id }) => {
|
switchMap(({ payload: id }) => {
|
||||||
let finalFiles;
|
let finalFiles;
|
||||||
const state = state$.value;
|
const state = state$.value;
|
||||||
|
@ -1,36 +0,0 @@
|
|||||||
import { of } from 'rxjs';
|
|
||||||
import { ofType } from 'redux-observable';
|
|
||||||
|
|
||||||
import { types } from './';
|
|
||||||
import { filter, switchMap, catchError, mapTo } from 'rxjs/operators';
|
|
||||||
import {
|
|
||||||
isSignedInSelector,
|
|
||||||
currentChallengeIdSelector,
|
|
||||||
updateComplete,
|
|
||||||
updateFailed
|
|
||||||
} from '../../../redux';
|
|
||||||
import postUpdate$ from '../utils/postUpdate$';
|
|
||||||
|
|
||||||
function currentChallengeEpic(action$, state$) {
|
|
||||||
return action$.pipe(
|
|
||||||
ofType(types.challengeMounted),
|
|
||||||
filter(() => isSignedInSelector(state$.value)),
|
|
||||||
filter(
|
|
||||||
({ payload }) => payload !== currentChallengeIdSelector(state$.value)
|
|
||||||
),
|
|
||||||
switchMap(({ payload }) => {
|
|
||||||
const update = {
|
|
||||||
endpoint: '/update-my-current-challenge',
|
|
||||||
payload: {
|
|
||||||
currentChallengeId: payload
|
|
||||||
}
|
|
||||||
};
|
|
||||||
return postUpdate$(update).pipe(
|
|
||||||
mapTo(updateComplete()),
|
|
||||||
catchError(() => of(updateFailed(update)))
|
|
||||||
);
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default currentChallengeEpic;
|
|
@ -0,0 +1,35 @@
|
|||||||
|
import { put, select, call, takeEvery } from 'redux-saga/effects';
|
||||||
|
|
||||||
|
import {
|
||||||
|
isSignedInSelector,
|
||||||
|
currentChallengeIdSelector,
|
||||||
|
updateComplete,
|
||||||
|
updateFailed
|
||||||
|
} from '../../../redux';
|
||||||
|
|
||||||
|
import { post } from '../../../utils/ajax';
|
||||||
|
|
||||||
|
function* currentChallengeSaga({ payload }) {
|
||||||
|
const isSignedIn = yield select(isSignedInSelector);
|
||||||
|
const currentChallengeId = yield select(currentChallengeIdSelector);
|
||||||
|
if (isSignedIn && payload !== currentChallengeId) {
|
||||||
|
const update = {
|
||||||
|
endpoint: '/update-my-current-challenge',
|
||||||
|
payload: {
|
||||||
|
currentChallengeId: payload
|
||||||
|
}
|
||||||
|
};
|
||||||
|
try {
|
||||||
|
yield call(post, update.endpoint, update.payload);
|
||||||
|
yield put(updateComplete());
|
||||||
|
} catch {
|
||||||
|
yield put(updateFailed(update));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createCurrentChallengeSaga(types) {
|
||||||
|
return [
|
||||||
|
takeEvery(types.challengeMounted, currentChallengeSaga)
|
||||||
|
];
|
||||||
|
}
|
@ -10,10 +10,10 @@ import completionEpic from './completion-epic';
|
|||||||
import codeLockEpic from './code-lock-epic';
|
import codeLockEpic from './code-lock-epic';
|
||||||
import createQuestionEpic from './create-question-epic';
|
import createQuestionEpic from './create-question-epic';
|
||||||
import codeStorageEpic from './code-storage-epic';
|
import codeStorageEpic from './code-storage-epic';
|
||||||
import currentChallengeEpic from './current-challenge-epic';
|
|
||||||
|
|
||||||
import { createIdToNameMapSaga } from './id-to-name-map-saga';
|
import { createIdToNameMapSaga } from './id-to-name-map-saga';
|
||||||
import { createExecuteChallengeSaga } from './execute-challenge-saga';
|
import { createExecuteChallengeSaga } from './execute-challenge-saga';
|
||||||
|
import { createCurrentChallengeSaga } from './current-challenge-saga';
|
||||||
|
|
||||||
export const ns = 'challenge';
|
export const ns = 'challenge';
|
||||||
export const backendNS = 'backendChallenge';
|
export const backendNS = 'backendChallenge';
|
||||||
@ -85,13 +85,13 @@ export const epics = [
|
|||||||
codeLockEpic,
|
codeLockEpic,
|
||||||
completionEpic,
|
completionEpic,
|
||||||
createQuestionEpic,
|
createQuestionEpic,
|
||||||
codeStorageEpic,
|
codeStorageEpic
|
||||||
currentChallengeEpic
|
|
||||||
];
|
];
|
||||||
|
|
||||||
export const sagas = [
|
export const sagas = [
|
||||||
...createIdToNameMapSaga(types),
|
...createIdToNameMapSaga(types),
|
||||||
...createExecuteChallengeSaga(types)
|
...createExecuteChallengeSaga(types),
|
||||||
|
...createCurrentChallengeSaga(types)
|
||||||
];
|
];
|
||||||
|
|
||||||
export const createFiles = createAction(types.createFiles, challengeFiles =>
|
export const createFiles = createAction(types.createFiles, challengeFiles =>
|
||||||
@ -300,7 +300,7 @@ export const reducer = handleActions(
|
|||||||
...state,
|
...state,
|
||||||
currentTab: payload
|
currentTab: payload
|
||||||
}),
|
}),
|
||||||
[types.executeChallenge]: (state, { payload }) => ({
|
[types.executeChallenge]: state => ({
|
||||||
...state,
|
...state,
|
||||||
currentTab: 3
|
currentTab: 3
|
||||||
})
|
})
|
||||||
|
Reference in New Issue
Block a user