mon-entreprise/source/reducers.js

146 lines
4.4 KiB
JavaScript
Raw Normal View History

import R from 'ramda'
import React from 'react'
import { combineReducers } from 'redux'
import reduceReducers from 'reduce-reducers'
import {reducer as formReducer, formValueSelector} from 'redux-form'
import {rules, findRuleByName } from 'Engine/rules'
import {buildNextSteps} from 'Engine/generateQuestions'
import computeThemeColours from 'Components/themeColours'
2017-07-08 11:28:50 +00:00
import { STEP_ACTION, START_CONVERSATION, EXPLAIN_VARIABLE, POINT_OUT_OBJECTIVES, CHANGE_THEME_COLOUR} from './actions'
import {analyseTopDown} from 'Engine/traverse'
2017-07-08 11:28:50 +00:00
// Our situationGate retrieves data from the "conversation" form
let fromConversation = state => name => formValueSelector('conversation')(state, name)
2017-07-08 11:28:50 +00:00
// assume "wraps" a given situation function with one that overrides its values with
// the given assumptions
let assume = (evaluator, assumptions) => state => name => {
let userInput = evaluator(state)(name)
return userInput != null ? userInput : assumptions[name]
}
2017-07-07 08:41:06 +00:00
export let reduceSteps = (state, action) => {
let flatRules = rules
2017-07-07 08:41:06 +00:00
if (![START_CONVERSATION, STEP_ACTION].includes(action.type))
return state
let rootVariable = action.type == START_CONVERSATION ? action.rootVariable : state.analysedSituation.root.name
2017-07-07 08:41:06 +00:00
let sim = findRuleByName(flatRules, rootVariable),
// Hard assumptions cannot be changed, they are used to specialise a simulator
hardAssumptions = R.pathOr({},['simulateur','hypothèses'],sim),
// Soft assumptions are revealed after the simulation starts, and can be changed
softAssumptions = R.pathOr({},['simulateur','par défaut'],sim),
intermediateSituation = assume(fromConversation, hardAssumptions),
completeSituation = assume(intermediateSituation,softAssumptions)
let situationGate = completeSituation(state),
analysedSituation = analyseTopDown(flatRules,rootVariable)(situationGate)
let newState = {
2017-07-07 08:41:06 +00:00
...state,
analysedSituation,
situationGate: situationGate
2017-07-07 08:41:06 +00:00
}
if (action.type == START_CONVERSATION) {
return {
...newState,
foldedSteps: [],
unfoldedSteps: buildNextSteps(situationGate, flatRules, newState.analysedSituation)
2017-07-07 08:41:06 +00:00
}
}
if (action.type == STEP_ACTION && action.name == 'fold') {
let foldedSteps = [...state.foldedSteps, R.head(state.unfoldedSteps)],
unfoldedSteps = buildNextSteps(situationGate, flatRules, newState.analysedSituation)
// The simulation is "over" - except we can now fill in extra questions
// where the answers were previously given reasonable assumptions
if (unfoldedSteps.length == 0 && !R.isEmpty(softAssumptions)) {
let newSituation = intermediateSituation(state),
reanalyse = analyseTopDown(flatRules,rootVariable)(newSituation),
extraSteps = buildNextSteps(newSituation, flatRules, reanalyse)
return {
...newState,
foldedSteps,
extraSteps,
unfoldedSteps: []
}
}
2017-07-07 08:41:06 +00:00
return {
...newState,
foldedSteps,
unfoldedSteps
2017-07-07 08:41:06 +00:00
}
}
if (action.type == STEP_ACTION && action.name == 'unfold') {
let stepFinder = R.propEq('name', action.step),
foldedSteps = R.reject(stepFinder)(state.foldedSteps),
extraSteps = R.reject(stepFinder)(state.extraSteps)
2017-07-07 08:41:06 +00:00
return {
...newState,
2017-07-07 08:41:06 +00:00
foldedSteps,
extraSteps,
unfoldedSteps: [R.find(stepFinder)(R.concat(state.foldedSteps,state.extraSteps))]
2017-07-07 08:41:06 +00:00
}
}
}
function themeColours(state = computeThemeColours(), {type, colour}) {
2017-07-08 11:28:50 +00:00
if (type == CHANGE_THEME_COLOUR)
return computeThemeColours(colour)
else return state
}
2017-02-09 17:38:51 +00:00
function explainedVariable(state = null, {type, variableName=null}) {
switch (type) {
case EXPLAIN_VARIABLE:
return variableName
2017-02-09 17:38:51 +00:00
default:
return state
}
}
function pointedOutObjectives(state=[], {type, objectives}) {
switch (type) {
case POINT_OUT_OBJECTIVES:
return objectives
default:
return state
}
}
export default reduceReducers(
combineReducers({
sessionId: (id = Math.floor(Math.random() * 1000000000000) + '') => id,
// this is handled by redux-form, pas touche !
form: formReducer,
/* Have forms been filled or ignored ?
false means the user is reconsidering its previous input */
foldedSteps: (steps = []) => steps,
extraSteps: (steps = []) => steps,
unfoldedSteps: (steps = []) => steps,
analysedSituation: (state = []) => state,
situationGate: (state = state => name => null) => state,
refine: (state = false) => state,
themeColours,
explainedVariable,
pointedOutObjectives,
}),
// cross-cutting concerns because here `state` is the whole state tree
reduceSteps
)