Skip to content

Commit 71ae217

Browse files
committed
Winners are not reloaded properly along with other challenge details
Fixes #429
1 parent 69cb8af commit 71ae217

File tree

4 files changed

+151
-84
lines changed

4 files changed

+151
-84
lines changed

.exchange-rates.cache

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
{"disclaimer":"Usage subject to terms: https://openexchangerates.org/terms","license":"https://openexchangerates.org/license","timestamp":1504544400,"base":"USD","rates":{"AED":3.673018,"AFN":68.462,"ALL":112.21,"AMD":478.41,"ANG":1.780586,"AOA":165.9205,"ARS":17.266,"AUD":1.258092,"AWG":1.794996,"AZN":1.7,"BAM":1.6413,"BBD":2,"BDT":81.227951,"BGN":1.641362,"BHD":0.377041,"BIF":1741.230974,"BMD":1,"BND":1.356266,"BOB":6.977304,"BRL":3.137699,"BSD":1,"BTC":0.000231816999,"BTN":63.994,"BWP":10.094548,"BYN":1.927287,"BZD":2.016397,"CAD":1.240861,"CDF":1562.881563,"CHF":0.958767,"CLF":0.0232,"CLP":624.4,"CNH":6.527202,"CNY":6.520396,"COP":2931,"CRC":578,"CUC":1,"CUP":25.5,"CVE":93.3,"CZK":21.930681,"DJF":178.97,"DKK":6.253926,"DOP":47.365,"DZD":111.128,"EGP":17.633,"ERN":15.33,"ETB":23.303876,"EUR":0.841034,"FJD":2.019001,"FKP":0.774048,"GBP":0.774048,"GEL":2.44815,"GGP":0.774048,"GHS":4.43,"GIP":0.774048,"GMD":46.05,"GNF":9003.9,"GTQ":7.299763,"GYD":208.057754,"HKD":7.825255,"HNL":23.417746,"HRK":6.2323,"HTG":63.057879,"HUF":257.454396,"IDR":13353.67612,"ILS":3.57325,"IMP":0.774048,"INR":64.01,"IQD":1165.384957,"IRR":33207.5,"ISK":104.767152,"JEP":0.774048,"JMD":128.74255,"JOD":0.7086,"JPY":109.69662963,"KES":103.21,"KGS":68.631499,"KHR":4051.2,"KMF":415.05,"KPW":900,"KRW":1132.37,"KWD":0.30163,"KYD":0.833633,"KZT":338.9425,"LAK":8291.45,"LBP":1506.5,"LKR":152.43,"LRD":115.075,"LSL":12.964322,"LYD":1.362992,"MAD":9.362624,"MDL":17.719587,"MGA":2951.1,"MKD":51.765,"MMK":1350.999087,"MNT":2424.666667,"MOP":8.061095,"MRO":364.255827,"MUR":32.994,"MVR":15.400126,"MWK":725.665,"MXN":17.88387,"MYR":4.270494,"MZN":61.435,"NAD":12.9625,"NGN":358.467572,"NIO":30.049714,"NOK":7.812714,"NPR":102.523085,"NZD":1.394932,"OMR":0.385049,"PAB":1,"PEN":3.240021,"PGK":3.19455,"PHP":51.135,"PKR":105.225,"PLN":3.566886,"PYG":5665.5,"QAR":3.689293,"RON":3.860918,"RSD":100.525,"RUB":57.820184,"RWF":830.004133,"SAR":3.7504,"SBD":7.736845,"SCR":13.697721,"SDG":6.678926,"SEK":7.968304,"SGD":1.356747,"SHP":0.774048,"SLL":7537.5,"SOS":578.655,"SRD":7.438,"SSP":125.264,"STD":20623.25,"SVC":8.752812,"SYP":515,"SZL":12.967152,"THB":33.1535,"TJS":8.812598,"TMT":3.499972,"TND":2.4479,"TOP":2.19125,"TRY":3.430944,"TTD":6.732058,"TWD":30.052,"TZS":2241.65,"UAH":25.9327,"UGX":3601.2,"USD":1,"UYU":28.78156,"UZS":4211.7,"VEF":10.122506,"VND":22762.933333,"VUV":104.308667,"WST":2.485967,"XAF":551.681965,"XAG":0.05588174,"XAU":0.00074931,"XCD":2.70255,"XDR":0.705531,"XOF":551.681965,"XPD":0.00102327,"XPF":100.362021,"XPT":0.0009919,"YER":250.35,"ZAR":12.972663,"ZMW":9.1,"ZWL":322.355011}}
1+
{"disclaimer":"Usage subject to terms: https://openexchangerates.org/terms","license":"https://openexchangerates.org/license","timestamp":1504605600,"base":"USD","rates":{"AED":3.673028,"AFN":68.5435,"ALL":112.2,"AMD":478.99,"ANG":1.782658,"AOA":165.9205,"ARS":17.24,"AUD":1.253391,"AWG":1.794996,"AZN":1.7,"BAM":1.644595,"BBD":2,"BDT":81.327375,"BGN":1.645962,"BHD":0.377055,"BIF":1745.25,"BMD":1,"BND":1.355548,"BOB":6.98579,"BRL":3.140901,"BSD":1,"BTC":0.000232115808,"BTN":64.06162,"BWP":10.10606,"BYN":1.929609,"BZD":2.018843,"CAD":1.239973,"CDF":1562.881563,"CHF":0.959693,"CLF":0.02322,"CLP":624.695,"CNH":6.558234,"CNY":6.5521,"COP":2934.19,"CRC":578.67,"CUC":1,"CUP":25.5,"CVE":93.3,"CZK":21.96135,"DJF":178.97,"DKK":6.259991,"DOP":47.687868,"DZD":111.062,"EGP":17.6318,"ERN":15.334802,"ETB":23.331469,"EUR":0.841618,"FJD":2.017794,"FKP":0.773452,"GBP":0.773452,"GEL":2.45675,"GGP":0.773452,"GHS":4.4275,"GIP":0.773452,"GMD":46.05,"GNF":9014.45,"GTQ":7.308611,"GYD":208.399935,"HKD":7.82514,"HNL":23.445727,"HRK":6.239063,"HTG":63.124507,"HUF":257.66099,"IDR":13342.242352,"ILS":3.570268,"IMP":0.773452,"INR":64.1205,"IQD":1168.8,"IRR":33213,"ISK":105.10897,"JEP":0.773452,"JMD":128.105,"JOD":0.7085,"JPY":109.388875,"KES":103.236159,"KGS":68.587499,"KHR":4056,"KMF":415.05,"KPW":900,"KRW":1129.8,"KWD":0.301511,"KYD":0.834628,"KZT":339.549023,"LAK":8301.8,"LBP":1510.45,"LKR":152.685,"LRD":115.075,"LSL":12.979646,"LYD":1.364649,"MAD":9.380409,"MDL":17.769968,"MGA":2954.65,"MKD":51.818233,"MMK":1352.1,"MNT":2428.122539,"MOP":8.071002,"MRO":364.555,"MUR":32.9925,"MVR":15.400126,"MWK":725.535,"MXN":17.841045,"MYR":4.260941,"MZN":61.44,"NAD":12.96875,"NGN":359.5,"NIO":30.085049,"NOK":7.80721,"NPR":102.638212,"NZD":1.391876,"OMR":0.38499,"PAB":1,"PEN":3.237503,"PGK":3.199308,"PHP":51.11,"PKR":105.272478,"PLN":3.563134,"PYG":5672.6,"QAR":3.69375,"RON":3.867628,"RSD":100.635,"RUB":57.867,"RWF":831.255,"SAR":3.7504,"SBD":7.736845,"SCR":13.72237,"SDG":6.686597,"SEK":7.969118,"SGD":1.355204,"SHP":0.773452,"SLL":7537.5,"SOS":579.345,"SRD":7.438,"SSP":125.209,"STD":20611.885421,"SVC":8.763509,"SYP":515,"SZL":12.982479,"THB":33.17,"TJS":8.823542,"TMT":3.499972,"TND":2.427009,"TOP":2.19125,"TRY":3.447989,"TTD":6.74021,"TWD":30.0599,"TZS":2240.15,"UAH":25.964936,"UGX":3605.55,"USD":1,"UYU":28.886091,"UZS":4216.85,"VEF":10.122506,"VND":22739.436299,"VUV":105.187518,"WST":2.486367,"XAF":552.065146,"XAG":0.05600136,"XAU":0.00075081,"XCD":2.70255,"XDR":0.705531,"XOF":552.065146,"XPD":0.00101888,"XPF":100.431729,"XPT":0.00099619,"YER":250.35,"ZAR":12.960557,"ZMW":9.139012,"ZWL":322.355011}}

src/shared/actions/challenge.js

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -90,11 +90,32 @@ function unregisterDone(auth, challengeId) {
9090
.then(() => getDetailsDone(challengeId, auth.tokenV3, auth.tokenV2));
9191
}
9292

93-
function loadResults(auth, challengeId, type) {
93+
/**
94+
* Initiates loading of challenge results. Any loading of results initiated
95+
* before will be silently discarted.
96+
* @param {Number|String} challengeId
97+
* @return {String}
98+
*/
99+
function loadResultsInit(challengeId) {
100+
return _.toString(challengeId);
101+
}
102+
103+
/**
104+
* Loads challenge results. Challenge ID should match with the one previously
105+
* passed to loadResultsInit(..), otherwise results will be silently discarted.
106+
* @param {Object} auth
107+
* @param {Number|String} challengeId
108+
* @param {String} type
109+
* @return {Object}
110+
*/
111+
function loadResultsDone(auth, challengeId, type) {
94112
return getApiV2(auth.tokenV2)
95113
.fetch(`/${type}/challenges/result/${challengeId}`)
96114
.then(response => response.json())
97-
.then(response => response.results);
115+
.then(response => ({
116+
challengeId: _.toString(challengeId),
117+
results: response.results,
118+
}));
98119
}
99120

100121
function fetchCheckpointsDone(tokenV2, challengeId) {
@@ -144,8 +165,8 @@ export default createActions({
144165
GET_DETAILS_DONE: getDetailsDone,
145166
GET_SUBMISSIONS_INIT: _.noop,
146167
GET_SUBMISSIONS_DONE: getSubmissionsDone,
147-
LOAD_RESULTS_INIT: _.noop,
148-
LOAD_RESULTS_DONE: loadResults,
168+
LOAD_RESULTS_INIT: loadResultsInit,
169+
LOAD_RESULTS_DONE: loadResultsDone,
149170
REGISTER_INIT: _.noop,
150171
REGISTER_DONE: registerDone,
151172
UNREGISTER_INIT: _.noop,

src/shared/containers/challenge-detail/index.jsx

Lines changed: 81 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -81,11 +81,17 @@ class ChallengeDetailPageContainer extends React.Component {
8181
&& !nextProps.checkpointResults) {
8282
this.props.fetchCheckpoints(this.props.authTokens, this.props.challengeId);
8383
}
84-
if (nextProps.challenge.status === 'COMPLETED' &&
85-
!nextProps.loadingResults &&
86-
!nextProps.results) {
87-
this.props.loadResults(this.props.authTokens, this.props.challengeId,
88-
nextProps.challenge.track.toLowerCase());
84+
85+
if (nextProps.challenge.status === 'COMPLETED'
86+
&& _.toString(nextProps.challengeId)
87+
!== nextProps.resultsLoadedForChallengeId
88+
&& _.toString(nextProps.challengeId)
89+
!== nextProps.loadingResultsForChallengeId) {
90+
this.props.loadResults(
91+
this.props.authTokens,
92+
this.props.challengeId,
93+
nextProps.challenge.track.toLowerCase(),
94+
);
8995
}
9096

9197
const userDetails = this.props.challenge.userDetails;
@@ -117,8 +123,13 @@ class ChallengeDetailPageContainer extends React.Component {
117123
render() {
118124
const {
119125
challenge,
126+
challengeId,
127+
resultsLoadedForChallengeId,
120128
} = this.props;
121129

130+
const results = resultsLoadedForChallengeId === _.toString(challengeId)
131+
? this.props.results : null;
132+
122133
const isEmpty = _.isEmpty(this.props.challenge);
123134

124135
const hasRegistered = isRegistered(this.props.challenge.userDetails,
@@ -174,7 +185,7 @@ class ChallengeDetailPageContainer extends React.Component {
174185
checkpoints={this.props.challenge.checkpoints}
175186
submissions={this.props.challenge.submissions}
176187
checkpointResults={this.props.checkpointResults}
177-
results={this.props.results}
188+
results={results}
178189
places={this.props.challenge.prizes.length}
179190
/>
180191
}
@@ -197,7 +208,7 @@ class ChallengeDetailPageContainer extends React.Component {
197208
{
198209
!isEmpty && this.props.selectedTab === DETAIL_TABS.WINNERS &&
199210
<Winners
200-
results={this.props.results}
211+
results={results}
201212
prizes={this.props.challenge.prizes}
202213
submissions={this.props.challenge.submissions}
203214
viewable={this.props.challenge.submissionsViewable === 'true'}
@@ -238,66 +249,68 @@ class ChallengeDetailPageContainer extends React.Component {
238249
}
239250

240251
ChallengeDetailPageContainer.defaultProps = {
241-
tokenV3: null,
252+
agreedTerms: {},
253+
agreeingTerm: '',
254+
checkpointResults: null,
255+
checkpoints: {},
256+
docuSignUrl: '',
242257
isLoadingChallenge: false,
258+
isLoadingTerms: false,
243259
loadingCheckpointResults: false,
244-
checkpointResults: null,
245-
loadingResults: false,
260+
loadingDocuSignUrl: '',
261+
loadingTermId: '',
246262
results: null,
247-
checkpoints: {},
248-
terms: [],
249263
showTermsModal: false,
250-
isLoadingTerms: false,
264+
terms: [],
251265
termDetails: {},
252-
docuSignUrl: '',
253-
loadingTermId: '',
254-
agreeingTerm: '',
255-
agreedTerms: {},
256-
loadingDocuSignUrl: '',
266+
tokenV3: null,
257267
};
258268

259269
ChallengeDetailPageContainer.propTypes = {
260-
tokenV3: PT.string,
261-
challenge: PT.shape().isRequired,
262-
isLoadingChallenge: PT.bool,
263-
loadChallengeDetails: PT.func.isRequired,
270+
agreedTerms: PT.shape(),
271+
agreeingTerm: PT.string,
272+
agreeTerm: PT.func.isRequired,
264273
authTokens: PT.shape().isRequired,
274+
challenge: PT.shape().isRequired,
265275
challengeId: PT.number.isRequired,
266-
registerForChallenge: PT.func.isRequired,
267-
registering: PT.bool.isRequired,
268-
reloadChallengeDetails: PT.func.isRequired,
269-
unregisterFromChallenge: PT.func.isRequired,
270-
unregistering: PT.bool.isRequired,
271-
loadingCheckpointResults: PT.bool,
276+
challengeSubtracksMap: PT.shape().isRequired,
272277
checkpointResults: PT.arrayOf(PT.shape()),
273-
toggleCheckpointFeedback: PT.func.isRequired,
274-
loadingResults: PT.bool,
275-
results: PT.arrayOf(PT.shape()),
276-
fetchCheckpoints: PT.func.isRequired,
277-
loadResults: PT.func.isRequired,
278278
checkpoints: PT.shape(),
279-
terms: PT.arrayOf(PT.shape()),
280-
openTermsModal: PT.func.isRequired,
281279
closeTermsModal: PT.func.isRequired,
282-
showTermsModal: PT.bool,
283-
loadTerms: PT.func.isRequired,
284-
isLoadingTerms: PT.bool,
285-
loadTermDetails: PT.func.isRequired,
286-
termDetails: PT.shape(),
287280
docuSignUrl: PT.string,
281+
fetchCheckpoints: PT.func.isRequired,
288282
getDocuSignUrl: PT.func.isRequired,
289-
loadingTermId: PT.string,
290-
agreeingTerm: PT.string,
291-
agreeTerm: PT.func.isRequired,
292-
agreedTerms: PT.shape(),
283+
getSubtracks: PT.func.isRequired,
284+
isLoadingChallenge: PT.bool,
285+
isLoadingTerms: PT.bool,
286+
loadChallengeDetails: PT.func.isRequired,
287+
loadResults: PT.func.isRequired,
288+
loadTerms: PT.func.isRequired,
289+
loadTermDetails: PT.func.isRequired,
290+
loadingCheckpointResults: PT.bool,
293291
loadingDocuSignUrl: PT.string,
294-
setChallengeListingFilter: PT.func.isRequired,
295-
selectedTab: PT.string.isRequired,
292+
loadingResultsForChallengeId: PT.string.isRequired,
293+
loadingTermId: PT.string,
296294
onSelectorClicked: PT.func.isRequired,
297-
challengeSubtracksMap: PT.shape().isRequired,
298-
getSubtracks: PT.func.isRequired,
295+
openTermsModal: PT.func.isRequired,
296+
registerForChallenge: PT.func.isRequired,
297+
registering: PT.bool.isRequired,
298+
reloadChallengeDetails: PT.func.isRequired,
299+
results: PT.arrayOf(PT.shape()),
300+
resultsLoadedForChallengeId: PT.string.isRequired,
301+
selectedTab: PT.string.isRequired,
302+
setChallengeListingFilter: PT.func.isRequired,
303+
showTermsModal: PT.bool,
304+
termDetails: PT.shape(),
305+
terms: PT.arrayOf(PT.shape()),
306+
toggleCheckpointFeedback: PT.func.isRequired,
307+
tokenV3: PT.string,
308+
unregisterFromChallenge: PT.func.isRequired,
309+
unregistering: PT.bool.isRequired,
299310
};
300311

312+
/* TODO: This function is ugly. We should do all this logic within normalization
313+
* function inside the challenge service. */
301314
function extractChallengeDetail(v3, v2, challengeId) {
302315
let challenge = {};
303316
if (!_.isEmpty(v3)) {
@@ -378,32 +391,34 @@ function extractChallengeDetail(v3, v2, challengeId) {
378391
}
379392

380393
const mapStateToProps = (state, props) => ({
394+
agreedTerms: state.terms.agreedTerms,
395+
agreeingTerm: state.terms.agreeingTerm,
396+
authTokens: state.auth,
381397
challengeId: Number(props.match.params.challengeId),
382398
challenge: extractChallengeDetail(state.challenge.details,
383399
state.challenge.detailsV2,
384400
Number(props.match.params.challengeId)),
385-
isLoadingChallenge: Boolean(state.challenge.loadingDetailsForChallengeId),
386-
authTokens: state.auth,
387-
tokenV2: state.auth && state.auth.tokenV2,
388-
tokenV3: state.auth && state.auth.tokenV3,
389-
registering: state.challenge.registering,
390-
unregistering: state.challenge.unregistering,
401+
challengeSubtracksMap: state.challengeListing.challengeSubtracksMap,
391402
checkpointResults: (state.challenge.checkpoints || {}).checkpointResults,
392-
loadingCheckpointResults: state.challenge.loadingCheckpoints,
393-
results: state.challenge.results,
394-
loadingResults: state.challenge.loadingResults,
395403
checkpoints: state.challenge.checkpoints,
396-
terms: state.terms.terms,
397-
showTermsModal: state.challenge.showTermsModal,
398-
loadingTermId: state.terms.loadingDetailsForTermId,
399-
termDetails: state.terms.details,
400404
docuSignUrl: state.terms.docuSignUrl,
405+
isLoadingChallenge: Boolean(state.challenge.loadingDetailsForChallengeId),
406+
isLoadingTerms: state.terms.loadingTermsForChallengeId
407+
=== props.match.params.challengeId,
408+
loadingCheckpointResults: state.challenge.loadingCheckpoints,
401409
loadingDocuSignUrl: state.terms.loadingDocuSignUrl,
402-
agreeingTerm: state.terms.agreeingTerm,
403-
agreedTerms: state.terms.agreedTerms,
404-
isLoadingTerms: state.terms.loadingTermsForChallengeId === props.match.params.challengeId,
410+
loadingResultsForChallengeId: state.challenge.loadingResultsForChallengeId,
411+
loadingTermId: state.terms.loadingDetailsForTermId,
412+
registering: state.challenge.registering,
413+
results: state.challenge.results,
414+
resultsLoadedForChallengeId: state.challenge.resultsLoadedForChallengeId,
405415
selectedTab: state.challenge.selectedTab || 'details',
406-
challengeSubtracksMap: state.challengeListing.challengeSubtracksMap,
416+
showTermsModal: state.challenge.showTermsModal,
417+
termDetails: state.terms.details,
418+
terms: state.terms.terms,
419+
tokenV2: state.auth && state.auth.tokenV2,
420+
tokenV3: state.auth && state.auth.tokenV3,
421+
unregistering: state.challenge.unregistering,
407422
});
408423

409424
const mapDispatchToProps = (dispatch) => {
@@ -443,7 +458,7 @@ const mapDispatchToProps = (dispatch) => {
443458
dispatch(a.unregisterDone(auth, challengeId));
444459
},
445460
loadResults: (auth, challengeId, type) => {
446-
dispatch(a.loadResultsInit());
461+
dispatch(a.loadResultsInit(challengeId));
447462
dispatch(a.loadResultsDone(auth, challengeId, type));
448463
},
449464
fetchCheckpoints: (tokens, challengeId) => {

src/shared/reducers/challenge.js

Lines changed: 44 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,44 @@ function onFetchCheckpointsDone(state, action) {
109109
}
110110
return state;
111111
}
112+
113+
/**
114+
* Handles CHALLENGE/LOAD_RESULTS_INIT action.
115+
* @param {Object} state
116+
* @param {Object} action
117+
* @return {Object}
118+
*/
119+
function onLoadResultsInit(state, { payload }) {
120+
return { ...state, loadingResultsForChallengeId: payload };
121+
}
122+
123+
/**
124+
* Handles CHALLENGE/LOAD_RESULTS_DONE action.
125+
* @param {Object} state
126+
* @param {Object} action
127+
* @return {Object}
128+
*/
129+
function onLoadResultsDone(state, action) {
130+
if (action.payload.challengeId !== state.loadingResultsForChallengeId) {
131+
return state;
132+
}
133+
if (action.error) {
134+
logger.error(action.payload);
135+
return {
136+
...state,
137+
loadingResultsForChallengeId: '',
138+
results: null,
139+
resultsLoadedForChallengeId: '',
140+
};
141+
}
142+
return {
143+
...state,
144+
loadingResultsForChallengeId: '',
145+
results: action.payload.results,
146+
resultsLoadedForChallengeId: action.payload.challengeId,
147+
};
148+
}
149+
112150
/**
113151
* Handles challengeActions.toggleCheckpointFeedback action.
114152
* @param {Object} state Previous state.
@@ -182,14 +220,6 @@ function onSelectTab(state, { payload }) {
182220
return { ...state, selectedTab: payload };
183221
}
184222

185-
function onLoadResultsDone(state, action) {
186-
return {
187-
...state,
188-
loadingResults: false,
189-
results: action.error ? null : action.payload,
190-
};
191-
}
192-
193223
/**
194224
* Creates a new Auth reducer with the specified initial state.
195225
* @param {Object} initialState Initial state.
@@ -218,10 +248,7 @@ function create(initialState) {
218248
[a.registerDone]: onRegisterDone,
219249
[a.unregisterInit]: state => ({ ...state, unregistering: true }),
220250
[a.unregisterDone]: onUnregisterDone,
221-
[a.loadResultsInit]: state => ({
222-
...state,
223-
loadingResults: true,
224-
}),
251+
[a.loadResultsInit]: onLoadResultsInit,
225252
[a.loadResultsDone]: onLoadResultsDone,
226253
[a.fetchCheckpointsInit]: state => ({
227254
...state,
@@ -238,8 +265,11 @@ function create(initialState) {
238265
detailsV2: null,
239266
loadingCheckpoints: false,
240267
loadingDetailsForChallengeId: '',
268+
loadingResultsForChallengeId: '',
241269
checkpoints: null,
242270
registering: false,
271+
results: null,
272+
resultsLoadedForChallengeId: '',
243273
unregistering: false,
244274
showTermsModal: false,
245275
selectedTab: DETAIL_TABS.DETAILS,
@@ -279,8 +309,9 @@ export function factory(req) {
279309
return Promise.all([details, checkpointsPromise, resultsPromise]);
280310
}).then(([details, checkpoints, results]) => {
281311
let state = {
282-
loadingDetailsForChallengeId: challengeId,
283312
loadingCheckpoints: true,
313+
loadingDetailsForChallengeId: challengeId,
314+
loadingResultsForChallengeId: challengeId,
284315
};
285316
if (req.query.tab) {
286317
state = onSelectTab(state, { payload: req.query.tab });

0 commit comments

Comments
 (0)