diff --git a/src/components/App/AppParticles.jsx b/src/components/App/AppParticles.jsx index d669d9a..a6acb57 100644 --- a/src/components/App/AppParticles.jsx +++ b/src/components/App/AppParticles.jsx @@ -2,6 +2,7 @@ import PropTypes from 'prop-types' import React, { useState, useEffect, useMemo, useCallback } from 'react' import Particles, { initParticlesEngine } from '@tsparticles/react' import { loadSlim } from '@tsparticles/slim' +import { useThemeContext } from '../Dashboard/context/ThemeContext' const ParticlesComponent = React.memo(({ options, particlesLoaded }) => { return ( @@ -17,6 +18,7 @@ ParticlesComponent.displayName = 'ParticlesComponent' const AppParticles = () => { const [init, setInit] = useState(false) + const { isDarkMode } = useThemeContext() // this should be run only once per application lifetime useEffect(() => { @@ -35,7 +37,7 @@ const AppParticles = () => { () => ({ background: { color: { - value: '#141414' + value: isDarkMode ? '#000000' : '#ffffff' } }, fpsLimit: 120, @@ -62,10 +64,10 @@ const AppParticles = () => { }, particles: { color: { - value: '#ffffff' + value: isDarkMode ? 'rgb(66, 66, 66)' : 'rgb(217, 217, 217)' }, links: { - color: '#ffffff', + color: isDarkMode ? 'rgb(66, 66, 66)' : 'rgb(217, 217, 217)', distance: 150, enable: true, opacity: 0.5, @@ -99,7 +101,7 @@ const AppParticles = () => { }, detectRetina: true }), - [] + [isDarkMode] ) return ( <> diff --git a/src/components/Dashboard/Management/Settings.jsx b/src/components/Dashboard/Management/Settings.jsx index 7a9965b..4990b2c 100644 --- a/src/components/Dashboard/Management/Settings.jsx +++ b/src/components/Dashboard/Management/Settings.jsx @@ -8,17 +8,30 @@ const { Title } = Typography const { Option } = Select const Settings = () => { - const { isDarkMode, toggleTheme, isCompact, toggleCompact } = - useThemeContext() + const { + isDarkMode, + toggleTheme, + isCompact, + toggleCompact, + isSystem, + toggleSystem + } = useThemeContext() const [collapseState, updateCollapseState] = useCollapseState('Settings', { appearance: true }) const handleThemeChange = (value) => { - if (value === 'dark' && !isDarkMode) { - toggleTheme() - } else if (value === 'light' && isDarkMode) { - toggleTheme() + if (value === 'system') { + toggleSystem() + } else { + if (isSystem) { + toggleSystem() + } + if (value === 'dark' && !isDarkMode) { + toggleTheme() + } else if (value === 'light' && isDarkMode) { + toggleTheme() + } } } @@ -30,6 +43,11 @@ const Settings = () => { } } + const getCurrentThemeValue = () => { + if (isSystem) return 'system' + return isDarkMode ? 'dark' : 'light' + } + return (
{ > diff --git a/src/components/Dashboard/context/ThemeContext.js b/src/components/Dashboard/context/ThemeContext.js index c5f20ae..73b34b2 100644 --- a/src/components/Dashboard/context/ThemeContext.js +++ b/src/components/Dashboard/context/ThemeContext.js @@ -1,17 +1,73 @@ -import React, { createContext, useContext, useState } from 'react' +import React, { createContext, useContext, useState, useEffect } from 'react' import { theme } from 'antd' import PropTypes from 'prop-types' const ThemeContext = createContext() export const ThemeProvider = ({ children }) => { - const [isDarkMode, setIsDarkMode] = useState(true) - const [isCompact, setIsCompact] = useState(false) + const [isSystem, setIsSystem] = useState(() => { + const savedSystem = sessionStorage.getItem('isSystem') + return savedSystem ? JSON.parse(savedSystem) : true + }) + const [isDarkMode, setIsDarkMode] = useState(() => { + const savedTheme = sessionStorage.getItem('isDarkMode') + return savedTheme ? JSON.parse(savedTheme) : true + }) + const [isCompact, setIsCompact] = useState(() => { + const savedCompact = sessionStorage.getItem('isCompact') + return savedCompact ? JSON.parse(savedCompact) : false + }) + + // System theme detection + useEffect(() => { + const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)') + + const handleSystemThemeChange = (e) => { + if (isSystem) { + setIsDarkMode(e.matches) + } + } + + // Set initial value + if (isSystem) { + setIsDarkMode(mediaQuery.matches) + } + + // Add listener + mediaQuery.addEventListener('change', handleSystemThemeChange) + + // Cleanup + return () => + mediaQuery.removeEventListener('change', handleSystemThemeChange) + }, [isSystem]) + + useEffect(() => { + sessionStorage.setItem('isSystem', JSON.stringify(isSystem)) + }, [isSystem]) + + useEffect(() => { + sessionStorage.setItem('isDarkMode', JSON.stringify(isDarkMode)) + }, [isDarkMode]) + + useEffect(() => { + sessionStorage.setItem('isCompact', JSON.stringify(isCompact)) + }, [isCompact]) const toggleTheme = () => { + if (isSystem) { + setIsSystem(false) + } setIsDarkMode(!isDarkMode) } + const toggleSystem = () => { + setIsSystem(!isSystem) + if (!isSystem) { + // When enabling system theme, update to match system preference + setIsDarkMode(window.matchMedia('(prefers-color-scheme: dark)').matches) + } + } + const toggleCompact = () => { setIsCompact(!isCompact) } @@ -50,6 +106,8 @@ export const ThemeProvider = ({ children }) => { toggleTheme, isCompact, toggleCompact, + isSystem, + toggleSystem, themeConfig }} >