diff options
-rw-r--r-- | modern/src/common/attributes/useCommonUserAttributes.js | 4 | ||||
-rw-r--r-- | modern/src/common/util/preferences.js | 32 | ||||
-rw-r--r-- | modern/src/map/MapGeofence.js | 4 | ||||
-rw-r--r-- | modern/src/resources/l10n/en.json | 1 | ||||
-rw-r--r-- | modern/src/settings/PreferencesPage.js | 64 |
5 files changed, 91 insertions, 14 deletions
diff --git a/modern/src/common/attributes/useCommonUserAttributes.js b/modern/src/common/attributes/useCommonUserAttributes.js index e64299bd..7fe2fcdf 100644 --- a/modern/src/common/attributes/useCommonUserAttributes.js +++ b/modern/src/common/attributes/useCommonUserAttributes.js @@ -1,6 +1,10 @@ import { useMemo } from 'react'; export default (t) => useMemo(() => ({ + mapGeofences: { + name: t('attributeShowGeofences'), + type: 'boolean', + }, locationIqKey: { name: t('mapLocationIqKey'), type: 'string', diff --git a/modern/src/common/util/preferences.js b/modern/src/common/util/preferences.js index aba3c82c..fb8bb4f2 100644 --- a/modern/src/common/util/preferences.js +++ b/modern/src/common/util/preferences.js @@ -2,14 +2,38 @@ import { useSelector } from 'react-redux'; export const usePreference = (key, defaultValue) => useSelector((state) => { if (state.session.server.forceSettings) { - return state.session.server[key] || state.session.user[key] || defaultValue; + if (state.session.server.hasOwnProperty(key)) { + return state.session.server[key]; + } + if (state.session.user.hasOwnProperty(key)) { + return state.session.user[key]; + } + return defaultValue; } - return state.session.user[key] || state.session.server[key] || defaultValue; + if (state.session.user.hasOwnProperty(key)) { + return state.session.user[key]; + } + if (state.session.server.hasOwnProperty(key)) { + return state.session.server[key]; + } + return defaultValue; }); export const useAttributePreference = (key, defaultValue) => useSelector((state) => { if (state.session.server.forceSettings) { - return state.session.server.attributes[key] || state.session.user.attributes[key] || defaultValue; + if (state.session.server.attributes.hasOwnProperty(key)) { + return state.session.server.attributes[key]; + } + if (state.session.user.attributes.hasOwnProperty(key)) { + return state.session.user.attributes[key]; + } + return defaultValue; + } + if (state.session.user.attributes.hasOwnProperty(key)) { + return state.session.user.attributes[key]; + } + if (state.session.server.attributes.hasOwnProperty(key)) { + return state.session.server.attributes[key]; } - return state.session.user.attributes[key] || state.session.server.attributes[key] || defaultValue; + return defaultValue; }); diff --git a/modern/src/map/MapGeofence.js b/modern/src/map/MapGeofence.js index 3bfbca24..6208795c 100644 --- a/modern/src/map/MapGeofence.js +++ b/modern/src/map/MapGeofence.js @@ -3,14 +3,14 @@ import { useSelector } from 'react-redux'; import { useTheme } from '@mui/styles'; import { map } from './core/MapView'; import { findFonts, geofenceToFeature } from './core/mapUtil'; -import usePersistedState from '../common/util/usePersistedState'; +import { useAttributePreference } from '../common/util/preferences'; const MapGeofence = () => { const id = useId(); const theme = useTheme(); - const [mapGeofences] = usePersistedState('mapGeofences', true); + const mapGeofences = useAttributePreference('mapGeofences', true); const geofences = useSelector((state) => state.geofences.items); diff --git a/modern/src/resources/l10n/en.json b/modern/src/resources/l10n/en.json index f6d43e7a..dd1f6723 100644 --- a/modern/src/resources/l10n/en.json +++ b/modern/src/resources/l10n/en.json @@ -112,6 +112,7 @@ "calendarThursday": "Thursday", "calendarFriday": "Friday", "calendarSaturday": "Saturday", + "attributeShowGeofences": "Show Geofences", "attributeSpeedLimit": "Speed Limit", "attributeFuelDropThreshold": "Fuel Drop Threshold", "attributeFuelIncreaseThreshold": "Fuel Increase Threshold", diff --git a/modern/src/settings/PreferencesPage.js b/modern/src/settings/PreferencesPage.js index 02c457a6..9a2b37e7 100644 --- a/modern/src/settings/PreferencesPage.js +++ b/modern/src/settings/PreferencesPage.js @@ -1,9 +1,9 @@ import React, { useState } from 'react'; import moment from 'moment'; -import { useSelector } from 'react-redux'; +import { useDispatch, useSelector } from 'react-redux'; import { useNavigate } from 'react-router-dom'; import { - Accordion, AccordionSummary, AccordionDetails, Typography, Container, FormControl, InputLabel, Select, MenuItem, Checkbox, FormControlLabel, FormGroup, InputAdornment, IconButton, OutlinedInput, Autocomplete, TextField, createFilterOptions, + Accordion, AccordionSummary, AccordionDetails, Typography, Container, FormControl, InputLabel, Select, MenuItem, Checkbox, FormControlLabel, FormGroup, InputAdornment, IconButton, OutlinedInput, Autocomplete, TextField, createFilterOptions, Button, } from '@mui/material'; import makeStyles from '@mui/styles/makeStyles'; import ExpandMoreIcon from '@mui/icons-material/ExpandMore'; @@ -19,6 +19,7 @@ import SelectField from '../common/components/SelectField'; import useMapStyles from '../map/core/useMapStyles'; import useMapOverlays from '../map/overlay/useMapOverlays'; import { useCatch } from '../reactHelper'; +import { sessionActions } from '../store'; const deviceFields = [ { id: 'name', name: 'sharedName' }, @@ -33,6 +34,15 @@ const useStyles = makeStyles((theme) => ({ container: { marginTop: theme.spacing(2), }, + buttons: { + marginTop: theme.spacing(2), + marginBottom: theme.spacing(2), + display: 'flex', + justifyContent: 'space-evenly', + '& > *': { + flexBasis: '33%', + }, + }, details: { display: 'flex', flexDirection: 'column', @@ -47,10 +57,12 @@ const useStyles = makeStyles((theme) => ({ const PreferencesPage = () => { const classes = useStyles(); + const dispatch = useDispatch(); const navigate = useNavigate(); const t = useTranslation(); - const userId = useSelector((state) => state.session.user.id); + const user = useSelector((state) => state.session.user); + const [attributes, setAttributes] = useState(user.attributes); const { languages, language, setLanguage } = useLocalization(); const languageList = Object.entries(languages).map((values) => ({ code: values[0], name: values[1].name })); @@ -67,7 +79,6 @@ const PreferencesPage = () => { const positionAttributes = usePositionAttributes(t); const [positionItems, setPositionItems] = usePersistedState('positionItems', ['speed', 'address', 'totalDistance', 'course']); - const [mapGeofences, setMapGeofences] = usePersistedState('mapGeofences', true); const [mapLiveRoutes, setMapLiveRoutes] = usePersistedState('mapLiveRoutes', false); const [mapFollow, setMapFollow] = usePersistedState('mapFollow', false); const [mapCluster, setMapCluster] = usePersistedState('mapCluster', true); @@ -99,6 +110,20 @@ const PreferencesPage = () => { const [soundEvents, setSoundEvents] = usePersistedState('soundEvents', []); const [soundAlarms, setSoundAlarms] = usePersistedState('soundAlarms', ['sos']); + const handleSave = useCatch(async () => { + const response = await fetch(`/api/users/${user.id}`, { + method: 'PUT', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ ...user, attributes }), + }); + if (response.ok) { + dispatch(sessionActions.updateUser(await response.json())); + navigate(-1); + } else { + throw Error(await response.text()); + } + }); + return ( <PageLayout menu={<SettingsMenu />} breadcrumbs={['settingsTitle', 'sharedPreferences']}> <Container maxWidth="xs" className={classes.container}> @@ -178,7 +203,7 @@ const PreferencesPage = () => { setActiveMapStyles(e.target.value); } else if (clicked.id !== 'custom') { const query = new URLSearchParams({ attribute: clicked.attribute }); - navigate(`/settings/user/${userId}?${query.toString()}`); + navigate(`/settings/user/${user.id}?${query.toString()}`); } }} multiple @@ -201,7 +226,7 @@ const PreferencesPage = () => { setSelectedMapOverlay(e.target.value); } else if (clicked.id !== 'custom') { const query = new URLSearchParams({ attribute: clicked.attribute }); - navigate(`/settings/user/${userId}?${query.toString()}`); + navigate(`/settings/user/${user.id}?${query.toString()}`); } }} > @@ -238,8 +263,13 @@ const PreferencesPage = () => { /> <FormGroup> <FormControlLabel - control={<Checkbox checked={mapGeofences} onChange={(e) => setMapGeofences(e.target.checked)} />} - label={t('sharedGeofences')} + control={( + <Checkbox + checked={attributes.hasOwnProperty('mapGeofences') ? attributes.mapGeofences : true} + onChange={(e) => setAttributes({ ...attributes, mapGeofences: e.target.checked })} + /> + )} + label={t('attributeShowGeofences')} /> <FormControlLabel control={<Checkbox checked={mapLiveRoutes} onChange={(e) => setMapLiveRoutes(e.target.checked)} />} @@ -311,6 +341,24 @@ const PreferencesPage = () => { /> </AccordionDetails> </Accordion> + <div className={classes.buttons}> + <Button + type="button" + color="primary" + variant="outlined" + onClick={() => navigate(-1)} + > + {t('sharedCancel')} + </Button> + <Button + type="button" + color="primary" + variant="contained" + onClick={handleSave} + > + {t('sharedSave')} + </Button> + </div> </Container> </PageLayout> ); |