feat(mapUi): Create mapUi specific service
This commit is contained in:
committed by
Stuart Taylor
parent
e0d084465e
commit
1d420b835c
46
common/app/Map/redux/fetch-map-ui-epic.js
Normal file
46
common/app/Map/redux/fetch-map-ui-epic.js
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
import { ofType } from 'redux-epic';
|
||||||
|
import debug from 'debug';
|
||||||
|
|
||||||
|
import {
|
||||||
|
types as appTypes,
|
||||||
|
createErrorObservable
|
||||||
|
} from '../../redux';
|
||||||
|
import { types, fetchMapUiComplete } from './';
|
||||||
|
import { langSelector } from '../../Router/redux';
|
||||||
|
import { shapeChallenges } from '../../redux/utils';
|
||||||
|
|
||||||
|
const isDev = debug.enabled('fcc:*');
|
||||||
|
|
||||||
|
export default function fetchMapUiEpic(
|
||||||
|
actions,
|
||||||
|
{ getState },
|
||||||
|
{ services }
|
||||||
|
) {
|
||||||
|
return actions::ofType(
|
||||||
|
appTypes.appMounted
|
||||||
|
)
|
||||||
|
.flatMapLatest(() => {
|
||||||
|
const lang = langSelector(getState());
|
||||||
|
const options = {
|
||||||
|
params: { lang },
|
||||||
|
service: 'map-ui'
|
||||||
|
};
|
||||||
|
return services.readService$(options)
|
||||||
|
.retry(3)
|
||||||
|
.map(({ entities, ...res }) => ({
|
||||||
|
entities: shapeChallenges(
|
||||||
|
entities,
|
||||||
|
isDev
|
||||||
|
),
|
||||||
|
...res
|
||||||
|
}))
|
||||||
|
.map(({ entities, result } = {}) => {
|
||||||
|
return fetchMapUiComplete(
|
||||||
|
entities,
|
||||||
|
result
|
||||||
|
);
|
||||||
|
})
|
||||||
|
.startWith({ type: types.fetchMapUi.start })
|
||||||
|
.catch(createErrorObservable);
|
||||||
|
});
|
||||||
|
}
|
@ -1,25 +1,26 @@
|
|||||||
import {
|
import {
|
||||||
createAction,
|
createAction,
|
||||||
|
createAsyncTypes,
|
||||||
createTypes,
|
createTypes,
|
||||||
handleActions
|
handleActions
|
||||||
} from 'berkeleys-redux-utils';
|
} from 'berkeleys-redux-utils';
|
||||||
import { createSelector } from 'reselect';
|
import { createSelector } from 'reselect';
|
||||||
import noop from 'lodash/noop';
|
import { capitalize, noop} from 'lodash';
|
||||||
import capitalize from 'lodash/capitalize';
|
|
||||||
|
|
||||||
import * as utils from './utils.js';
|
import * as utils from './utils.js';
|
||||||
import ns from '../ns.json';
|
import ns from '../ns.json';
|
||||||
import {
|
import {
|
||||||
types as app,
|
|
||||||
createEventMetaCreator
|
createEventMetaCreator
|
||||||
} from '../../redux';
|
} from '../../redux';
|
||||||
|
|
||||||
export const epics = [];
|
import fewtchMapUiEpic from './fetch-map-ui-epic';
|
||||||
|
|
||||||
|
export const epics = [ fewtchMapUiEpic ];
|
||||||
|
|
||||||
export const types = createTypes([
|
export const types = createTypes([
|
||||||
'onRouteMap',
|
'onRouteMap',
|
||||||
'initMap',
|
'initMap',
|
||||||
|
createAsyncTypes('fetchMapUi'),
|
||||||
'toggleThisPanel',
|
'toggleThisPanel',
|
||||||
|
|
||||||
'isAllCollapsed',
|
'isAllCollapsed',
|
||||||
@ -31,6 +32,12 @@ export const types = createTypes([
|
|||||||
|
|
||||||
export const initMap = createAction(types.initMap);
|
export const initMap = createAction(types.initMap);
|
||||||
|
|
||||||
|
export const fetchMapUiComplete = createAction(
|
||||||
|
types.fetchMapUi.complete,
|
||||||
|
(entities, result) => ({ entities, result }),
|
||||||
|
entities => ({ entities })
|
||||||
|
);
|
||||||
|
|
||||||
export const toggleThisPanel = createAction(types.toggleThisPanel);
|
export const toggleThisPanel = createAction(types.toggleThisPanel);
|
||||||
export const collapseAll = createAction(types.collapseAll);
|
export const collapseAll = createAction(types.collapseAll);
|
||||||
|
|
||||||
@ -100,7 +107,7 @@ export default handleActions(
|
|||||||
mapUi
|
mapUi
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
[app.fetchChallenges.complete]: (state, { payload }) => {
|
[types.fetchMapUi.complete]: (state, { payload }) => {
|
||||||
const { entities, result } = payload;
|
const { entities, result } = payload;
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { findIndex, invert, pick, property } from 'lodash';
|
import { findIndex, invert, pick, property, merge } from 'lodash';
|
||||||
import uuid from 'uuid/v4';
|
import uuid from 'uuid/v4';
|
||||||
import {
|
import {
|
||||||
composeReducers,
|
composeReducers,
|
||||||
@ -162,10 +162,7 @@ export default composeReducers(
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
return {
|
return merge(state, action.meta.entities);
|
||||||
...state,
|
|
||||||
...action.meta.entities
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
return state;
|
return state;
|
||||||
},
|
},
|
||||||
|
@ -19,7 +19,7 @@ import { langSelector } from '../Router/redux';
|
|||||||
|
|
||||||
const isDev = debug.enabled('fcc:*');
|
const isDev = debug.enabled('fcc:*');
|
||||||
|
|
||||||
export function fetchChallengeEpic(actions, { getState }, { services }) {
|
export default function fetchChallengeEpic(actions, { getState }, { services }) {
|
||||||
return actions::ofType(challenge.onRouteChallenges)
|
return actions::ofType(challenge.onRouteChallenges)
|
||||||
.filter(({ payload }) => !isChallengeLoaded(getState(), payload))
|
.filter(({ payload }) => !isChallengeLoaded(getState(), payload))
|
||||||
.flatMapLatest(({ payload: params }) => {
|
.flatMapLatest(({ payload: params }) => {
|
||||||
@ -84,4 +84,3 @@ export function fetchChallengesEpic(
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export default combineEpics(fetchChallengeEpic, fetchChallengesEpic);
|
|
||||||
|
@ -1,12 +1,15 @@
|
|||||||
import Fetchr from 'fetchr';
|
import Fetchr from 'fetchr';
|
||||||
import getUserServices from '../services/user';
|
import getUserServices from '../services/user';
|
||||||
import getMapServices from '../services/map';
|
import getMapServices from '../services/map';
|
||||||
|
import getMapUiServices from '../services/mapUi';
|
||||||
|
|
||||||
export default function bootServices(app) {
|
export default function bootServices(app) {
|
||||||
const userServices = getUserServices(app);
|
const userServices = getUserServices(app);
|
||||||
const mapServices = getMapServices(app);
|
const mapServices = getMapServices(app);
|
||||||
|
const mapUiServices = getMapUiServices(app);
|
||||||
|
|
||||||
Fetchr.registerFetcher(userServices);
|
Fetchr.registerFetcher(userServices);
|
||||||
Fetchr.registerFetcher(mapServices);
|
Fetchr.registerFetcher(mapServices);
|
||||||
|
Fetchr.registerFetcher(mapUiServices);
|
||||||
app.use('/services', Fetchr.middleware());
|
app.use('/services', Fetchr.middleware());
|
||||||
}
|
}
|
||||||
|
56
server/services/mapUi.js
Normal file
56
server/services/mapUi.js
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
import debug from 'debug';
|
||||||
|
import { Observable } from 'rx';
|
||||||
|
|
||||||
|
import { cachedMap, getMapForLang } from '../utils/map';
|
||||||
|
|
||||||
|
const log = debug('fcc:services:mapUi');
|
||||||
|
|
||||||
|
export default function mapUiService(app) {
|
||||||
|
const challengeMap = cachedMap(app.models);
|
||||||
|
return {
|
||||||
|
name: 'map-ui',
|
||||||
|
read: function readMapUi(req, resource, { lang = 'en' } = {}, config, cb) {
|
||||||
|
log(`generating mapUi for ${lang}`);
|
||||||
|
return challengeMap.map(getMapForLang(lang))
|
||||||
|
.flatMap(({
|
||||||
|
result: { superBlocks },
|
||||||
|
entities: {
|
||||||
|
superBlock: fullSuperBlockMap,
|
||||||
|
block: fullBlockMap,
|
||||||
|
challenge: 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, { dashedName, name, id}) => {
|
||||||
|
map[dashedName] = {name, dashedName, id};
|
||||||
|
return map;
|
||||||
|
}, {});
|
||||||
|
|
||||||
|
return Observable.of({
|
||||||
|
result: { superBlocks },
|
||||||
|
entities: {
|
||||||
|
superBlock: superBlockMap,
|
||||||
|
block: blockMap,
|
||||||
|
challenge: challengeMap
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}).subscribe(
|
||||||
|
mapUi => cb(null, mapUi ),
|
||||||
|
err => { log(err); return cb(err); }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
@ -74,43 +74,43 @@ export function _cachedMap({ Block, Challenge }) {
|
|||||||
}, blocksMap);
|
}, blocksMap);
|
||||||
});
|
});
|
||||||
const superBlockMap = blocks.map(blocks => blocks.reduce((map, block) => {
|
const superBlockMap = blocks.map(blocks => blocks.reduce((map, block) => {
|
||||||
if (
|
if (
|
||||||
map[block.superBlock] &&
|
map[block.superBlock] &&
|
||||||
map[block.superBlock].blocks
|
map[block.superBlock].blocks
|
||||||
) {
|
) {
|
||||||
map[block.superBlock].blocks.push(block.dashedName);
|
map[block.superBlock].blocks.push(block.dashedName);
|
||||||
} else {
|
} else {
|
||||||
map[block.superBlock] = {
|
map[block.superBlock] = {
|
||||||
title: _.startCase(block.superBlock),
|
title: _.startCase(block.superBlock),
|
||||||
order: block.superOrder,
|
order: block.superOrder,
|
||||||
name: nameify(_.startCase(block.superBlock)),
|
name: nameify(_.startCase(block.superBlock)),
|
||||||
dashedName: block.superBlock,
|
dashedName: block.superBlock,
|
||||||
blocks: [block.dashedName],
|
blocks: [block.dashedName],
|
||||||
message: block.superBlockMessage
|
message: block.superBlockMessage
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
return map;
|
return map;
|
||||||
}, {}));
|
}, {}));
|
||||||
const superBlocks = superBlockMap.map(superBlockMap => {
|
const superBlocks = superBlockMap.map(superBlockMap => {
|
||||||
return Object.keys(superBlockMap)
|
return Object.keys(superBlockMap)
|
||||||
.map(key => superBlockMap[key])
|
.map(key => superBlockMap[key])
|
||||||
.map(({ dashedName }) => dashedName);
|
.map(({ dashedName }) => dashedName);
|
||||||
});
|
});
|
||||||
return Observable.combineLatest(
|
return Observable.combineLatest(
|
||||||
superBlockMap,
|
superBlockMap,
|
||||||
blockMap,
|
blockMap,
|
||||||
challengeMap,
|
challengeMap,
|
||||||
superBlocks,
|
superBlocks,
|
||||||
(superBlock, block, challenge, superBlocks) => ({
|
(superBlock, block, challenge, superBlocks) => ({
|
||||||
entities: {
|
entities: {
|
||||||
superBlock,
|
superBlock,
|
||||||
block,
|
block,
|
||||||
challenge
|
challenge
|
||||||
},
|
},
|
||||||
result: {
|
result: {
|
||||||
superBlocks
|
superBlocks
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
.do(checkMapData)
|
.do(checkMapData)
|
||||||
.shareReplay();
|
.shareReplay();
|
||||||
|
Reference in New Issue
Block a user