Skip to content

Commit

Permalink
feat(darkMode): store darkMode in Redux store
Browse files Browse the repository at this point in the history
  • Loading branch information
Clm-Roig committed Apr 11, 2022
1 parent e16eac1 commit 45b3313
Show file tree
Hide file tree
Showing 12 changed files with 78 additions and 38 deletions.
24 changes: 11 additions & 13 deletions src/app/App.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import VisibilityOffIcon from '@mui/icons-material/VisibilityOff';
import { LocalizationProvider } from '@mui/lab';
import DateAdapter from '@mui/lab/AdapterDateFns';
import { Container, IconButton, PaletteMode, Paper, useMediaQuery } from '@mui/material';
import { Container, IconButton, Paper } from '@mui/material';
import {
StyledEngineProvider,
ThemeProvider,
Expand All @@ -11,13 +11,17 @@ import {
} from '@mui/material/styles';
import frLocale from 'date-fns/locale/fr';
import { SnackbarKey, SnackbarProvider } from 'notistack';
import { createRef, useEffect, useMemo, useState } from 'react';
import { createRef, useMemo, useState } from 'react';

import { DRAWER_MENU_WIDTH } from '../config/Constants';
import { components, getPalette, typography } from '../config/CustomTheme';
import ThemeMode from '../models/ThemeMode';
import { selectThemeMode } from '../store/theme/theme.selectors';
import { toggleThemeMode } from '../store/theme/themeSlice';
import AppBar from './AppBar';
import DrawerMenu from './DrawerMenu';
import Router from './Router';
import { useAppDispatch, useAppSelector } from './hooks';

const MainContent = styled(Paper)(({ theme }) => ({
padding: theme.spacing(2)
Expand All @@ -31,15 +35,13 @@ const MainContainer = styled(Container)(({ theme }) => ({
// Main component
function App() {
// Theme configuration
const prefersDarkMode = useMediaQuery('(prefers-color-scheme: dark)');
const [themeMode, setThemeMode] = useState<PaletteMode>(prefersDarkMode ? 'dark' : 'light');
useEffect(() => {
setThemeMode(prefersDarkMode ? 'dark' : 'light');
}, [prefersDarkMode]);
const themeMode = useAppSelector(selectThemeMode);
const dispatch = useAppDispatch();

let theme = useMemo(() => {
return createTheme({
components,
palette: getPalette(themeMode),
palette: getPalette(themeMode === ThemeMode.LIGHT ? 'light' : 'dark'),
typography: typography
});
}, [themeMode]);
Expand All @@ -56,10 +58,6 @@ function App() {
setIsMenuOpen(!isMenuOpen);
};

const toggleThemeMode = () => {
setThemeMode(themeMode === 'light' ? 'dark' : 'light');
};

return (
<LocalizationProvider dateAdapter={DateAdapter} locale={frLocale}>
<StyledEngineProvider injectFirst>
Expand All @@ -79,7 +77,7 @@ function App() {
width={DRAWER_MENU_WIDTH}
open={isMenuOpen}
toggleDrawerMenu={toggleDrawerMenu}
toggleThemeMode={toggleThemeMode}
toggleThemeMode={() => dispatch(toggleThemeMode())}
/>
<MainContent>
<Router />
Expand Down
5 changes: 3 additions & 2 deletions src/app/DrawerMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ import {
import { FC } from 'react';
import { Link } from 'react-router-dom';

import { useThemeMode } from './hooks';
import { selectThemeMode } from '../store/theme/theme.selectors';
import { useAppSelector } from './hooks';

interface MenuItemProps {
icon: React.ReactNode;
Expand All @@ -41,7 +42,7 @@ interface Props {
}

const DrawerMenu: FC<Props> = ({ open, toggleDrawerMenu, toggleThemeMode, width }) => {
const themeMode = useThemeMode();
const themeMode = useAppSelector(selectThemeMode);
return (
<SwipeableDrawer
anchor="left"
Expand Down
11 changes: 0 additions & 11 deletions src/app/hooks.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,7 @@
import { useTheme } from '@mui/material';
import { useEffect, useState } from 'react';
import { TypedUseSelectorHook, useDispatch, useSelector } from 'react-redux';

import type { AppDispatch, RootState } from '../store/store';

// Use throughout your app instead of plain `useDispatch` and `useSelector`
export const useAppDispatch = () => useDispatch<AppDispatch>();
export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector;

export const useThemeMode = () => {
const [themeMode, setThemeMode] = useState('light');
const theme = useTheme();
useEffect(() => {
setThemeMode(theme.palette.mode);
}, [theme]);
return themeMode;
};
8 changes: 5 additions & 3 deletions src/components/TrackerList/AddTrackerCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,19 @@ import RemoveCircleOutlineIcon from '@mui/icons-material/RemoveCircleOutline';
import { Box, Card, CardActionArea, CardContent, CardProps } from '@mui/material';
import { FC, useState } from 'react';

import { useThemeMode } from '../../app/hooks';
import { useAppSelector } from '../../app/hooks';
import ThemeMode from '../../models/ThemeMode';
import { selectThemeMode } from '../../store/theme/theme.selectors';
import TrackerForm from '../forms/TrackerForm/TrackerForm';

interface Props {
cardProps?: CardProps;
}
const AddTrackerCard: FC<Props> = ({ cardProps }) => {
const [displayCreateForm, setDisplayCreateForm] = useState(false);
const themeMode = useThemeMode();
const themeMode = useAppSelector(selectThemeMode);

const hoverColor = themeMode === 'light' ? 'accent.main' : 'secondary.main';
const hoverColor = themeMode === ThemeMode.LIGHT ? 'accent.main' : 'secondary.main';
const cardActionSx = {
'&:hover': {
backgroundColor: displayCreateForm ? '' : hoverColor
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@ import {
UseFieldArrayRemove
} from 'react-hook-form';

import { useThemeMode } from '../../../app/hooks';
import { useAppSelector } from '../../../app/hooks';
import ThemeMode from '../../../models/ThemeMode';
import { selectThemeMode } from '../../../store/theme/theme.selectors';
import { FormValues } from '../TrackerForm/types';
import CompletionQuantityTextField from '../completions/CompletionQuantityTextField';
import CompletionUnitTextField from '../completions/CompletionUnitTextField';
Expand All @@ -37,11 +39,11 @@ interface Props {
* @return {*}
*/
const RequiredCompletionsForm: FC<Props> = ({ append, control, fields, gridProps, remove }) => {
const themeMode = useThemeMode();
const themeMode = useAppSelector(selectThemeMode);
const theme = useTheme();

const fieldsetSx = {
bgcolor: themeMode === 'light' ? theme.palette.grey[100] : theme.palette.grey[900],
bgcolor: themeMode === ThemeMode.LIGHT ? theme.palette.grey[100] : theme.palette.grey[900],
mb: 1
};

Expand Down
4 changes: 3 additions & 1 deletion src/config/CustomTheme.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
import { PaletteMode, PaletteOptions } from '@mui/material';
import { grey } from '@mui/material/colors';

import ThemeMode from '../models/ThemeMode';

const CHARCOAL = {
main: '#2E4057'
};
Expand Down Expand Up @@ -53,7 +55,7 @@ const commonTheme = {

export const getPalette = (mode: PaletteMode): PaletteOptions => ({
mode,
...(mode === 'light'
...(mode === ThemeMode.LIGHT
? {
// palette values for light mode
...commonTheme
Expand Down
6 changes: 6 additions & 0 deletions src/models/ThemeMode.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
enum ThemeMode {
DARK = 'dark',
LIGHT = 'light'
}

export default ThemeMode;
8 changes: 5 additions & 3 deletions src/pages/Trackers.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@ import VisibilityOffIcon from '@mui/icons-material/VisibilityOff';
import { Alert, Box, Tab, Tabs, Typography, useTheme } from '@mui/material';
import { useState } from 'react';

import { useAppSelector, useThemeMode } from '../app/hooks';
import { useAppSelector } from '../app/hooks';
import TabPanel from '../components/TabPanel/TabPanel';
import AddTrackerCard from '../components/TrackerList/AddTrackerCard';
import DateSelector from '../components/TrackerList/DateSelector';
import TrackerList from '../components/TrackerList/TrackerList';
import ThemeMode from '../models/ThemeMode';
import { selectThemeMode } from '../store/theme/theme.selectors';
import {
selectHiddenTrackers,
selectTodoTrackers,
Expand All @@ -24,11 +26,11 @@ function Trackers() {
setSelectedTab(newTab);
};
const theme = useTheme();
const themeMode = useThemeMode();
const themeMode = useAppSelector(selectThemeMode);

const cardSxProp = {
mb: 2,
bgcolor: themeMode === 'light' ? 'secondary.main' : theme.palette.grey[900]
bgcolor: themeMode === ThemeMode.LIGHT ? 'secondary.main' : theme.palette.grey[900]
};

return (
Expand Down
4 changes: 3 additions & 1 deletion src/store/rootReducer.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import { combineReducers } from '@reduxjs/toolkit';

import themeReducer from './theme/themeSlice';
import trackersReducer from './trackers/trackersSlice';

const rootReducer = combineReducers({
trackers: trackersReducer
trackers: trackersReducer,
theme: themeReducer
});

export default rootReducer;
2 changes: 1 addition & 1 deletion src/store/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ import {
persistReducer,
persistStore
} from 'redux-persist';
// defaults to localStorage for web
import storage from 'redux-persist/lib/storage';

// defaults to localStorage for web
import rootReducer from './rootReducer';

const persistConfig = {
Expand Down
7 changes: 7 additions & 0 deletions src/store/theme/theme.selectors.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { RootState } from '../store';

const selectThemeMode = (state: RootState) => {
return state.theme.themeMode;
};

export { selectThemeMode };
29 changes: 29 additions & 0 deletions src/store/theme/themeSlice.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { PayloadAction, createSlice } from '@reduxjs/toolkit';

import ThemeMode from '../../models/ThemeMode';

export interface ThemeState {
themeMode: ThemeMode;
}

const initialState: ThemeState = {
themeMode: ThemeMode.LIGHT
};

export const themeSlice = createSlice({
name: 'theme',
initialState,
reducers: {
setThemeMode: (state, action: PayloadAction<ThemeMode>) => {
state.themeMode = action.payload;
},
toggleThemeMode: (state) => {
const { themeMode } = state;
state.themeMode = themeMode === ThemeMode.LIGHT ? ThemeMode.DARK : ThemeMode.LIGHT;
}
}
});

export const { setThemeMode, toggleThemeMode } = themeSlice.actions;

export default themeSlice.reducer;

0 comments on commit 45b3313

Please sign in to comment.