feat(challenge): Initial build of the challenge service
This commit is contained in:
committed by
Stuart Taylor
parent
d17c2d33eb
commit
a7587ed6f0
@ -9,13 +9,16 @@ import ns from './ns.json';
|
||||
import Challenges from './Challenges.jsx';
|
||||
import {
|
||||
toggleThisPanel,
|
||||
|
||||
makePanelOpenSelector
|
||||
} from './redux';
|
||||
import { fetchNewBlock } from '../redux';
|
||||
|
||||
import { makeBlockSelector } from '../entities';
|
||||
|
||||
const dispatchActions = { toggleThisPanel };
|
||||
const mapDispatchToProps = {
|
||||
fetchNewBlock,
|
||||
toggleThisPanel
|
||||
};
|
||||
function makeMapStateToProps(_, { dashedName }) {
|
||||
return createSelector(
|
||||
makeBlockSelector(dashedName),
|
||||
@ -34,6 +37,7 @@ function makeMapStateToProps(_, { dashedName }) {
|
||||
const propTypes = {
|
||||
challenges: PropTypes.array,
|
||||
dashedName: PropTypes.string,
|
||||
fetchNewBlock: PropTypes.func.isRequired,
|
||||
isOpen: PropTypes.bool,
|
||||
time: PropTypes.string,
|
||||
title: PropTypes.string,
|
||||
@ -74,7 +78,8 @@ export class Block extends PureComponent {
|
||||
time,
|
||||
dashedName,
|
||||
isOpen,
|
||||
challenges
|
||||
challenges,
|
||||
fetchNewBlock
|
||||
} = this.props;
|
||||
return (
|
||||
<Panel
|
||||
@ -85,6 +90,7 @@ export class Block extends PureComponent {
|
||||
header={ this.renderHeader(isOpen, title, time) }
|
||||
id={ title }
|
||||
key={ title }
|
||||
onClick={ () => fetchNewBlock(dashedName) }
|
||||
onSelect={ this.handleSelect }
|
||||
>
|
||||
{ isOpen && <Challenges challenges={ challenges } /> }
|
||||
@ -96,4 +102,4 @@ export class Block extends PureComponent {
|
||||
Block.displayName = 'Block';
|
||||
Block.propTypes = propTypes;
|
||||
|
||||
export default connect(makeMapStateToProps, dispatchActions)(Block);
|
||||
export default connect(makeMapStateToProps, mapDispatchToProps)(Block);
|
||||
|
@ -4,23 +4,32 @@ import { connect } from 'react-redux';
|
||||
import { Col, Row } from 'react-bootstrap';
|
||||
|
||||
import ns from './ns.json';
|
||||
import { Loader } from '../helperComponents';
|
||||
import SuperBlock from './Super-Block.jsx';
|
||||
import { superBlocksSelector } from '../redux';
|
||||
import { fetchMapUi } from './redux';
|
||||
|
||||
const mapStateToProps = state => ({
|
||||
superBlocks: superBlocksSelector(state)
|
||||
});
|
||||
|
||||
const mapDispatchToProps = {};
|
||||
const mapDispatchToProps = { fetchMapUi };
|
||||
const propTypes = {
|
||||
fetchMapUi: PropTypes.func.isRequired,
|
||||
params: PropTypes.object,
|
||||
superBlocks: PropTypes.array
|
||||
};
|
||||
|
||||
export class ShowMap extends PureComponent {
|
||||
renderSuperBlocks(superBlocks) {
|
||||
|
||||
renderSuperBlocks() {
|
||||
const { superBlocks } = this.props;
|
||||
if (!Array.isArray(superBlocks) || !superBlocks.length) {
|
||||
return <div>No Super Blocks</div>;
|
||||
return (
|
||||
<div style={{ hieght: '300px' }}>
|
||||
<Loader />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return superBlocks.map(dashedName => (
|
||||
<SuperBlock
|
||||
@ -31,12 +40,11 @@ export class ShowMap extends PureComponent {
|
||||
}
|
||||
|
||||
render() {
|
||||
const { superBlocks } = this.props;
|
||||
return (
|
||||
<Row>
|
||||
<Col xs={ 12 }>
|
||||
<div className={ `${ns}-accordion center-block` }>
|
||||
{ this.renderSuperBlocks(superBlocks) }
|
||||
{ this.renderSuperBlocks() }
|
||||
<div className='spacer' />
|
||||
</div>
|
||||
</Col>
|
||||
|
@ -17,7 +17,8 @@ export default function fetchMapUiEpic(
|
||||
{ services }
|
||||
) {
|
||||
return actions::ofType(
|
||||
appTypes.appMounted
|
||||
appTypes.appMounted,
|
||||
types.fetchMapUi.start
|
||||
)
|
||||
.flatMapLatest(() => {
|
||||
const lang = langSelector(getState());
|
||||
@ -34,13 +35,7 @@ export default function fetchMapUiEpic(
|
||||
),
|
||||
...res
|
||||
}))
|
||||
.map(({ entities, result } = {}) => {
|
||||
return fetchMapUiComplete(
|
||||
entities,
|
||||
result
|
||||
);
|
||||
})
|
||||
.startWith({ type: types.fetchMapUi.start })
|
||||
.map(fetchMapUiComplete)
|
||||
.catch(createErrorObservable);
|
||||
});
|
||||
}
|
||||
|
@ -32,11 +32,8 @@ export const types = createTypes([
|
||||
|
||||
export const initMap = createAction(types.initMap);
|
||||
|
||||
export const fetchMapUiComplete = createAction(
|
||||
types.fetchMapUi.complete,
|
||||
(entities, result) => ({ entities, result }),
|
||||
entities => ({ entities })
|
||||
);
|
||||
export const fetchMapUi = createAction(types.fetchMapUi.start);
|
||||
export const fetchMapUiComplete = createAction(types.fetchMapUi.complete);
|
||||
|
||||
export const toggleThisPanel = createAction(types.toggleThisPanel);
|
||||
export const collapseAll = createAction(types.collapseAll);
|
||||
@ -111,6 +108,7 @@ export default handleActions(
|
||||
const { entities, result } = payload;
|
||||
return {
|
||||
...state,
|
||||
...result,
|
||||
mapUi: utils.createMapUi(entities, result)
|
||||
};
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { findIndex, invert, pick, property, merge } from 'lodash';
|
||||
import { findIndex, invert, pick, property, merge, union } from 'lodash';
|
||||
import uuid from 'uuid/v4';
|
||||
import {
|
||||
combineActions,
|
||||
composeReducers,
|
||||
createAction,
|
||||
createTypes,
|
||||
@ -8,8 +9,9 @@ import {
|
||||
} from 'berkeleys-redux-utils';
|
||||
|
||||
import { themes } from '../../utils/themes';
|
||||
import { usernameSelector, types as app } from '../redux';
|
||||
import { types as challenges } from '../routes/Challenges/redux';
|
||||
import { usernameSelector } from '../redux';
|
||||
import { types as map } from '../Map/redux';
|
||||
|
||||
export const ns = 'entities';
|
||||
export const getNS = state => state[ns];
|
||||
@ -85,7 +87,8 @@ const defaultState = {
|
||||
superBlock: {},
|
||||
block: {},
|
||||
challenge: {},
|
||||
user: {}
|
||||
user: {},
|
||||
fullBlocks: []
|
||||
};
|
||||
|
||||
export function selectiveChallengeTitleSelector(state, dashedName) {
|
||||
@ -148,6 +151,8 @@ export function makeSuperBlockSelector(name) {
|
||||
export const isChallengeLoaded = (state, { dashedName }) =>
|
||||
!!challengeMapSelector(state)[dashedName];
|
||||
|
||||
export const fullBlocksSelector = state => getNS(state).fullBlocks;
|
||||
|
||||
export default composeReducers(
|
||||
ns,
|
||||
function metaReducer(state = defaultState, action) {
|
||||
@ -162,7 +167,9 @@ export default composeReducers(
|
||||
}
|
||||
};
|
||||
}
|
||||
return merge(state, action.meta.entities);
|
||||
return {
|
||||
...merge(state, action.meta.entities)
|
||||
};
|
||||
}
|
||||
return state;
|
||||
},
|
||||
@ -184,6 +191,18 @@ export default composeReducers(
|
||||
},
|
||||
handleActions(
|
||||
() => ({
|
||||
[
|
||||
combineActions(
|
||||
app.fetchChallenges.complete,
|
||||
map.fetchMapUi.complete
|
||||
)
|
||||
]: (state, { payload }) => {
|
||||
const {entities: { block } } = payload;
|
||||
return {
|
||||
...merge(state, payload.entities),
|
||||
fullBlocks: union(state.fullBlocks, [ Object.keys(block)[0] ])
|
||||
};
|
||||
},
|
||||
[
|
||||
challenges.submitChallenge.complete
|
||||
]: (state, { payload: { username, points, challengeInfo } }) => ({
|
||||
|
@ -9,9 +9,10 @@ import {
|
||||
delayedRedirect,
|
||||
|
||||
fetchChallengeCompleted,
|
||||
fetchChallengesCompleted
|
||||
fetchChallengesCompleted,
|
||||
challengeSelector
|
||||
} from './';
|
||||
import { isChallengeLoaded } from '../entities/index.js';
|
||||
import { isChallengeLoaded, fullBlocksSelector } from '../entities/index.js';
|
||||
|
||||
import { shapeChallenges } from './utils';
|
||||
import { types as challenge } from '../routes/Challenges/redux';
|
||||
@ -19,7 +20,7 @@ import { langSelector } from '../Router/redux';
|
||||
|
||||
const isDev = debug.enabled('fcc:*');
|
||||
|
||||
export default function fetchChallengeEpic(actions, { getState }, { services }) {
|
||||
function fetchChallengeEpic(actions, { getState }, { services }) {
|
||||
return actions::ofType(challenge.onRouteChallenges)
|
||||
.filter(({ payload }) => !isChallengeLoaded(getState(), payload))
|
||||
.flatMapLatest(({ payload: params }) => {
|
||||
@ -49,38 +50,40 @@ export default function fetchChallengeEpic(actions, { getState }, { services })
|
||||
});
|
||||
}
|
||||
|
||||
export function fetchChallengesEpic(
|
||||
export function fetchChallengesForBlockEpic(
|
||||
actions,
|
||||
{ getState },
|
||||
{ services }
|
||||
) {
|
||||
return actions::ofType(
|
||||
types.appMounted,
|
||||
types.updateChallenges
|
||||
types.updateChallenges,
|
||||
types.fetchNewBlock.start
|
||||
)
|
||||
.flatMapLatest(() => {
|
||||
const lang = langSelector(getState());
|
||||
.flatMapLatest(({ type, payload }) => {
|
||||
const fetchAnotherBlock = type === types.fetchNewBlock.start;
|
||||
const state = getState();
|
||||
let { block: blockName } = challengeSelector(state);
|
||||
const lang = langSelector(state);
|
||||
|
||||
if (fetchAnotherBlock) {
|
||||
const fullBlocks = fullBlocksSelector(state);
|
||||
if (fullBlocks.includes(payload)) {
|
||||
return Observable.of({ type: 'NULL'});
|
||||
}
|
||||
blockName = payload;
|
||||
}
|
||||
|
||||
const options = {
|
||||
params: { lang },
|
||||
service: 'map'
|
||||
params: { lang, blockName },
|
||||
service: 'challenges-for-block'
|
||||
};
|
||||
return services.readService$(options)
|
||||
.retry(3)
|
||||
.map(({ entities, ...res }) => ({
|
||||
entities: shapeChallenges(
|
||||
entities,
|
||||
isDev
|
||||
),
|
||||
...res
|
||||
}))
|
||||
.map(({ entities, result } = {}) => {
|
||||
return fetchChallengesCompleted(
|
||||
entities,
|
||||
result
|
||||
);
|
||||
})
|
||||
.map(fetchChallengesCompleted)
|
||||
.startWith({ type: types.fetchChallenges.start })
|
||||
.catch(createErrorObservable);
|
||||
});
|
||||
}
|
||||
|
||||
export default combineEpics(fetchChallengeEpic, fetchChallengesForBlockEpic);
|
||||
|
@ -1,4 +1,4 @@
|
||||
import _ from 'lodash';
|
||||
import { flow, identity } from 'lodash';
|
||||
import { Observable } from 'rx';
|
||||
import {
|
||||
combineActions,
|
||||
@ -19,6 +19,7 @@ import { updateThemeMetacreator, entitiesSelector } from '../entities';
|
||||
import { utils } from '../Flash/redux';
|
||||
import { paramsSelector } from '../Router/redux';
|
||||
import { types as challenges } from '../routes/Challenges/redux';
|
||||
import { types as map } from '../Map/redux';
|
||||
import { challengeToFiles } from '../routes/Challenges/utils';
|
||||
|
||||
import ns from '../ns.json';
|
||||
@ -41,6 +42,7 @@ export const types = createTypes([
|
||||
|
||||
createAsyncTypes('fetchChallenge'),
|
||||
createAsyncTypes('fetchChallenges'),
|
||||
createAsyncTypes('fetchNewBlock'),
|
||||
'updateChallenges',
|
||||
createAsyncTypes('fetchOtherUser'),
|
||||
createAsyncTypes('fetchUser'),
|
||||
@ -66,7 +68,7 @@ const throwIfUndefined = () => {
|
||||
// label?: String,
|
||||
// value?: Number
|
||||
// }) => () => Object
|
||||
export const createEventMetaCreator = ({
|
||||
export function createEventMetaCreator({
|
||||
// categories are features or namespaces of the app (capitalized):
|
||||
// Map, Nav, Challenges, and so on
|
||||
category = throwIfUndefined,
|
||||
@ -80,15 +82,17 @@ export const createEventMetaCreator = ({
|
||||
label,
|
||||
// used to tack some specific value for a GA event
|
||||
value
|
||||
} = throwIfUndefined) => () => ({
|
||||
analytics: {
|
||||
type: 'event',
|
||||
category,
|
||||
action,
|
||||
label,
|
||||
value
|
||||
}
|
||||
});
|
||||
} = throwIfUndefined) {
|
||||
return () => ({
|
||||
analytics: {
|
||||
type: 'event',
|
||||
category,
|
||||
action,
|
||||
label,
|
||||
value
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export const onRouteHome = createAction(types.onRouteHome);
|
||||
export const appMounted = createAction(types.appMounted);
|
||||
@ -101,15 +105,20 @@ export const fetchChallengeCompleted = createAction(
|
||||
null,
|
||||
meta => ({
|
||||
...meta,
|
||||
..._.flow(challengeToFiles, createFilesMetaCreator)(meta.challenge)
|
||||
...flow(challengeToFiles, createFilesMetaCreator)(meta.challenge)
|
||||
})
|
||||
);
|
||||
export const fetchChallenges = createAction('' + types.fetchChallenges);
|
||||
export const fetchChallengesCompleted = createAction(
|
||||
types.fetchChallenges.complete,
|
||||
(entities, result) => ({ entities, result }),
|
||||
entities => ({ entities })
|
||||
types.fetchChallenges.complete
|
||||
);
|
||||
|
||||
export const fetchNewBlock = createAction(types.fetchNewBlock.start);
|
||||
export const fetchNewBlockComplete = createAction(
|
||||
types.fetchNewBlock.complete,
|
||||
({ entities }) => entities
|
||||
);
|
||||
|
||||
export const updateChallenges = createAction(types.updateChallenges);
|
||||
|
||||
// updateTitle(title: String) => Action
|
||||
@ -122,7 +131,7 @@ export const fetchOtherUser = createAction(types.fetchOtherUser.start);
|
||||
export const fetchOtherUserComplete = createAction(
|
||||
types.fetchOtherUser.complete,
|
||||
({ result }) => result,
|
||||
_.identity
|
||||
identity
|
||||
);
|
||||
|
||||
// fetchUser() => Action
|
||||
@ -131,7 +140,7 @@ export const fetchUser = createAction(types.fetchUser);
|
||||
export const fetchUserComplete = createAction(
|
||||
types.fetchUser.complete,
|
||||
({ result }) => result,
|
||||
_.identity
|
||||
identity
|
||||
);
|
||||
|
||||
export const showSignIn = createAction(types.showSignIn);
|
||||
@ -209,7 +218,7 @@ export const userByNameSelector = state => {
|
||||
return userMap[username] || {};
|
||||
};
|
||||
|
||||
export const themeSelector = _.flow(
|
||||
export const themeSelector = flow(
|
||||
userSelector,
|
||||
user => user.theme || themes.default
|
||||
);
|
||||
@ -275,11 +284,13 @@ export default handleActions(
|
||||
}),
|
||||
[combineActions(
|
||||
types.fetchChallenge.complete,
|
||||
types.fetchChallenges.complete
|
||||
)]: (state, { payload }) => ({
|
||||
map.fetchMapUi.complete
|
||||
)]: (state, { payload }) => {
|
||||
return ({
|
||||
...state,
|
||||
superBlocks: payload.result.superBlocks
|
||||
}),
|
||||
});
|
||||
},
|
||||
[challenges.onRouteChallenges]: (state, { payload: { dashedName } }) => ({
|
||||
...state,
|
||||
currentChallenge: dashedName
|
||||
|
@ -1,5 +1,6 @@
|
||||
import flowRight from 'lodash/flowRight';
|
||||
import { createNameIdMap } from '../../utils/map.js';
|
||||
import { partial } from 'lodash';
|
||||
|
||||
export function filterComingSoonBetaChallenge(
|
||||
isDev = false,
|
||||
@ -13,7 +14,7 @@ export function filterComingSoonBetaFromEntities(
|
||||
{ challenge: challengeMap, block: blockMap = {}, ...rest },
|
||||
isDev = false
|
||||
) {
|
||||
const filter = filterComingSoonBetaChallenge.bind(null, isDev);
|
||||
const filter = partial(filterComingSoonBetaChallenge, isDev);
|
||||
return {
|
||||
...rest,
|
||||
block: Object.keys(blockMap)
|
||||
|
@ -12,6 +12,7 @@ import BackEnd from './views/backend';
|
||||
import Quiz from './views/quiz';
|
||||
import Modern from './views/Modern';
|
||||
|
||||
import { fullBlocksSelector } from '../../entities';
|
||||
import {
|
||||
fetchChallenge,
|
||||
challengeSelector,
|
||||
@ -42,11 +43,14 @@ const mapStateToProps = createSelector(
|
||||
challengeSelector,
|
||||
challengeMetaSelector,
|
||||
paramsSelector,
|
||||
fullBlocksSelector,
|
||||
(
|
||||
{ dashedName, isTranslated },
|
||||
{ viewType, title },
|
||||
params
|
||||
params,
|
||||
blocks
|
||||
) => ({
|
||||
blocks,
|
||||
challenge: dashedName,
|
||||
isTranslated,
|
||||
params,
|
||||
|
@ -54,7 +54,6 @@ class InternetSettings extends PureComponent {
|
||||
}
|
||||
|
||||
handleSubmit(values) {
|
||||
console.log(values);
|
||||
this.props.updateUserBackend(values);
|
||||
}
|
||||
|
||||
|
@ -2,14 +2,19 @@ import Fetchr from 'fetchr';
|
||||
import getUserServices from '../services/user';
|
||||
import getMapServices from '../services/map';
|
||||
import getMapUiServices from '../services/mapUi';
|
||||
import getChallengesForBlockService from '../services/challenge';
|
||||
|
||||
export default function bootServices(app) {
|
||||
const userServices = getUserServices(app);
|
||||
const mapServices = getMapServices(app);
|
||||
const mapUiServices = getMapUiServices(app);
|
||||
|
||||
Fetchr.registerFetcher(userServices);
|
||||
Fetchr.registerFetcher(mapServices);
|
||||
Fetchr.registerFetcher(mapUiServices);
|
||||
const user = getUserServices(app);
|
||||
const map = getMapServices(app);
|
||||
const mapUi = getMapUiServices(app);
|
||||
const challenge = getChallengesForBlockService(app);
|
||||
|
||||
Fetchr.registerFetcher(user);
|
||||
Fetchr.registerFetcher(map);
|
||||
Fetchr.registerFetcher(mapUi);
|
||||
Fetchr.registerFetcher(challenge);
|
||||
|
||||
app.use('/services', Fetchr.middleware());
|
||||
}
|
||||
|
53
server/services/challenge.js
Normal file
53
server/services/challenge.js
Normal file
@ -0,0 +1,53 @@
|
||||
import debug from 'debug';
|
||||
import { pickBy } from 'lodash';
|
||||
import { Observable } from 'rx';
|
||||
|
||||
import { cachedMap, getMapForLang } from '../utils/map';
|
||||
import { shapeChallenges } from '../../common/app/redux/utils';
|
||||
|
||||
const log = debug('fcc:services:challenge');
|
||||
const isDev = debug.enabled('fcc:*');
|
||||
|
||||
export default function getChallengesForBlock(app) {
|
||||
const challengeMap = cachedMap(app.models);
|
||||
return {
|
||||
name: 'challenges-for-block',
|
||||
read: function readChallengesForBlock(
|
||||
req,
|
||||
resource,
|
||||
{ blockName, lang = 'en' } = {},
|
||||
config,
|
||||
cb
|
||||
) {
|
||||
log(`sourcing challenges for the ${blockName} block`);
|
||||
return challengeMap.map(getMapForLang(lang))
|
||||
.flatMap(({
|
||||
result: { superBlocks },
|
||||
entities: {
|
||||
block: fullBlockMap,
|
||||
challenge: challengeMap
|
||||
}
|
||||
}) => {
|
||||
const requestedChallenges = pickBy(
|
||||
challengeMap,
|
||||
ch => ch.block === blockName
|
||||
);
|
||||
const entities = {
|
||||
block: {
|
||||
[blockName]: fullBlockMap[blockName]
|
||||
},
|
||||
challenge: requestedChallenges
|
||||
};
|
||||
const { challenge, block } = shapeChallenges(entities, isDev);
|
||||
return Observable.of({
|
||||
result: { superBlocks },
|
||||
entities: { challenge, block }
|
||||
});
|
||||
})
|
||||
.subscribe(
|
||||
result => cb(null, result),
|
||||
cb
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
@ -5,12 +5,18 @@ import { cachedMap, getMapForLang } from '../utils/map';
|
||||
|
||||
const log = debug('fcc:services:mapUi');
|
||||
|
||||
|
||||
export default function mapUiService(app) {
|
||||
const supportedLangMap = {};
|
||||
const challengeMap = cachedMap(app.models);
|
||||
return {
|
||||
name: 'map-ui',
|
||||
read: function readMapUi(req, resource, { lang = 'en' } = {}, config, cb) {
|
||||
log(`generating mapUi for ${lang}`);
|
||||
if (lang in supportedLangMap) {
|
||||
log(`using cache for ${lang} map`);
|
||||
return cb(null, supportedLangMap[lang]);
|
||||
}
|
||||
return challengeMap.map(getMapForLang(lang))
|
||||
.flatMap(({
|
||||
result: { superBlocks },
|
||||
@ -34,19 +40,39 @@ export default function mapUiService(app) {
|
||||
}, {});
|
||||
const challengeMap = Object.keys(fullChallengeMap)
|
||||
.map(challenge => fullChallengeMap[challenge])
|
||||
.reduce((map, { dashedName, name, id}) => {
|
||||
map[dashedName] = {name, dashedName, id};
|
||||
.reduce((map, challenge) => {
|
||||
const {
|
||||
dashedName,
|
||||
id,
|
||||
title,
|
||||
name,
|
||||
block,
|
||||
isLocked,
|
||||
isComingSoon,
|
||||
isBeta
|
||||
} = challenge;
|
||||
map[dashedName] = {
|
||||
dashedName,
|
||||
id,
|
||||
title,
|
||||
name,
|
||||
block,
|
||||
isLocked,
|
||||
isComingSoon,
|
||||
isBeta
|
||||
};
|
||||
return map;
|
||||
}, {});
|
||||
|
||||
return Observable.of({
|
||||
const mapUi = {
|
||||
result: { superBlocks },
|
||||
entities: {
|
||||
superBlock: superBlockMap,
|
||||
block: blockMap,
|
||||
challenge: challengeMap
|
||||
}
|
||||
});
|
||||
};
|
||||
supportedLangMap[lang] = mapUi;
|
||||
return Observable.of(mapUi);
|
||||
}).subscribe(
|
||||
mapUi => cb(null, mapUi ),
|
||||
err => { log(err); return cb(err); }
|
||||
|
@ -154,6 +154,58 @@ export function getMapForLang(lang) {
|
||||
};
|
||||
}
|
||||
|
||||
export function generateMapForLang(
|
||||
superBlocks,
|
||||
fullSuperBlockMap,
|
||||
fullBlockMap,
|
||||
fullChallengeMap
|
||||
) {
|
||||
const superBlockMap = superBlocks
|
||||
.map(superBlock => fullSuperBlockMap[superBlock])
|
||||
.reduce((map, { dashedName, blocks, title }) => {
|
||||
map[dashedName] = { blocks, title, dashedName};
|
||||
return map;
|
||||
}, {});
|
||||
const blockMap = Object.keys(fullBlockMap)
|
||||
.map(block => fullBlockMap[block])
|
||||
.reduce((map, { dashedName, title, time, challenges }) => {
|
||||
map[dashedName] = { dashedName, title, time, challenges };
|
||||
return map;
|
||||
}, {});
|
||||
const challengeMap = Object.keys(fullChallengeMap)
|
||||
.map(challenge => fullChallengeMap[challenge])
|
||||
.reduce((map, challenge) => {
|
||||
const {
|
||||
dashedName,
|
||||
id,
|
||||
title,
|
||||
block,
|
||||
isLocked,
|
||||
isComingSoon,
|
||||
isBeta
|
||||
} = challenge;
|
||||
map[dashedName] = {
|
||||
dashedName,
|
||||
id,
|
||||
title,
|
||||
block,
|
||||
isLocked,
|
||||
isComingSoon,
|
||||
isBeta
|
||||
};
|
||||
return map;
|
||||
}, {});
|
||||
|
||||
return {
|
||||
result: { superBlocks },
|
||||
entities: {
|
||||
superBlock: superBlockMap,
|
||||
block: blockMap,
|
||||
challenge: challengeMap
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// type ObjectId: String;
|
||||
// getChallengeById(
|
||||
// map: Observable[map],
|
||||
|
Reference in New Issue
Block a user