aboutsummaryrefslogtreecommitdiff
path: root/modern
diff options
context:
space:
mode:
authorAnton Tananaev <anton@traccar.org>2022-05-15 09:58:37 -0700
committerAnton Tananaev <anton@traccar.org>2022-05-15 09:58:37 -0700
commit63cf082bd077087e551d2946c3e2a48a605a3532 (patch)
tree15587eaa69ea5daa4ab483e2caa16cf665f37597 /modern
parentdcaaa40f71f45070add4f23ffda66d18b611562c (diff)
downloadtrackermap-web-63cf082bd077087e551d2946c3e2a48a605a3532.tar.gz
trackermap-web-63cf082bd077087e551d2946c3e2a48a605a3532.tar.bz2
trackermap-web-63cf082bd077087e551d2946c3e2a48a605a3532.zip
Clean up replay page
Diffstat (limited to 'modern')
-rw-r--r--modern/src/map/PositionsMap.js13
-rw-r--r--modern/src/map/main/CurrentPositionsMap.js13
-rw-r--r--modern/src/other/PositionPage.js2
-rw-r--r--modern/src/other/ReplayPage.js192
-rw-r--r--modern/src/reports/components/ReportFilter.js2
5 files changed, 103 insertions, 119 deletions
diff --git a/modern/src/map/PositionsMap.js b/modern/src/map/PositionsMap.js
index 74b58beb..2e152d34 100644
--- a/modern/src/map/PositionsMap.js
+++ b/modern/src/map/PositionsMap.js
@@ -1,16 +1,14 @@
import { useCallback, useEffect } from 'react';
-import { useDispatch, useSelector } from 'react-redux';
+import { useSelector } from 'react-redux';
import { map } from './core/Map';
import { getStatusColor } from '../common/util/formatter';
-import { devicesActions } from '../store';
import usePersistedState from '../common/util/usePersistedState';
-const PositionsMap = ({ positions }) => {
+const PositionsMap = ({ positions, onClick }) => {
const id = 'positions';
const clusters = `${id}-clusters`;
- const dispatch = useDispatch();
const devices = useSelector((state) => state.devices.items);
const [mapCluster] = usePersistedState('mapCluster', true);
@@ -18,6 +16,7 @@ const PositionsMap = ({ positions }) => {
const createFeature = (devices, position) => {
const device = devices[position.deviceId];
return {
+ id: position.id,
deviceId: position.deviceId,
name: device.name,
category: device.category || 'default',
@@ -30,8 +29,10 @@ const PositionsMap = ({ positions }) => {
const onMarkerClick = useCallback((event) => {
const feature = event.features[0];
- dispatch(devicesActions.select(feature.properties.deviceId));
- }, [dispatch]);
+ if (onClick) {
+ onClick(feature.properties.id, feature.properties.deviceId);
+ }
+ }, [onClick]);
const onClusterClick = useCallback((event) => {
const features = map.queryRenderedFeatures(event.point, {
diff --git a/modern/src/map/main/CurrentPositionsMap.js b/modern/src/map/main/CurrentPositionsMap.js
index 80795497..76a0a0b9 100644
--- a/modern/src/map/main/CurrentPositionsMap.js
+++ b/modern/src/map/main/CurrentPositionsMap.js
@@ -1,11 +1,18 @@
-import React, { } from 'react';
-import { useSelector } from 'react-redux';
+import React, { useCallback } from 'react';
+import { useDispatch, useSelector } from 'react-redux';
+import { devicesActions } from '../../store';
import PositionsMap from '../PositionsMap';
const CurrentPositionsMap = () => {
+ const dispatch = useDispatch();
+
+ const onClick = useCallback((_, deviceId) => {
+ dispatch(devicesActions.select(deviceId));
+ }, [dispatch]);
+
const positions = useSelector((state) => state.positions.items);
- return (<PositionsMap positions={Object.values(positions)} />);
+ return (<PositionsMap positions={Object.values(positions)} onClick={onClick} />);
};
export default CurrentPositionsMap;
diff --git a/modern/src/other/PositionPage.js b/modern/src/other/PositionPage.js
index d9dcd6b3..4c13955a 100644
--- a/modern/src/other/PositionPage.js
+++ b/modern/src/other/PositionPage.js
@@ -61,7 +61,7 @@ const PositionPage = () => {
<div className={classes.root}>
<AppBar position="sticky" color="inherit">
<Toolbar>
- <IconButton color="inherit" edge="start" onClick={() => history.push('/')}>
+ <IconButton color="inherit" edge="start" onClick={() => history.goBack()}>
<ArrowBackIcon />
</IconButton>
<Typography variant="h6">
diff --git a/modern/src/other/ReplayPage.js b/modern/src/other/ReplayPage.js
index 9b66853d..1fddc21c 100644
--- a/modern/src/other/ReplayPage.js
+++ b/modern/src/other/ReplayPage.js
@@ -1,6 +1,8 @@
-import React, { useState, useEffect, useRef } from 'react';
+import React, {
+ useState, useEffect, useRef, useCallback,
+} from 'react';
import {
- Grid, FormControlLabel, Switch, IconButton, TextField, makeStyles, Paper, Slider, Toolbar, Tooltip, Typography,
+ Grid, IconButton, makeStyles, Paper, Slider, Toolbar, Tooltip, Typography,
} from '@material-ui/core';
import ArrowBackIcon from '@material-ui/icons/ArrowBack';
import SettingsIcon from '@material-ui/icons/Settings';
@@ -22,7 +24,10 @@ const useStyles = makeStyles((theme) => ({
height: '100%',
},
sidebar: {
+ display: 'flex',
+ flexDirection: 'column',
position: 'absolute',
+ zIndex: 3,
left: 0,
top: 0,
margin: theme.spacing(1.5),
@@ -32,6 +37,17 @@ const useStyles = makeStyles((theme) => ({
margin: 0,
},
},
+ title: {
+ flexGrow: 1,
+ },
+ slider: {
+ width: '100%',
+ },
+ controls: {
+ display: 'flex',
+ justifyContent: 'space-between',
+ alignItems: 'center',
+ },
formControlLabel: {
height: '100%',
width: '100%',
@@ -39,15 +55,16 @@ const useStyles = makeStyles((theme) => ({
justifyContent: 'space-between',
alignItems: 'center',
},
- reportFilterContainer: {
- flex: 1,
+ content: {
+ display: 'flex',
+ flexDirection: 'column',
padding: theme.spacing(2),
[theme.breakpoints.down('sm')]: {
margin: theme.spacing(1),
},
- },
- sliderContainer: {
- padding: theme.spacing(2),
+ [theme.breakpoints.up('md')]: {
+ marginTop: theme.spacing(1),
+ },
},
}));
@@ -63,12 +80,13 @@ const ReplayPage = () => {
const history = useHistory();
const timerRef = useRef();
+ const defaultDeviceId = useSelector((state) => state.devices.selectedId);
+
const [positions, setPositions] = useState([]);
const [index, setIndex] = useState(0);
- const [selectedDeviceId, setSelectedDeviceId] = useState();
- const [playbackSpeed, setPlaybackSpeed] = useState('');
+ const [selectedDeviceId, setSelectedDeviceId] = useState(defaultDeviceId);
const [expanded, setExpanded] = useState(true);
- const [isPlaying, setIsPlaying] = useState(false);
+ const [playing, setPlaying] = useState(false);
const deviceName = useSelector((state) => {
if (selectedDeviceId) {
@@ -80,8 +98,12 @@ const ReplayPage = () => {
return null;
});
+ const onClick = useCallback((positionId) => {
+ history.push(`/position/${positionId}`);
+ }, [history]);
+
useEffect(() => {
- if (isPlaying && positions.length > 0) {
+ if (playing && positions.length > 0) {
timerRef.current = setInterval(() => {
setIndex((index) => index + 1);
}, 500);
@@ -90,7 +112,7 @@ const ReplayPage = () => {
}
return () => clearInterval(timerRef.current);
- }, [isPlaying, positions]);
+ }, [playing, positions]);
useEffect(() => {
if (index >= positions.length) {
@@ -113,105 +135,59 @@ const ReplayPage = () => {
<div className={classes.root}>
<Map>
<ReplayPathMap positions={positions} />
- {index < positions.length && <PositionsMap positions={[positions[index]]} />}
+ {index < positions.length && (
+ <PositionsMap positions={[positions[index]]} onClick={onClick} />
+ )}
</Map>
<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 variant="h6">{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>
- {deviceName}
- </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 ? formatTime(positions[i]) : '')}
- 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>{formatTime(positions[index])}</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>
+ <Paper elevation={3} square>
+ <Toolbar>
+ <IconButton onClick={() => history.push('/')}>
+ <ArrowBackIcon />
+ </IconButton>
+ <Typography variant="h6" className={classes.title}>{t('reportReplay')}</Typography>
+ {!expanded && (
+ <Grid item>
+ <IconButton onClick={() => setExpanded(true)}>
+ <SettingsIcon />
+ </IconButton>
+ </Grid>
)}
- </Grid>
- </Grid>
+ </Toolbar>
+ </Paper>
+ <Paper className={classes.content} square>
+ {!expanded ? (
+ <>
+ <Typography variant="subtitle1" align="center">{deviceName}</Typography>
+ <Slider
+ className={classes.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 ? formatTime(positions[i]) : '')}
+ ValueLabelComponent={TimeLabel}
+ />
+ <div className={classes.controls}>
+ {`${index}/${positions.length}`}
+ <IconButton onClick={() => setIndex((index) => index - 1)} disabled={playing}>
+ <FastRewindIcon />
+ </IconButton>
+ <IconButton onClick={() => setPlaying(!playing)}>
+ {playing ? <PauseIcon /> : <PlayArrowIcon /> }
+ </IconButton>
+ <IconButton onClick={() => setIndex((index) => index + 1)} disabled={playing}>
+ <FastForwardIcon />
+ </IconButton>
+ {formatTime(positions[index].fixTime)}
+ </div>
+ </>
+ ) : (
+ <ReportFilter handleSubmit={handleSubmit} fullScreen showOnly />
+ )}
+ </Paper>
</div>
</div>
);
diff --git a/modern/src/reports/components/ReportFilter.js b/modern/src/reports/components/ReportFilter.js
index c7f6a197..350bbe6d 100644
--- a/modern/src/reports/components/ReportFilter.js
+++ b/modern/src/reports/components/ReportFilter.js
@@ -91,7 +91,7 @@ const ReportFilter = ({
<div className={classes.item}>
<FormControl variant="filled" fullWidth>
<InputLabel>{t('reportDevice')}</InputLabel>
- <Select value={deviceId} onChange={(e) => setDeviceId(e.target.value)}>
+ <Select value={deviceId || ''} onChange={(e) => setDeviceId(e.target.value)}>
{Object.values(devices).map((device) => (
<MenuItem key={device.id} value={device.id}>{device.name}</MenuItem>
))}