import { createSelector } from 'reselect' ; import * as cinema from './../actions/cinema' ; import { Cinema } from './../models' ; import { ScreeningsViewModel } from './../viewModels' ; import { AsyncOperation , AsyncStatus , defaultAsyncOp , makeAsyncOp } from './../utils' ; import * as _ from 'lodash' ; export interface State { cinemas : { [ id ] : Cinema ; } ; currentCinemaId : string ; loadingOp : AsyncOperation ; updates : { [ cinemaId ] : AsyncOperation ; } ; screenings : { [ cinemaId ] : { [ movieId ] : ScreeningsViewModel ; } ; } ; } export const initialState = { cinemas : { } , currentCinemaId : null , loadingOp : defaultAsyncOp ( ) , updates : { } , screenings : { } , } ; export function reducer ( state = initialState , actionRaw ) { switch ( actionRaw . type ) { case cinema . ActionTypes . LOAD : { return { ... state , loadingOp : makeAsyncOp ( AsyncStatus . Pending ) , } ; } case cinema . ActionTypes . LOAD_SUCCESS : { let action = < cinema . LoadSuccessAction > actionRaw ; let cinemas = action . payload . reduce ( ( entities , cinema ) => { return { ... entities , [ cinema . id ] : cinema } ; } , { } ) ; let currentCinemaId = cinemas [ state . currentCinemaId ] ? state . currentCinemaId : cinemas [ Object . keys ( cinemas ) [ 0 ] ] . id ; return { ... state , cinemas : cinemas , currentCinemaId : currentCinemaId , loadingOp : makeAsyncOp ( AsyncStatus . Success ) , } ; } case cinema . ActionTypes . LOAD_FAIL : { let action = < cinema . LoadFailAction > actionRaw ; return { ... state , loadingOp : makeAsyncOp ( AsyncStatus . Fail , action . payload . errorMessage ) } ; } case cinema . ActionTypes . UPDATE : { let action = < cinema . UpdateAction > actionRaw ; let cinemaId = action . payload . cinemaId ; return { ... state , updates : { ... state . updates , [ cinemaId ] : makeAsyncOp ( AsyncStatus . Pending ) , } , } ; } case cinema . ActionTypes . UPDATE_SUCCESS : { let action = < cinema . UpdateSuccessAction > actionRaw ; let cinemas = action . payload . cinemas ; let updatesState = cinemas . reduce ( ( updates , cinema ) => { return { ... updates , [ cinema . id ] : makeAsyncOp ( AsyncStatus . Success ) , } ; } , state . updates ) ; let cinemasState = cinemas . reduce ( ( cinemas , cinema ) => { return { ... cinemas , [ cinema . id ] : cinema } ; } , state . cinemas ) ; return { ... state , cinemas : cinemasState , updates : updatesState } ; } case cinema . ActionTypes . UPDATE_FAIL : { let action = < cinema . UpdateFailAction > actionRaw ; let cinemaId = action . payload . cinemaId ; return { ... state , updates : { ... state . updates , [ cinemaId ] : makeAsyncOp ( AsyncStatus . Fail , action . payload . errorMessage ) , } , } ; } case cinema . ActionTypes . CHANGE_CURRENT : { let action = < cinema . ChangeCurrentAction > actionRaw ; let newCinemaId = action . payload ; if ( state . cinemas [ newCinemaId ] === undefined ) { return state ; } return { ... state , currentCinemaId : newCinemaId } ; } case cinema . ActionTypes . SHOWTIME_CHECK_AND_LOAD : { return state ; } case cinema . ActionTypes . SHOWTIME_LOAD : { let action = < cinema . ShowtimeLoadAction > actionRaw ; let cinemaId = action . payload . cinemaId ; let movieId = action . payload . movieId ; return { ... state , screenings : { ... state . screenings , [ cinemaId ] : { ... state . screenings [ cinemaId ] , [ movieId ] : < ScreeningsViewModel > { loadingOp : makeAsyncOp ( AsyncStatus . Pending ) , } , } , } , } ; } case cinema . ActionTypes . SHOWTIME_LOAD_SUCCESS : { let action = < cinema . ShowtimeLoadSuccessAction > actionRaw ; let cinemaId = action . payload . cinemaId ; let movieId = action . payload . movieId ; let screenings = { showtimes : _ . keyBy ( action . payload . showtimes , s => s . id ) , loadingOp : makeAsyncOp ( AsyncStatus . Success ) , } ; return { ... state , screenings : { ... state . screenings , [ cinemaId ] : { ... state . screenings [ cinemaId ] , [ movieId ] : screenings } , } , } ; } case cinema . ActionTypes . SHOWTIME_LOAD_FAIL : { let action = < cinema . ShowtimeLoadFailAction > actionRaw ; let cinemaId = action . payload . cinemaId ; let movieId = action . payload . movieId ; return { ... state , screenings : { ... state . screenings , [ cinemaId ] : { ... state . screenings [ cinemaId ] , [ movieId ] : { ... state . screenings [ cinemaId ] [ movieId ] , loadingOp : makeAsyncOp ( AsyncStatus . Fail , action . payload . errorMessage ) , } , } , } } ; } default : { return state ; } } } export const getCinemas = ( state ) => state . cinemas ; export const getCurrentCinemaId = ( state ) => state . currentCinemaId ; export const getCurrentCinema = createSelector ( getCinemas , getCurrentCinemaId , ( entities , currentId ) => { return entities [ currentId ] ; } ) ; export const getUpdates = ( state ) => state . updates ; export const getAllScreeningEntities = ( state ) => state . screenings ; export const getCurrentCinemaShowtimes = createSelector ( getCurrentCinemaId , getAllScreeningEntities , ( cinemaId , screenings ) => { return screenings [ cinemaId ] ; } ) ; export const getLoadingOp = ( state ) => state . loadingOp ;