diff options
-rw-r--r-- | modern/src/settings/PreferencesPage.js | 48 | ||||
-rw-r--r-- | modern/src/settings/UserPage.js | 27 | ||||
-rw-r--r-- | web/app/model/User.js | 3 | ||||
-rw-r--r-- | web/app/view/dialog/User.js | 11 | ||||
-rw-r--r-- | web/app/view/dialog/UserController.js | 12 |
5 files changed, 47 insertions, 54 deletions
diff --git a/modern/src/settings/PreferencesPage.js b/modern/src/settings/PreferencesPage.js index 11cf860c..3b0c7e9c 100644 --- a/modern/src/settings/PreferencesPage.js +++ b/modern/src/settings/PreferencesPage.js @@ -1,11 +1,13 @@ -import React from 'react'; +import React, { useState } from 'react'; import { useSelector } from 'react-redux'; import { useNavigate } from 'react-router-dom'; import { - Accordion, AccordionSummary, AccordionDetails, Typography, Container, FormControl, InputLabel, Select, MenuItem, Checkbox, FormControlLabel, FormGroup, + Accordion, AccordionSummary, AccordionDetails, Typography, Container, FormControl, InputLabel, Select, MenuItem, Checkbox, FormControlLabel, FormGroup, InputAdornment, IconButton, OutlinedInput, } from '@mui/material'; import makeStyles from '@mui/styles/makeStyles'; import ExpandMoreIcon from '@mui/icons-material/ExpandMore'; +import CachedIcon from '@mui/icons-material/Cached'; +import ContentCopyIcon from '@mui/icons-material/ContentCopy'; import { useLocalization, useTranslation, useTranslationKeys } from '../common/components/LocalizationProvider'; import usePersistedState from '../common/util/usePersistedState'; import PageLayout from '../common/components/PageLayout'; @@ -15,6 +17,7 @@ import { prefixString, unprefixString } from '../common/util/stringUtils'; import SelectField from '../common/components/SelectField'; import useMapStyles from '../map/core/useMapStyles'; import useMapOverlays from '../map/overlay/useMapOverlays'; +import { useCatch } from '../reactHelper'; const useStyles = makeStyles((theme) => ({ container: { @@ -26,6 +29,10 @@ const useStyles = makeStyles((theme) => ({ gap: theme.spacing(2), paddingBottom: theme.spacing(3), }, + tokenActions: { + display: 'flex', + flexDirection: 'column', + }, })); const PreferencesPage = () => { @@ -38,6 +45,8 @@ const PreferencesPage = () => { const { languages, language, setLanguage } = useLocalization(); const languageList = Object.entries(languages).map((values) => ({ code: values[0], name: values[1].name })); + const [token, setToken] = useState(); + const mapStyles = useMapStyles(); const [activeMapStyles, setActiveMapStyles] = usePersistedState('activeMapStyles', ['locationIqStreets', 'osm', 'carto']); @@ -53,6 +62,18 @@ const PreferencesPage = () => { const [mapCluster, setMapCluster] = usePersistedState('mapCluster', true); const [mapOnSelect, setMapOnSelect] = usePersistedState('mapOnSelect', false); + const generateToken = useCatch(async () => { + const response = await fetch('/api/session/token', { + method: 'POST', + body: new URLSearchParams(), + }); + if (response.ok) { + setToken(await response.text()); + } else { + throw Error(await response.text()); + } + }); + const alarms = useTranslationKeys((it) => it.startsWith('alarm')).map((it) => ({ key: unprefixString('alarm', it), name: t(it), @@ -81,6 +102,29 @@ const PreferencesPage = () => { {languageList.map((it) => <MenuItem key={it.code} value={it.code}>{it.name}</MenuItem>)} </Select> </FormControl> + <FormControl> + <InputLabel>{t('userToken')}</InputLabel> + <OutlinedInput + multiline + rows={6} + readOnly + type="text" + label={t('userToken')} + value={token || ''} + endAdornment={( + <InputAdornment position="end"> + <div className={classes.tokenActions}> + <IconButton size="small" onClick={generateToken} disabled={!!token}> + <CachedIcon fontSize="small" /> + </IconButton> + <IconButton size="small" onClick={() => navigator.clipboard.writeText(token)} disabled={!token}> + <ContentCopyIcon fontSize="small" /> + </IconButton> + </div> + </InputAdornment> + )} + /> + </FormControl> </AccordionDetails> </Accordion> <Accordion> diff --git a/modern/src/settings/UserPage.js b/modern/src/settings/UserPage.js index 85f4ae34..4dc6be95 100644 --- a/modern/src/settings/UserPage.js +++ b/modern/src/settings/UserPage.js @@ -11,16 +11,12 @@ import { MenuItem, FormControlLabel, Checkbox, - InputAdornment, - IconButton, - OutlinedInput, FormGroup, TextField, Button, } from '@mui/material'; import makeStyles from '@mui/styles/makeStyles'; import ExpandMoreIcon from '@mui/icons-material/ExpandMore'; -import CachedIcon from '@mui/icons-material/Cached'; import DeleteForeverIcon from '@mui/icons-material/DeleteForever'; import { useDispatch, useSelector } from 'react-redux'; import moment from 'moment'; @@ -263,28 +259,7 @@ const UserPage = () => { </Typography> </AccordionSummary> <AccordionDetails className={classes.details}> - <FormControl> - <InputLabel>{t('userToken')}</InputLabel> - <OutlinedInput - type="text" - label={t('userToken')} - value={item.token || ''} - onChange={(e) => setItem({ ...item, token: e.target.value })} - endAdornment={( - <InputAdornment position="end"> - <IconButton - size="small" - onClick={() => { - const token = [...Array(30)].map(() => Math.random().toString(36)[2]).join(''); - setItem({ ...item, token }); - }} - > - <CachedIcon fontSize="small" /> - </IconButton> - </InputAdornment> - )} - /> - </FormControl> + token <TextField label={t('userExpirationTime')} type="date" diff --git a/web/app/model/User.js b/web/app/model/User.js index fcaff007..455bca64 100644 --- a/web/app/model/User.js +++ b/web/app/model/User.js @@ -87,9 +87,6 @@ Ext.define('Traccar.model.User', { name: 'poiLayer', type: 'string' }, { - name: 'token', - type: 'string' - }, { name: 'attributes' }], diff --git a/web/app/view/dialog/User.js b/web/app/view/dialog/User.js index 7335ee79..5da56424 100644 --- a/web/app/view/dialog/User.js +++ b/web/app/view/dialog/User.js @@ -172,17 +172,6 @@ Ext.define('Traccar.view.dialog.User', { fieldLabel: Strings.userUserLimit, disabled: true, reference: 'userLimitField' - }, { - xtype: 'unescapedTextField', - name: 'token', - reference: 'tokenField', - fieldLabel: Strings.userToken, - triggers: { - generate: { - cls: 'iconCls: x-fa fa-refresh', - handler: 'generateToken' - } - } }] }] }, diff --git a/web/app/view/dialog/UserController.js b/web/app/view/dialog/UserController.js index 4ba48563..0d620614 100644 --- a/web/app/view/dialog/UserController.js +++ b/web/app/view/dialog/UserController.js @@ -35,18 +35,6 @@ Ext.define('Traccar.view.dialog.UserController', { } }, - symbols: 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789', - - generateToken: function () { - var i, newToken = ''; - - for (i = 0; i < 32; i++) { - newToken += this.symbols.charAt(Math.floor(Math.random() * this.symbols.length)); - } - - this.lookupReference('tokenField').setValue(newToken); - }, - testNotification: function () { Ext.Ajax.request({ url: 'api/notifications/test', |