11import * as React from 'react'
22import { useEffect , useState } from 'react'
3- import { FlatList , Platform , StyleSheet , Text , TouchableOpacity } from 'react-native'
4- import type { BusSchedule , UnprocessedBusLine } from './types'
3+ import {
4+ FlatList ,
5+ Platform ,
6+ StyleSheet ,
7+ Text ,
8+ TouchableOpacity ,
9+ View ,
10+ } from 'react-native'
11+ import type { BusSchedule , UnprocessedBusLine , DayOfWeek } from './types'
512import {
613 BusStateEnum ,
714 getCurrentBusIteration ,
@@ -13,11 +20,16 @@ import find from 'lodash/find'
1320import findLast from 'lodash/findLast'
1421import { Separator } from '@frogpond/separator'
1522import { BusStopRow } from './components/bus-stop-row'
16- import { ListFooter , ListRow , ListSectionHeader } from '@frogpond/lists'
23+ import { ListFooter , ListRow } from '@frogpond/lists'
1724import { InfoHeader } from '@frogpond/info-header'
1825import * as c from '@frogpond/colors'
1926import { useNavigation } from '@react-navigation/native'
2027import { BUS_FOOTER_MESSAGE } from './constants'
28+ import {
29+ DayPickerHeader ,
30+ momentToDayOfWeek ,
31+ createMomentForDay ,
32+ } from './components/day-picker'
2133
2234const styles = StyleSheet . create ( {
2335 container : {
@@ -31,6 +43,34 @@ const styles = StyleSheet.create({
3143 // erase the gap in the bar caused by the separators' block-ness
3244 marginTop : - 1 ,
3345 } ,
46+ headerContainer : {
47+ paddingLeft : 15 ,
48+ paddingRight : 15 ,
49+ paddingVertical : Platform . OS === 'ios' ? 6 : 10 ,
50+ backgroundColor : c . systemGroupedBackground ,
51+ borderTopWidth : Platform . OS === 'ios' ? StyleSheet . hairlineWidth : 1 ,
52+ borderBottomWidth : Platform . OS === 'ios' ? StyleSheet . hairlineWidth : 0 ,
53+ borderTopColor : c . separator ,
54+ borderBottomColor : c . separator ,
55+ flexDirection : 'row' ,
56+ alignItems : 'center' ,
57+ justifyContent : 'space-between' ,
58+ } ,
59+ headerTextContainer : {
60+ flex : 1 ,
61+ } ,
62+ headerTitle : {
63+ fontSize : 16 ,
64+ fontWeight : Platform . OS === 'ios' ? '500' : '600' ,
65+ color : c . label ,
66+ fontFamily : Platform . OS === 'android' ? 'sans-serif-condensed' : undefined ,
67+ } ,
68+ headerSubtitle : {
69+ fontSize : 16 ,
70+ fontWeight : '400' ,
71+ color : c . secondaryLabel ,
72+ fontFamily : Platform . OS === 'android' ? 'sans-serif-condensed' : undefined ,
73+ } ,
3474} )
3575
3676const isTruthy = ( x : unknown ) => Boolean ( x )
@@ -106,7 +146,7 @@ function deriveFromProps({line, now}: {line: UnprocessedBusLine; now: Moment}) {
106146
107147 if ( process . env . NODE_ENV !== 'production' ) {
108148 // for debugging
109- subtitle += ` (${ now . format ( 'dd h:mma' ) } )`
149+ subtitle += ` (${ now . format ( 'h:mma' ) } )`
110150 }
111151
112152 return {
@@ -127,34 +167,64 @@ export function BusLine(props: Props): JSX.Element {
127167 null ,
128168 )
129169 let [ status , setStatus ] = useState < BusStateEnum > ( 'none' )
170+ let [ selectedDay , setSelectedDay ] = useState < DayOfWeek > ( ( ) =>
171+ momentToDayOfWeek ( now ) ,
172+ )
173+
174+ const currentDay = momentToDayOfWeek ( now )
130175
131176 useEffect ( ( ) => {
177+ const newCurrentDay = momentToDayOfWeek ( now )
178+ if ( selectedDay === currentDay && newCurrentDay !== currentDay ) {
179+ setSelectedDay ( newCurrentDay )
180+ }
181+ } , [ now , selectedDay , currentDay ] )
182+
183+ useEffect ( ( ) => {
184+ const momentForSelectedDay = createMomentForDay ( now , selectedDay )
185+
132186 let {
133187 schedule : scheduleForToday ,
134188 subtitle : scheduleSubtitle ,
135189 currentBusIteration : busIteration ,
136190 status : currentStatus ,
137191 } = deriveFromProps ( {
138192 line,
139- now,
193+ now : momentForSelectedDay ,
140194 } )
141195 setSchedule ( scheduleForToday )
142196 setSubtitle ( scheduleSubtitle )
143197 setStatus ( currentStatus )
144198 setCurrentBusIteration ( busIteration )
145- } , [ line , now ] )
199+ } , [ line , now , selectedDay ] )
146200
147201 let INFO_EL = (
148- < ListSectionHeader
149- subtitle = { subtitle }
150- title = { line . line }
151- titleStyle = { Platform . OS === 'android' ? { color : line . colors . bar } : null }
152- />
202+ < View style = { styles . headerContainer } >
203+ < View style = { styles . headerTextContainer } >
204+ < Text >
205+ < Text style = { [ styles . headerTitle ] } > { line . line } </ Text >
206+ { subtitle ? (
207+ < Text style = { styles . headerSubtitle } >
208+ { ' — ' }
209+ { subtitle }
210+ </ Text >
211+ ) : null }
212+ </ Text >
213+ </ View >
214+
215+ < DayPickerHeader
216+ accentColor = { line . colors . bar }
217+ currentDay = { currentDay }
218+ onDaySelect = { setSelectedDay }
219+ selectedDay = { selectedDay }
220+ />
221+ </ View >
153222 )
154223
155224 let lineMessage = line . notice || ''
156225
157226 let footerElement = < ListFooter title = { BUS_FOOTER_MESSAGE } />
227+
158228 let headerElement = lineMessage ? (
159229 < >
160230 < InfoHeader message = { lineMessage } title = { `About ${ line . line } ` } />
@@ -166,6 +236,8 @@ export function BusLine(props: Props): JSX.Element {
166236
167237 let timetable = schedule ?. timetable ?? [ ]
168238
239+ const momentForSelectedDay = createMomentForDay ( now , selectedDay )
240+
169241 return (
170242 < FlatList
171243 ItemSeparatorComponent = { BusLineSeparator }
@@ -186,7 +258,7 @@ export function BusLine(props: Props): JSX.Element {
186258 departureIndex = { currentBusIteration }
187259 isFirstRow = { index === 0 }
188260 isLastRow = { timetable . length === 0 || index === timetable . length - 1 }
189- now = { now }
261+ now = { momentForSelectedDay }
190262 status = { status }
191263 stop = { item }
192264 />
0 commit comments