mirror of
https://github.com/strapi/strapi.git
synced 2025-09-27 01:09:49 +00:00
Add theme toggle to user profile
Signed-off-by: soupette <cyril@strapi.io>
This commit is contained in:
parent
afb2c8e3fc
commit
3a74150d3a
@ -1,9 +1,6 @@
|
|||||||
import darkTheme from './temp-dark-theme';
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
config: {
|
config: {
|
||||||
locales: ['fr'],
|
locales: ['fr'],
|
||||||
theme: darkTheme,
|
|
||||||
},
|
},
|
||||||
bootstrap() {},
|
bootstrap() {},
|
||||||
};
|
};
|
||||||
|
@ -5,10 +5,10 @@ import { useThemeToggle } from '../../hooks';
|
|||||||
import GlobalStyle from '../GlobalStyle';
|
import GlobalStyle from '../GlobalStyle';
|
||||||
|
|
||||||
const Theme = ({ children }) => {
|
const Theme = ({ children }) => {
|
||||||
const { currentTheme } = useThemeToggle();
|
const { currentTheme, themes } = useThemeToggle();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ThemeProvider theme={currentTheme}>
|
<ThemeProvider theme={themes[currentTheme] || themes.light}>
|
||||||
{children}
|
{children}
|
||||||
<GlobalStyle />
|
<GlobalStyle />
|
||||||
</ThemeProvider>
|
</ThemeProvider>
|
||||||
|
@ -9,14 +9,14 @@ import PropTypes from 'prop-types';
|
|||||||
import { ThemeToggleContext } from '../../contexts';
|
import { ThemeToggleContext } from '../../contexts';
|
||||||
|
|
||||||
const ThemeToggleProvider = ({ children, themes }) => {
|
const ThemeToggleProvider = ({ children, themes }) => {
|
||||||
const [currentTheme, setCurrentTheme] = useState(themes.light);
|
const [currentTheme, setCurrentTheme] = useState('light');
|
||||||
|
|
||||||
const handleChangeTheme = nextTheme => {
|
const handleChangeTheme = nextTheme => {
|
||||||
setCurrentTheme(themes[nextTheme]);
|
setCurrentTheme(nextTheme);
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ThemeToggleContext.Provider value={{ currentTheme, onChangeTheme: handleChangeTheme }}>
|
<ThemeToggleContext.Provider value={{ currentTheme, onChangeTheme: handleChangeTheme, themes }}>
|
||||||
{children}
|
{children}
|
||||||
</ThemeToggleContext.Provider>
|
</ThemeToggleContext.Provider>
|
||||||
);
|
);
|
||||||
|
@ -12,9 +12,9 @@ import {
|
|||||||
} from '@strapi/helper-plugin';
|
} from '@strapi/helper-plugin';
|
||||||
import { useIntl } from 'react-intl';
|
import { useIntl } from 'react-intl';
|
||||||
import { Formik } from 'formik';
|
import { Formik } from 'formik';
|
||||||
|
import upperFirst from 'lodash/upperFirst';
|
||||||
import { useQuery, useMutation, useQueryClient } from 'react-query';
|
import { useQuery, useMutation, useQueryClient } from 'react-query';
|
||||||
import pick from 'lodash/pick';
|
import pick from 'lodash/pick';
|
||||||
import omit from 'lodash/omit';
|
|
||||||
import { Helmet } from 'react-helmet';
|
import { Helmet } from 'react-helmet';
|
||||||
import { Main } from '@strapi/design-system/Main';
|
import { Main } from '@strapi/design-system/Main';
|
||||||
import { Typography } from '@strapi/design-system/Typography';
|
import { Typography } from '@strapi/design-system/Typography';
|
||||||
@ -31,6 +31,7 @@ import Eye from '@strapi/icons/Eye';
|
|||||||
import EyeStriked from '@strapi/icons/EyeStriked';
|
import EyeStriked from '@strapi/icons/EyeStriked';
|
||||||
import Check from '@strapi/icons/Check';
|
import Check from '@strapi/icons/Check';
|
||||||
import useLocalesProvider from '../../components/LocalesProvider/useLocalesProvider';
|
import useLocalesProvider from '../../components/LocalesProvider/useLocalesProvider';
|
||||||
|
import { useThemeToggle } from '../../hooks';
|
||||||
import { fetchUser, putUser } from './utils/api';
|
import { fetchUser, putUser } from './utils/api';
|
||||||
import schema from './utils/schema';
|
import schema from './utils/schema';
|
||||||
import { getFullName } from '../../utils';
|
import { getFullName } from '../../utils';
|
||||||
@ -62,6 +63,7 @@ const ProfilePage = () => {
|
|||||||
const toggleNotification = useNotification();
|
const toggleNotification = useNotification();
|
||||||
const { lockApp, unlockApp } = useOverlayBlocker();
|
const { lockApp, unlockApp } = useOverlayBlocker();
|
||||||
const { notifyStatus } = useNotifyAT();
|
const { notifyStatus } = useNotifyAT();
|
||||||
|
const { currentTheme, themes: allApplicationThemes, onChangeTheme } = useThemeToggle();
|
||||||
useFocusWhenNavigate();
|
useFocusWhenNavigate();
|
||||||
|
|
||||||
const { status, data } = useQuery('user', () => fetchUser(), {
|
const { status, data } = useQuery('user', () => fetchUser(), {
|
||||||
@ -83,14 +85,17 @@ const ProfilePage = () => {
|
|||||||
|
|
||||||
const isLoading = status !== 'success';
|
const isLoading = status !== 'success';
|
||||||
|
|
||||||
const submitMutation = useMutation(body => putUser(omit(body, 'confirmPassword')), {
|
const submitMutation = useMutation(body => putUser(body), {
|
||||||
onSuccess: async data => {
|
onSuccess: async data => {
|
||||||
await queryClient.invalidateQueries('user');
|
await queryClient.invalidateQueries('user');
|
||||||
|
|
||||||
auth.setUserInfo(data);
|
auth.setUserInfo(
|
||||||
|
pick(data, ['email', 'firstname', 'lastname', 'username', 'preferedLanguage'])
|
||||||
|
);
|
||||||
const userDisplayName = data.username || getFullName(data.firstname, data.lastname);
|
const userDisplayName = data.username || getFullName(data.firstname, data.lastname);
|
||||||
setUserDisplayName(userDisplayName);
|
setUserDisplayName(userDisplayName);
|
||||||
changeLocale(data.preferedLanguage);
|
changeLocale(data.preferedLanguage);
|
||||||
|
onChangeTheme(data.currentTheme);
|
||||||
|
|
||||||
toggleNotification({
|
toggleNotification({
|
||||||
type: 'success',
|
type: 'success',
|
||||||
@ -128,9 +133,16 @@ const ProfilePage = () => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const fieldsToPick = ['email', 'firstname', 'lastname', 'username', 'preferedLanguage'];
|
const fieldsToPick = [
|
||||||
|
'currentTheme',
|
||||||
|
'email',
|
||||||
|
'firstname',
|
||||||
|
'lastname',
|
||||||
|
'username',
|
||||||
|
'preferedLanguage',
|
||||||
|
];
|
||||||
|
|
||||||
const initialData = pick(data, fieldsToPick);
|
const initialData = pick({ ...data, currentTheme }, fieldsToPick);
|
||||||
|
|
||||||
if (isLoading) {
|
if (isLoading) {
|
||||||
return (
|
return (
|
||||||
@ -154,6 +166,10 @@ const ProfilePage = () => {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const themesToDisplay = Object.keys(allApplicationThemes).filter(
|
||||||
|
themeName => allApplicationThemes[themeName]
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Main aria-busy={isSubmittingForm}>
|
<Main aria-busy={isSubmittingForm}>
|
||||||
<Helmet
|
<Helmet
|
||||||
@ -487,6 +503,47 @@ const ProfilePage = () => {
|
|||||||
})}
|
})}
|
||||||
</Select>
|
</Select>
|
||||||
</GridItem>
|
</GridItem>
|
||||||
|
<GridItem s={12} col={6}>
|
||||||
|
<Select
|
||||||
|
label={formatMessage({
|
||||||
|
id: 'Settings.profile.form.section.experience.mode.label',
|
||||||
|
defaultMessage: 'Interface mode',
|
||||||
|
})}
|
||||||
|
placeholder={formatMessage({
|
||||||
|
id: 'components.Select.placeholder',
|
||||||
|
defaultMessage: 'Select',
|
||||||
|
})}
|
||||||
|
hint={formatMessage({
|
||||||
|
id: 'Settings.profile.form.section.experience.mode.hint',
|
||||||
|
defaultMessage: 'Displays your interface in the chosen mode.',
|
||||||
|
})}
|
||||||
|
value={values.currentTheme}
|
||||||
|
onChange={e => {
|
||||||
|
handleChange({
|
||||||
|
target: { name: 'currentTheme', value: e },
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{themesToDisplay.map(theme => {
|
||||||
|
const label = formatMessage(
|
||||||
|
{
|
||||||
|
id:
|
||||||
|
'Settings.profile.form.section.experience.mode.option-label',
|
||||||
|
defaultMessage: '{name} mode',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: upperFirst(theme),
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Option value={theme} key={theme}>
|
||||||
|
{label}
|
||||||
|
</Option>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</Select>
|
||||||
|
</GridItem>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Stack>
|
</Stack>
|
||||||
</Box>
|
</Box>
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import omit from 'lodash/omit';
|
||||||
import { axiosInstance } from '../../../core/utils';
|
import { axiosInstance } from '../../../core/utils';
|
||||||
|
|
||||||
const fetchUser = async () => {
|
const fetchUser = async () => {
|
||||||
@ -7,9 +8,10 @@ const fetchUser = async () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const putUser = async body => {
|
const putUser = async body => {
|
||||||
const { data } = await axiosInstance.put('/admin/users/me', body);
|
const dataToSend = omit(body, ['confirmPassword', 'currentTheme']);
|
||||||
|
const { data } = await axiosInstance.put('/admin/users/me', dataToSend);
|
||||||
|
|
||||||
return data.data;
|
return { ...data.data, currentTheme: body.currentTheme };
|
||||||
};
|
};
|
||||||
|
|
||||||
export { fetchUser, putUser };
|
export { fetchUser, putUser };
|
||||||
|
@ -137,6 +137,9 @@
|
|||||||
"Settings.profile.form.section.experience.interfaceLanguage": "Interface language",
|
"Settings.profile.form.section.experience.interfaceLanguage": "Interface language",
|
||||||
"Settings.profile.form.section.experience.interfaceLanguage.hint": "This will only display your own interface in the chosen language.",
|
"Settings.profile.form.section.experience.interfaceLanguage.hint": "This will only display your own interface in the chosen language.",
|
||||||
"Settings.profile.form.section.experience.interfaceLanguageHelp": "Selection will change the interface language only for you. Please refer to this {documentation} to make other languages available for your team.",
|
"Settings.profile.form.section.experience.interfaceLanguageHelp": "Selection will change the interface language only for you. Please refer to this {documentation} to make other languages available for your team.",
|
||||||
|
"Settings.profile.form.section.experience.mode.label": "Interface mode",
|
||||||
|
"Settings.profile.form.section.experience.mode.hint": "Displays your interface in the chosen mode.",
|
||||||
|
"Settings.profile.form.section.experience.mode.option-label": "{name} mode",
|
||||||
"Settings.profile.form.section.experience.title": "Experience",
|
"Settings.profile.form.section.experience.title": "Experience",
|
||||||
"Settings.profile.form.section.helmet.title": "User profile",
|
"Settings.profile.form.section.helmet.title": "User profile",
|
||||||
"Settings.profile.form.section.password.title": "Change password",
|
"Settings.profile.form.section.password.title": "Change password",
|
||||||
|
Loading…
x
Reference in New Issue
Block a user