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,40 @@ const styles = StyleSheet.create({
4848 } ,
4949} )
5050
51- const CustomSeparator = ( ) => (
52- < ListSeparator spacing = { { left : leftSideSpacing } } />
53- )
51+ class FancyMenu extends React . PureComponent < any , Props , void > {
52+ static leftSideSpacing = 28
53+ static separator = ( ) => (
54+ < ListSeparator spacing = { { left : FancyMenu . leftSideSpacing } } />
55+ )
5456
55- class FancyMenuView extends React . PureComponent {
5657 static defaultProps = {
5758 applyFilters : applyFiltersToItem ,
5859 }
5960
60- props : FancyMenuPropsType
61-
6261 componentWillMount ( ) {
63- let { foodItems, menuCorIcons, filters, meals, now} = this . props
62+ this . updateFilters ( this . props )
63+ }
64+
65+ componentWillReceiveProps ( nextProps : Props ) {
66+ this . updateFilters ( nextProps )
67+ }
68+
69+ updateFilters = ( props : Props ) => {
70+ const { foodItems, menuCorIcons, filters, meals, now} = props
6471
6572 // prevent ourselves from overwriting the filters from redux on mount
6673 if ( filters . length ) {
6774 return
6875 }
6976
70- const foodItemsArray = values ( foodItems )
71- this . props . onFiltersChange (
72- buildFilters ( foodItemsArray , menuCorIcons , meals , now ) ,
73- )
77+ const newFilters = buildFilters ( values ( foodItems ) , menuCorIcons , meals , now )
78+ props . onFiltersChange ( newFilters )
7479 }
7580
81+ areSpecialsFiltered = filters => Boolean ( filters . find ( this . isSpecialsFilter ) )
82+ isSpecialsFilter = f =>
83+ f . enabled && f . type === 'toggle' && f . spec . label === 'Only Show Specials'
84+
7685 openFilterView = ( ) => {
7786 this . props . navigation . navigate ( 'FilterView' , {
7887 title : `Filter ${ this . props . name } Menu` ,
@@ -81,120 +90,110 @@ class FancyMenuView extends React.PureComponent {
8190 } )
8291 }
8392
93+ groupMenuData = ( props : Props , stations : Array < StationMenuType > ) => {
94+ const { applyFilters, filters, foodItems} = props
95+
96+ const derefrenceMenuItems = menu =>
97+ menu . items
98+ // Dereference each menu item
99+ . map ( id => foodItems [ id ] )
100+ // Ensure that the referenced menu items exist,
101+ // and apply the selected filters to the items in the menu
102+ . filter ( item => item && applyFilters ( filters , item ) )
103+
104+ const menusWithItems = stations
105+ // We're grouping the menu items in a [label, Array<items>] tuple.
106+ . map ( menu => [ menu . label , derefrenceMenuItems ( menu ) ] )
107+ // We only want to show stations with at least one item in them
108+ . filter ( ( [ _ , items ] ) => items . length )
109+ // We need to map the tuples into objects for SectionList
110+ . map ( ( [ title , data ] ) => ( { title, data} ) )
111+
112+ return menusWithItems
113+ }
114+
84115 renderSectionHeader = ( { section : { title} } : any ) => {
85116 const { filters, now, meals} = this . props
86117 const { stations} = chooseMeal ( meals , filters , now )
87118 const menu = stations . find ( m => m . label === title )
88- const note = menu ? menu . note : ''
89119
90120 return (
91121 < ListSectionHeader
92122 title = { title }
93- subtitle = { note }
94- spacing = { { left : leftSideSpacing } }
123+ subtitle = { menu ? menu . note : '' }
124+ spacing = { { left : FancyMenu . leftSideSpacing } }
95125 />
96126 )
97127 }
98128
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 ,
129+ renderItem = ( { item} : { item : MenuItemType } ) => {
130+ const specialsFilterEnabled = this . areSpecialsFiltered ( this . props . filters )
131+ return (
132+ < FoodItemRow
133+ data = { item }
134+ corIcons = { this . props . menuCorIcons }
135+ badgeSpecials = { ! specialsFilterEnabled }
136+ spacing = { { left : FancyMenu . leftSideSpacing } }
137+ />
108138 )
139+ }
109140
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 )
141+ keyExtractor = ( item , index ) => index . toString ( )
123142
124- // map the tuples into objects for SectionList
125- const grouped = filteredByMenu . map ( ( [ title , data ] ) => ( { title , data } ) )
143+ render ( ) {
144+ const { filters , now , meals , cafeMessage } = this . props
126145
146+ const { label : mealName , stations} = chooseMeal ( meals , filters , now )
127147 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- )
148+ const specialsFilterEnabled = this . areSpecialsFiltered ( filters )
149+ const groupedMenuData = this . groupMenuData ( this . props , stations )
150+
151+ let message = 'No items to show.'
152+ if ( cafeMessage ) {
153+ message = cafeMessage
154+ } else if ( specialsFilterEnabled && stations . length === 0 ) {
155+ message =
156+ 'No items to show. There may be no specials today. Try changing the filters.'
157+ } else if ( anyFiltersEnabled && ! size ( groupedMenuData ) ) {
158+ message = 'No items to show. Try changing the filters.'
159+ }
160+
161+ const messageView = < NoticeView style = { styles . message } text = { message } />
136162
137- let messageView = (
138- < NoticeView style = { styles . message } text = "No items to show." />
163+ const header = (
164+ < FilterToolbar
165+ date = { now }
166+ title = { mealName }
167+ filters = { filters }
168+ onPress = { this . openFilterView }
169+ />
139170 )
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- }
155171
156172 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 >
173+ < SectionList
174+ ItemSeparatorComponent = { FancyMenu . separator }
175+ ListEmptyComponent = { messageView }
176+ ListHeaderComponent = { header }
177+ data = { filters }
178+ keyExtractor = { this . keyExtractor }
179+ onRefresh = { this . props . onRefresh }
180+ refreshing = { this . props . refreshing }
181+ renderItem = { this . renderItem }
182+ renderSectionHeader = { this . renderSectionHeader }
183+ sections = { ( groupedMenuData : any ) }
184+ style = { styles . inner }
185+ />
181186 )
182187 }
183188}
184189
185- function mapStateToProps ( state , actualProps : FancyMenuPropsType ) {
186- return {
187- filters : state . menus [ actualProps . name ] || [ ] ,
188- }
189- }
190+ const mapState = ( state , actualProps : Props ) => ( {
191+ filters : state . menus [ actualProps . name ] || [ ] ,
192+ } )
190193
191- function mapDispatchToProps ( dispatch , actualProps : FancyMenuPropsType ) {
192- return {
193- onFiltersChange : ( filters : FilterType [ ] ) =>
194- dispatch ( updateMenuFilters ( actualProps . name , filters ) ) ,
195- }
196- }
194+ const mapDispatch = ( dispatch , actualProps : Props ) => ( {
195+ onFiltersChange : ( filters : FilterType [ ] ) =>
196+ dispatch ( updateMenuFilters ( actualProps . name , filters ) ) ,
197+ } )
197198
198- export const FancyMenu = connect ( mapStateToProps , mapDispatchToProps ) (
199- FancyMenuView ,
200- )
199+ export const ConnectedFancyMenu = connect ( mapState , mapDispatch ) ( FancyMenu )
0 commit comments