mirror of
https://gitlab.com/soapbox-pub/mkstack.git
synced 2025-09-07 10:09:23 +00:00
82 lines
2.1 KiB
TypeScript
82 lines
2.1 KiB
TypeScript
![]() |
import { ReactNode, useEffect } from 'react';
|
||
|
import { useLocalStorage } from '@/hooks/useLocalStorage';
|
||
|
import { AppContext, type AppConfig, type AppContextType, type Theme } from '@/contexts/AppContext';
|
||
|
|
||
|
interface AppProviderProps {
|
||
|
children: ReactNode;
|
||
|
/** Application storage key */
|
||
|
storageKey: string;
|
||
|
/** Default app configuration */
|
||
|
defaultConfig: AppConfig;
|
||
|
}
|
||
|
|
||
|
export function AppProvider(props: AppProviderProps) {
|
||
|
const {
|
||
|
children,
|
||
|
storageKey,
|
||
|
defaultConfig
|
||
|
} = props;
|
||
|
|
||
|
// App configuration state with localStorage persistence
|
||
|
const [config, setConfig] = useLocalStorage<AppConfig>(storageKey, defaultConfig);
|
||
|
|
||
|
// Generic config updater with callback pattern
|
||
|
const updateConfig = (updater: (currentConfig: AppConfig) => AppConfig) => {
|
||
|
setConfig(updater);
|
||
|
};
|
||
|
|
||
|
const appContextValue: AppContextType = {
|
||
|
config,
|
||
|
updateConfig,
|
||
|
};
|
||
|
|
||
|
// Apply theme effects to document
|
||
|
useApplyTheme(config.theme);
|
||
|
|
||
|
return (
|
||
|
<AppContext.Provider value={appContextValue}>
|
||
|
{children}
|
||
|
</AppContext.Provider>
|
||
|
);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Hook to apply theme changes to the document root
|
||
|
*/
|
||
|
function useApplyTheme(theme: Theme) {
|
||
|
useEffect(() => {
|
||
|
const root = window.document.documentElement;
|
||
|
|
||
|
root.classList.remove('light', 'dark');
|
||
|
|
||
|
if (theme === 'system') {
|
||
|
const systemTheme = window.matchMedia('(prefers-color-scheme: dark)')
|
||
|
.matches
|
||
|
? 'dark'
|
||
|
: 'light';
|
||
|
|
||
|
root.classList.add(systemTheme);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
root.classList.add(theme);
|
||
|
}, [theme]);
|
||
|
|
||
|
// Handle system theme changes when theme is set to "system"
|
||
|
useEffect(() => {
|
||
|
if (theme !== 'system') return;
|
||
|
|
||
|
const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
|
||
|
|
||
|
const handleChange = () => {
|
||
|
const root = window.document.documentElement;
|
||
|
root.classList.remove('light', 'dark');
|
||
|
|
||
|
const systemTheme = mediaQuery.matches ? 'dark' : 'light';
|
||
|
root.classList.add(systemTheme);
|
||
|
};
|
||
|
|
||
|
mediaQuery.addEventListener('change', handleChange);
|
||
|
return () => mediaQuery.removeEventListener('change', handleChange);
|
||
|
}, [theme]);
|
||
|
}
|