diff --git a/packages/web/src/components/ThemeProvider/index.tsx b/packages/web/src/components/ThemeProvider/index.tsx index f6562d80..daa5c584 100644 --- a/packages/web/src/components/ThemeProvider/index.tsx +++ b/packages/web/src/components/ThemeProvider/index.tsx @@ -1,5 +1,6 @@ import CssBaseline from '@mui/material/CssBaseline'; import { ThemeProvider as BaseThemeProvider } from '@mui/material/styles'; +import clone from 'lodash/clone'; import get from 'lodash/get'; import set from 'lodash/set'; import * as React from 'react'; @@ -13,16 +14,19 @@ type ThemeProviderProps = { }; const customizeTheme = (defaultTheme: typeof theme, config: IJSONObject) => { + // `clone` is needed so that the new theme reference triggers re-render + const shallowDefaultTheme = clone(defaultTheme); + for (const key in config) { const value = config[key]; const exists = get(defaultTheme, key); if (exists) { - set(defaultTheme, key, value); + set(shallowDefaultTheme, key, value); } } - return defaultTheme; + return shallowDefaultTheme; }; const ThemeProvider = ({ diff --git a/packages/web/src/pages/UserInterface/index.tsx b/packages/web/src/pages/UserInterface/index.tsx index 962fa61b..c6ad6fd4 100644 --- a/packages/web/src/pages/UserInterface/index.tsx +++ b/packages/web/src/pages/UserInterface/index.tsx @@ -7,6 +7,7 @@ import LoadingButton from '@mui/lab/LoadingButton'; import { useSnackbar } from 'notistack'; import merge from 'lodash/merge'; +import { GET_CONFIG } from 'graphql/queries/get-config.ee'; import { UPDATE_CONFIG } from 'graphql/mutations/update-config.ee'; import useConfig from 'hooks/useConfig'; import PageTitle from 'components/PageTitle'; @@ -49,9 +50,7 @@ const defaultValues = { export default function UserInterface(): React.ReactElement { const formatMessage = useFormatMessage(); - const [updateConfig, { loading }] = useMutation(UPDATE_CONFIG, { - refetchQueries: ['GetConfig'], - }); + const [updateConfig, { loading }] = useMutation(UPDATE_CONFIG); const { config, loading: configLoading } = useConfig([ 'title', 'palette.primary.main', @@ -61,27 +60,69 @@ export default function UserInterface(): React.ReactElement { ]); const { enqueueSnackbar } = useSnackbar(); const configWithDefaults = merge( + {}, defaultValues, nestObject(config) ); const handleUserInterfaceUpdate = async (uiData: Partial) => { try { + const input = { + title: uiData?.title, + 'palette.primary.main': getPrimaryMainColor( + uiData?.palette?.primary.main + ), + 'palette.primary.dark': getPrimaryDarkColor( + uiData?.palette?.primary.dark + ), + 'palette.primary.light': getPrimaryLightColor( + uiData?.palette?.primary.light + ), + 'logo.svgData': uiData?.logo?.svgData, + }; + await updateConfig({ variables: { - input: { - title: uiData?.title, - 'palette.primary.main': getPrimaryMainColor( - uiData?.palette?.primary.main - ), - 'palette.primary.dark': getPrimaryDarkColor( - uiData?.palette?.primary.dark - ), - 'palette.primary.light': getPrimaryLightColor( - uiData?.palette?.primary.light - ), - 'logo.svgData': uiData?.logo?.svgData, - }, + input, + }, + optimisticResponse: { + updateConfig: input, + }, + update: async function (cache, { data: { updateConfig } }) { + const newConfigWithDefaults = merge({}, defaultValues, updateConfig); + + cache.writeQuery({ + query: GET_CONFIG, + data: { + getConfig: newConfigWithDefaults, + }, + }); + + cache.writeQuery({ + query: GET_CONFIG, + data: { + getConfig: newConfigWithDefaults, + }, + variables: { + keys: ['logo.svgData'], + }, + }); + + cache.writeQuery({ + query: GET_CONFIG, + data: { + getConfig: newConfigWithDefaults, + }, + variables: { + keys: [ + 'title', + 'palette.primary.main', + 'palette.primary.light', + 'palette.primary.dark', + 'logo.svgData', + ], + }, + }); }, });