| 
									
										
										
										
											2018-02-23 12:20:13 +00:00
										 |  |  | import { flow, identity } from 'lodash'; | 
					
						
							| 
									
										
										
										
											2017-07-31 20:04:01 -07:00
										 |  |  | import { Observable } from 'rx'; | 
					
						
							| 
									
										
										
										
											2017-11-09 17:10:30 -08:00
										 |  |  | import { | 
					
						
							|  |  |  |   combineActions, | 
					
						
							|  |  |  |   createAction, | 
					
						
							|  |  |  |   createAsyncTypes, | 
					
						
							|  |  |  |   createTypes, | 
					
						
							|  |  |  |   handleActions | 
					
						
							|  |  |  | } from 'berkeleys-redux-utils'; | 
					
						
							| 
									
										
										
										
											2017-07-31 20:04:01 -07:00
										 |  |  | import { createSelector } from 'reselect'; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import fetchUserEpic from './fetch-user-epic.js'; | 
					
						
							| 
									
										
										
										
											2018-01-29 11:26:24 -08:00
										 |  |  | import nightModeEpic from './night-mode-epic.js'; | 
					
						
							| 
									
										
										
										
											2017-12-07 16:13:19 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-08 15:47:48 +00:00
										 |  |  | import { | 
					
						
							|  |  |  |   updateThemeMetacreator, | 
					
						
							| 
									
										
										
										
											2018-05-15 06:12:05 +01:00
										 |  |  |   entitiesSelector | 
					
						
							| 
									
										
										
										
											2018-03-08 15:47:48 +00:00
										 |  |  | } from '../entities'; | 
					
						
							| 
									
										
										
										
											2018-01-29 11:26:24 -08:00
										 |  |  | import { utils } from '../Flash/redux'; | 
					
						
							| 
									
										
										
										
											2018-02-19 20:32:14 +00:00
										 |  |  | import { paramsSelector } from '../Router/redux'; | 
					
						
							| 
									
										
										
										
											2018-02-23 12:20:13 +00:00
										 |  |  | import { types as map } from '../Map/redux'; | 
					
						
							| 
									
										
										
										
											2017-07-31 20:04:01 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | import ns from '../ns.json'; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-12-07 16:13:19 -08:00
										 |  |  | import { themes, invertTheme } from '../../utils/themes.js'; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-07-31 20:04:01 -07:00
										 |  |  | export const epics = [ | 
					
						
							| 
									
										
										
										
											2018-01-29 11:26:24 -08:00
										 |  |  |   fetchUserEpic, | 
					
						
							| 
									
										
										
										
											2018-05-15 06:12:05 +01:00
										 |  |  |   nightModeEpic | 
					
						
							| 
									
										
										
										
											2016-08-03 12:56:00 -07:00
										 |  |  | ]; | 
					
						
							| 
									
										
										
										
											2017-07-31 20:04:01 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | export const types = createTypes([ | 
					
						
							| 
									
										
										
										
											2017-11-09 17:10:30 -08:00
										 |  |  |   'onRouteHome', | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-07-31 20:04:01 -07:00
										 |  |  |   'appMounted', | 
					
						
							|  |  |  |   'analytics', | 
					
						
							|  |  |  |   'updateTitle', | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-19 20:32:14 +00:00
										 |  |  |   createAsyncTypes('fetchOtherUser'), | 
					
						
							| 
									
										
										
										
											2017-12-07 16:13:19 -08:00
										 |  |  |   createAsyncTypes('fetchUser'), | 
					
						
							| 
									
										
										
										
											2017-07-31 20:04:01 -07:00
										 |  |  |   'showSignIn', | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   'handleError', | 
					
						
							|  |  |  |   // used to hit the server
 | 
					
						
							|  |  |  |   'hardGoTo', | 
					
						
							|  |  |  |   'delayedRedirect', | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // night mode
 | 
					
						
							|  |  |  |   'toggleNightMode', | 
					
						
							| 
									
										
										
										
											2018-01-29 11:26:24 -08:00
										 |  |  |   createAsyncTypes('postTheme') | 
					
						
							| 
									
										
										
										
											2017-07-31 20:04:01 -07:00
										 |  |  | ], ns); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const throwIfUndefined = () => { | 
					
						
							|  |  |  |   throw new TypeError('Argument must not be of  type `undefined`'); | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // createEventMetaCreator({
 | 
					
						
							|  |  |  | //   category: String,
 | 
					
						
							|  |  |  | //   action: String,
 | 
					
						
							|  |  |  | //   label?: String,
 | 
					
						
							|  |  |  | //   value?: Number
 | 
					
						
							|  |  |  | // }) => () => Object
 | 
					
						
							| 
									
										
										
										
											2018-02-23 12:20:13 +00:00
										 |  |  | export function createEventMetaCreator({ | 
					
						
							| 
									
										
										
										
											2017-07-31 20:04:01 -07:00
										 |  |  |   // categories are features or namespaces of the app (capitalized):
 | 
					
						
							|  |  |  |   //   Map, Nav, Challenges, and so on
 | 
					
						
							|  |  |  |   category = throwIfUndefined, | 
					
						
							|  |  |  |   // can be a one word the event
 | 
					
						
							|  |  |  |   // click, play, toggle.
 | 
					
						
							|  |  |  |   // This is not a hard and fast rule
 | 
					
						
							|  |  |  |   action = throwIfUndefined, | 
					
						
							|  |  |  |   // any additional information
 | 
					
						
							|  |  |  |   // when in doubt use redux action type
 | 
					
						
							|  |  |  |   // or a short sentence describing the action
 | 
					
						
							|  |  |  |   label, | 
					
						
							|  |  |  |   // used to tack some specific value for a GA event
 | 
					
						
							|  |  |  |   value | 
					
						
							| 
									
										
										
										
											2018-02-23 12:20:13 +00:00
										 |  |  | } = throwIfUndefined) { | 
					
						
							|  |  |  |   return () => ({ | 
					
						
							|  |  |  |     analytics: { | 
					
						
							|  |  |  |       type: 'event', | 
					
						
							|  |  |  |       category, | 
					
						
							|  |  |  |       action, | 
					
						
							|  |  |  |       label, | 
					
						
							|  |  |  |       value | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2017-07-31 20:04:01 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-11-09 17:10:30 -08:00
										 |  |  | export const onRouteHome = createAction(types.onRouteHome); | 
					
						
							| 
									
										
										
										
											2017-07-31 20:04:01 -07:00
										 |  |  | export const appMounted = createAction(types.appMounted); | 
					
						
							| 
									
										
										
										
											2018-02-23 12:20:13 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-07-31 20:04:01 -07:00
										 |  |  | // updateTitle(title: String) => Action
 | 
					
						
							|  |  |  | export const updateTitle = createAction(types.updateTitle); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-19 20:32:14 +00:00
										 |  |  | // fetchOtherUser() => Action
 | 
					
						
							|  |  |  | // used in combination with fetch-user-epic
 | 
					
						
							|  |  |  | // to fetch another users profile
 | 
					
						
							|  |  |  | export const fetchOtherUser = createAction(types.fetchOtherUser.start); | 
					
						
							|  |  |  | export const fetchOtherUserComplete = createAction( | 
					
						
							|  |  |  |   types.fetchOtherUser.complete, | 
					
						
							|  |  |  |   ({ result }) => result, | 
					
						
							| 
									
										
										
										
											2018-02-23 12:20:13 +00:00
										 |  |  |   identity | 
					
						
							| 
									
										
										
										
											2018-02-19 20:32:14 +00:00
										 |  |  | ); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-07-31 20:04:01 -07:00
										 |  |  | // fetchUser() => Action
 | 
					
						
							|  |  |  | // used in combination with fetch-user-epic
 | 
					
						
							|  |  |  | export const fetchUser = createAction(types.fetchUser); | 
					
						
							| 
									
										
										
										
											2017-12-07 16:13:19 -08:00
										 |  |  | export const fetchUserComplete = createAction( | 
					
						
							|  |  |  |   types.fetchUser.complete, | 
					
						
							|  |  |  |   ({ result }) => result, | 
					
						
							| 
									
										
										
										
											2018-02-23 12:20:13 +00:00
										 |  |  |   identity | 
					
						
							| 
									
										
										
										
											2017-07-31 20:04:01 -07:00
										 |  |  | ); | 
					
						
							| 
									
										
										
										
											2017-12-07 16:13:19 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-07-31 20:04:01 -07:00
										 |  |  | export const showSignIn = createAction(types.showSignIn); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // used when server needs client to redirect
 | 
					
						
							|  |  |  | export const delayedRedirect = createAction(types.delayedRedirect); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // hardGoTo(path: String) => Action
 | 
					
						
							|  |  |  | export const hardGoTo = createAction(types.hardGoTo); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | export const createErrorObservable = error => Observable.just({ | 
					
						
							|  |  |  |   type: types.handleError, | 
					
						
							|  |  |  |   error | 
					
						
							|  |  |  | }); | 
					
						
							| 
									
										
										
										
											2018-01-29 11:26:24 -08:00
										 |  |  | // use sparingly
 | 
					
						
							| 
									
										
										
										
											2017-07-31 20:04:01 -07:00
										 |  |  | // doActionOnError(
 | 
					
						
							|  |  |  | //   actionCreator: (() => Action|Null)
 | 
					
						
							|  |  |  | // ) => (error: Error) => Observable[Action]
 | 
					
						
							|  |  |  | export const doActionOnError = actionCreator => error => Observable.of( | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  |     type: types.handleError, | 
					
						
							|  |  |  |     error | 
					
						
							|  |  |  |   }, | 
					
						
							|  |  |  |   actionCreator() | 
					
						
							|  |  |  | ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | export const toggleNightMode = createAction( | 
					
						
							|  |  |  |   types.toggleNightMode, | 
					
						
							| 
									
										
										
										
											2017-12-07 16:13:19 -08:00
										 |  |  |   null, | 
					
						
							|  |  |  |   (username, theme) => updateThemeMetacreator(username, invertTheme(theme)) | 
					
						
							|  |  |  | ); | 
					
						
							|  |  |  | export const postThemeComplete = createAction( | 
					
						
							| 
									
										
										
										
											2018-01-29 11:26:24 -08:00
										 |  |  |   types.postTheme.complete, | 
					
						
							|  |  |  |   null, | 
					
						
							|  |  |  |   utils.createFlashMetaAction | 
					
						
							|  |  |  | ); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | export const postThemeError = createAction( | 
					
						
							|  |  |  |   types.postTheme.error, | 
					
						
							| 
									
										
										
										
											2017-12-07 16:13:19 -08:00
										 |  |  |   null, | 
					
						
							| 
									
										
										
										
											2018-01-29 11:26:24 -08:00
										 |  |  |   (username, theme, err) => ({ | 
					
						
							|  |  |  |     ...updateThemeMetacreator(username, invertTheme(theme)), | 
					
						
							|  |  |  |     ...utils.createFlashMetaAction(err) | 
					
						
							|  |  |  |   }) | 
					
						
							| 
									
										
										
										
											2017-07-31 20:04:01 -07:00
										 |  |  | ); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-12-07 16:13:19 -08:00
										 |  |  | const defaultState = { | 
					
						
							| 
									
										
										
										
											2017-07-31 20:04:01 -07:00
										 |  |  |   title: 'Learn To Code | freeCodeCamp', | 
					
						
							|  |  |  |   isSignInAttempted: false, | 
					
						
							|  |  |  |   user: '', | 
					
						
							|  |  |  |   csrfToken: '', | 
					
						
							|  |  |  |   superBlocks: [] | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | export const getNS = state => state[ns]; | 
					
						
							|  |  |  | export const csrfSelector = state => getNS(state).csrfToken; | 
					
						
							|  |  |  | export const titleSelector = state => getNS(state).title; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | export const signInLoadingSelector = state => !getNS(state).isSignInAttempted; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-12-07 16:13:19 -08:00
										 |  |  | export const usernameSelector = state => getNS(state).user || ''; | 
					
						
							| 
									
										
										
										
											2017-07-31 20:04:01 -07:00
										 |  |  | export const userSelector = createSelector( | 
					
						
							|  |  |  |   state => getNS(state).user, | 
					
						
							|  |  |  |   state => entitiesSelector(state).user, | 
					
						
							|  |  |  |   (username, userMap) => userMap[username] || {} | 
					
						
							|  |  |  | ); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-19 20:32:14 +00:00
										 |  |  | export const userByNameSelector = state => { | 
					
						
							|  |  |  |   const username = paramsSelector(state).username; | 
					
						
							|  |  |  |   const userMap = entitiesSelector(state).user; | 
					
						
							|  |  |  |   return userMap[username] || {}; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-23 12:20:13 +00:00
										 |  |  | export const themeSelector = flow( | 
					
						
							| 
									
										
										
										
											2017-12-07 16:13:19 -08:00
										 |  |  |   userSelector, | 
					
						
							|  |  |  |   user => user.theme || themes.default | 
					
						
							|  |  |  | ); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-11-09 17:10:30 -08:00
										 |  |  | export const isSignedInSelector = state => !!userSelector(state).username; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | export default handleActions( | 
					
						
							|  |  |  |   () => ({ | 
					
						
							|  |  |  |     [types.updateTitle]: (state, { payload = 'Learn To Code' }) => ({ | 
					
						
							|  |  |  |       ...state, | 
					
						
							|  |  |  |       title: payload + ' | freeCodeCamp' | 
					
						
							|  |  |  |     }), | 
					
						
							| 
									
										
										
										
											2017-07-31 20:04:01 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-12-07 16:13:19 -08:00
										 |  |  |     [types.fetchUser.complete]: (state, { payload: user }) => ({ | 
					
						
							| 
									
										
										
										
											2017-11-09 17:10:30 -08:00
										 |  |  |       ...state, | 
					
						
							|  |  |  |       user | 
					
						
							|  |  |  |     }), | 
					
						
							| 
									
										
										
										
											2018-05-15 06:12:05 +01:00
										 |  |  |     [map.fetchMapUi.complete]: (state, { payload }) => ({ | 
					
						
							| 
									
										
										
										
											2017-11-09 17:10:30 -08:00
										 |  |  |       ...state, | 
					
						
							|  |  |  |       superBlocks: payload.result.superBlocks | 
					
						
							| 
									
										
										
										
											2018-02-24 08:44:12 +00:00
										 |  |  |     }), | 
					
						
							| 
									
										
										
										
											2017-12-07 16:13:19 -08:00
										 |  |  |     [ | 
					
						
							|  |  |  |       combineActions(types.showSignIn, types.fetchUser.complete) | 
					
						
							|  |  |  |     ]: state => ({ | 
					
						
							| 
									
										
										
										
											2017-11-09 17:10:30 -08:00
										 |  |  |       ...state, | 
					
						
							|  |  |  |       isSignInAttempted: true | 
					
						
							|  |  |  |     }), | 
					
						
							|  |  |  |     [types.delayedRedirect]: (state, { payload }) => ({ | 
					
						
							|  |  |  |       ...state, | 
					
						
							|  |  |  |       delayedRedirect: payload | 
					
						
							|  |  |  |     }) | 
					
						
							|  |  |  |   }), | 
					
						
							| 
									
										
										
										
											2017-12-07 16:13:19 -08:00
										 |  |  |   defaultState, | 
					
						
							| 
									
										
										
										
											2017-11-09 17:10:30 -08:00
										 |  |  |   ns | 
					
						
							|  |  |  | ); |