Skip to content

Instantly share code, notes, and snippets.

@juliensanmartin
Last active October 4, 2017 07:20
Show Gist options
  • Save juliensanmartin/329b718b38a4dc58ebe215f0cc569dc1 to your computer and use it in GitHub Desktop.
Save juliensanmartin/329b718b38a4dc58ebe215f0cc569dc1 to your computer and use it in GitHub Desktop.
Dumb component displaying a map. It's a bit heavy and need to get cleaned but it's functionnal
import {
StyleSheet,
View,
TouchableOpacity
} from 'react-native'
import { Icon } from 'react-native-elements'
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import MapView, { PROVIDER_GOOGLE } from 'react-native-maps'
import styled from 'styled-components/native'
import { debounce, isEmpty } from 'lodash'
import LoaderComponent from '../Loader/loader'
import ToastComponent from '../Toast/index'
import VehicleMarkerComponent from '../../components/VehicleMarker/vehicle-marker'
import VehicleDetailsScreen from '../../screens/VehicleDetails/index'
import FilterScreen from '../../screens/Filter/index'
import { INITIAL_REGION } from '../../constants'
const DEFAULT_PADDING = { top: 40, right: 40, bottom: 40, left: 40 }
export default class MapComponent extends Component {
constructor(props) {
super(props)
this.state = {
currentPosition: null,
errorGPS: false,
locationFetched: false,
showVehicleDetailsScreen: false,
marker: null,
showFilterScreen: false
}
this.onHideVehicleDetailsScreen = this.onHideVehicleDetailsScreen.bind(this)
this.onHideFilterScreen = this.onHideFilterScreen.bind(this)
this.debouncedOnRegionChangeComplete = debounce(this.props.onRegionChangeComplete, 500)
}
componentDidMount() {
navigator.geolocation.getCurrentPosition((position) => {
this.props.onPositionFetched(position.coords)
if (this.props.isPositionInVancouver) {
this.setState({
...this.state,
currentPosition: {
latitude: position.coords.latitude,
longitude: position.coords.longitude
},
locationFetched: true
})
} else {
this.setState({
...this.state,
currentPosition: {
latitude: INITIAL_REGION.latitude,
longitude: INITIAL_REGION.longitude
},
locationFetched: true
})
}
this.onCurrentPositionFetch()
},
error => {
this.setState({
...this.state,
errorGPS: true,
locationFetched: true,
currentPosition: {
latitude: INITIAL_REGION.latitude,
longitude: INITIAL_REGION.longitude
}
})
this.onCurrentPositionFetch()
},
{
timeout: 20000,
maximumAge: 1000
}
)
}
onCurrentPositionFetch () {
const currentRegion = {
latitude: this.state.currentPosition.latitude,
longitude: this.state.currentPosition.longitude,
latitudeDelta: INITIAL_REGION.latitudeDelta,
longitudeDelta: INITIAL_REGION.longitudeDelta
}
this.map.animateToRegion(currentRegion)
}
onMarkerPress (marker) {
this.map.fitToCoordinates([marker.latlng, this.state.currentPosition], {
edgePadding: DEFAULT_PADDING,
animated: true
})
this.setState({...this.state, showVehicleDetailsScreen: true, marker})
}
onHideVehicleDetailsScreen() {
this.setState({...this.state, showVehicleDetailsScreen: false})
}
onFilterPress () {
this.setState({...this.state, showFilterScreen: true})
}
onHideFilterScreen() {
this.setState({...this.state, showFilterScreen: false})
}
render() {
const {
isPositionInVancouver,
onRegionChange,
markers,
direction,
loading,
errorApi
} = this.props
return (
<MapContainer>
<MapView
ref={ref => { this.map = ref }}
showsUserLocation={isPositionInVancouver}
followsUserLocation={isPositionInVancouver}
showsMyLocationButton={isPositionInVancouver}
showsPointsOfInterest={false}
showsBuildings={false}
showsIndoors={false}
toolbarEnabled={false}
initialRegion={INITIAL_REGION}
style={styles.map}
onRegionChangeComplete={this.debouncedOnRegionChangeComplete}
onRegionChange={onRegionChange}>
{
markers.map(marker =>
<MapView.Marker
key={marker.id}
coordinate={marker.latlng}
onPress={() => this.onMarkerPress(marker)}>
<IconMarkerComponent marker={marker}/>
</MapView.Marker>
)
}
{ direction &&
<MapView.Polyline
coordinates={direction}
strokeWidth={4}
strokeColor="#6699ff"/>
}
</MapView>
{this.state.showVehicleDetailsScreen &&
<VehicleDetailsScreen
visible={this.state.showVehicleDetailsScreen}
marker={this.state.marker}
currentPosition={this.state.currentPosition}
onClose={this.onHideVehicleDetailsScreen}/>
}
<TouchableOpacityStyle>
<Icon
type='font-awesome'
size={ 20 }
name='sliders'
onPress={() => this.onFilterPress()}
color='#135589'
reverse
raised/>
</TouchableOpacityStyle>
{this.state.showFilterScreen &&
<FilterScreen
visible={this.state.showFilterScreen}
onClose={this.onHideFilterScreen}/>
}
<LoaderContainer>
<LoaderComponent animating={loading}/>
</LoaderContainer>
<View>
<ToastComponent message='Problems to locate your position' visible={this.state.errorGPS}/>
<ToastComponent message='Problems to retrieve vehicles' visible={errorApi !== ''}/>
<ToastComponent message='You are not in Vancouver area' visible={!isPositionInVancouver && this.state.locationFetched}/>
<ToastComponent
message='There is no vehicle around you'
visible={markers.length === 0 && isPositionInVancouver && !loading && isEmpty(errorApi)}
clickable={false}/>
</View>
</MapContainer>
)
}
}
MapComponent.propTypes = {
markers: PropTypes.array.isRequired,
loading: PropTypes.bool.isRequired,
navigation: PropTypes.object.isRequired,
direction: PropTypes.array,
onRegionChangeComplete: PropTypes.func.isRequired,
onRegionChange: PropTypes.func.isRequired,
onPositionFetched: PropTypes.func.isRequired,
isPositionInVancouver: PropTypes.bool.isRequired,
errorApi: PropTypes.string.isRequired
}
const MapContainer = styled.View`
flex: 1;
flex-direction: column;
justify-content: flex-end;
alignItems: center;
backgroundColor: #F5FCFF;
`
const TouchableOpacityStyle = styled.TouchableOpacity`
align-self: flex-start;
flex: 1;
`
const LoaderContainer = styled.View`
align-self: center;
flex: 1;
`
// But need to styled-components this one!!
const styles = StyleSheet.create({
map: {
// width: Screen.width,
// height: Screen.height
...StyleSheet.absoluteFillObject
}
})
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment