Skip to content

Commit

Permalink
add future support for setting to system theme
Browse files Browse the repository at this point in the history
  • Loading branch information
js0mmer committed Jan 24, 2024
1 parent 4f98781 commit 0c63c43
Show file tree
Hide file tree
Showing 4 changed files with 57 additions and 34 deletions.
2 changes: 1 addition & 1 deletion api/src/controllers/users.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ router.get('/preferences', async (req, res) => {
});

interface UserPreferences {
theme?: string;
theme?: 'light' | 'dark' | 'system';
}

router.post('/preferences', async (req, res) => {
Expand Down
74 changes: 45 additions & 29 deletions site/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useEffect, useState } from 'react';
import { useCallback, useEffect, useState } from 'react';
import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
import 'semantic-ui-css/semantic.min.css';
import 'bootstrap/dist/css/bootstrap.min.css';
Expand All @@ -17,56 +17,72 @@ import AdminPage from './pages/AdminPage';
import ReviewsPage from './pages/ReviewsPage';
import SideBar from './component/SideBar/SideBar';

import ThemeContext from './style/theme-context';
import ThemeContext, { Theme } from './style/theme-context';
import axios from 'axios';
import { useCookies } from 'react-cookie';

function getDefaultDarkModeValue() {
switch (localStorage.getItem('theme')) {
case 'dark':
return true;
case 'light':
return false;
default:
return window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches;
}
function isSystemDark() {
return window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches;
}

export default function App() {
// default darkMode to local or system preferences
const [darkMode, setDarkMode] = useState(getDefaultDarkModeValue());
const [usingSystemTheme, setUsingSystemTheme] = useState(
localStorage.getItem('theme') === 'system' || !localStorage.getItem('theme'),
);
const [darkMode, setDarkMode] = useState(
usingSystemTheme ? isSystemDark() : localStorage.getItem('theme') === 'dark',
);
const [cookies] = useCookies(['user']);

/**
* Sets the theme state
* @param theme
*/
const setThemeState = useCallback((theme: Theme) => {
if (theme === 'system') {
setDarkMode(isSystemDark());
setUsingSystemTheme(true);
} else {
setDarkMode(theme === 'dark');
setUsingSystemTheme(false);
}
}, []);

/**
* Sets the theme state and saves the users theme preference.
* Saves to account if logged in, local storage if not
* @param theme
*/
const setTheme = (theme: Theme) => {
setThemeState(theme);
if (cookies.user) {
axios.post('/api/users/preferences', { theme });
} else {
localStorage.setItem('theme', theme);
}
};

useEffect(() => {
// if logged in, load user prefs (theme) from mongo
if (cookies.user) {
axios.get('/api/users/preferences').then((res) => {
const { theme } = res.data;
if (theme === 'dark') {
setDarkMode(true);
} else if (theme === 'light') {
setDarkMode(false);
const { theme }: { theme?: Theme } = res.data;
if (theme) {
setThemeState(theme);
}
});
}
}, [cookies.user]);
}, [cookies.user, setThemeState]);

useEffect(() => {
// Theme styling is controlled by data-theme attribute on body being set to light or dark
document.querySelector('body')!.setAttribute('data-theme', darkMode ? 'dark' : 'light');
if (cookies.user) {
axios.post('/api/users/preferences', { theme: darkMode ? 'dark' : 'light' });
} else {
localStorage.setItem('theme', darkMode ? 'dark' : 'light');
}
}, [cookies.user, darkMode]);

const toggleTheme = () => {
setDarkMode(!darkMode);
};
}, [darkMode]);

return (
<Router>
<ThemeContext.Provider value={{ darkMode: darkMode, toggleTheme: toggleTheme }}>
<ThemeContext.Provider value={{ darkMode, usingSystemTheme, setTheme }}>
<AppHeader />
<div className="app-body">
<div className="app-sidebar">
Expand Down
4 changes: 2 additions & 2 deletions site/src/component/SideBar/SideBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ const SideBar = () => {
const [name, setName] = useState('');
const [picture, setPicture] = useState('');
const [isAdmin, setIsAdmin] = useState<boolean>(false);
const { darkMode, toggleTheme } = useContext(ThemeContext);
const { darkMode, setTheme } = useContext(ThemeContext);

const isLoggedIn = cookies.user !== undefined;

Expand Down Expand Up @@ -89,7 +89,7 @@ const SideBar = () => {
)}
{showSidebar && (
<li>
<a className="theme-toggle" onClick={toggleTheme}>
<a className="theme-toggle" onClick={() => setTheme(darkMode ? 'light' : 'dark')}>
<div>
<Icon name={darkMode ? 'moon outline' : 'sun outline'} size="large" />
</div>
Expand Down
11 changes: 9 additions & 2 deletions site/src/style/theme-context.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,15 @@
import React from 'react';

const ThemeContext = React.createContext<{ darkMode: boolean; toggleTheme: () => void }>({
export type Theme = 'dark' | 'light' | 'system';

const ThemeContext = React.createContext<{
darkMode: boolean;
usingSystemTheme: boolean;
setTheme: (theme: Theme) => void;
}>({
darkMode: false,
toggleTheme: () => {},
usingSystemTheme: false,
setTheme: () => {},
});

export default ThemeContext;

0 comments on commit 0c63c43

Please sign in to comment.