206 lines
5.0 KiB
JavaScript
206 lines
5.0 KiB
JavaScript
import React, { useEffect, useState } from "react";
|
|
import useStyles from "../useStyles";
|
|
import L from "leaflet";
|
|
import { MapContainer, TileLayer, Marker, Popup, useMap } from "react-leaflet";
|
|
import { Button } from "@material-ui/core";
|
|
|
|
import { useUserContext } from "../Contexts/UserContext";
|
|
import { useVehicleContext, VehicleProvider } from "../Contexts/VehicleContext";
|
|
import { VehiclePopUp } from "./popup";
|
|
import GreenCarIcon from "../../assets/green-car.png";
|
|
import RedCarIcon from "../../assets/red-car.png";
|
|
|
|
const Component = () => {
|
|
const classes = useStyles();
|
|
const {
|
|
token: {
|
|
idToken: { jwtToken: token },
|
|
}
|
|
} = useUserContext();
|
|
const { getConnections, getLocations, getState } = useVehicleContext();
|
|
|
|
const REQUEST_INTERVAL = 10000;
|
|
|
|
const [center, setCenter] = useState([0, 0]);
|
|
const [zoom, setZoom] = useState(2);
|
|
const [markers, setMarkers] = useState([]);
|
|
|
|
useEffect(() => {
|
|
retrieveAndStoreLocations()
|
|
.then(points => {
|
|
centerAroundMarkers(points);
|
|
})
|
|
const id = setInterval(function () {
|
|
retrieveAndStoreLocations();
|
|
}, REQUEST_INTERVAL);
|
|
return () => { clearInterval(id) };
|
|
// eslint-disable-next-line
|
|
}, []);
|
|
|
|
const retrieveAndStoreLocations = () => {
|
|
return getLocations(token)
|
|
.then(result => {
|
|
if (result.data != null) {
|
|
const points = result.data.map(point => [point.latitude, point.longitude, point.vin]);
|
|
setMarkers(points);
|
|
return points
|
|
}
|
|
return []
|
|
})
|
|
.catch(error => console.log(error));
|
|
}
|
|
|
|
const centerAroundMarkers = (markers) => {
|
|
if (markers == null) {
|
|
markers = []
|
|
}
|
|
const coord = markers.reduce((coord, marker) => {
|
|
coord[0] += marker[0] / markers.length;
|
|
coord[1] += marker[1] / markers.length;
|
|
return coord;
|
|
}, [0, 0])
|
|
|
|
setCenter(coord);
|
|
setZoom(4);
|
|
}
|
|
|
|
const [connections, setConnections] = useState({});
|
|
|
|
useEffect(() => {
|
|
if (markers.length > 0) {
|
|
const vins = markers.map(marker => marker[2]);
|
|
getConnections(vins, token)
|
|
.then(connections => {
|
|
setConnections(connections);
|
|
})
|
|
}
|
|
// eslint-disable-next-line
|
|
}, [markers, token])
|
|
|
|
const [selectedVIN, setSelectedVIN] = useState(null);
|
|
const [carState, setCarState] = useState(null);
|
|
|
|
useEffect(() => {
|
|
if (selectedVIN != null) {
|
|
retrieveAndStoreCarState(selectedVIN);
|
|
const id = setInterval(function () {
|
|
retrieveAndStoreCarState(selectedVIN);
|
|
}, REQUEST_INTERVAL);
|
|
return () => { clearInterval(id) };
|
|
}
|
|
// eslint-disable-next-line
|
|
}, [selectedVIN]);
|
|
|
|
const selectCar = (e, vin) => {
|
|
e.preventDefault();
|
|
setSelectedVIN(vin);
|
|
}
|
|
|
|
const retrieveAndStoreCarState = (vin) => {
|
|
getState(token, vin)
|
|
.then(results => {
|
|
setCarState({ ...results.data, vin: vin });
|
|
});
|
|
}
|
|
|
|
const handleClose = () => {
|
|
setSelectedVIN(null);
|
|
setCarState(null);
|
|
};
|
|
|
|
function getCarIcon(vin) {
|
|
let icon = RedCarIcon;
|
|
|
|
if (connections[vin]) {
|
|
icon = GreenCarIcon;
|
|
} else {
|
|
icon = RedCarIcon;
|
|
}
|
|
|
|
return new L.Icon({
|
|
iconUrl: icon,
|
|
iconAnchor: [15, 0]
|
|
});
|
|
}
|
|
|
|
return (
|
|
<MapContainer
|
|
center={center}
|
|
zoom={zoom}
|
|
style={{
|
|
width: '100%',
|
|
height: '900px'
|
|
}}
|
|
>
|
|
<TileLayer
|
|
attribution='© <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
|
|
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
|
|
/>
|
|
<CenterFocus center={center} zoom={zoom} />
|
|
|
|
{markers.map((marker) => (
|
|
<Marker
|
|
icon={getCarIcon(marker[2])}
|
|
key={marker[2]}
|
|
position={[marker[0], marker[1]]}
|
|
title={marker[2]}
|
|
opacity={0.9}
|
|
>
|
|
<Popup>
|
|
<div align="center">
|
|
<p className={classes.markerTitle}><b>{marker[2]}</b></p>
|
|
<Button
|
|
type="submit"
|
|
variant="contained"
|
|
color="primary"
|
|
onClick={e => selectCar(e, marker[2])}
|
|
>
|
|
View Stats
|
|
</Button>
|
|
</div>
|
|
</Popup>
|
|
</Marker>
|
|
))
|
|
}
|
|
|
|
{
|
|
carState ? (
|
|
<VehiclePopUp
|
|
key={carState.vin}
|
|
vin={carState.vin}
|
|
online={carState.online}
|
|
battery={carState.battery}
|
|
doors={carState.doors}
|
|
location={carState.location}
|
|
windows={carState.windows}
|
|
className={classes.popup}
|
|
onClose={handleClose}
|
|
/>
|
|
) : null
|
|
}
|
|
</MapContainer >
|
|
);
|
|
};
|
|
|
|
const CenterFocus = ({ center, zoom }) => {
|
|
const map = useMap();
|
|
|
|
useEffect(() => {
|
|
if (center[0] === 0 && center[1] === 0) {
|
|
map.flyTo([0, 0], 2, { duration: 1.5 });
|
|
} else {
|
|
map.flyTo(center, zoom, { duration: 1.5 });
|
|
}
|
|
}, [center, zoom, map]);
|
|
|
|
return null;
|
|
}
|
|
|
|
const VehicleMap = () => (
|
|
<VehicleProvider>
|
|
<Component />
|
|
</VehicleProvider>
|
|
)
|
|
|
|
export default VehicleMap;
|