Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Save style to persistent storage to prevent flashes after loading finishes #860

Merged
merged 2 commits into from
Feb 7, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
63 changes: 26 additions & 37 deletions frontend/src/api/contexts/themeConfig/ThemeConfig.tsx
Original file line number Diff line number Diff line change
@@ -1,46 +1,35 @@
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()
const [persistentStyle, setPersistentStyle] = useState<Style | undefined>(getPersistentStyle())

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
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was very bad code

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 { colorMode, setColorMode } = useColorMode()
useEffect(() => {
if (!style) return
if (colorMode !== 'dark' && style.forceDarkMode) {
if (!persistentStyle) return
if (colorMode !== 'dark' && persistentStyle.forceDarkMode) {
setColorMode('dark')
} else if (!style.darkModeEnabled) setColorMode('white')
}, [!!style, style?.deviceTheme, style?.forceDarkMode])
} else if (!persistentStyle.darkModeEnabled) setColorMode('white')
}, [!!persistentStyle, persistentStyle?.deviceTheme, persistentStyle?.forceDarkMode])

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}>{children}</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.

85 changes: 56 additions & 29 deletions frontend/src/util/configs/theme.config.ts
Original file line number Diff line number Diff line change
@@ -1,36 +1,63 @@
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'
},
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))
}
Loading