/** * External dependencies */ import { isEqual, keyBy, omit } from 'lodash'; import { EMPTY_OBJECT, isDefined } from '@nab/utils'; import type { AnyAction } from '@nab/types'; /** * Internal dependencies */ import { INIT_STATE as IS } from '../config'; import type { State as FullState } from '../types'; type State = FullState[ 'experiment' ][ 'goals' ]; import type { GoalAction } from '../actions/goals'; import type { SetupEditor } from '../actions/editor'; type Action = GoalAction | SetupEditor; const INIT_STATE = IS.experiment.goals; export function goals( state = INIT_STATE, action: AnyAction ): State { return actualReducer( state, action as Action ) ?? state; } function actualReducer( state: State, action: Action ): State { switch ( action.type ) { case 'ADD_GOALS': { const newGoals = action.goals.filter( ( goal ) => ! isEqual( state[ goal.id ], goal ) ); if ( ! newGoals.length ) { return state; } return { ...state, ...keyBy( newGoals, 'id' ), }; } case 'UPDATE_GOAL': { const goal = state[ action.goalId ]; if ( ! goal ) { return state; } const newGoal = { ...goal, attributes: { ...goal.attributes, ...action.attributes, }, }; if ( isEqual( goal, newGoal ) ) { return state; } return { ...state, [ action.goalId ]: newGoal, }; } case 'REMOVE_GOALS': return omit( state, action.ids ); case 'SORT_GOALS': return keyBy( action.ids.map( ( id ) => state[ id ] ).filter( isDefined ), 'id' ); case 'SETUP_EDITOR': if ( 'nab/heatmap' === action.experiment.type ) { return EMPTY_OBJECT; } const newState = keyBy( action.experiment.goals.map( ( goal ) => omit( goal, 'conversionActions' ) ), 'id' ); return isEqual( state, newState ) ? state : newState; } }