@goongmaps/goong-map-react-native
Version:
A Goong GL react native module for creating custom maps
217 lines (183 loc) • 4.99 kB
JavaScript
import React from 'react';
import PropTypes from 'prop-types';
import locationManager from '../modules/location/locationManager';
import Annotation from './annotations/Annotation'; // eslint-disable-line import/no-cycle
import CircleLayer from './CircleLayer';
const mapboxBlue = 'rgba(51, 181, 229, 100)';
const layerStyles = {
normal: {
pluse: {
circleRadius: 15,
circleColor: mapboxBlue,
circleOpacity: 0.2,
circlePitchAlignment: 'map',
},
background: {
circleRadius: 9,
circleColor: '#fff',
circlePitchAlignment: 'map',
},
foreground: {
circleRadius: 6,
circleColor: mapboxBlue,
circlePitchAlignment: 'map',
},
},
};
const normalIcon = [
<CircleLayer
key="mapboxUserLocationPluseCircle"
id="mapboxUserLocationPluseCircle"
style={layerStyles.normal.pluse}
/>,
<CircleLayer
key="mapboxUserLocationWhiteCircle"
id="mapboxUserLocationWhiteCircle"
style={layerStyles.normal.background}
/>,
<CircleLayer
key="mapboxUserLocationBlueCicle"
id="mapboxUserLocationBlueCicle"
aboveLayerID="mapboxUserLocationWhiteCircle"
style={layerStyles.normal.foreground}
/>,
];
class UserLocation extends React.Component {
static propTypes = {
/**
* Wheather location icon is animated between updates
*/
animated: PropTypes.bool,
/**
* Rendermode of user location icon.
* One of `"normal"`, `"custom"`.
* "custom" must be of type mapbox-gl-native components
*/
renderMode: PropTypes.oneOf(['normal', 'custom']),
/**
* Wheather location icon is visible
*/
visible: PropTypes.bool,
/**
* Callback that is triggered on location icon press
*/
onPress: PropTypes.func,
/**
* Callback that is triggered on location update
*/
onUpdate: PropTypes.func,
/**
* Custom location icon of type mapbox-gl-native components
*/
children: PropTypes.any,
};
static defaultProps = {
animated: true,
visible: true,
renderMode: 'normal',
};
static RenderMode = {
Normal: 'normal',
Custom: 'custom',
};
constructor(props) {
super(props);
this.state = {
shouldShowUserLocation: false,
coordinates: null,
};
this._onLocationUpdate = this._onLocationUpdate.bind(this);
}
async componentDidMount() {
locationManager.addListener(this._onLocationUpdate);
await this.setLocationManager({
running: this.needsLocationManagerRunning(),
});
}
locationManagerRunning = false;
/**
* Wheather to start or stop the locationManager
* Notice, that locationManager will start automatically when
* either `onUpdate` or `visible` are set
*
* @param {Object} running - Object with key `running` and `boolean` value
* @return {void}
*/
setLocationManager = async ({running}) => {
if (this.locationManagerRunning !== running) {
this.locationManagerRunning = running;
if (running) {
locationManager.start();
const lastKnownLocation = await locationManager.getLastKnownLocation();
if (lastKnownLocation) {
this.setState({
coordinates: this._getCoordinatesFromLocation(lastKnownLocation),
});
}
}
}
};
/**
*
* If locationManager should be running
*
* @return {void}
*/
needsLocationManagerRunning() {
return this.props.onUpdate || this.props.visible;
}
async componentWillUnmount() {
locationManager.removeListener(this._onLocationUpdate);
await this.setLocationManager({running: false});
}
_onLocationUpdate(location) {
this.setState({
coordinates: this._getCoordinatesFromLocation(location),
});
if (this.props.onUpdate) {
this.props.onUpdate(location);
}
}
_getCoordinatesFromLocation(location) {
if (!location || !location.coords) {
return;
}
return [location.coords.longitude, location.coords.latitude];
}
_userIconLayers() {
switch (this.props.renderMode) {
case UserLocation.RenderMode.Normal:
return normalIcon;
default:
return this.props.children;
}
}
async componentDidUpdate() {
await this.setLocationManager({
running: this.needsLocationManagerRunning(),
});
}
render() {
if (!this.props.visible || !this.state.coordinates) {
return null;
}
const children = this.props.children
? this.props.children
: this._userIconLayers();
return (
<Annotation
animated={this.props.animated}
id="mapboxUserLocation"
onPress={this.props.onPress}
coordinates={this.state.coordinates}
>
{children}
</Annotation>
);
}
}
export default UserLocation;
// TODO:
// * why is there even a RenderMode if children are rendered regardless?
// * why is #userIconLayers a getter?!
// * state.shouldShowUserLocation is unused