263 lines
6.8 KiB
JavaScript
263 lines
6.8 KiB
JavaScript
import React from 'react';
|
|
import Axios from "axios";
|
|
import ReactMapGL, { Marker } from 'react-map-gl';
|
|
import { Button } from "react-bootstrap";
|
|
import { withToastManager } from 'react-toast-notifications';
|
|
import PropTypes from 'prop-types';
|
|
import { MdLocalGasStation } from "react-icons/md";
|
|
import { GiPositionMarker } from "react-icons/gi";
|
|
import { haveSelectedGas } from '../helpers';
|
|
import { mapboxToken, radius, baseApiUrl } from "../config";
|
|
|
|
import 'mapbox-gl/dist/mapbox-gl.css';
|
|
|
|
class Map extends React.Component {
|
|
constructor(props) {
|
|
super(props);
|
|
this.state = {
|
|
viewport: {
|
|
width: '100vw',
|
|
height: '100vh',
|
|
latitude: 44.837789,
|
|
longitude: -0.57918,
|
|
zoom: 11,
|
|
},
|
|
userLocation: {
|
|
// latitude: 44.837789,
|
|
// longitude: -0.57918,
|
|
},
|
|
gasStations: [],
|
|
};
|
|
|
|
this.currentLoadId = null;
|
|
this.isFirstLoading = true;
|
|
|
|
this.mapRef = React.createRef();
|
|
|
|
this.loadGasStations();
|
|
}
|
|
|
|
/**
|
|
* Méthode appelée au premier chargement du module
|
|
*/
|
|
componentDidMount() {
|
|
this.setUserLocation();
|
|
}
|
|
|
|
/**
|
|
* Méthode appelée à chaque modificiation de l'état
|
|
* @param {Object} prevProps
|
|
*/
|
|
componentDidUpdate( prevProps ) {
|
|
const {
|
|
setNeedUpdateUserLocation,
|
|
} = this.props;
|
|
|
|
// L'utilisateur souhaite que l'app le géoloc
|
|
if ( prevProps.needUpdateUserLocation ) {
|
|
setNeedUpdateUserLocation(false);
|
|
this.setUserLocation(() => {
|
|
this.loadGasStations();
|
|
});
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Méthode permettant de mettre à jour la position de la map
|
|
* @param {Object} viewport
|
|
*/
|
|
onViewportChange = (viewport) => {
|
|
this.setState({ viewport }, () => {
|
|
this.loadGasStations();
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Méthode permettant de sauvegarder la position de l'utilisateur
|
|
* @param {Function} [cb]
|
|
*/
|
|
setUserLocation(cb) {
|
|
navigator.geolocation.getCurrentPosition((position) => {
|
|
const setUserLocation = {
|
|
latitude: position.coords.latitude,
|
|
longitude: position.coords.longitude,
|
|
};
|
|
const newViewport = {
|
|
height: '100vh',
|
|
width: '100vw',
|
|
latitude: position.coords.latitude,
|
|
longitude: position.coords.longitude,
|
|
zoom: 10,
|
|
};
|
|
this.setState({
|
|
viewport: newViewport,
|
|
userLocation: setUserLocation,
|
|
}, () => {
|
|
if ( cb ) {
|
|
cb();
|
|
}
|
|
});
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Méthode récursive permettant de charger tous les stations d'une zone
|
|
* @param {Number} start
|
|
* @param {Number} newLoadId
|
|
* @param {Function} callback
|
|
*/
|
|
loadAllGasStations(start, newLoadId, callback) {
|
|
const limit = 20;
|
|
|
|
const {
|
|
viewport,
|
|
} = this.state;
|
|
const {
|
|
latitude,
|
|
longitude
|
|
} = viewport;
|
|
|
|
if ( newLoadId !== this.currentLoadId ) {
|
|
callback(new Error('Request canceled'))
|
|
} else {
|
|
Axios.get(`${baseApiUrl}stations?loadId=${newLoadId}&radius=${radius}&lat=${latitude}&lon=${longitude}&start=${start}&limit=${limit}`)
|
|
.then( (response) => {
|
|
if ( response.status === 200 ){
|
|
const newStations = response.data.items;
|
|
let stations = newStations;
|
|
const nextStart = start + limit;
|
|
if ( nextStart > response.data.total ) {
|
|
callback( null, stations )
|
|
} else {
|
|
this.loadAllGasStations(nextStart, newLoadId, (err, otherStations) => {
|
|
if ( err ) {
|
|
callback(err);
|
|
} else {
|
|
stations = stations.concat(otherStations)
|
|
callback(null, stations);
|
|
}
|
|
})
|
|
}
|
|
} else {
|
|
callback(null, []);
|
|
}
|
|
})
|
|
.catch( (err) => {
|
|
callback(err);
|
|
})
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Méthode permettant de charger le fichier xml contenant la liste des stations
|
|
*/
|
|
loadGasStations() {
|
|
const newLoadId = new Date().getTime();
|
|
const {
|
|
toastManager,
|
|
} = this.props;
|
|
|
|
if ( !this.currentLoadId ){
|
|
if ( this.isFirstLoading ) {
|
|
toastManager.add('Chargement des données...', { appearance: 'info', autoDismiss: true });
|
|
} else {
|
|
toastManager.add('Actualisation des données...', { appearance: 'info', autoDismiss: true });
|
|
}
|
|
|
|
}
|
|
|
|
this.currentLoadId = newLoadId;
|
|
|
|
this.loadAllGasStations(0, newLoadId, (err,gasStations) => {
|
|
this.isLoading = false;
|
|
if ( this.currentLoadId === newLoadId ) {
|
|
toastManager.removeAll();
|
|
if ( err ) {
|
|
toastManager.add('Erreur lors du chargement des données', { appearance: 'error', autoDismiss: true });
|
|
} else {
|
|
toastManager.add('Chargement des données terminé', { appearance: 'success', autoDismiss: true });
|
|
this.currentLoadId = null;
|
|
this.isFirstLoading = false;
|
|
this.setState((prevState) => ({
|
|
...prevState,
|
|
gasStations,
|
|
}));
|
|
}
|
|
}
|
|
})
|
|
}
|
|
|
|
/**
|
|
* Méthode gérant le rendu de la vue
|
|
*/
|
|
render() {
|
|
const {
|
|
viewport,
|
|
userLocation,
|
|
gasStations,
|
|
} = this.state;
|
|
|
|
const {
|
|
showGasStation,
|
|
selectedGasType,
|
|
} = this.props;
|
|
|
|
return (
|
|
<>
|
|
<ReactMapGL
|
|
{...viewport}
|
|
mapStyle="mapbox://styles/mapbox/outdoors-v11"
|
|
onViewportChange={this.onViewportChange}
|
|
mapboxApiAccessToken={mapboxToken}
|
|
ref={(map) => { if (map) this.mapRef = map; }}
|
|
>
|
|
{
|
|
Object.keys(userLocation).length !== 0 ? (
|
|
<Marker
|
|
latitude={userLocation.latitude}
|
|
longitude={userLocation.longitude}
|
|
>
|
|
<GiPositionMarker size="2em" />
|
|
</Marker>
|
|
) : (null)
|
|
}
|
|
{gasStations.filter((station) => haveSelectedGas(station, selectedGasType)).map((gasStation) => (
|
|
<Marker
|
|
key={gasStation.stationId}
|
|
latitude={gasStation.location.coordinates[1]}
|
|
longitude={gasStation.location.coordinates[0]}
|
|
className="gasStation"
|
|
>
|
|
<Button
|
|
variant="link"
|
|
onClick={() => showGasStation(gasStation)}
|
|
onFocus={() => { }}
|
|
onBlur={() => { }}
|
|
>
|
|
<MdLocalGasStation size="2em" />
|
|
</Button>
|
|
|
|
</Marker>
|
|
))}
|
|
</ReactMapGL>
|
|
</>
|
|
);
|
|
}
|
|
}
|
|
|
|
Map.defaultProps = {
|
|
needUpdateUserLocation: false
|
|
};
|
|
|
|
Map.propTypes = {
|
|
needUpdateUserLocation: PropTypes.bool,
|
|
selectedGasType: PropTypes.string.isRequired,
|
|
showGasStation: PropTypes.func.isRequired,
|
|
setNeedUpdateUserLocation: PropTypes.func.isRequired,
|
|
toastManager: PropTypes.shape({
|
|
add: PropTypes.func,
|
|
removeAll: PropTypes.func,
|
|
}).isRequired,
|
|
};
|
|
|
|
export default withToastManager(Map);
|