import React, { useState, useEffect, useRef, useCallback, } from 'react'; import { IconButton, Paper, Slider, Toolbar, Typography, } from '@mui/material'; import makeStyles from '@mui/styles/makeStyles'; import ArrowBackIcon from '@mui/icons-material/ArrowBack'; import TuneIcon from '@mui/icons-material/Tune'; import DownloadIcon from '@mui/icons-material/Download'; import PlayArrowIcon from '@mui/icons-material/PlayArrow'; import PauseIcon from '@mui/icons-material/Pause'; import FastForwardIcon from '@mui/icons-material/FastForward'; import FastRewindIcon from '@mui/icons-material/FastRewind'; import { useNavigate } from 'react-router-dom'; import { useSelector } from 'react-redux'; import MapView from '../map/core/MapView'; import MapRoutePath from '../map/MapRoutePath'; import MapRoutePoints from '../map/MapRoutePoints'; import MapPositions from '../map/MapPositions'; import { formatTime } from '../common/util/formatter'; import ReportFilter from '../reports/components/ReportFilter'; import { useTranslation } from '../common/components/LocalizationProvider'; import { useCatch } from '../reactHelper'; import MapCamera from '../map/MapCamera'; import MapGeofence from '../map/MapGeofence'; import StatusCard from '../common/components/StatusCard'; import { usePreference } from '../common/util/preferences'; const useStyles = makeStyles((theme) => ({ root: { height: '100%', }, sidebar: { display: 'flex', flexDirection: 'column', position: 'fixed', zIndex: 3, left: 0, top: 0, margin: theme.spacing(1.5), width: theme.dimensions.drawerWidthDesktop, [theme.breakpoints.down('md')]: { width: '100%', margin: 0, }, }, title: { flexGrow: 1, }, slider: { width: '100%', }, controls: { display: 'flex', justifyContent: 'space-between', alignItems: 'center', }, formControlLabel: { height: '100%', width: '100%', paddingRight: theme.spacing(1), justifyContent: 'space-between', alignItems: 'center', }, content: { display: 'flex', flexDirection: 'column', padding: theme.spacing(2), [theme.breakpoints.down('md')]: { margin: theme.spacing(1), }, [theme.breakpoints.up('md')]: { marginTop: theme.spacing(1), }, }, })); const ReplayPage = () => { const t = useTranslation(); const classes = useStyles(); const navigate = useNavigate(); const timerRef = useRef(); const hours12 = usePreference('twelveHourFormat'); const defaultDeviceId = useSelector((state) => state.devices.selectedId); const [positions, setPositions] = useState([]); const [index, setIndex] = useState(0); const [selectedDeviceId, setSelectedDeviceId] = useState(defaultDeviceId); const [showCard, setShowCard] = useState(false); const [from, setFrom] = useState(); const [to, setTo] = useState(); const [expanded, setExpanded] = useState(true); const [playing, setPlaying] = useState(false); const deviceName = useSelector((state) => { if (selectedDeviceId) { const device = state.devices.items[selectedDeviceId]; if (device) { return device.name; } } return null; }); useEffect(() => { if (playing && positions.length > 0) { timerRef.current = setInterval(() => { setIndex((index) => index + 1); }, 500); } else { clearInterval(timerRef.current); } return () => clearInterval(timerRef.current); }, [playing, positions]); useEffect(() => { if (index >= positions.length - 1) { clearInterval(timerRef.current); setPlaying(false); } }, [index, positions]); const onPointClick = useCallback((_, index) => { setIndex(index); }, [setIndex]); const onMarkerClick = useCallback((positionId) => { setShowCard(!!positionId); }, [setShowCard]); const handleSubmit = useCatch(async ({ deviceId, from, to }) => { setSelectedDeviceId(deviceId); setFrom(from); setTo(to); const query = new URLSearchParams({ deviceId, from, to }); const response = await fetch(`/api/positions?${query.toString()}`); if (response.ok) { setIndex(0); const positions = await response.json(); setPositions(positions); if (positions.length) { setExpanded(false); } else { throw Error(t('sharedNoData')); } } else { throw Error(await response.text()); } }); const handleDownload = () => { const query = new URLSearchParams({ deviceId: selectedDeviceId, from, to }); window.location.assign(`/api/positions/kml?${query.toString()}`); }; return (