-
Notifications
You must be signed in to change notification settings - Fork 55
Mobile batch results #339
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Mobile batch results #339
Changes from 7 commits
ec0662d
d35ce7f
b1a84b6
9c21e13
90be15a
50d8d23
e802be6
c10d7ca
edea628
100c1d6
df664e0
79488a8
4a1d32a
d615fd8
22beb2c
15329fe
fc76d3e
2e7757c
557bfc7
3edc3d4
9e71d59
922e28c
2b77be6
ef95547
794aeaf
b1ab768
b738bd2
19f0569
7c82cd7
d0a4f35
df927a8
7678132
dffebbf
77bfc8f
d773783
48bafb6
d634ac9
25e497d
1c48f41
cf678bb
3b2b880
72fad25
5d6a393
804b53c
af9f653
403ca61
7a8b67e
3f138bb
602e2ff
43fef77
cabab54
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,123 @@ | ||
import PropTypes from 'prop-types' | ||
import React, { Component } from 'react' | ||
import { Button } from 'react-bootstrap' | ||
import { connect } from 'react-redux' | ||
import styled from 'styled-components' | ||
|
||
import Map from '../map/map' | ||
import Icon from '../narrative/icon' | ||
import NarrativeItineraries from '../narrative/narrative-itineraries' | ||
import { getActiveError } from '../../util/state' | ||
|
||
import MobileContainer from './container' | ||
import ResultsHeaderAndError from './results-header-and-error' | ||
|
||
const StyledMobileContainer = styled(MobileContainer)` | ||
.options > .header { | ||
margin: 10px; | ||
} | ||
|
||
&.otp.mobile .mobile-narrative-container { | ||
bottom: 0; | ||
left: 0; | ||
overflow-y: auto; | ||
padding: 0; | ||
position: fixed; | ||
right: 0; | ||
} | ||
` | ||
|
||
const ExpandMapButton = styled(Button)` | ||
bottom: 25px; | ||
position: absolute; | ||
right: 10px; | ||
binh-dam-ibigroup marked this conversation as resolved.
Show resolved
Hide resolved
|
||
z-index: 999999; | ||
` | ||
|
||
class BatchMobileResultsScreen extends Component { | ||
binh-dam-ibigroup marked this conversation as resolved.
Show resolved
Hide resolved
|
||
static propTypes = { | ||
error: PropTypes.object | ||
} | ||
|
||
constructor () { | ||
super() | ||
this.state = { | ||
itineraryExpanded: false, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not sure if it should be in this PR, but I'd like this itin expanded item to perhaps be moved to a UI query param. Might require some changes to the desktop view as well. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I went ahead and added a URL parameter |
||
mapExpanded: false | ||
} | ||
} | ||
|
||
componentDidUpdate (prevProps) { | ||
// Check if the active leg changed | ||
if (this.props.activeLeg !== prevProps.activeLeg) { | ||
binh-dam-ibigroup marked this conversation as resolved.
Show resolved
Hide resolved
|
||
this._setItineraryExpanded(false) | ||
} | ||
} | ||
|
||
_setItineraryExpanded = itineraryExpanded => { | ||
this.setState({ itineraryExpanded }) | ||
} | ||
|
||
_toggleMapExpanded = () => { | ||
this.setState({ mapExpanded: !this.state.mapExpanded }) | ||
} | ||
|
||
renderMap () { | ||
const { mapExpanded } = this.state | ||
return ( | ||
<div className='results-map' style={{bottom: mapExpanded ? 0 : '60%'}}> | ||
<Map /> | ||
<ExpandMapButton | ||
bsSize='small' | ||
onClick={this._toggleMapExpanded} | ||
> | ||
<Icon name={mapExpanded ? 'list-ul' : 'arrows-alt'} /> | ||
binh-dam-ibigroup marked this conversation as resolved.
Show resolved
Hide resolved
|
||
{mapExpanded ? 'Show results' : 'Expand map'} | ||
</ExpandMapButton> | ||
</div> | ||
) | ||
} | ||
|
||
render () { | ||
const { error } = this.props | ||
const { itineraryExpanded, mapExpanded } = this.state | ||
|
||
return ( | ||
<StyledMobileContainer> | ||
<ResultsHeaderAndError /> | ||
binh-dam-ibigroup marked this conversation as resolved.
Show resolved
Hide resolved
|
||
{!error && (mapExpanded | ||
// Set up two separate renderings of the map according to mapExpanded, | ||
// so that the map is properly sized and itineraries fit under either conditions. | ||
// (Otherwise, if just the narrative is added/removed, the map doesn't resize properly.) | ||
? this.renderMap() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is it possible to have this transition be animated (transition animation to expand the map div and possibly remove the refresh on the map tiles and have a smoother zoom to itinerary)? I find the current approach kind of jarring. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think you would have to reload the tiles anyway after expanding/collapsing the map because the zoom and fit would be different in either case. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
: ( | ||
<> | ||
{!itineraryExpanded && this.renderMap()} | ||
<div className='mobile-narrative-container' style={{top: itineraryExpanded ? '100px' : '40%'}}> | ||
<NarrativeItineraries | ||
containerStyle={{ | ||
display: 'flex', | ||
flexDirection: 'column', | ||
height: '100%', | ||
width: '100%' | ||
}} | ||
onToggleDetailedItinerary={this._setItineraryExpanded} | ||
/> | ||
</div> | ||
</> | ||
)) | ||
} | ||
</StyledMobileContainer> | ||
) | ||
} | ||
} | ||
|
||
// connect to the redux store | ||
|
||
const mapStateToProps = (state, ownProps) => { | ||
return { | ||
error: getActiveError(state.otp) | ||
} | ||
} | ||
|
||
export default connect(mapStateToProps)(BatchMobileResultsScreen) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,142 @@ | ||
import LocationIcon from '@opentripplanner/location-icon' | ||
import PropTypes from 'prop-types' | ||
import React, { Component } from 'react' | ||
import { Button, Col, Row } from 'react-bootstrap' | ||
import { connect } from 'react-redux' | ||
import styled from 'styled-components' | ||
|
||
import * as formActions from '../../actions/form' | ||
import * as uiActions from '../../actions/ui' | ||
import ErrorMessage from '../form/error-message' | ||
import Map from '../map/map' | ||
import { | ||
getActiveError, | ||
getActiveItineraries, | ||
getActiveSearch | ||
} from '../../util/state' | ||
|
||
import MobileNavigationBar from './navigation-bar' | ||
|
||
const LocationContainer = styled.div` | ||
font-weight: 300; | ||
overflow: hidden; | ||
text-overflow: ellipsis; | ||
white-space: nowrap; | ||
` | ||
|
||
const LocationSummaryContainer = styled.div` | ||
height: 50px; | ||
left: 0; | ||
padding-right: 10px; | ||
position: fixed; | ||
right: 0; | ||
top: 50px; | ||
` | ||
|
||
const LocationsSummaryColFromTo = styled(Col)` | ||
font-size: 1.1em; | ||
line-height: 1.2em; | ||
` | ||
|
||
const LocationsSummaryRow = styled(Row)` | ||
padding: 4px 8px; | ||
` | ||
|
||
const StyledLocationIcon = styled(LocationIcon)` | ||
margin: 3px; | ||
` | ||
|
||
class ResultsHeaderAndError extends Component { | ||
static propTypes = { | ||
query: PropTypes.object, | ||
resultCount: PropTypes.number, | ||
setMobileScreen: PropTypes.func | ||
} | ||
|
||
_editSearchClicked = () => { | ||
this.props.clearActiveSearch() | ||
this.props.setMobileScreen(uiActions.MobileScreens.SEARCH_FORM) | ||
} | ||
|
||
render () { | ||
const { error, query, resultCount } = this.props | ||
const headerText = error | ||
? 'No Trip Found' | ||
: (resultCount | ||
? `We Found ${resultCount} Option${resultCount > 1 ? 's' : ''}` | ||
: 'Waiting...' | ||
) | ||
|
||
return ( | ||
<> | ||
<MobileNavigationBar headerText={headerText} /> | ||
|
||
<LocationSummaryContainer> | ||
<LocationsSummaryRow className='locations-summary'> | ||
<LocationsSummaryColFromTo sm={11} xs={8}> | ||
<LocationContainer> | ||
<StyledLocationIcon type='from' /> { query.from ? query.from.name : '' } | ||
</LocationContainer> | ||
<LocationContainer style={{ marginTop: 2 }}> | ||
<StyledLocationIcon type='to' /> { query.to ? query.to.name : '' } | ||
</LocationContainer> | ||
</LocationsSummaryColFromTo> | ||
<Col sm={1} xs={4}> | ||
<Button | ||
className='edit-search-button pull-right' | ||
onClick={this._editSearchClicked} | ||
>Edit</Button> | ||
</Col> | ||
</LocationsSummaryRow> | ||
</LocationSummaryContainer> | ||
|
||
{error && ( | ||
<> | ||
<div className='results-error-map'><Map /></div> | ||
<div className='results-error-message'> | ||
<ErrorMessage error={error} /> | ||
<div className='options-lower-tray mobile-padding'> | ||
<Button | ||
className='back-to-search-button' | ||
onClick={this._editSearchClicked} | ||
style={{ width: '100%' }} | ||
> | ||
<i className='fa fa-arrow-left' /> Back to Search | ||
</Button> | ||
</div> | ||
</div> | ||
</> | ||
)} | ||
</> | ||
) | ||
} | ||
} | ||
|
||
// connect to the redux store | ||
|
||
const mapStateToProps = (state, ownProps) => { | ||
const activeSearch = getActiveSearch(state.otp) | ||
const {useRealtime} = state.otp | ||
const response = !activeSearch | ||
? null | ||
: useRealtime ? activeSearch.response : activeSearch.nonRealtimeResponse | ||
|
||
const itineraries = getActiveItineraries(state.otp) | ||
return { | ||
error: getActiveError(state.otp), | ||
binh-dam-ibigroup marked this conversation as resolved.
Show resolved
Hide resolved
|
||
query: state.otp.currentQuery, | ||
resultCount: | ||
response | ||
? activeSearch.query.routingType === 'ITINERARY' | ||
? itineraries.length | ||
: response.otp.profile.length | ||
binh-dam-ibigroup marked this conversation as resolved.
Show resolved
Hide resolved
|
||
: null | ||
} | ||
} | ||
|
||
const mapDispatchToProps = { | ||
clearActiveSearch: formActions.clearActiveSearch, | ||
setMobileScreen: uiActions.setMobileScreen | ||
} | ||
|
||
export default connect(mapStateToProps, mapDispatchToProps)(ResultsHeaderAndError) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add this component as a required component in the comment about the creation of the
components
variable.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated in e802be6.