aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--modern/src/reports/ReplayPage.js222
1 files changed, 174 insertions, 48 deletions
diff --git a/modern/src/reports/ReplayPage.js b/modern/src/reports/ReplayPage.js
index bb32daa5..d79e19ce 100644
--- a/modern/src/reports/ReplayPage.js
+++ b/modern/src/reports/ReplayPage.js
@@ -1,9 +1,15 @@
-import React, { useState } from 'react';
+import React, { useState, useEffect, useRef } from 'react';
import {
- Accordion, AccordionDetails, AccordionSummary, Container, makeStyles, Paper, Slider, Tooltip, Typography,
+ Grid, FormControlLabel, Switch, IconButton, TextField, makeStyles, Paper, Slider, Toolbar, Tooltip, Typography,
} from '@material-ui/core';
-import ExpandMoreIcon from '@material-ui/icons/ExpandMore';
-import MainToolbar from '../MainToolbar';
+import ArrowBackIcon from '@material-ui/icons/ArrowBack';
+import SettingsIcon from '@material-ui/icons/Settings';
+import PlayArrowIcon from '@material-ui/icons/PlayArrow';
+import PauseIcon from '@material-ui/icons/Pause';
+import FastForwardIcon from '@material-ui/icons/FastForward';
+import FastRewindIcon from '@material-ui/icons/FastRewind';
+import { useHistory } from 'react-router-dom';
+import { useSelector } from 'react-redux';
import Map from '../map/Map';
import ReplayPathMap from '../map/ReplayPathMap';
import PositionsMap from '../map/PositionsMap';
@@ -13,23 +19,38 @@ import { useTranslation } from '../LocalizationProvider';
const useStyles = makeStyles((theme) => ({
root: {
- height: '100%',
- display: 'flex',
- flexDirection: 'column',
+ height: '100vh',
+ },
+ title: {
+ ...theme.typography.title,
},
- controlPanel: {
+ sidebar: {
position: 'absolute',
- bottom: theme.spacing(5),
- left: '50%',
- transform: 'translateX(-50%)',
+ left: 0,
+ top: 0,
+ margin: theme.spacing(1.5),
+ width: theme.dimensions.drawerWidthDesktop,
+ [theme.breakpoints.down('md')]: {
+ width: '100%',
+ margin: 0,
+ },
+ },
+ formControlLabel: {
+ height: '100%',
+ width: '100%',
+ paddingRight: theme.spacing(1),
+ justifyContent: 'space-between',
+ alignItems: 'center',
},
- controlContent: {
+ reportFilterContainer: {
+ flex: 1,
padding: theme.spacing(2),
- marginBottom: theme.spacing(2),
+ [theme.breakpoints.down('md')]: {
+ margin: theme.spacing(1),
+ },
},
- configForm: {
- display: 'flex',
- flexDirection: 'column',
+ sliderContainer: {
+ padding: theme.spacing(2),
},
}));
@@ -40,14 +61,49 @@ const TimeLabel = ({ children, open, value }) => (
);
const ReplayPage = () => {
- const classes = useStyles();
const t = useTranslation();
+ const classes = useStyles();
+ const history = useHistory();
+ const timerRef = useRef();
- const [expanded, setExpanded] = useState(true);
const [positions, setPositions] = useState([]);
const [index, setIndex] = useState(0);
+ const [selectedDeviceId, setSelectedDeviceId] = useState();
+ const [playbackSpeed, setPlaybackSpeed] = useState('');
+ const [expanded, setExpanded] = useState(true);
+ const [isPlaying, setIsPlaying] = useState(false);
+
+ const deviceName = useSelector((state) => {
+ if (selectedDeviceId) {
+ const device = state.devices.items[selectedDeviceId];
+ if (device) {
+ return device.name;
+ }
+ }
+ return null;
+ });
+
+ useEffect(() => {
+ if (isPlaying && positions.length > 0) {
+ timerRef.current = setInterval(() => {
+ console.log('in setInterval func');
+ setIndex((index) => index + 1);
+ }, 500);
+ } else {
+ clearInterval(timerRef.current);
+ }
+
+ return () => clearInterval(timerRef.current);
+ }, [isPlaying, positions]);
+
+ useEffect(() => {
+ if (index >= positions.length) {
+ clearInterval(timerRef.current);
+ }
+ }, [index]);
const handleSubmit = async (deviceId, from, to, _, headers) => {
+ setSelectedDeviceId(deviceId);
const query = new URLSearchParams({ deviceId, from, to });
const response = await fetch(`/api/positions?${query.toString()}`, { headers });
if (response.ok) {
@@ -59,41 +115,111 @@ const ReplayPage = () => {
return (
<div className={classes.root}>
- <MainToolbar />
<Map>
<ReplayPathMap positions={positions} />
{index < positions.length
&& <PositionsMap positions={[positions[index]]} />}
</Map>
- <Container maxWidth="sm" className={classes.controlPanel}>
- {!!positions.length
- && (
- <Paper className={classes.controlContent}>
- <Slider
- max={positions.length - 1}
- step={null}
- marks={positions.map((_, index) => ({ value: index }))}
- value={index}
- onChange={(_, index) => setIndex(index)}
- valueLabelDisplay="auto"
- valueLabelFormat={(i) => (i < positions.length ? formatPosition(positions[i], 'fixTime', t) : '')}
- ValueLabelComponent={TimeLabel}
- />
- </Paper>
- )}
- <div>
- <Accordion expanded={expanded} onChange={() => setExpanded(!expanded)}>
- <AccordionSummary expandIcon={<ExpandMoreIcon />}>
- <Typography align="center">
- {t('reportConfigure')}
- </Typography>
- </AccordionSummary>
- <AccordionDetails className={classes.configForm}>
- <ReportFilter handleSubmit={handleSubmit} showOnly />
- </AccordionDetails>
- </Accordion>
- </div>
- </Container>
+ <div className={classes.sidebar}>
+ <Grid container direction="column" spacing={1}>
+ <Grid item>
+ <Paper elevation={3} square>
+ <Toolbar disableGutters>
+ <Grid container alignItems="center">
+ <Grid item>
+ <IconButton onClick={() => history.push('/')}>
+ <ArrowBackIcon />
+ </IconButton>
+ </Grid>
+ <Grid item xs>
+ <Typography className={classes.replay} color="primary">
+ {t('reportReplay')}
+ </Typography>
+ </Grid>
+ {!expanded && (
+ <Grid item>
+ <IconButton onClick={() => setExpanded(true)}>
+ <SettingsIcon />
+ </IconButton>
+ </Grid>
+ )}
+ </Grid>
+ </Toolbar>
+ </Paper>
+ </Grid>
+ <Grid item>
+ {!expanded ? (
+ <Paper className={classes.sliderContainer}>
+ <Grid container direction="column" alignItems="center">
+ <Grid item>
+ <span className={classes.title}>{deviceName}</span>
+ </Grid>
+ <Grid item style={{ width: '100%' }}>
+ <Slider
+ max={positions.length - 1}
+ step={null}
+ marks={positions.map((_, index) => ({ value: index }))}
+ value={index}
+ onChange={(_, index) => setIndex(index)}
+ valueLabelDisplay="auto"
+ valueLabelFormat={(i) => (i < positions.length ? formatPosition(positions[i], 'fixTime') : '')}
+ ValueLabelComponent={TimeLabel}
+ />
+ </Grid>
+ <Grid item container justifyContent="space-between" alignItems="center">
+ <Grid item xs={2}>{`${index}/${positions.length}`}</Grid>
+ <Grid item xs={2}>
+ <IconButton onClick={() => setIndex((index) => index - 1)} disabled={isPlaying}>
+ <FastRewindIcon />
+ </IconButton>
+ </Grid>
+ <Grid item xs={2}>
+ <IconButton onClick={() => setIsPlaying(!isPlaying)}>
+ {isPlaying ? <PauseIcon /> : <PlayArrowIcon /> }
+ </IconButton>
+ </Grid>
+ <Grid item xs={2}>
+ <IconButton onClick={() => setIndex((index) => index + 1)} disabled={isPlaying}>
+ <FastForwardIcon />
+ </IconButton>
+ </Grid>
+ <Grid item xs>{formatPosition(positions[index], 'fixTime')}</Grid>
+ </Grid>
+ </Grid>
+ </Paper>
+ ) : (
+ <Paper elevation={3} className={classes.reportFilterContainer} square>
+ <ReportFilter handleSubmit={handleSubmit} fullScreen showOnly>
+ <Grid item xs={6}>
+ <TextField
+ fullWidth
+ label={t('reportPlaybackPerMinute')}
+ value={playbackSpeed}
+ onChange={(e) => setPlaybackSpeed(e.target.value)}
+ variant="filled"
+ />
+ </Grid>
+ <Grid item xs={6}>
+ <FormControlLabel
+ classes={{ root: classes.formControlLabel }}
+ control={(
+ <Switch
+ checked={isPlaying}
+ onChange={(e) => setIsPlaying(e.target.checked)}
+ color="primary"
+ edge="start"
+ />
+ )}
+ label={t('reportAutoPlay')}
+ labelPlacement="start"
+ />
+ </Grid>
+ </ReportFilter>
+ </Paper>
+ )}
+ </Grid>
+ </Grid>
+ </div>
</div>
);
};