Browse Source
* This Geo container needs to be publicly available to comply with Mapbox policymaster
sipp11
7 years ago
22 changed files with 980 additions and 120 deletions
@ -0,0 +1,67 @@ |
|||||||
|
import { RSAA } from 'redux-api-middleware' |
||||||
|
|
||||||
|
import * as types from '../constants/ActionTypes' |
||||||
|
import { RSAAHeaders } from '../utils/ApiClient' |
||||||
|
import { API_URL } from '../constants/Api' |
||||||
|
|
||||||
|
|
||||||
|
export const getStopTime = (query) => ({ |
||||||
|
[RSAA]: { |
||||||
|
endpoint: `${API_URL}/stoptime/?${query || ''}`, |
||||||
|
method: 'GET', |
||||||
|
headers: RSAAHeaders, |
||||||
|
bailout: (state) => state.stoptime.fetching, |
||||||
|
types: [ |
||||||
|
{ |
||||||
|
type: types.STOPTIME_REQUEST, |
||||||
|
meta: { query: query }, |
||||||
|
}, |
||||||
|
types.STOPTIME_SUCCESS, |
||||||
|
types.STOPTIME_FAILURE, |
||||||
|
] |
||||||
|
} |
||||||
|
}) |
||||||
|
|
||||||
|
export const updateStopTime = (id, body) => ({ |
||||||
|
[RSAA]: { |
||||||
|
endpoint: `${API_URL}/stoptime/${id}/`, |
||||||
|
body: JSON.stringify(body), |
||||||
|
method: 'PATCH', |
||||||
|
headers: RSAAHeaders, |
||||||
|
types: [ |
||||||
|
types.STOPTIME_REQUEST, |
||||||
|
types.STOPTIME_UPDATE, |
||||||
|
types.STOPTIME_FAILURE, |
||||||
|
] |
||||||
|
} |
||||||
|
}) |
||||||
|
|
||||||
|
export const createStopTime = (body) => ({ |
||||||
|
[RSAA]: { |
||||||
|
endpoint: `${API_URL}/stoptime/`, |
||||||
|
body: JSON.stringify(body), |
||||||
|
method: 'POST', |
||||||
|
headers: RSAAHeaders, |
||||||
|
types: [ |
||||||
|
types.STOPTIME_REQUEST, |
||||||
|
types.STOPTIME_CREATE, |
||||||
|
types.STOPTIME_FAILURE, |
||||||
|
] |
||||||
|
} |
||||||
|
}) |
||||||
|
|
||||||
|
export const deleteStopTime = (id) => ({ |
||||||
|
[RSAA]: { |
||||||
|
endpoint: `${API_URL}/stoptime/${id}/`, |
||||||
|
method: 'DELETE', |
||||||
|
headers: RSAAHeaders, |
||||||
|
types: [ |
||||||
|
types.STOPTIME_REQUEST, |
||||||
|
{ |
||||||
|
type: types.STOPTIME_DELETE, |
||||||
|
meta: { id } |
||||||
|
}, |
||||||
|
types.STOPTIME_FAILURE, |
||||||
|
] |
||||||
|
} |
||||||
|
}) |
@ -0,0 +1,26 @@ |
|||||||
|
import React from 'react' |
||||||
|
import styled from 'styled-components' |
||||||
|
import { Link } from 'react-router-dom' |
||||||
|
|
||||||
|
const StyledBreadcrumb = styled.nav` |
||||||
|
padding: 0 5px; |
||||||
|
font-size: 0.7rem; |
||||||
|
margin-bottom: 5px !important; |
||||||
|
` |
||||||
|
|
||||||
|
|
||||||
|
const Breadcrumb = (props) => ( |
||||||
|
<StyledBreadcrumb className="breadcrumb has-succeeds-separator" aria-label="breadcrumbs"> |
||||||
|
<ul> |
||||||
|
<li><Link to='/map'>ALL</Link></li> |
||||||
|
{props.agencyId && <li className={`${!props.routeId && 'is-active'}`}> |
||||||
|
<Link to={`/map/${props.agencyId}`} aria-current="page"> |
||||||
|
{props.agencyId}</Link></li>} |
||||||
|
{props.routeId && <li className={`${props.routeId && 'is-active'}`}> |
||||||
|
<Link to={`/map/${props.agencyId}/route/${props.routeId}`} aria-current="page"> |
||||||
|
{props.routeId}</Link></li>} |
||||||
|
</ul> |
||||||
|
</StyledBreadcrumb> |
||||||
|
) |
||||||
|
|
||||||
|
export default Breadcrumb |
@ -0,0 +1,157 @@ |
|||||||
|
import React, { Component } from 'react' |
||||||
|
import { Link, Route } from 'react-router-dom' |
||||||
|
import { connect } from 'react-redux' |
||||||
|
import styled from 'styled-components' |
||||||
|
|
||||||
|
import { logout } from '../utils/Auth' |
||||||
|
import RouteList from './RouteList' |
||||||
|
import RouteDetail from './RouteDetail' |
||||||
|
import Breadcrumb from './Breadcrumb' |
||||||
|
import { polygonReset } from '../actions' |
||||||
|
import store from '../store' |
||||||
|
|
||||||
|
const StyledLink = styled(Link)` |
||||||
|
&[data-active] { |
||||||
|
color: red; |
||||||
|
} |
||||||
|
` |
||||||
|
|
||||||
|
const StyledLRPaddingNav = styled.nav` |
||||||
|
padding-right: 10px; |
||||||
|
padding-left: 0; |
||||||
|
margin-bottom: 5px !important; |
||||||
|
` |
||||||
|
|
||||||
|
const StyledFloatPane = styled.div` |
||||||
|
min-width: 300px; |
||||||
|
height: 100%; |
||||||
|
width: 25vw; |
||||||
|
z-index: 30; |
||||||
|
background: #fefefefe; |
||||||
|
border-radius: 0 5px 5px 0; |
||||||
|
position: fixed; |
||||||
|
left: ${props => props.hidePane ? '-500px' : 0}; |
||||||
|
top: 0px; |
||||||
|
` |
||||||
|
|
||||||
|
const StyledPaneToggler = styled.div` |
||||||
|
width: 20px |
||||||
|
height: 30px; |
||||||
|
background: #fefefefe; |
||||||
|
color: #209cee; |
||||||
|
position: fixed; |
||||||
|
text-align: center; |
||||||
|
left: ${props => props.hidePane ? 0 : '25vw'}; |
||||||
|
top: 30px; |
||||||
|
border-radius: 0 5px 5px 0; |
||||||
|
|
||||||
|
@media (max-width: 1200px) { |
||||||
|
left: ${props => props.hidePane ? 0 : '300px'}; |
||||||
|
} |
||||||
|
` |
||||||
|
|
||||||
|
const StyledScrollY = styled.div` |
||||||
|
height: 100vh; |
||||||
|
overflow: auto; |
||||||
|
padding-bottom: 5rem; |
||||||
|
` |
||||||
|
|
||||||
|
const SimpleAgencyList = (props) => ( |
||||||
|
<nav className="panel"> |
||||||
|
<p className="panel-heading"> |
||||||
|
Agency |
||||||
|
</p> |
||||||
|
{props.agencies.map(ele => ( |
||||||
|
<Link key={ele.agency_id} className="panel-block" to={`/map/${ele.agency_id}`}> |
||||||
|
{ele.name} |
||||||
|
</Link>))} |
||||||
|
</nav> |
||||||
|
) |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class FloatPane extends Component { |
||||||
|
|
||||||
|
state = { hidePane: false, breadcrumb: {} } |
||||||
|
|
||||||
|
renderTopLevel(loggedIn) { |
||||||
|
return ( |
||||||
|
<StyledLRPaddingNav className="level is-mobile"> |
||||||
|
<div className="level-left"> |
||||||
|
<StyledLink to="/" className="navbar-item"> |
||||||
|
<img src="https://static.10ninox.com/goth-rect-640x160.svg" alt="GoTH" width="112" height="28" /> |
||||||
|
</StyledLink> |
||||||
|
</div> |
||||||
|
<div className="level-right"> |
||||||
|
<a className="button is-small is-primary" |
||||||
|
onClick={() => store.dispatch(polygonReset())}> |
||||||
|
<span className="icon"> |
||||||
|
<i className="fas fa-sign-out-alt"></i> |
||||||
|
</span> |
||||||
|
<span>Clear PG</span> |
||||||
|
</a> |
||||||
|
{loggedIn && |
||||||
|
<a className="button is-small is-outlined" |
||||||
|
onClick={() => logout()}> |
||||||
|
<span className="icon"> |
||||||
|
<i className="fas fa-sign-out-alt"></i> |
||||||
|
</span> |
||||||
|
<span>Logout</span> |
||||||
|
</a> |
||||||
|
} |
||||||
|
{!loggedIn && |
||||||
|
<Link className="button is-small is-outlined is-info" to='/login'> |
||||||
|
<span className="icon"> |
||||||
|
<i className="fas fa-sign-in-alt"></i> |
||||||
|
</span> |
||||||
|
<span>Sign in</span> |
||||||
|
</Link> |
||||||
|
} |
||||||
|
</div> |
||||||
|
</StyledLRPaddingNav> |
||||||
|
) |
||||||
|
} |
||||||
|
|
||||||
|
togglePane() { |
||||||
|
this.setState({ hidePane: !this.state.hidePane }) |
||||||
|
} |
||||||
|
|
||||||
|
handleChildMatchParams(params) { |
||||||
|
this.setState({ breadcrumb: params }) |
||||||
|
} |
||||||
|
|
||||||
|
render () { |
||||||
|
const { loggedIn, agency } = this.props |
||||||
|
const { hidePane } = this.state |
||||||
|
const { results } = agency |
||||||
|
|
||||||
|
return ( |
||||||
|
<StyledFloatPane hidePane={hidePane}> |
||||||
|
<StyledPaneToggler hidePane={hidePane} onClick={this.togglePane.bind(this)}> |
||||||
|
<i className='fas fa-align-justify' /> |
||||||
|
</StyledPaneToggler> |
||||||
|
{this.renderTopLevel(loggedIn)} |
||||||
|
<StyledScrollY> |
||||||
|
<Breadcrumb {...this.state.breadcrumb} /> |
||||||
|
<Route exact path={`/map/:agencyId/route/:routeId/:routeParams?`} render={(props) => ( |
||||||
|
<RouteDetail {...props} |
||||||
|
updateBreadcrumb={this.handleChildMatchParams.bind(this)} />)} /> |
||||||
|
<Route exact path={`/map`} render={(props) => ( |
||||||
|
<SimpleAgencyList agencies={results} {...props} />)} /> |
||||||
|
<Route exact path={`/map/stop`} component={RouteList} /> |
||||||
|
<Route exact path={`/map/:agencyId`} render={(props) => ( |
||||||
|
<RouteList {...props} |
||||||
|
updateBreadcrumb={this.handleChildMatchParams.bind(this)} />)} /> |
||||||
|
</StyledScrollY> |
||||||
|
|
||||||
|
</StyledFloatPane> |
||||||
|
) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
const mapStateToProps = state => ({ |
||||||
|
agency: state.agency, |
||||||
|
}) |
||||||
|
export default connect( |
||||||
|
mapStateToProps |
||||||
|
)(FloatPane) |
@ -0,0 +1,167 @@ |
|||||||
|
import React, { Component } from 'react' |
||||||
|
import styled from 'styled-components' |
||||||
|
import { Link, Route } from 'react-router-dom' |
||||||
|
import { connect } from 'react-redux' |
||||||
|
|
||||||
|
import Spinner from './Spinner' |
||||||
|
import { getRoute, polygonUpdate } from '../actions' |
||||||
|
import { getStopTime } from '../actions/stoptime' |
||||||
|
import store from '../store' |
||||||
|
|
||||||
|
/* |
||||||
|
TODO: add shape |
||||||
|
TODO: add Route detail |
||||||
|
|
||||||
|
*/ |
||||||
|
|
||||||
|
const StyledTripDesc = styled.div` |
||||||
|
font-size: 0.87rem; |
||||||
|
line-height: 0.85rem; |
||||||
|
margin-left: 10px; |
||||||
|
` |
||||||
|
|
||||||
|
|
||||||
|
const RouteDesc = (props) => ( |
||||||
|
<div> |
||||||
|
<span className="panel-block"> |
||||||
|
ID: {props.route.id} |
||||||
|
</span> |
||||||
|
<span className="panel-block"> |
||||||
|
long_name: {props.route.long_name} |
||||||
|
</span> |
||||||
|
<span className="panel-block"> |
||||||
|
short_name: {props.route.short_name} |
||||||
|
</span> |
||||||
|
<span className="panel-block"> |
||||||
|
type: {props.route.route_type} |
||||||
|
</span> |
||||||
|
<span className="panel-block"> |
||||||
|
color: <br/>Text #{props.route.text_color || '-'} <br />BG #{props.route.route_text_color} |
||||||
|
</span> |
||||||
|
<span className="panel-block"> |
||||||
|
Sort order: {props.route.route_sort_order} |
||||||
|
</span> |
||||||
|
<span className="panel-block"> |
||||||
|
URL: {props.route.route_url || '-'} |
||||||
|
</span> |
||||||
|
<span className="panel-block"> |
||||||
|
desc: {props.route.desc || '-'} |
||||||
|
</span> |
||||||
|
<span className="panel-block"> |
||||||
|
shapes: {props.route.geosjson !== null ? 'yes' : 'n/a'} |
||||||
|
</span> |
||||||
|
</div> |
||||||
|
) |
||||||
|
|
||||||
|
|
||||||
|
const TripList = (props) => ( |
||||||
|
<div> |
||||||
|
{props.trips.map(ele => ( |
||||||
|
<span key={ele.id} className="panel-block" |
||||||
|
onClick={() => { store.dispatch(getStopTime(`trip=${ele.id}`))}}> |
||||||
|
{ele.trip_id} |
||||||
|
<StyledTripDesc> |
||||||
|
(<b>ID</b> {ele.id}) |
||||||
|
<b>service</b> <Link to={`/calendar/${ele.service.service_id}`}>{ele.service.service_id}</Link> |
||||||
|
<br /> |
||||||
|
{ele.stoptime.count > 0 && <div> |
||||||
|
<b>Stop</b> #{ele.stoptime.count} |
||||||
|
<br /> |
||||||
|
<b>Time period</b> {ele.stoptime.period[0]} - {ele.stoptime.period[1]} |
||||||
|
</div>} |
||||||
|
</StyledTripDesc> |
||||||
|
</span> |
||||||
|
))} |
||||||
|
{props.trips.length === 0 && <span key="empty-trips" className="panel-block"> |
||||||
|
No trip set |
||||||
|
</span>} |
||||||
|
</div> |
||||||
|
) |
||||||
|
|
||||||
|
const FareRuleList = (props) => ( |
||||||
|
<div> |
||||||
|
{props.farerules.map(ele => ( |
||||||
|
<span key={ele.id} className="panel-block"> |
||||||
|
{ele.fare.fare_id} - {ele.fare.price} |
||||||
|
</span> |
||||||
|
))} |
||||||
|
{props.farerules.length === 0 && <span key="empty-farerule" className="panel-block"> |
||||||
|
No fare rule set |
||||||
|
</span>} |
||||||
|
</div> |
||||||
|
) |
||||||
|
|
||||||
|
class RouteDetail extends Component { |
||||||
|
|
||||||
|
state = { |
||||||
|
tab: 'detail', |
||||||
|
} |
||||||
|
|
||||||
|
componentDidMount() { |
||||||
|
const { updateBreadcrumb, match } = this.props |
||||||
|
updateBreadcrumb(match.params) |
||||||
|
} |
||||||
|
|
||||||
|
componentWillMount() { |
||||||
|
const { route, match } = this.props |
||||||
|
if (route.count === 0) { |
||||||
|
store.dispatch(getRoute(`agency=${match.params.agencyId}`)) |
||||||
|
} else { |
||||||
|
this.pushShapeToStore(match, route) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
componentWillReceiveProps(newProps) { |
||||||
|
if (this.props.route.count < newProps.route.count) { |
||||||
|
this.pushShapeToStore(this.props.match, newProps.route) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
pushShapeToStore(match, routeStore) { |
||||||
|
const { routeId } = match.params |
||||||
|
const tRoute = routeStore.results.filter(ele => ele.route_id === routeId) |
||||||
|
if (tRoute.length === 0) |
||||||
|
return |
||||||
|
const one = tRoute[0] |
||||||
|
const oneStyle = {} |
||||||
|
if (one.route_color) |
||||||
|
oneStyle['color'] = `#${one.route_color}` |
||||||
|
store.dispatch(polygonUpdate(one.route_id, one.geojson, oneStyle)) |
||||||
|
} |
||||||
|
|
||||||
|
render() { |
||||||
|
const { route, match } = this.props |
||||||
|
const { routeId, agencyId, routeParams } = match.params |
||||||
|
const tRoute = route.results.filter(ele => ele.route_id === routeId) |
||||||
|
if (tRoute.length === 0) { |
||||||
|
return <Spinner show={true} /> |
||||||
|
} |
||||||
|
const item = tRoute[0] |
||||||
|
const baseUrl = `/map/${agencyId}/route/${routeId}` |
||||||
|
return ( |
||||||
|
<nav className="panel"> |
||||||
|
<p className="panel-heading"> |
||||||
|
{item.long_name} <small>#{item.id}</small> |
||||||
|
</p> |
||||||
|
<p className="panel-tabs"> |
||||||
|
<Link to={`${baseUrl}`} className={`${!routeParams && 'is-active'}`}>detail</Link> |
||||||
|
<Link to={`${baseUrl}/trip`} className={`${routeParams === 'trip' && 'is-active'}`}>trip</Link> |
||||||
|
<Link to={`${baseUrl}/fare`} className={`${routeParams === 'fare' && 'is-active'}`}>fare</Link> |
||||||
|
</p> |
||||||
|
<Route exact path={`${baseUrl}`} render={(props) => ( |
||||||
|
<RouteDesc route={item} {...props} />)} /> |
||||||
|
<Route exact path={`${baseUrl}/fare`} render={(props) => ( |
||||||
|
<FareRuleList farerules={item.farerule_set} {...props} />)} /> |
||||||
|
<Route exact path={`${baseUrl}/trip`} render={(props) => ( |
||||||
|
<TripList trips={item.trip_set} {...props} />)} /> |
||||||
|
</nav> |
||||||
|
) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
const mapStateToProps = state => ({ |
||||||
|
route: state.route, |
||||||
|
}) |
||||||
|
export default connect( |
||||||
|
mapStateToProps |
||||||
|
)(RouteDetail) |
@ -0,0 +1,134 @@ |
|||||||
|
import React, { Component } from 'react' |
||||||
|
import { connect } from 'react-redux' |
||||||
|
import styled from 'styled-components' |
||||||
|
// import { Redirect, Route, Switch } from 'react-router-dom'
|
||||||
|
import { |
||||||
|
Map, TileLayer, CircleMarker, ZoomControl, |
||||||
|
FeatureGroup, GeoJSON } from 'react-leaflet' |
||||||
|
// import { EditControl } from 'react-leaflet-draw'
|
||||||
|
// import L from 'leaflet'
|
||||||
|
|
||||||
|
import { loggedIn } from '../reducers/auth' |
||||||
|
import { geoLocationFailed, geoLocationUpdate, getAgency } from '../actions' |
||||||
|
import FloatPane from '../components/FloatPane' |
||||||
|
import store from '../store' |
||||||
|
|
||||||
|
|
||||||
|
const FullPageBox = styled.div` |
||||||
|
height: 100%; |
||||||
|
min-height: 100vh; |
||||||
|
z-index: 1; |
||||||
|
flex: 1; |
||||||
|
display: flex; |
||||||
|
flex-direction: column; |
||||||
|
` |
||||||
|
|
||||||
|
class Geo extends Component { |
||||||
|
|
||||||
|
constructor(props) { |
||||||
|
super(props) |
||||||
|
this.renderGeoJSON = this.renderGeoJSON.bind(this) |
||||||
|
} |
||||||
|
|
||||||
|
componentWillMount() { |
||||||
|
const { count } = this.props.agency |
||||||
|
if (count === 0) |
||||||
|
store.dispatch(getAgency()) |
||||||
|
} |
||||||
|
|
||||||
|
componentWillUnmount() { |
||||||
|
navigator.geolocation.clearWatch(this.watchID) |
||||||
|
} |
||||||
|
|
||||||
|
componentDidMount() { |
||||||
|
/* if (!navigator.geolocation) { |
||||||
|
getCurrentPosition: (success, failure) => { |
||||||
|
const failureMsg = "Your browser doesn't support geolocation." |
||||||
|
console.log(success, failure) |
||||||
|
// failure(dispatch(locationError(failureMsg)))
|
||||||
|
} |
||||||
|
} */ |
||||||
|
navigator.geolocation.getCurrentPosition( |
||||||
|
(position) => store.dispatch(geoLocationUpdate(position)), |
||||||
|
(error) => store.dispatch(geoLocationFailed(error)), |
||||||
|
{ enableHighAccuracy: true, timeout: 10000, maximumAge: 1000 }, |
||||||
|
) |
||||||
|
this.watchID = navigator.geolocation.watchPosition( |
||||||
|
(position) => store.dispatch(geoLocationUpdate(position)), |
||||||
|
(error) => store.dispatch(geoLocationFailed(error)), |
||||||
|
) |
||||||
|
} |
||||||
|
|
||||||
|
renderGeoJSON() { |
||||||
|
const { polygons } = this.props.geo |
||||||
|
const style = { |
||||||
|
color: '#a63eff', |
||||||
|
weight: 5, |
||||||
|
opacity: 0.65 |
||||||
|
} |
||||||
|
return ( |
||||||
|
<FeatureGroup> |
||||||
|
{polygons && polygons.map(ele => ( |
||||||
|
<GeoJSON |
||||||
|
key={`geojson-${ele.id}`} |
||||||
|
data={ele.geojson} |
||||||
|
style={{...style, ...ele.style}} /> |
||||||
|
))} |
||||||
|
</FeatureGroup> |
||||||
|
) |
||||||
|
} |
||||||
|
|
||||||
|
render() { |
||||||
|
const { loggedIn, geo } = this.props |
||||||
|
const myLocationMarker = geo.coords ? ( |
||||||
|
<CircleMarker |
||||||
|
center={[geo.coords.latitude, geo.coords.longitude]} |
||||||
|
radius={8} |
||||||
|
fillColor={'rgb(33, 150, 243)'} |
||||||
|
fillOpacity={0.9} |
||||||
|
color={'white'} |
||||||
|
stroke |
||||||
|
weight={2} |
||||||
|
opacity={1} |
||||||
|
className='my-location-marker' /> |
||||||
|
) : null |
||||||
|
// const mapCenter = geo.coords
|
||||||
|
// ? [geo.coords.latitude, geo.coords.longitude]
|
||||||
|
// : [13.84626739, 100.538]
|
||||||
|
const mapCenter = [13.84626739, 100.538] |
||||||
|
return ( |
||||||
|
<FullPageBox> |
||||||
|
{/* <Nav loggedIn={loggedIn} /> */} |
||||||
|
<FloatPane loggedIn={loggedIn} {...this.props} /> |
||||||
|
<Map |
||||||
|
center={mapCenter} |
||||||
|
zoom={13} |
||||||
|
length={4} |
||||||
|
zoomControl={false} |
||||||
|
animate |
||||||
|
style={{flex: 1}} |
||||||
|
ref='map'> |
||||||
|
<TileLayer |
||||||
|
attribution='© <a href="http://osm.org/copyright">OpenStreetMap</a> contributors' |
||||||
|
// url="http://{s}.tile.osm.org/{z}/{x}/{y}.png"
|
||||||
|
url='https://api.tiles.mapbox.com/v4/sipp11.p4efho4p/{z}/{x}/{y}.png?access_token=pk.eyJ1Ijoic2lwcDExIiwiYSI6ImNpa2hzbGpzcDAyYWl0eWo3azhkaHR3aWIifQ.cc4CGgGKkpP_8XVa2BUwtQ' |
||||||
|
/> |
||||||
|
<ZoomControl position="topright" /> |
||||||
|
{myLocationMarker} |
||||||
|
{this.renderGeoJSON()} |
||||||
|
</Map> |
||||||
|
</FullPageBox> |
||||||
|
) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
const mapStateToProps = state => ({ |
||||||
|
loggedIn: loggedIn(state.auth), |
||||||
|
agency: state.agency, |
||||||
|
geo: state.geo, |
||||||
|
}) |
||||||
|
export default connect( |
||||||
|
mapStateToProps, |
||||||
|
{} |
||||||
|
)(Geo) |
@ -1,31 +0,0 @@ |
|||||||
import { |
|
||||||
FIRST_INCREMENT_COUNTER, FIRST_ASSIGN_ID |
|
||||||
} from '../constants/ActionTypes' |
|
||||||
|
|
||||||
const initialState = { |
|
||||||
counter: 1, |
|
||||||
obj: {} |
|
||||||
} |
|
||||||
|
|
||||||
const first = (state = initialState, action) => { |
|
||||||
switch (action.type) { |
|
||||||
case FIRST_INCREMENT_COUNTER: |
|
||||||
return { |
|
||||||
...state, |
|
||||||
counter: state.counter +1 |
|
||||||
} |
|
||||||
case FIRST_ASSIGN_ID: |
|
||||||
return { |
|
||||||
...state, |
|
||||||
obj: action.newId |
|
||||||
} |
|
||||||
default: |
|
||||||
return state |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
export default first |
|
||||||
|
|
||||||
|
|
||||||
export const getCounter = state => state.counter |
|
||||||
export const getId = state => state.id |
|
@ -0,0 +1,131 @@ |
|||||||
|
import { |
||||||
|
GEO_LOCATION_SUCCESS, GEO_LOCATION_FAILURE, |
||||||
|
GEO_MARKER_ADD, GEO_MARKER_RESET, GEO_MARKER_UPDATE, |
||||||
|
GEO_POLYGON_ADD, GEO_POLYGON_RESET, GEO_POLYGON_UPDATE, |
||||||
|
STOPTIME_SUCCESS, |
||||||
|
} from '../constants/ActionTypes' |
||||||
|
|
||||||
|
const initialState = { |
||||||
|
timestamp: 0, |
||||||
|
coords: null, |
||||||
|
message: '', |
||||||
|
polygons: [], |
||||||
|
markers: [], |
||||||
|
} |
||||||
|
|
||||||
|
const geo = (state = initialState, action) => { |
||||||
|
switch (action.type) { |
||||||
|
case GEO_LOCATION_SUCCESS: |
||||||
|
/* |
||||||
|
action = { |
||||||
|
coords: { |
||||||
|
latitude: 13.8462448, |
||||||
|
longitude: 100.53825479999999, |
||||||
|
altitude: null, |
||||||
|
altitudeAccuracy: null, |
||||||
|
accuracy: 20, |
||||||
|
speed: 20, |
||||||
|
}, |
||||||
|
timestamp: 1530214120419 |
||||||
|
} |
||||||
|
*/ |
||||||
|
return { |
||||||
|
...state, |
||||||
|
message: '', |
||||||
|
timestamp: action.timestamp, |
||||||
|
coords: {...action.coords}, |
||||||
|
} |
||||||
|
case GEO_LOCATION_FAILURE: |
||||||
|
// action = {code: 3, message: "Timeout expired"}
|
||||||
|
return { |
||||||
|
...state, |
||||||
|
timestamp: 0, |
||||||
|
coords: null, |
||||||
|
message: action.message, |
||||||
|
} |
||||||
|
case GEO_POLYGON_ADD: |
||||||
|
return { |
||||||
|
...state, |
||||||
|
polygons: [ |
||||||
|
...state.polygons, |
||||||
|
action.payload, |
||||||
|
], |
||||||
|
} |
||||||
|
case STOPTIME_SUCCESS: |
||||||
|
/* add all stop into another polygon // we will assign unique id as |
||||||
|
`stoptime` so it won't get in other ways |
||||||
|
*/ |
||||||
|
const stInd = state.polygons.findIndex(ele => ele.id === 'stoptime') |
||||||
|
let polyWstoptime = [...state.polygons] |
||||||
|
let stopFeatures = { |
||||||
|
"type": "FeatureCollection", |
||||||
|
"features": [] |
||||||
|
} |
||||||
|
action.payload.results.map(ele => { |
||||||
|
stopFeatures.features.push({ |
||||||
|
"type": "Feature", |
||||||
|
"properties": { |
||||||
|
"name": `${ele.stop.name} - ${ele.arrival}`, |
||||||
|
}, |
||||||
|
"geometry": ele.stop.geojson, |
||||||
|
}) |
||||||
|
return true |
||||||
|
}) |
||||||
|
const n = { id: 'stoptime', geojson: stopFeatures } |
||||||
|
if (stInd > -1) { |
||||||
|
polyWstoptime[stInd] = n |
||||||
|
} else { |
||||||
|
polyWstoptime.push(n) |
||||||
|
} |
||||||
|
return { |
||||||
|
...state, |
||||||
|
polygons: polyWstoptime, |
||||||
|
} |
||||||
|
case GEO_POLYGON_UPDATE: |
||||||
|
const polyInd = state.polygons.findIndex(ele => ele.id === action.payload.id) |
||||||
|
const oldPolygons = state.polygons |
||||||
|
let newPolys = [...oldPolygons] |
||||||
|
if (polyInd > -1) { |
||||||
|
newPolys[polyInd] = action.payload |
||||||
|
} else { |
||||||
|
newPolys.push(action.payload) |
||||||
|
} |
||||||
|
return { |
||||||
|
...state, |
||||||
|
polygons: newPolys, |
||||||
|
} |
||||||
|
case GEO_POLYGON_RESET: |
||||||
|
return { |
||||||
|
...state, |
||||||
|
polygons: [], |
||||||
|
} |
||||||
|
case GEO_MARKER_ADD: |
||||||
|
return { |
||||||
|
...state, |
||||||
|
markers: [ |
||||||
|
...state.markers, |
||||||
|
action.polygon, |
||||||
|
], |
||||||
|
} |
||||||
|
case GEO_MARKER_UPDATE: |
||||||
|
const markerInd = state.markers.findIndex(ele => ele.id === action.marker.id) |
||||||
|
const oldMarkers = state.markers |
||||||
|
return { |
||||||
|
...state, |
||||||
|
markers: [ |
||||||
|
...oldMarkers.slice(0, markerInd), |
||||||
|
action.polygon, |
||||||
|
...oldMarkers.slice(markerInd + 1), |
||||||
|
], |
||||||
|
} |
||||||
|
case GEO_MARKER_RESET: |
||||||
|
return { |
||||||
|
...state, |
||||||
|
markers: [], |
||||||
|
} |
||||||
|
default: |
||||||
|
return state |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
export default geo |
@ -1,16 +1,18 @@ |
|||||||
import { combineReducers } from 'redux' |
import { combineReducers } from 'redux' |
||||||
import first from './first' |
import geo from './geo' |
||||||
import auth from './auth' |
import auth from './auth' |
||||||
import agency from './agency' |
import agency from './agency' |
||||||
import route from './route' |
import route from './route' |
||||||
import fareattr from './fareattr' |
import fareattr from './fareattr' |
||||||
import calendar from './calendar' |
import calendar from './calendar' |
||||||
|
import stoptime from './stoptime' |
||||||
|
|
||||||
export default combineReducers({ |
export default combineReducers({ |
||||||
auth, |
auth, |
||||||
first, |
geo, |
||||||
agency, |
agency, |
||||||
route, |
route, |
||||||
fareattr, |
fareattr, |
||||||
calendar, |
calendar, |
||||||
|
stoptime, |
||||||
}) |
}) |
||||||
|
@ -0,0 +1,79 @@ |
|||||||
|
import { |
||||||
|
STOPTIME_CREATE, STOPTIME_DELETE, STOPTIME_UPDATE, |
||||||
|
STOPTIME_REQUEST, STOPTIME_SUCCESS, STOPTIME_FAILURE, |
||||||
|
} from '../constants/ActionTypes' |
||||||
|
|
||||||
|
|
||||||
|
const stoptimeInitState = { |
||||||
|
results: [], |
||||||
|
next: null, |
||||||
|
count: 0, |
||||||
|
fetching: false, |
||||||
|
query: '', |
||||||
|
} |
||||||
|
const stoptime = (state = stoptimeInitState, action) => { |
||||||
|
switch (action.type) { |
||||||
|
case STOPTIME_REQUEST: |
||||||
|
const { query } = action.meta |
||||||
|
return { |
||||||
|
...state, |
||||||
|
fetching: true, |
||||||
|
query, |
||||||
|
} |
||||||
|
case STOPTIME_SUCCESS: |
||||||
|
const { count, next, prev, results } = action.payload |
||||||
|
return { |
||||||
|
...state, |
||||||
|
fetching: false, |
||||||
|
count, |
||||||
|
next, |
||||||
|
results: [ |
||||||
|
...((prev) ? state.results : []), |
||||||
|
...results, |
||||||
|
] |
||||||
|
} |
||||||
|
case STOPTIME_UPDATE: |
||||||
|
const { id } = action.payload |
||||||
|
const oldResults = state.results |
||||||
|
const targetInd = oldResults.findIndex(ele => ele.id === id) |
||||||
|
return { |
||||||
|
...state, |
||||||
|
fetching: false, |
||||||
|
results: [ |
||||||
|
...oldResults.slice(0, targetInd), |
||||||
|
action.payload, |
||||||
|
...oldResults.slice(targetInd + 1) |
||||||
|
] |
||||||
|
} |
||||||
|
case STOPTIME_CREATE: |
||||||
|
return { |
||||||
|
...state, |
||||||
|
fetching: false, |
||||||
|
count: state.count + 1, |
||||||
|
results: [ |
||||||
|
...state.results, |
||||||
|
action.payload, |
||||||
|
] |
||||||
|
} |
||||||
|
case STOPTIME_DELETE: |
||||||
|
const deleteInd = state.results.findIndex(ele => ele.id === action.meta.id) |
||||||
|
return { |
||||||
|
...state, |
||||||
|
count: state.count - 1, |
||||||
|
fetching: false, |
||||||
|
results: [ |
||||||
|
...state.results.slice(0, deleteInd), |
||||||
|
...state.results.slice(deleteInd + 1) |
||||||
|
] |
||||||
|
} |
||||||
|
case STOPTIME_FAILURE: |
||||||
|
return { |
||||||
|
...state, |
||||||
|
fetching: false, |
||||||
|
} |
||||||
|
default: |
||||||
|
return state; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
export default stoptime |
Loading…
Reference in new issue