diff options
-rw-r--r-- | modern/src/App.js | 44 | ||||
-rw-r--r-- | modern/src/EventPage.js | 77 | ||||
-rw-r--r-- | modern/src/PositionPage.js | 12 | ||||
-rw-r--r-- | modern/src/common/useQuery.js | 6 | ||||
-rw-r--r-- | modern/src/index.js | 6 | ||||
-rw-r--r-- | modern/src/map/Map.js | 4 | ||||
-rw-r--r-- | modern/src/reports/ReplayPage.js | 3 |
7 files changed, 132 insertions, 20 deletions
diff --git a/modern/src/App.js b/modern/src/App.js index a53ffc6c..9417feab 100644 --- a/modern/src/App.js +++ b/modern/src/App.js @@ -1,8 +1,8 @@ -import React from 'react'; +import React, { useState } from 'react'; import { ThemeProvider } from '@material-ui/core/styles'; -import { Switch, Route } from 'react-router-dom'; +import { Switch, Route, useHistory } from 'react-router-dom'; import CssBaseline from '@material-ui/core/CssBaseline'; -import { useSelector } from 'react-redux'; +import { useDispatch, useSelector } from 'react-redux'; import { LinearProgress } from '@material-ui/core'; import MainPage from './MainPage'; import RouteReportPage from './reports/RouteReportPage'; @@ -41,11 +41,44 @@ import theme from './theme'; import GeofencesPage from './GeofencesPage'; import GeofencePage from './GeofencePage'; import { LocalizationProvider } from './LocalizationProvider'; +import useQuery from './common/useQuery'; +import { useEffectAsync } from './reactHelper'; +import { devicesActions } from './store'; +import EventPage from './EventPage'; const App = () => { + const history = useHistory(); + const dispatch = useDispatch(); + const initialized = useSelector((state) => !!state.session.server && !!state.session.user); + const [redirectsHandled, setRedirectsHandled] = useState(false); + + const query = useQuery(); + + useEffectAsync(async () => { + if (query.get('token')) { + const token = query.get('token'); + await fetch(`/api/session?token=${encodeURIComponent(token)}`); + history.push('/'); + } else if (query.get('deviceId')) { + const deviceId = query.get('deviceId'); + const response = await fetch(`/api/devices?uniqueId=${deviceId}`); + if (response.ok) { + const items = await response.json(); + if (items.length > 0) { + dispatch(devicesActions.select(items[0])); + } + } + history.push('/'); + } else if (query.get('eventId')) { + const eventId = parseInt(query.get('eventId'), 10); + history.push(`/event/${eventId}`); + } else { + setRedirectsHandled(true); + } + }, [query]); - return ( + return (!redirectsHandled ? (<LinearProgress />) : ( <LocalizationProvider> <ThemeProvider theme={theme}> <CssBaseline /> @@ -61,6 +94,7 @@ const App = () => { <Route exact path="/" component={MainPage} /> <Route exact path="/replay" component={ReplayPage} /> <Route exact path="/position/:id?" component={PositionPage} /> + <Route exact path="/event/:id?" component={EventPage} /> <Route exact path="/user/:id?" component={UserPage} /> <Route exact path="/device/:id?" component={DevicePage} /> <Route exact path="/geofence/:id?" component={GeofencePage} /> @@ -92,7 +126,7 @@ const App = () => { </Switch> </ThemeProvider> </LocalizationProvider> - ); + )); }; export default App; diff --git a/modern/src/EventPage.js b/modern/src/EventPage.js new file mode 100644 index 00000000..48c6d807 --- /dev/null +++ b/modern/src/EventPage.js @@ -0,0 +1,77 @@ +import React, { useState } from 'react'; + +import { + makeStyles, Typography, AppBar, Toolbar, IconButton, +} from '@material-ui/core'; +import ArrowBackIcon from '@material-ui/icons/ArrowBack'; +import { useHistory, useParams } from 'react-router-dom'; +import { useEffectAsync } from './reactHelper'; +import { useTranslation } from './LocalizationProvider'; +import ContainerDimensions from 'react-container-dimensions'; +import Map from './map/Map'; +import PositionsMap from './map/PositionsMap'; + +const useStyles = makeStyles(() => ({ + root: { + height: '100%', + display: 'flex', + flexDirection: 'column', + }, + mapContainer: { + flexGrow: 1, + }, +})); + +const EventPage = () => { + const classes = useStyles(); + const history = useHistory(); + const t = useTranslation(); + + const { id } = useParams(); + + const [event, setEvent] = useState(); + const [position, setPosition] = useState(); + + useEffectAsync(async () => { + if (id) { + const response = await fetch(`/api/events/${id}`); + if (response.ok) { + setEvent(await response.json()); + } + } + }, [id]); + + useEffectAsync(async () => { + if (event && event.positionId) { + const response = await fetch(`/api/positions?id=${event.positionId}`); + if (response.ok) { + const positions = await response.json(); + if (positions.length > 0) { + setPosition(positions[0]); + } + } + } + }, [event]); + + return ( + <div className={classes.root}> + <AppBar color="inherit" position="static"> + <Toolbar> + <IconButton color="inherit" edge="start" onClick={() => history.push('/')}> + <ArrowBackIcon /> + </IconButton> + <Typography variant="h6">{t('positionEvent')}</Typography> + </Toolbar> + </AppBar> + <div className={classes.mapContainer}> + <ContainerDimensions> + <Map> + {position && <PositionsMap positions={[position]} />} + </Map> + </ContainerDimensions> + </div> + </div> + ); +}; + +export default EventPage; diff --git a/modern/src/PositionPage.js b/modern/src/PositionPage.js index 37b0145f..08502668 100644 --- a/modern/src/PositionPage.js +++ b/modern/src/PositionPage.js @@ -29,14 +29,12 @@ const PositionPage = () => { useEffectAsync(async () => { if (id) { - const response = await fetch(`/api/positions?id=${id}`, { - headers: { - Accept: 'application/json', - }, - }); + const response = await fetch(`/api/positions?id=${id}`); if (response.ok) { - const items = await response.json(); - setItem(items[0]); + const positions = await response.json(); + if (positions.length > 0) { + setItem(positions[0]); + } } } else { setItem({}); diff --git a/modern/src/common/useQuery.js b/modern/src/common/useQuery.js index 92398bf8..ed3f74c1 100644 --- a/modern/src/common/useQuery.js +++ b/modern/src/common/useQuery.js @@ -1,3 +1,7 @@ +import { useMemo } from 'react'; import { useLocation } from 'react-router-dom'; -export default () => new URLSearchParams(useLocation().search); +export default () => { + const { search } = useLocation(); + return useMemo(() => new URLSearchParams(search), [search]); +} diff --git a/modern/src/index.js b/modern/src/index.js index 155bf623..28f0bbf1 100644 --- a/modern/src/index.js +++ b/modern/src/index.js @@ -1,7 +1,7 @@ import 'typeface-roboto'; import React from 'react'; import ReactDOM from 'react-dom'; -import { HashRouter } from 'react-router-dom'; +import { BrowserRouter } from 'react-router-dom'; import { Provider } from 'react-redux'; import App from './App'; @@ -10,9 +10,9 @@ import store from './store'; ReactDOM.render(( <Provider store={store}> - <HashRouter> + <BrowserRouter> <App /> - </HashRouter> + </BrowserRouter> </Provider> ), document.getElementById('root')); diff --git a/modern/src/map/Map.js b/modern/src/map/Map.js index 7c87797d..2555e23f 100644 --- a/modern/src/map/Map.js +++ b/modern/src/map/Map.js @@ -42,14 +42,14 @@ const updateReadyValue = (value) => { const initMap = async () => { if (ready) return; if (!map.hasImage('background')) { - const background = await loadImage('images/background.svg'); + const background = await loadImage('/images/background.svg'); map.addImage('background', await prepareIcon(background), { pixelRatio: window.devicePixelRatio, }); await Promise.all(deviceCategories.map(async (category) => { const results = []; ['positive', 'negative', 'neutral'].forEach((color) => { - results.push(loadImage(`images/icon/${category}.svg`).then((icon) => { + results.push(loadImage(`/images/icon/${category}.svg`).then((icon) => { map.addImage(`${category}-${color}`, prepareIcon(background, icon, palette.colors[color]), { pixelRatio: window.devicePixelRatio, }); diff --git a/modern/src/reports/ReplayPage.js b/modern/src/reports/ReplayPage.js index 53c223d8..9840ea8e 100644 --- a/modern/src/reports/ReplayPage.js +++ b/modern/src/reports/ReplayPage.js @@ -116,8 +116,7 @@ const ReplayPage = () => { <div className={classes.root}> <Map> <ReplayPathMap positions={positions} /> - {index < positions.length - && <PositionsMap positions={[positions[index]]} />} + {index < positions.length && <PositionsMap positions={[positions[index]]} />} </Map> <div className={classes.sidebar}> <Grid container direction="column" spacing={1}> |