| 
									
										
										
										
											2016-08-01 16:54:33 -07:00
										 |  |  | import _ from 'lodash'; | 
					
						
							|  |  |  | import { Observable } from 'rx'; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-03 20:45:36 -07:00
										 |  |  | import { unDasherize, nameify } from '../utils'; | 
					
						
							| 
									
										
										
										
											2016-08-01 16:54:33 -07:00
										 |  |  | import supportedLanguages from '../../common/utils/supported-languages'; | 
					
						
							| 
									
										
										
										
											2017-08-03 20:45:36 -07:00
										 |  |  | import { | 
					
						
							|  |  |  |   addNameIdMap as _addNameIdToMap, | 
					
						
							|  |  |  |   checkMapData, | 
					
						
							|  |  |  |   getFirstChallenge as _getFirstChallenge | 
					
						
							|  |  |  | } from '../../common/utils/map.js'; | 
					
						
							| 
									
										
										
										
											2016-08-01 16:54:33 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-03 20:45:36 -07:00
										 |  |  | const isDev = process.env.NODE_ENV !== 'production'; | 
					
						
							|  |  |  | const isBeta = !!process.env.BETA; | 
					
						
							|  |  |  | const challengesRegex = /^(bonfire|waypoint|zipline|basejump|checkpoint)/i; | 
					
						
							|  |  |  | const addNameIdMap = _.once(_addNameIdToMap); | 
					
						
							|  |  |  | const getFirstChallenge = _.once(_getFirstChallenge); | 
					
						
							| 
									
										
										
										
											2016-08-01 16:54:33 -07:00
										 |  |  | /* | 
					
						
							|  |  |  |  * interface ChallengeMap { | 
					
						
							| 
									
										
										
										
											2017-07-31 20:04:01 -07:00
										 |  |  |  *   result: { | 
					
						
							|  |  |  |  *     superBlocks: [ ...superBlockDashedName: String ] | 
					
						
							|  |  |  | *    }, | 
					
						
							| 
									
										
										
										
											2016-08-01 16:54:33 -07:00
										 |  |  |  *   entities: { | 
					
						
							|  |  |  |  *     superBlock: { | 
					
						
							| 
									
										
										
										
											2017-08-03 20:45:36 -07:00
										 |  |  |  *       [ ...superBlockDashedName ]: SuperBlock | 
					
						
							| 
									
										
										
										
											2016-08-01 16:54:33 -07:00
										 |  |  |  *     }, | 
					
						
							|  |  |  |  *     block: { | 
					
						
							| 
									
										
										
										
											2017-08-03 20:45:36 -07:00
										 |  |  |  *       [ ...blockDashedNameg ]: Block, | 
					
						
							| 
									
										
										
										
											2016-08-01 16:54:33 -07:00
										 |  |  |  *     challenge: { | 
					
						
							| 
									
										
										
										
											2017-08-03 20:45:36 -07:00
										 |  |  |  *       [ ...challengeDashedNameg ]: Challenge | 
					
						
							| 
									
										
										
										
											2016-08-01 16:54:33 -07:00
										 |  |  |  *     } | 
					
						
							|  |  |  |  *   } | 
					
						
							|  |  |  |  * } | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2017-08-03 20:45:36 -07:00
										 |  |  | export function _cachedMap({ Block, Challenge }) { | 
					
						
							|  |  |  |   const challenges = Challenge.find$({ | 
					
						
							| 
									
										
										
										
											2018-02-21 09:24:52 +00:00
										 |  |  |     order: [ 'order ASC', 'suborder ASC' ], | 
					
						
							|  |  |  |     where: { isPrivate: false } | 
					
						
							| 
									
										
										
										
											2017-08-03 20:45:36 -07:00
										 |  |  |   }); | 
					
						
							|  |  |  |   const challengeMap = challenges | 
					
						
							|  |  |  |     .map( | 
					
						
							|  |  |  |       challenges => challenges | 
					
						
							|  |  |  |         .map(challenge => challenge.toJSON()) | 
					
						
							|  |  |  |         .reduce((hash, challenge) => { | 
					
						
							|  |  |  |           hash[challenge.dashedName] = challenge; | 
					
						
							|  |  |  |           return hash; | 
					
						
							|  |  |  |         }, {}) | 
					
						
							|  |  |  |     ); | 
					
						
							| 
									
										
										
										
											2018-02-21 09:24:52 +00:00
										 |  |  |   const blocks = Block.find$({ | 
					
						
							|  |  |  |     order: [ 'superOrder ASC', 'order ASC' ], | 
					
						
							|  |  |  |     where: { isPrivate: false } | 
					
						
							|  |  |  |   }); | 
					
						
							| 
									
										
										
										
											2017-08-03 20:45:36 -07:00
										 |  |  |   const blockMap = Observable.combineLatest( | 
					
						
							|  |  |  |     blocks.map( | 
					
						
							|  |  |  |       blocks => blocks | 
					
						
							|  |  |  |         .map(block => block.toJSON()) | 
					
						
							|  |  |  |         .reduce((hash, block) => { | 
					
						
							|  |  |  |           hash[block.dashedName] = block; | 
					
						
							|  |  |  |           return hash; | 
					
						
							|  |  |  |         }, {}) | 
					
						
							|  |  |  |     ), | 
					
						
							|  |  |  |     challenges | 
					
						
							|  |  |  |   ) | 
					
						
							|  |  |  |     .map(([ blocksMap, challenges ]) => { | 
					
						
							|  |  |  |       return challenges.reduce((blocksMap, challenge) => { | 
					
						
							|  |  |  |         if (blocksMap[challenge.block].challenges) { | 
					
						
							|  |  |  |           blocksMap[challenge.block].challenges.push(challenge.dashedName); | 
					
						
							|  |  |  |         } else { | 
					
						
							|  |  |  |           blocksMap[challenge.block] = { | 
					
						
							|  |  |  |             ...blocksMap[challenge.block], | 
					
						
							|  |  |  |             challenges: [ challenge.dashedName ] | 
					
						
							|  |  |  |           }; | 
					
						
							| 
									
										
										
										
											2017-07-31 20:04:01 -07:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2017-08-03 20:45:36 -07:00
										 |  |  |         return blocksMap; | 
					
						
							|  |  |  |       }, blocksMap); | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  |   const superBlockMap = blocks.map(blocks => blocks.reduce((map, block) => { | 
					
						
							|  |  |  |     if ( | 
					
						
							|  |  |  |       map[block.superBlock] && | 
					
						
							|  |  |  |       map[block.superBlock].blocks | 
					
						
							|  |  |  |     ) { | 
					
						
							|  |  |  |       map[block.superBlock].blocks.push(block.dashedName); | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |       map[block.superBlock] = { | 
					
						
							|  |  |  |         title: _.startCase(block.superBlock), | 
					
						
							|  |  |  |         order: block.superOrder, | 
					
						
							|  |  |  |         name: nameify(_.startCase(block.superBlock)), | 
					
						
							|  |  |  |         dashedName: block.superBlock, | 
					
						
							|  |  |  |         blocks: [block.dashedName], | 
					
						
							|  |  |  |         message: block.superBlockMessage | 
					
						
							| 
									
										
										
										
											2016-08-01 16:54:33 -07:00
										 |  |  |       }; | 
					
						
							| 
									
										
										
										
											2017-08-03 20:45:36 -07:00
										 |  |  |     } | 
					
						
							|  |  |  |     return map; | 
					
						
							|  |  |  |   }, {})); | 
					
						
							|  |  |  |   const superBlocks = superBlockMap.map(superBlockMap => { | 
					
						
							|  |  |  |     return Object.keys(superBlockMap) | 
					
						
							|  |  |  |       .map(key => superBlockMap[key]) | 
					
						
							|  |  |  |       .map(({ dashedName }) => dashedName); | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  |   return Observable.combineLatest( | 
					
						
							|  |  |  |     superBlockMap, | 
					
						
							|  |  |  |     blockMap, | 
					
						
							|  |  |  |     challengeMap, | 
					
						
							|  |  |  |     superBlocks, | 
					
						
							|  |  |  |     (superBlock, block, challenge, superBlocks) => ({ | 
					
						
							|  |  |  |       entities: { | 
					
						
							|  |  |  |         superBlock, | 
					
						
							|  |  |  |         block, | 
					
						
							|  |  |  |         challenge | 
					
						
							|  |  |  |       }, | 
					
						
							|  |  |  |       result: { | 
					
						
							|  |  |  |         superBlocks | 
					
						
							|  |  |  |       } | 
					
						
							| 
									
										
										
										
											2016-08-01 16:54:33 -07:00
										 |  |  |     }) | 
					
						
							| 
									
										
										
										
											2017-08-03 20:45:36 -07:00
										 |  |  |   ) | 
					
						
							|  |  |  |     .do(checkMapData) | 
					
						
							| 
									
										
										
										
											2016-08-01 16:54:33 -07:00
										 |  |  |     .shareReplay(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-03 20:45:36 -07:00
										 |  |  | export const cachedMap = _.once(_cachedMap); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-01 16:54:33 -07:00
										 |  |  | export function mapChallengeToLang( | 
					
						
							|  |  |  |   { translations = {}, ...challenge }, | 
					
						
							|  |  |  |   lang | 
					
						
							|  |  |  | ) { | 
					
						
							|  |  |  |   if (!supportedLanguages[lang]) { | 
					
						
							|  |  |  |     lang = 'en'; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   const translation = translations[lang] || {}; | 
					
						
							| 
									
										
										
										
											2016-11-06 10:41:37 +00:00
										 |  |  |   const isTranslated = Object.keys(translation).length > 0; | 
					
						
							| 
									
										
										
										
											2016-08-01 16:54:33 -07:00
										 |  |  |   if (lang !== 'en') { | 
					
						
							|  |  |  |     challenge = { | 
					
						
							|  |  |  |       ...challenge, | 
					
						
							| 
									
										
										
										
											2016-11-06 10:41:37 +00:00
										 |  |  |       ...translation, | 
					
						
							|  |  |  |       isTranslated | 
					
						
							| 
									
										
										
										
											2016-08-01 16:54:33 -07:00
										 |  |  |     }; | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2016-11-06 10:41:37 +00:00
										 |  |  |   return { | 
					
						
							|  |  |  |     ...challenge, | 
					
						
							|  |  |  |     isTranslated | 
					
						
							|  |  |  |   }; | 
					
						
							| 
									
										
										
										
											2016-08-01 16:54:33 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | export function getMapForLang(lang) { | 
					
						
							|  |  |  |   return ({ entities: { challenge: challengeMap, ...entities }, result }) => { | 
					
						
							|  |  |  |     entities.challenge = Object.keys(challengeMap) | 
					
						
							|  |  |  |       .reduce((translatedChallengeMap, key) => { | 
					
						
							|  |  |  |         translatedChallengeMap[key] = mapChallengeToLang( | 
					
						
							|  |  |  |           challengeMap[key], | 
					
						
							|  |  |  |           lang | 
					
						
							|  |  |  |         ); | 
					
						
							|  |  |  |         return translatedChallengeMap; | 
					
						
							|  |  |  |       }, {}); | 
					
						
							|  |  |  |     return { result, entities }; | 
					
						
							|  |  |  |   }; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2017-08-03 20:45:36 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | // type ObjectId: String;
 | 
					
						
							|  |  |  | // getChallengeById(
 | 
					
						
							|  |  |  | //   map: Observable[map],
 | 
					
						
							|  |  |  | //   id: ObjectId
 | 
					
						
							|  |  |  | // ) => Observable[Challenge] | Void;
 | 
					
						
							|  |  |  | export function getChallengeById(map, id) { | 
					
						
							|  |  |  |   return Observable.if( | 
					
						
							|  |  |  |     () => !id, | 
					
						
							|  |  |  |     map.map(getFirstChallenge), | 
					
						
							|  |  |  |     map.map(addNameIdMap) | 
					
						
							|  |  |  |       .map(map => { | 
					
						
							|  |  |  |         const { | 
					
						
							|  |  |  |           entities: { challenge: challengeMap, challengeIdToName } | 
					
						
							|  |  |  |         } = map; | 
					
						
							|  |  |  |         let finalChallenge; | 
					
						
							|  |  |  |         const dashedName = challengeIdToName[id]; | 
					
						
							|  |  |  |         finalChallenge = challengeMap[dashedName]; | 
					
						
							|  |  |  |         if (!finalChallenge) { | 
					
						
							|  |  |  |           finalChallenge = getFirstChallenge(map); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         return finalChallenge; | 
					
						
							|  |  |  |       }) | 
					
						
							|  |  |  |   ); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | export function getChallengeInfo(map) { | 
					
						
							|  |  |  |   return map.map(addNameIdMap) | 
					
						
							|  |  |  |     .map(({ | 
					
						
							|  |  |  |       entities: { | 
					
						
							|  |  |  |         challenge: challengeMap, | 
					
						
							|  |  |  |         challengeIdToName | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     }) => ({ | 
					
						
							|  |  |  |       challengeMap, | 
					
						
							|  |  |  |       challengeIdToName | 
					
						
							|  |  |  |     })); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // if challenge is not isComingSoon or isBeta => load
 | 
					
						
							|  |  |  | // if challenge is ComingSoon we are in beta||dev => load
 | 
					
						
							|  |  |  | // if challenge is beta and we are in beta||dev => load
 | 
					
						
							|  |  |  | // else hide
 | 
					
						
							|  |  |  | function loadComingSoonOrBetaChallenge({ | 
					
						
							|  |  |  |   isComingSoon, | 
					
						
							|  |  |  |   isBeta: challengeIsBeta | 
					
						
							|  |  |  | }) { | 
					
						
							|  |  |  |   return !(isComingSoon || challengeIsBeta) || isDev || isBeta; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // this is a hard search
 | 
					
						
							|  |  |  | // falls back to soft search
 | 
					
						
							|  |  |  | export function getChallenge( | 
					
						
							|  |  |  |   challengeDashedName, | 
					
						
							|  |  |  |   blockDashedName, | 
					
						
							|  |  |  |   map, | 
					
						
							|  |  |  |   lang | 
					
						
							|  |  |  | ) { | 
					
						
							|  |  |  |   return map | 
					
						
							|  |  |  |     .flatMap(({ entities, result: { superBlocks } }) => { | 
					
						
							| 
									
										
										
										
											2017-12-27 10:56:30 +00:00
										 |  |  |       const superBlock = entities.superBlock; | 
					
						
							| 
									
										
										
										
											2017-08-03 20:45:36 -07:00
										 |  |  |       const block = entities.block[blockDashedName]; | 
					
						
							|  |  |  |       const challenge = entities.challenge[challengeDashedName]; | 
					
						
							|  |  |  |       return Observable.if( | 
					
						
							|  |  |  |         () => ( | 
					
						
							|  |  |  |           !blockDashedName || | 
					
						
							|  |  |  |           !block || | 
					
						
							|  |  |  |           !challenge || | 
					
						
							|  |  |  |           !loadComingSoonOrBetaChallenge(challenge) | 
					
						
							|  |  |  |         ), | 
					
						
							|  |  |  |         getChallengeByDashedName(challengeDashedName, map), | 
					
						
							|  |  |  |         Observable.just({ block, challenge }) | 
					
						
							|  |  |  |       ) | 
					
						
							|  |  |  |         .map(({ challenge, block }) => ({ | 
					
						
							|  |  |  |           redirect: challenge.block !== blockDashedName ? | 
					
						
							|  |  |  |             `/challenges/${block.dashedName}/${challenge.dashedName}` : | 
					
						
							|  |  |  |             false, | 
					
						
							|  |  |  |           entities: { | 
					
						
							| 
									
										
										
										
											2017-12-27 10:56:30 +00:00
										 |  |  |             superBlock, | 
					
						
							| 
									
										
										
										
											2017-08-03 20:45:36 -07:00
										 |  |  |             challenge: { | 
					
						
							|  |  |  |               [challenge.dashedName]: mapChallengeToLang(challenge, lang) | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |           }, | 
					
						
							|  |  |  |           result: { | 
					
						
							|  |  |  |             block: block.dashedName, | 
					
						
							|  |  |  |             challenge: challenge.dashedName, | 
					
						
							|  |  |  |             superBlocks | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  |         })); | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | export function getBlockForChallenge(map, challenge) { | 
					
						
							|  |  |  |   return map.map(({ entities: { block } }) => block[challenge.block]); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | export function getChallengeByDashedName(dashedName, map) { | 
					
						
							|  |  |  |   const challengeName = unDasherize(dashedName) | 
					
						
							|  |  |  |     .replace(challengesRegex, ''); | 
					
						
							|  |  |  |   const testChallengeName = new RegExp(challengeName, 'i'); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return map | 
					
						
							|  |  |  |     .map(({ entities }) => entities.challenge) | 
					
						
							|  |  |  |     .flatMap(challengeMap => { | 
					
						
							|  |  |  |       return Observable.from(Object.keys(challengeMap)) | 
					
						
							|  |  |  |         .map(key => challengeMap[key]); | 
					
						
							|  |  |  |     }) | 
					
						
							|  |  |  |     .filter(challenge => { | 
					
						
							|  |  |  |       return loadComingSoonOrBetaChallenge(challenge) && | 
					
						
							|  |  |  |         testChallengeName.test(challenge.name); | 
					
						
							|  |  |  |     }) | 
					
						
							|  |  |  |     .last({ defaultValue: null }) | 
					
						
							|  |  |  |     .flatMap(challengeOrNull => { | 
					
						
							|  |  |  |       return Observable.if( | 
					
						
							|  |  |  |         () => !!challengeOrNull, | 
					
						
							|  |  |  |         Observable.just(challengeOrNull), | 
					
						
							|  |  |  |         map.map(getFirstChallenge) | 
					
						
							|  |  |  |       ); | 
					
						
							|  |  |  |     }) | 
					
						
							|  |  |  |     .flatMap(challenge => { | 
					
						
							|  |  |  |       return getBlockForChallenge(map, challenge) | 
					
						
							|  |  |  |         .map(block => ({ challenge, block })); | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 |