Skip to content

Commit da5ae85

Browse files
authored
Merge pull request #1729 from StoDevX/menus-pull-above-filter
Reconfigure the innards of FancyMenu
2 parents 9680dc7 + 83e3222 commit da5ae85

File tree

3 files changed

+249
-222
lines changed

3 files changed

+249
-222
lines changed
Lines changed: 107 additions & 110 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// @flow
22

33
import React from 'react'
4-
import {View, StyleSheet, SectionList} from 'react-native'
4+
import {StyleSheet, SectionList} from 'react-native'
55
import * as c from '../../components/colors'
66
import {connect} from 'react-redux'
77
import {updateMenuFilters} from '../../../flux'
@@ -12,34 +12,34 @@ import type {
1212
MasterCorIconMapType,
1313
ProcessedMealType,
1414
MenuItemContainerType,
15+
StationMenuType,
1516
} from '../types'
1617
import size from 'lodash/size'
1718
import values from 'lodash/values'
1819
import {ListSeparator, ListSectionHeader} from '../../components/list'
1920
import type {FilterType} from '../../components/filter'
2021
import {applyFiltersToItem} from '../../components/filter'
2122
import {NoticeView} from '../../components/notice'
22-
import {FilterMenuToolbar} from './filter-menu-toolbar'
23+
import {FilterMenuToolbar as FilterToolbar} from './filter-menu-toolbar'
2324
import {FoodItemRow} from './food-item-row'
2425
import {chooseMeal} from '../lib/choose-meal'
2526
import {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
3942
const 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

Comments
 (0)