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: {}, 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: 11, }; 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 ( <> { if (map) this.mapRef = map; }} > { Object.keys(userLocation).length !== 0 ? ( ) : (null) } {gasStations.filter((station) => haveSelectedGas(station, selectedGasType)).map((gasStation) => ( ))} ); } } 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);