11// @flow
22
33import React from 'react'
4- import { View , StyleSheet , SectionList } from 'react-native'
4+ import { StyleSheet , SectionList } from 'react-native'
55import * as c from '../../components/colors'
66import { connect } from 'react-redux'
77import { updateMenuFilters } from '../../../flux'
@@ -12,34 +12,34 @@ import type {
1212 MasterCorIconMapType ,
1313 ProcessedMealType ,
1414 MenuItemContainerType ,
15+ StationMenuType ,
1516} from '../types'
1617import size from 'lodash/size'
1718import values from 'lodash/values'
1819import { ListSeparator , ListSectionHeader } from '../../components/list'
1920import type { FilterType } from '../../components/filter'
2021import { applyFiltersToItem } from '../../components/filter'
2122import { NoticeView } from '../../components/notice'
22- import { FilterMenuToolbar } from './filter-menu-toolbar'
23+ import { FilterMenuToolbar as FilterToolbar } from './filter-menu-toolbar'
2324import { FoodItemRow } from './food-item-row'
2425import { chooseMeal } from '../lib/choose-meal'
2526import { buildFilters } from '../lib/build-filters'
2627
27- type FancyMenuPropsType = TopLevelViewPropsType & {
28+ type Props = TopLevelViewPropsType & {
2829 applyFilters : ( filters : FilterType [ ] , item : MenuItemType ) => boolean ,
29- now : momentT ,
30- name : string ,
30+ cafeMessage ?: ?string ,
3131 filters : FilterType [ ] ,
3232 foodItems : MenuItemContainerType ,
3333 meals : ProcessedMealType [ ] ,
3434 menuCorIcons : MasterCorIconMapType ,
35+ name : string ,
36+ now : momentT ,
3537 onFiltersChange : ( f : FilterType [ ] ) => any ,
38+ onRefresh ?: ?( ) => any ,
39+ refreshing ?: ?boolean ,
3640}
3741
38- const leftSideSpacing = 28
3942const styles = StyleSheet . create ( {
40- container : {
41- flex : 1 ,
42- } ,
4343 inner : {
4444 backgroundColor : c . white ,
4545 } ,
@@ -48,31 +48,38 @@ const styles = StyleSheet.create({
4848 } ,
4949} )
5050
51- const CustomSeparator = ( ) => (
52- < ListSeparator spacing = { { left : leftSideSpacing } } />
53- )
51+ const LEFT_MARGIN = 28
52+ const Separator = ( ) => < ListSeparator spacing = { { left : LEFT_MARGIN } } />
5453
55- class FancyMenuView extends React . PureComponent {
54+ class FancyMenu extends React . PureComponent < any , Props , void > {
5655 static defaultProps = {
5756 applyFilters : applyFiltersToItem ,
5857 }
5958
60- props : FancyMenuPropsType
61-
6259 componentWillMount ( ) {
63- let { foodItems, menuCorIcons, filters, meals, now} = this . props
60+ this . updateFilters ( this . props )
61+ }
62+
63+ componentWillReceiveProps ( nextProps : Props ) {
64+ this . updateFilters ( nextProps )
65+ }
66+
67+ updateFilters = ( props : Props ) => {
68+ const { foodItems, menuCorIcons, filters, meals, now} = props
6469
6570 // prevent ourselves from overwriting the filters from redux on mount
6671 if ( filters . length ) {
6772 return
6873 }
6974
70- const foodItemsArray = values ( foodItems )
71- this . props . onFiltersChange (
72- buildFilters ( foodItemsArray , menuCorIcons , meals , now ) ,
73- )
75+ const newFilters = buildFilters ( values ( foodItems ) , menuCorIcons , meals , now )
76+ props . onFiltersChange ( newFilters )
7477 }
7578
79+ areSpecialsFiltered = filters => Boolean ( filters . find ( this . isSpecialsFilter ) )
80+ isSpecialsFilter = f =>
81+ f . enabled && f . type === 'toggle' && f . spec . label === 'Only Show Specials'
82+
7683 openFilterView = ( ) => {
7784 this . props . navigation . navigate ( 'FilterView' , {
7885 title : `Filter ${ this . props . name } Menu` ,
@@ -81,120 +88,110 @@ class FancyMenuView extends React.PureComponent {
8188 } )
8289 }
8390
91+ groupMenuData = ( props : Props , stations : Array < StationMenuType > ) => {
92+ const { applyFilters, filters, foodItems} = props
93+
94+ const derefrenceMenuItems = menu =>
95+ menu . items
96+ // Dereference each menu item
97+ . map ( id => foodItems [ id ] )
98+ // Ensure that the referenced menu items exist,
99+ // and apply the selected filters to the items in the menu
100+ . filter ( item => item && applyFilters ( filters , item ) )
101+
102+ const menusWithItems = stations
103+ // We're grouping the menu items in a [label, Array<items>] tuple.
104+ . map ( menu => [ menu . label , derefrenceMenuItems ( menu ) ] )
105+ // We only want to show stations with at least one item in them
106+ . filter ( ( [ _ , items ] ) => items . length )
107+ // We need to map the tuples into objects for SectionList
108+ . map ( ( [ title , data ] ) => ( { title, data} ) )
109+
110+ return menusWithItems
111+ }
112+
84113 renderSectionHeader = ( { section : { title} } : any ) => {
85114 const { filters, now, meals} = this . props
86115 const { stations} = chooseMeal ( meals , filters , now )
87116 const menu = stations . find ( m => m . label === title )
88- const note = menu ? menu . note : ''
89117
90118 return (
91119 < ListSectionHeader
92120 title = { title }
93- subtitle = { note }
94- spacing = { { left : leftSideSpacing } }
121+ subtitle = { menu ? menu . note : '' }
122+ spacing = { { left : LEFT_MARGIN } }
95123 />
96124 )
97125 }
98126
99- keyExtractor = ( item , index ) => index . toString ( )
100-
101- render ( ) {
102- const { applyFilters , filters , foodItems , now , meals } = this . props
103-
104- const { label : mealName , stations : stationMenus } = chooseMeal (
105- meals ,
106- filters ,
107- now ,
127+ renderItem = ( { item} : { item : MenuItemType } ) => {
128+ const specialsFilterEnabled = this . areSpecialsFiltered ( this . props . filters )
129+ return (
130+ < FoodItemRow
131+ data = { item }
132+ corIcons = { this . props . menuCorIcons }
133+ badgeSpecials = { ! specialsFilterEnabled }
134+ spacing = { { left : LEFT_MARGIN } }
135+ />
108136 )
137+ }
109138
110- const filteredByMenu = stationMenus
111- . map ( menu => [
112- // we're grouping the menu items in a [label, Array<items>] tuple.
113- menu . label ,
114- // dereference each menu item
115- menu . items
116- . map ( id => foodItems [ id ] )
117- // ensure that the referenced menu items exist
118- // and apply the selected filters to the items in the menu
119- . filter ( item => item && applyFilters ( filters , item ) ) ,
120- ] )
121- // we only want to show stations with at least one item in them
122- . filter ( ( [ _ , items ] ) => items . length )
139+ keyExtractor = ( item , index ) => index . toString ( )
123140
124- // map the tuples into objects for SectionList
125- const grouped = filteredByMenu . map ( ( [ title , data ] ) => ( { title , data } ) )
141+ render ( ) {
142+ const { filters , now , meals , cafeMessage } = this . props
126143
144+ const { label : mealName , stations} = chooseMeal ( meals , filters , now )
127145 const anyFiltersEnabled = filters . some ( f => f . enabled )
128- const specialsFilterEnabled = Boolean (
129- filters . find (
130- f =>
131- f . enabled &&
132- f . type === 'toggle' &&
133- f . spec . label === 'Only Show Specials' ,
134- ) ,
135- )
146+ const specialsFilterEnabled = this . areSpecialsFiltered ( filters )
147+ const groupedMenuData = this . groupMenuData ( this . props , stations )
148+
149+ let message = 'No items to show.'
150+ if ( cafeMessage ) {
151+ message = cafeMessage
152+ } else if ( specialsFilterEnabled && stations . length === 0 ) {
153+ message =
154+ 'No items to show. There may be no specials today. Try changing the filters.'
155+ } else if ( anyFiltersEnabled && ! size ( groupedMenuData ) ) {
156+ message = 'No items to show. Try changing the filters.'
157+ }
158+
159+ const messageView = < NoticeView style = { styles . message } text = { message } />
136160
137- let messageView = (
138- < NoticeView style = { styles . message } text = "No items to show." />
161+ const header = (
162+ < FilterToolbar
163+ date = { now }
164+ title = { mealName }
165+ filters = { filters }
166+ onPress = { this . openFilterView }
167+ />
139168 )
140- if ( specialsFilterEnabled && stationMenus . length === 0 ) {
141- messageView = (
142- < NoticeView
143- style = { styles . message }
144- text = "No items to show. There may be no specials today. Try changing the filters."
145- />
146- )
147- } else if ( anyFiltersEnabled && ! size ( grouped ) ) {
148- messageView = (
149- < NoticeView
150- style = { styles . message }
151- text = "No items to show. Try changing the filters."
152- />
153- )
154- }
155169
156170 return (
157- < View style = { styles . container } >
158- < FilterMenuToolbar
159- date = { now }
160- title = { mealName }
161- filters = { filters }
162- onPress = { this . openFilterView }
163- />
164- < SectionList
165- ItemSeparatorComponent = { CustomSeparator }
166- ListEmptyComponent = { messageView }
167- keyExtractor = { this . keyExtractor }
168- style = { styles . inner }
169- sections = { grouped }
170- renderSectionHeader = { this . renderSectionHeader }
171- renderItem = { ( { item} : { item : MenuItemType } ) => (
172- < FoodItemRow
173- data = { item }
174- corIcons = { this . props . menuCorIcons }
175- badgeSpecials = { ! specialsFilterEnabled }
176- spacing = { { left : leftSideSpacing } }
177- />
178- ) }
179- />
180- </ View >
171+ < SectionList
172+ ItemSeparatorComponent = { Separator }
173+ ListEmptyComponent = { messageView }
174+ ListHeaderComponent = { header }
175+ data = { filters }
176+ keyExtractor = { this . keyExtractor }
177+ onRefresh = { this . props . onRefresh }
178+ refreshing = { this . props . refreshing }
179+ renderItem = { this . renderItem }
180+ renderSectionHeader = { this . renderSectionHeader }
181+ sections = { ( groupedMenuData : any ) }
182+ style = { styles . inner }
183+ />
181184 )
182185 }
183186}
184187
185- function mapStateToProps ( state , actualProps : FancyMenuPropsType ) {
186- return {
187- filters : state . menus [ actualProps . name ] || [ ] ,
188- }
189- }
188+ const mapState = ( state , actualProps : Props ) => ( {
189+ filters : state . menus [ actualProps . name ] || [ ] ,
190+ } )
190191
191- function mapDispatchToProps ( dispatch , actualProps : FancyMenuPropsType ) {
192- return {
193- onFiltersChange : ( filters : FilterType [ ] ) =>
194- dispatch ( updateMenuFilters ( actualProps . name , filters ) ) ,
195- }
196- }
192+ const mapDispatch = ( dispatch , actualProps : Props ) => ( {
193+ onFiltersChange : ( filters : FilterType [ ] ) =>
194+ dispatch ( updateMenuFilters ( actualProps . name , filters ) ) ,
195+ } )
197196
198- export const FancyMenu = connect ( mapStateToProps , mapDispatchToProps ) (
199- FancyMenuView ,
200- )
197+ export const ConnectedFancyMenu = connect ( mapState , mapDispatch ) ( FancyMenu )
0 commit comments