diff --git a/components/map/map.js b/components/map/map.js index 1c6b8455f..074aa70fb 100644 --- a/components/map/map.js +++ b/components/map/map.js @@ -5,7 +5,7 @@ import MapGl, {Source, Layer} from 'react-map-gl' import maplibregl from 'maplibre-gl' import {Pane, Alert} from 'evergreen-ui' -import MapContext, {BAL_API_URL, SOURCE_TILE_ID} from '@/contexts/map' +import MapContext, {SOURCE_TILE_ID} from '@/contexts/map' import TokenContext from '@/contexts/token' import DrawContext from '@/contexts/draw' import ParcellesContext from '@/contexts/parcelles' @@ -81,7 +81,9 @@ function Map({commune, isAddressFormOpen, handleAddressForm}) { viewport, setViewport, isCadastreDisplayed, - setIsCadastreDisplayed + setIsCadastreDisplayed, + balTilesUrl, + isMapLoaded } = useContext(MapContext) const {isParcelleSelectionEnabled, handleParcelle} = useContext(ParcellesContext) @@ -90,7 +92,6 @@ function Map({commune, isAddressFormOpen, handleAddressForm}) { const [mapStyle, setMapStyle] = useState(generateNewStyle(defaultStyle)) const {balId} = router.query - const BAL_TILES_URL = BAL_API_URL + '/bases-locales/' + balId + '/tiles/{z}/{x}/{y}.pbf' const { voie, toponyme, @@ -244,10 +245,10 @@ function Map({commune, isAddressFormOpen, handleAddressForm}) { return { id: SOURCE_TILE_ID, type: 'vector', - tiles: [BAL_TILES_URL], + tiles: [balTilesUrl], promoteId: 'id', } - }, [BAL_TILES_URL]) + }, [balTilesUrl]) const sourceCommune = useMemo(() => { return { @@ -276,6 +277,18 @@ function Map({commune, isAddressFormOpen, handleAddressForm}) { } } + const selectedVoieColor = useMemo(() => { + if (!voie || !isMapLoaded) { + return + } + + const featuresList = map?.querySourceFeatures(SOURCE_TILE_ID, { + sourceLayer: [LAYERS_SOURCE.VOIES_POINTS] + }) + + return featuresList?.find(feature => feature.id === voie._id)?.properties.color + }, [map, voie, isMapLoaded]) + return ( _id !== editingId)} - voie={voie} isLabelDisplayed={isLabelsDisplayed} isContextMenuDisplayed={isContextMenuDisplayed} setIsContextMenuDisplayed={setIsContextMenuDisplayed} + color={selectedVoieColor} /> )} diff --git a/components/map/numeros-markers.js b/components/map/numeros-markers.js index 987c4adfd..132715601 100644 --- a/components/map/numeros-markers.js +++ b/components/map/numeros-markers.js @@ -1,7 +1,6 @@ import {useCallback, useContext} from 'react' import PropTypes from 'prop-types' import {css} from 'glamor' -import randomColor from 'randomcolor' import {softRemoveNumero} from '@/lib/bal-api' @@ -13,7 +12,7 @@ import useError from '@/hooks/error' import NumeroMarker from '@/components/map/numero-marker' -function NumerosMarkers({numeros, voie, isLabelDisplayed, isContextMenuDisplayed, setIsContextMenuDisplayed}) { +function NumerosMarkers({numeros, isLabelDisplayed, isContextMenuDisplayed, setIsContextMenuDisplayed, color}) { const [setError] = useError() const {token} = useContext(TokenContext) @@ -32,14 +31,7 @@ function NumerosMarkers({numeros, voie, isLabelDisplayed, isContextMenuDisplayed } }, [setEditingId, setIsContextMenuDisplayed, isEditing]) - const colorSeed = useCallback(id => { - return id ? randomColor({ - luminosity: 'dark', - seed: id - }) : '#1070ca' - }, []) - - const markerStyle = useCallback(colorSeed => css({ + const markerStyle = useCallback(color => css({ borderRadius: 20, marginTop: -10, marginLeft: -10, @@ -50,7 +42,7 @@ function NumerosMarkers({numeros, voie, isLabelDisplayed, isContextMenuDisplayed '&:before': { content: ' ', - backgroundColor: colorSeed, + backgroundColor: color, border: '1px solid white', display: 'inline-block', width: 8, @@ -91,7 +83,7 @@ function NumerosMarkers({numeros, voie, isLabelDisplayed, isContextMenuDisplayed { - const {isSettingsDisplayed, setIsSettingsDisplayed} = useContext(SettingsContext) + const {settingsDisplayed, setSettingsDisplayed} = useContext(SettingsContext) const {baseLocale} = useContext(BalDataContext) return ( setIsSettingsDisplayed(false)} + isShown={Boolean(settingsDisplayed)} + onCloseComplete={() => setSettingsDisplayed(null)} > - - - {baseLocale.status === 'demo' && ( - - - Il est impossible de modifier les paramètres de la base adresse locale en version d’essai. - - + {settingsDisplayed === 'user-settings' && ( + )} + + {settingsDisplayed === 'bal-settings' && ( + <> + + + {baseLocale.status === 'demo' && ( + + + Il est impossible de modifier les paramètres de la base adresse locale en version d’essai. + + + )} + + )} + ) }) diff --git a/components/settings/settings-form.js b/components/settings/bal-settings-form.js similarity index 98% rename from components/settings/settings-form.js rename to components/settings/bal-settings-form.js index eb1084961..11350b2bf 100644 --- a/components/settings/settings-form.js +++ b/components/settings/bal-settings-form.js @@ -31,7 +31,7 @@ const mailHasChanged = (listA, listB) => { return !isEqual([...listA].sort(), [...listB].sort()) } -const Settings = React.memo(({baseLocale}) => { +const BALSettings = React.memo(({baseLocale}) => { const {token, emails, reloadEmails} = useContext(TokenContext) const {reloadBaseLocale} = useContext(BalDataContext) @@ -227,7 +227,7 @@ const Settings = React.memo(({baseLocale}) => { ) }) -Settings.propTypes = { +BALSettings.propTypes = { baseLocale: PropTypes.shape({ _id: PropTypes.string.isRequired, status: PropTypes.string.isRequired, @@ -235,4 +235,4 @@ Settings.propTypes = { }).isRequired } -export default Settings +export default BALSettings diff --git a/components/settings/user-settings-form.js b/components/settings/user-settings-form.js new file mode 100644 index 000000000..ff0389378 --- /dev/null +++ b/components/settings/user-settings-form.js @@ -0,0 +1,78 @@ +import {useContext, useState} from 'react' +import LocalStorageContext from '@/contexts/local-storage' +import PropTypes from 'prop-types' +import { + Pane, + Heading, + Button, + Checkbox, + toaster +} from 'evergreen-ui' + +import FormContainer from '@/components/form-container' +import FormInput from '@/components/form-input' + +function UserSettings() { + const {userSettings, setUserSettings} = useContext(LocalStorageContext) + const [userSettingsForm, setUserSettingsForm] = useState(userSettings) + + const hasChanged = () => JSON.stringify(userSettingsForm) !== JSON.stringify(userSettings) + + const onSubmit = e => { + e.preventDefault() + setUserSettings(userSettingsForm) + toaster.success('Les préférences utilisateurs ont été enregistrées avec succès !') + } + + return ( + + + + Préférences utilisateur + + + + + + + setUserSettingsForm(settings => ({...settings, colorblindMode: !settings?.colorblindMode}))} + /> + + + + + + + + ) +} + +UserSettings.propTypes = { + baseLocale: PropTypes.shape({ + _id: PropTypes.string.isRequired, + status: PropTypes.string.isRequired, + nom: PropTypes.string.isRequired + }).isRequired +} + +export default UserSettings diff --git a/components/sub-header/settings-menu.js b/components/sub-header/settings-menu.js index 2d258f42f..f1c50773d 100644 --- a/components/sub-header/settings-menu.js +++ b/components/sub-header/settings-menu.js @@ -1,11 +1,11 @@ import {useContext} from 'react' import PropTypes from 'prop-types' -import {Popover, Menu, Position, Button, CogIcon, DownloadIcon, TrashIcon} from 'evergreen-ui' +import {Popover, Menu, Position, Button, CogIcon, DownloadIcon, TrashIcon, SettingsIcon} from 'evergreen-ui' import SettingsContext from '@/contexts/settings' function SettingsMenu({isAdmin, csvBalUrl, csvVoiesUrl, setIsTrashOpen}) { - const {isSettingsDisplayed, setIsSettingsDisplayed} = useContext(SettingsContext) + const {setSettingsDisplayed} = useContext(SettingsContext) return ( Télécharger Base Locale CSV - - Télécharger liste des voies CSV @@ -30,14 +28,19 @@ function SettingsMenu({isAdmin, csvBalUrl, csvVoiesUrl, setIsTrashOpen}) { Voir la corbeille - - - setIsSettingsDisplayed(!isSettingsDisplayed)}> - Gérer les droits - - - - )} + )} + + + setSettingsDisplayed('user-settings')}> + Préférences utilisateur + + {isAdmin && ( + setSettingsDisplayed('bal-settings')}> + Gérer les droits + + )} + + } > diff --git a/contexts/local-storage.js b/contexts/local-storage.js index 4fa99e764..0b0285ec6 100644 --- a/contexts/local-storage.js +++ b/contexts/local-storage.js @@ -11,6 +11,7 @@ const WELCOMED_KEY = 'was-welcomed' const RECOVERY_EMAIL = 'recovery-email-sent' const CERTIFICATION_AUTO_KEY = 'certificationAutoAlert' const VISIBILITY_KEY = 'hidden-bal' +const USER_SETTINGS = 'user-settings' export function LocalStorageContextProvider(props) { const [balAccess, , getBalToken, addBalAccess, removeBalAccess] = useLocalStorage(STORAGE_KEY) // Do not assign a default value @@ -18,6 +19,7 @@ export function LocalStorageContextProvider(props) { const [recoveryEmailSent, setRecoveryEmailSent] = useLocalStorage(RECOVERY_EMAIL) const [informedAboutCertification, , getInformedAboutCertification, addInformedAboutCertification] = useLocalStorage(CERTIFICATION_AUTO_KEY) const [hiddenBal, setHiddenBal, getHiddenBal, addHiddenBal, removeHiddenBal] = useLocalStorage(VISIBILITY_KEY) + const [userSettings, setUserSettings, getUserSettings, addUserSettings, removeUserSettings] = useLocalStorage(USER_SETTINGS) const removeBAL = useCallback(async balId => { const token = getBalToken(balId) @@ -30,7 +32,8 @@ export function LocalStorageContextProvider(props) { wasWelcomed, setWasWelcomed, recoveryEmailSent, setRecoveryEmailSent, informedAboutCertification, getInformedAboutCertification, addInformedAboutCertification, - hiddenBal, setHiddenBal, getHiddenBal, addHiddenBal, removeHiddenBal + hiddenBal, setHiddenBal, getHiddenBal, addHiddenBal, removeHiddenBal, + userSettings, setUserSettings, getUserSettings, addUserSettings, removeUserSettings }), [ balAccess, getBalToken, @@ -47,7 +50,12 @@ export function LocalStorageContextProvider(props) { setHiddenBal, getHiddenBal, addHiddenBal, - removeHiddenBal + removeHiddenBal, + userSettings, + setUserSettings, + getUserSettings, + addUserSettings, + removeUserSettings ]) return ( diff --git a/contexts/map.js b/contexts/map.js index 3ff269f1b..b49008162 100644 --- a/contexts/map.js +++ b/contexts/map.js @@ -1,5 +1,6 @@ import React, {useCallback, useState, useMemo, useEffect, useContext} from 'react' import BalContext from '@/contexts/bal-data' +import LocalStorageContext from '@/contexts/local-storage' const MapContext = React.createContext() @@ -23,8 +24,12 @@ export function MapContextProvider(props) { const [isCadastreDisplayed, setIsCadastreDisplayed] = useState(false) const [isTileSourceLoaded, setIsTileSourceLoaded] = useState(false) const [isStyleLoaded, setIsStyleLoaded] = useState(false) + const [isMapLoaded, setIsMapLoaded] = useState(false) const {baseLocale} = useContext(BalContext) + const {userSettings} = useContext(LocalStorageContext) + + const balTilesUrl = `${BAL_API_URL}/bases-locales/${baseLocale._id}/tiles/{z}/{x}/{y}.pbf${userSettings?.colorblindMode ? '?colorblindMode=true' : ''}` useEffect(() => { map?.on('load', () => { @@ -35,14 +40,18 @@ export function MapContextProvider(props) { }) }, [map]) + // When the map is fully loaded (with the layers), this event is triggered + map?.on('idle', () => { + setIsMapLoaded(true) + }) + const reloadTiles = useCallback(() => { if (map && isTileSourceLoaded) { const source = map.getSource(SOURCE_TILE_ID) // Remplace les tuiles avec de nouvelles tuiles - const BAL_TILES_URL = BAL_API_URL + '/bases-locales/' + baseLocale._id + '/tiles/{z}/{x}/{y}.pbf' - source.setTiles([BAL_TILES_URL]) + source.setTiles([balTilesUrl]) } - }, [map, isTileSourceLoaded, baseLocale._id]) + }, [map, isTileSourceLoaded, balTilesUrl]) const handleMapRef = useCallback(ref => { if (ref) { @@ -60,6 +69,8 @@ export function MapContextProvider(props) { isStyleLoaded, viewport, setViewport, isCadastreDisplayed, setIsCadastreDisplayed, + balTilesUrl, + isMapLoaded }), [ map, isTileSourceLoaded, @@ -68,7 +79,9 @@ export function MapContextProvider(props) { isStyleLoaded, style, viewport, - isCadastreDisplayed + isCadastreDisplayed, + balTilesUrl, + isMapLoaded ]) return ( diff --git a/contexts/settings.js b/contexts/settings.js index 387448ab6..0cc6a0596 100644 --- a/contexts/settings.js +++ b/contexts/settings.js @@ -3,12 +3,12 @@ import React, {useState, useMemo} from 'react' const SettingsContext = React.createContext() export function SettingsContextProvider(props) { - const [isSettingsDisplayed, setIsSettingsDisplayed] = useState(false) + const [settingsDisplayed, setSettingsDisplayed] = useState('') const value = useMemo(() => ({ - isSettingsDisplayed, - setIsSettingsDisplayed - }), [isSettingsDisplayed]) + settingsDisplayed, + setSettingsDisplayed + }), [settingsDisplayed]) return (