Skip to content

Commit 501f876

Browse files
author
Emile Frey
committed
adds themecontext with theme toggler in topbar (includes localstorage for persistent theme)
1 parent e78b8ac commit 501f876

File tree

6 files changed

+64
-14
lines changed

6 files changed

+64
-14
lines changed

frontend/src/App.tsx

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React, { Dispatch, useContext } from 'react';
1+
import React, { Dispatch, useContext, useState } from 'react';
22
import Router from './routes/Router';
33
import Layout from './components/Layout/Layout';
44
import { connect } from 'react-redux';
@@ -9,6 +9,7 @@ import { theme } from './Theme'
99
import { AlertContext } from './contexts/AlertContext';
1010
import Alert from '@material-ui/lab/Alert';
1111
import { AxiosError } from './interfaces/axios/AxiosError'
12+
import { ThemeContext } from './contexts/ThemeContext';
1213

1314

1415

@@ -31,15 +32,17 @@ export interface AppProps extends AuthProps, PrivateRouteProps { }
3132
function App(props: AppProps) {
3233

3334
const { alertType, openAlert, alertMessage, handleAlertClose } = useContext(AlertContext);
35+
const { darkMode } = useContext(ThemeContext);
36+
const palletType = darkMode ? "dark" : "light"
3437

3538
React.useEffect(() => {
3639
props.setAuthenticatedIfRequired();
3740
}, [props]);
3841

3942
return (
4043
<div className="App">
41-
<ThemeProvider theme={theme}>
42-
<Layout {...props}>
44+
<ThemeProvider theme={theme(palletType)}>
45+
<Layout {...props} >
4346
<Router {...props} />
4447
</Layout>
4548
<Snackbar id="appAlertSnackbar" open={openAlert} autoHideDuration={6000} onClose={handleAlertClose}>

frontend/src/Theme.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import { createMuiTheme } from '@material-ui/core/styles';
22

3-
export const theme = createMuiTheme({
3+
export const theme = (type: "dark" | "light") => createMuiTheme({
44
palette: {
5+
type: type,
56
primary: {
67
main: '#1976d2',
78
light: '#63a4ff',

frontend/src/components/Home.tsx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ const useStyles = makeStyles((theme) => ({
2121
marginTop: theme.spacing(2),
2222
marginBottom: theme.spacing(2),
2323
padding: theme.spacing(2), paddingLeft: theme.spacing(4),
24-
color: theme.palette.primary.main,
2524
fontWeight: 700
2625
},
2726
textInput: {
@@ -140,7 +139,7 @@ function Home(props: AuthProps) {
140139
<Grid container spacing={3}>
141140
<Grid item xs={6}>
142141
<Paper className={classes.textInput}>
143-
<Typography variant="h6" color="primary">
142+
<Typography variant="h6">
144143
Enter your name:
145144
</Typography>
146145
<TextField

frontend/src/components/Layout/TopBar.tsx

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
1-
import React from 'react';
1+
import React, { useContext } from 'react';
22
import { makeStyles } from '@material-ui/core/styles';
3-
import { AppBar, Toolbar, Typography } from '@material-ui/core';
3+
import { AppBar, IconButton, Toolbar, Typography } from '@material-ui/core';
44
import { APP_NAME } from '../../settings'
5-
import { AppProps } from '../../App'
65
import { AccountCircle } from '@material-ui/icons';
76
import MenuItem from '@material-ui/core/MenuItem';
87
import DropdownMenu from '../Layout/DropdownMenu';
9-
8+
import Brightness7Icon from '@material-ui/icons/Brightness7';
9+
import Brightness4Icon from '@material-ui/icons/Brightness4';
10+
import { ThemeContext } from '../../contexts/ThemeContext';
11+
import { AppProps } from '../../App';
1012
const useStyles = makeStyles((theme) => ({
1113
root: {
1214
flexGrow: 1,
@@ -21,13 +23,17 @@ const useStyles = makeStyles((theme) => ({
2123

2224
export default function TopBar(props: AppProps) {
2325
const classes = useStyles();
24-
26+
const { darkMode, setDarkMode } = useContext(ThemeContext);
27+
2528
return (
2629
<AppBar position="relative">
2730
<Toolbar className={props.isAuthenticated ? classes.authToolbar : undefined}>
2831
<Typography variant="h5" align="center" className={classes.title}>
2932
{APP_NAME}
3033
</Typography>
34+
<IconButton color="inherit" onClick={() => setDarkMode(!darkMode)} >
35+
{darkMode ? <Brightness4Icon/> : <Brightness7Icon />}
36+
</IconButton>
3137
{props.isAuthenticated && (
3238
<DropdownMenu dropdownButtonIcon={<AccountCircle />}>
3339
<div>
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import React, { createContext, useState, useReducer, useEffect } from 'react';
2+
3+
export type ThemeContextProps = {
4+
darkMode: boolean
5+
setDarkMode: React.Dispatch<React.SetStateAction<boolean>>;
6+
};
7+
8+
export const ThemeContext = createContext<ThemeContextProps>({
9+
darkMode: false,
10+
setDarkMode: () => { }
11+
});
12+
13+
14+
let localState = false
15+
const storedThemeState = localStorage.getItem("darkMode");
16+
if (typeof storedThemeState === 'string') {
17+
localState = JSON.parse(storedThemeState)
18+
}
19+
20+
const ThemeContextProvider = (props: any) => {
21+
22+
const [darkMode, setDarkMode] = useState<boolean>(localState)
23+
24+
useEffect(() => {
25+
localStorage.setItem("darkMode", JSON.stringify(darkMode));
26+
}, [darkMode]);
27+
28+
return (
29+
<ThemeContext.Provider value={{
30+
darkMode,
31+
setDarkMode
32+
}}>
33+
{props.children}
34+
</ThemeContext.Provider>
35+
)
36+
}
37+
38+
export default ThemeContextProvider;

frontend/src/index.js

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { Provider } from 'react-redux';
88
import thunk from 'redux-thunk';
99
import authReducer from './auth/authReducer';
1010
import AlertContextProvider from './contexts/AlertContext'
11+
import ThemeContextProvider from './contexts/ThemeContext'
1112
import { BrowserRouter } from 'react-router-dom';
1213

1314
const reducer = combineReducers({ auth: authReducer }); // Using Combine Reducers here although only one reducer is present.
@@ -21,9 +22,11 @@ ReactDOM.render(
2122
<React.StrictMode>
2223
<Provider store={store}>
2324
<AlertContextProvider>
24-
<BrowserRouter>
25-
<App />
26-
</BrowserRouter>
25+
<ThemeContextProvider>
26+
<BrowserRouter>
27+
<App />
28+
</BrowserRouter>
29+
</ThemeContextProvider>
2730
</AlertContextProvider>
2831
</Provider>
2932
</React.StrictMode>,

0 commit comments

Comments
 (0)