1
+ import { useCallback , useEffect , useState } from 'react' ;
1
2
import { BrowserRouter as Router , Routes , Route } from 'react-router-dom' ;
2
3
import 'semantic-ui-css/semantic.min.css' ;
3
4
import 'bootstrap/dist/css/bootstrap.min.css' ;
4
5
import 'react-bootstrap-range-slider/dist/react-bootstrap-range-slider.css' ;
6
+ import './style/theme.scss' ;
5
7
import './App.scss' ;
6
8
7
9
import AppHeader from './component/AppHeader/AppHeader' ;
@@ -15,28 +17,92 @@ import AdminPage from './pages/AdminPage';
15
17
import ReviewsPage from './pages/ReviewsPage' ;
16
18
import SideBar from './component/SideBar/SideBar' ;
17
19
20
+ import ThemeContext , { Theme } from './style/theme-context' ;
21
+ import axios from 'axios' ;
22
+ import { useCookies } from 'react-cookie' ;
23
+
24
+ function isSystemDark ( ) {
25
+ return window . matchMedia && window . matchMedia ( '(prefers-color-scheme: dark)' ) . matches ;
26
+ }
27
+
18
28
export default function App ( ) {
29
+ // default darkMode to local or system preferences
30
+ const [ usingSystemTheme , setUsingSystemTheme ] = useState (
31
+ localStorage . getItem ( 'theme' ) === 'system' || ! localStorage . getItem ( 'theme' ) ,
32
+ ) ;
33
+ const [ darkMode , setDarkMode ] = useState (
34
+ usingSystemTheme ? isSystemDark ( ) : localStorage . getItem ( 'theme' ) === 'dark' ,
35
+ ) ;
36
+ const [ cookies ] = useCookies ( [ 'user' ] ) ;
37
+
38
+ /**
39
+ * Sets the theme state
40
+ * @param theme
41
+ */
42
+ const setThemeState = useCallback ( ( theme : Theme ) => {
43
+ if ( theme === 'system' ) {
44
+ setDarkMode ( isSystemDark ( ) ) ;
45
+ setUsingSystemTheme ( true ) ;
46
+ } else {
47
+ setDarkMode ( theme === 'dark' ) ;
48
+ setUsingSystemTheme ( false ) ;
49
+ }
50
+ } , [ ] ) ;
51
+
52
+ /**
53
+ * Sets the theme state and saves the users theme preference.
54
+ * Saves to account if logged in, local storage if not
55
+ * @param theme
56
+ */
57
+ const setTheme = ( theme : Theme ) => {
58
+ setThemeState ( theme ) ;
59
+ if ( cookies . user ) {
60
+ axios . post ( '/api/users/preferences' , { theme } ) ;
61
+ } else {
62
+ localStorage . setItem ( 'theme' , theme ) ;
63
+ }
64
+ } ;
65
+
66
+ useEffect ( ( ) => {
67
+ // if logged in, load user prefs (theme) from mongo
68
+ if ( cookies . user ) {
69
+ axios . get ( '/api/users/preferences' ) . then ( ( res ) => {
70
+ const { theme } : { theme ?: Theme } = res . data ;
71
+ if ( theme ) {
72
+ setThemeState ( theme ) ;
73
+ }
74
+ } ) ;
75
+ }
76
+ } , [ cookies . user , setThemeState ] ) ;
77
+
78
+ useEffect ( ( ) => {
79
+ // Theme styling is controlled by data-theme attribute on body being set to light or dark
80
+ document . querySelector ( 'body' ) ! . setAttribute ( 'data-theme' , darkMode ? 'dark' : 'light' ) ;
81
+ } , [ darkMode ] ) ;
82
+
19
83
return (
20
84
< Router >
21
- < AppHeader />
22
- < div className = "app-body" >
23
- < div className = "app-sidebar" >
24
- < SideBar > </ SideBar >
25
- </ div >
26
- < div className = "app-content" >
27
- < Routes >
28
- < Route path = "/roadmap" element = { < RoadmapPage /> } />
29
- < Route path = "/" element = { < SearchPage /> } />
30
- < Route path = "/search/:index" element = { < SearchPage /> } />
31
- < Route path = "/course/:id" element = { < CoursePage /> } />
32
- < Route path = "/professor/:id" element = { < ProfessorPage /> } />
33
- < Route path = "/admin/*" element = { < AdminPage /> } />
34
- < Route path = "/reviews" element = { < ReviewsPage /> } />
35
- < Route path = "*" element = { < ErrorPage /> } />
36
- </ Routes >
37
- < Footer />
85
+ < ThemeContext . Provider value = { { darkMode, usingSystemTheme, setTheme } } >
86
+ < AppHeader />
87
+ < div className = "app-body" >
88
+ < div className = "app-sidebar" >
89
+ < SideBar > </ SideBar >
90
+ </ div >
91
+ < div className = "app-content" >
92
+ < Routes >
93
+ < Route path = "/roadmap" element = { < RoadmapPage /> } />
94
+ < Route path = "/" element = { < SearchPage /> } />
95
+ < Route path = "/search/:index" element = { < SearchPage /> } />
96
+ < Route path = "/course/:id" element = { < CoursePage /> } />
97
+ < Route path = "/professor/:id" element = { < ProfessorPage /> } />
98
+ < Route path = "/admin/*" element = { < AdminPage /> } />
99
+ < Route path = "/reviews" element = { < ReviewsPage /> } />
100
+ < Route path = "*" element = { < ErrorPage /> } />
101
+ </ Routes >
102
+ < Footer />
103
+ </ div >
38
104
</ div >
39
- </ div >
105
+ </ ThemeContext . Provider >
40
106
</ Router >
41
107
) ;
42
108
}
0 commit comments