Skip to content

Commit

Permalink
Save style to persistent storage to prevent flashes after loading fin…
Browse files Browse the repository at this point in the history
…ishes (#860)
  • Loading branch information
Isti01 committed Feb 7, 2025
1 parent e1f46dc commit 1383f9b
Show file tree
Hide file tree
Showing 9 changed files with 149 additions and 159 deletions.
2 changes: 1 addition & 1 deletion frontend/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
body: JSON.stringify(report),
method: 'POST',
headers: { 'Content-Type': 'application/json' }
})
}).catch((err) => console.error('Failed to report error', err))
}

function processAndReportError(error) {
Expand Down
10 changes: 10 additions & 0 deletions frontend/src/api/contexts/config/ConfigContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { useConfigQuery } from '../../hooks/config/useConfigQuery'
import { ConfigDto } from './types'
import { LoadingView } from '../../../util/LoadingView.tsx'
import { l } from '../../../util/language.ts'
import { usePersistentStyleSetting } from '../../../util/configs/themeStyle.config.ts'

export const ConfigContext = createContext<ConfigDto | undefined>(undefined)

Expand All @@ -13,6 +14,15 @@ export const ConfigProvider = ({ children }: PropsWithChildren) => {
if (isError) console.error('[ERROR] at ConfigProvider', JSON.stringify(error, null, 2))
}, [isError, error])

const style = data?.components?.style
const { persistentStyle, setPersistentStyle } = usePersistentStyleSetting()
useEffect(() => {
// style should always be truthy if there is value
if (style && style != persistentStyle) {
setPersistentStyle(style)
}
}, [style, persistentStyle, setPersistentStyle])

const is500Status = Math.floor(Number(error?.response?.status) / 100) === 5
return (
<LoadingView
Expand Down
68 changes: 32 additions & 36 deletions frontend/src/api/contexts/themeConfig/ThemeConfig.tsx
Original file line number Diff line number Diff line change
@@ -1,46 +1,42 @@
import { customTheme } from '../../../util/configs/theme.config'
import { useConfigContext } from '../config/ConfigContext'
import { ChakraProvider, useColorMode } from '@chakra-ui/react'
import { PropsWithChildren, useEffect, useMemo } from 'react'
import { getColorShadesForColor } from '../../../util/core-functions.util'
import { PropsWithChildren, useEffect, useMemo, useState } from 'react'
import { Style } from '../config/types.ts'
import { getCustomTheme } from '../../../util/configs/theme.config.ts'
import { getPersistentStyle, PersistentStyleSettingContext, savePersistentStyle } from '../../../util/configs/themeStyle.config.ts'

export const ThemeConfig = ({ children }: PropsWithChildren) => {
const config = useConfigContext()

useThemeUpdate(config?.components?.style)
const chakraConfig = useMemo(() => {
if (config?.components.style) {
customTheme.colors.brand = getColorShadesForColor(config.components.style.lightBrandingColor)
customTheme.colors.lightContainerColor = getColorShadesForColor(config.components.style.lightContainerColor)
customTheme.colors.lightContainerBg = config.components.style.lightContainerColor
customTheme.colors.darkContainerColor = getColorShadesForColor(config.components.style.darkContainerColor)
customTheme.colors.darkContainerBg = config.components.style.darkContainerColor
customTheme.fonts = {
heading: config.components.style.mainFontName,
body: config.components.style.mainFontName,
display: config.components.style.displayFontName,
mono: 'monospace'
}
customTheme.components.Heading = {
...customTheme.components.Heading,
variants: {
'main-title': { fontFamily: config.components.style.displayFontName }
}
}
}
return customTheme
}, [config])

return <ChakraProvider theme={chakraConfig}>{children}</ChakraProvider>
}

const useThemeUpdate = (style?: Style) => {
const ColorModeSetter = ({ children, style }: PropsWithChildren & { style?: Style }) => {
const { colorMode, setColorMode } = useColorMode()
useEffect(() => {
if (!setColorMode) return
if (!style) return
if (colorMode !== 'dark' && style.forceDarkMode) {
setColorMode('dark')
} else if (!style.darkModeEnabled) setColorMode('white')
}, [!!style, style?.deviceTheme, style?.forceDarkMode])
}, [!!setColorMode, !!style, style?.deviceTheme, style?.forceDarkMode])

return <>{children}</>
}

export const ThemeConfig = ({ children }: PropsWithChildren) => {
const [persistentStyle, setPersistentStyle] = useState<Style | undefined>(getPersistentStyle())

const theme = useMemo(() => getCustomTheme(persistentStyle), [persistentStyle])
const contextData = useMemo(
() => ({
persistentStyle,
setPersistentStyle: (style?: Style) => {
savePersistentStyle(style)
setPersistentStyle(style)
}
}),
[persistentStyle]
)

return (
<PersistentStyleSettingContext.Provider value={contextData}>
<ChakraProvider theme={theme}>
<ColorModeSetter style={persistentStyle}>{children}</ColorModeSetter>
</ChakraProvider>
</PersistentStyleSettingContext.Provider>
)
}
41 changes: 19 additions & 22 deletions frontend/src/index.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { ChakraProvider, ColorModeScript } from '@chakra-ui/react'
import { ColorModeScript } from '@chakra-ui/react'
import React from 'react'
import { QueryClientProvider } from '@tanstack/react-query'
import { ReactQueryDevtools } from '@tanstack/react-query-devtools'
Expand All @@ -11,7 +11,6 @@ import { createRoot } from 'react-dom/client'
import { ThemeConfig } from './api/contexts/themeConfig/ThemeConfig'
import { ConfigProvider } from './api/contexts/config/ConfigContext'
import { ServiceProvider } from './api/contexts/service/ServiceContext'
import { customTheme } from './util/configs/theme.config'
import { HelmetProvider } from 'react-helmet-async'
import { ErrorBoundary } from './util/errorBoundary'
import { PushNotificationHandler } from './common-components/PushNotificationHandler.tsx'
Expand All @@ -23,27 +22,25 @@ const root = createRoot(document.getElementById('root')!)
root.render(
<React.StrictMode>
<ColorModeScript />
<QueryClientProvider client={queryClient}>
<HelmetProvider>
<BrowserRouter>
<ServiceProvider>
<ChakraProvider theme={customTheme}>
<ThemeConfig>
<QueryClientProvider client={queryClient}>
<HelmetProvider>
<BrowserRouter>
<ServiceProvider>
<ConfigProvider>
<ThemeConfig>
<ErrorBoundary>
<AuthProvider>
<PushNotificationHandler>
<App />
<ReactQueryDevtools />
</PushNotificationHandler>
</AuthProvider>
</ErrorBoundary>
</ThemeConfig>
<ErrorBoundary>
<AuthProvider>
<PushNotificationHandler>
<App />
<ReactQueryDevtools />
</PushNotificationHandler>
</AuthProvider>
</ErrorBoundary>
</ConfigProvider>
</ChakraProvider>
</ServiceProvider>
</BrowserRouter>
</HelmetProvider>
</QueryClientProvider>
</ServiceProvider>
</BrowserRouter>
</HelmetProvider>
</QueryClientProvider>
</ThemeConfig>
</React.StrictMode>
)
4 changes: 2 additions & 2 deletions frontend/src/pages/impressum/components/DeveloperWrapItem.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Flex, HStack, Image, Tag, Text } from '@chakra-ui/react'
import { Dev } from '../../../api/hooks/developers/useDevelopers'
import { API_BASE_URL } from '../../../util/configs/environment.config'
import { customTheme } from '../../../util/configs/theme.config'
import { KirDevColor } from '../../../util/configs/theme.config'

type Props = {
dev: Dev
Expand All @@ -14,7 +14,7 @@ export const DeveloperWrapItem = ({ dev: { name, img, tags } }: Props) => {
<Image src={img} h="15rem" fallbackSrc={`${API_BASE_URL}/img/big_pear_logo.png`} />
<HStack spacing={2} my={2}>
{tags.map((tag) => (
<Tag size={'md'} variant="solid" fontWeight="bold" color="white" bgColor={customTheme.colors.kirDev} key={tag}>
<Tag size={'md'} variant="solid" fontWeight="bold" color="white" bgColor={KirDevColor} key={tag}>
{tag}
</Tag>
))}
Expand Down
4 changes: 0 additions & 4 deletions frontend/src/util/configs/cookies.config.ts

This file was deleted.

66 changes: 0 additions & 66 deletions frontend/src/util/configs/nav.config.ts

This file was deleted.

87 changes: 59 additions & 28 deletions frontend/src/util/configs/theme.config.ts
Original file line number Diff line number Diff line change
@@ -1,36 +1,67 @@
import { extendTheme } from '@chakra-ui/react'
import { mode } from '@chakra-ui/theme-tools'
import { getColorShadesForColor } from '../core-functions.util.ts'
import { Style } from '../../api/contexts/config/types.ts'

// See more: https://chakra-ui.com/docs/theming/customize-theme
export let customTheme = extendTheme({
styles: {
global: (props: any) => ({
body: {
color: mode('gray.900', 'whiteAlpha.900')(props),
bg: mode('white', 'gray.900')(props)
export const KirDevColor = '#F15A29'

export const getCustomTheme = (style?: Style) =>
extendTheme(
{
styles: {
global: (props: any) => ({
body: {
color: mode('gray.900', 'whiteAlpha.900')(props),
bg: mode('white', 'gray.900')(props)
}
})
},
colors: {
brand: {
50: '#d9fae7',
100: '#fcded4',
200: '#f9bda9',
300: '#f79c7f',
400: '#f47b54',
500: '#F15A29',
600: '#c14821',
700: '#913619',
800: '#602410',
900: '#301208'
},
kirDev: '#F15A29'
},
components: {
Heading: {
baseStyle: {
marginTop: 5
}
}
}
})
},
colors: {
brand: {
50: '#d9fae7',
100: '#fcded4',
200: '#f9bda9',
300: '#f79c7f',
400: '#f47b54',
500: '#F15A29',
600: '#c14821',
700: '#913619',
800: '#602410',
900: '#301208'
},
kirDev: '#F15A29'
},
components: {
Heading: {
baseStyle: {
marginTop: 5
getThemeExtensionFromStyle(style) || {}
)

const getThemeExtensionFromStyle = (style?: Style) =>
style && {
colors: {
brand: getColorShadesForColor(style.lightBrandingColor),
lightContainerColor: getColorShadesForColor(style.lightContainerColor),
lightContainerBg: style.lightContainerColor,
darkContainerColor: getColorShadesForColor(style.darkContainerColor),
darkContainerBg: style.darkContainerColor
},
fonts: {
heading: style.mainFontName,
body: style.mainFontName,
display: style.displayFontName,
mono: 'monospace'
},
components: {
Heading: {
variants: {
'main-title': { fontFamily: style.displayFontName }
}
}
}
}
})
26 changes: 26 additions & 0 deletions frontend/src/util/configs/themeStyle.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { Style } from '../../api/contexts/config/types.ts'
import { createContext, useContext } from 'react'

export type PersistentStyleSettingData = {
persistentStyle?: Style
setPersistentStyle: (style?: Style) => void
}

export const PersistentStyleSettingContext = createContext<PersistentStyleSettingData>({
persistentStyle: undefined,
setPersistentStyle: () => {}
})

export const usePersistentStyleSetting = () => useContext(PersistentStyleSettingContext)

const StyleKey = 'persistent-style-setting'
export const getPersistentStyle = () => {
const styleJson = localStorage.getItem(StyleKey)
if (!styleJson) return undefined
return JSON.parse(styleJson) as Style
}

export const savePersistentStyle = (value?: Style) => {
if (!value) localStorage.removeItem(StyleKey)
else localStorage.setItem(StyleKey, JSON.stringify(value))
}

0 comments on commit 1383f9b

Please sign in to comment.