Add pure black mode option

This commit is contained in:
schroda
2024-09-05 00:08:23 +02:00
parent ba2ce37468
commit 1444d51ed2
6 changed files with 37 additions and 7 deletions

View File

@@ -818,6 +818,7 @@
}, },
"appearance": { "appearance": {
"device_theme": "Device theme", "device_theme": "Device theme",
"pure_black_mode": "Pure black dark mode",
"theme": "Theme", "theme": "Theme",
"title": "Appearance" "title": "Appearance"
}, },
@@ -914,12 +915,12 @@
} }
}, },
"label": { "label": {
"theme": "Theme",
"hide_library_entries": "Hide entries already in library", "hide_library_entries": "Hide entries already in library",
"language_description": "Feel free to translate the project on", "language_description": "Feel free to translate the project on",
"manga_item_width": "Manga item width", "manga_item_width": "Manga item width",
"show_nsfw": "Show NSFW", "show_nsfw": "Show NSFW",
"show_nsfw_description": "Hide NSFW extensions and sources" "show_nsfw_description": "Hide NSFW extensions and sources",
"theme": "Theme"
}, },
"server": { "server": {
"address": { "address": {

View File

@@ -57,14 +57,17 @@ export const AppContext: React.FC<Props> = ({ children }) => {
}, []); }, []);
const [themeMode, setThemeMode] = useLocalStorage<ThemeMode>('themeMode', ThemeMode.SYSTEM); const [themeMode, setThemeMode] = useLocalStorage<ThemeMode>('themeMode', ThemeMode.SYSTEM);
const [pureBlackMode, setPureBlackMode] = useLocalStorage<boolean>('pureBlackMode', false);
const [activeDevice, setActiveDeviceContext] = useLocalStorage('activeDevice', DEFAULT_DEVICE); const [activeDevice, setActiveDeviceContext] = useLocalStorage('activeDevice', DEFAULT_DEVICE);
const darkThemeContext = useMemo( const darkThemeContext = useMemo(
() => ({ () => ({
themeMode, themeMode,
setThemeMode, setThemeMode,
pureBlackMode,
setPureBlackMode,
}), }),
[themeMode], [themeMode, pureBlackMode],
); );
const activeDeviceContext = useMemo( const activeDeviceContext = useMemo(
@@ -73,8 +76,8 @@ export const AppContext: React.FC<Props> = ({ children }) => {
); );
const theme = useMemo( const theme = useMemo(
() => createTheme(themeMode, currentDirection), () => createTheme(themeMode, currentDirection, undefined, pureBlackMode),
[themeMode, currentDirection, systemThemeMode], [themeMode, currentDirection, systemThemeMode, pureBlackMode],
); );
setActiveDevice(activeDevice); setActiveDevice(activeDevice);

View File

@@ -17,9 +17,13 @@ export enum ThemeMode {
type ContextType = { type ContextType = {
themeMode: ThemeMode; themeMode: ThemeMode;
setThemeMode: React.Dispatch<React.SetStateAction<ThemeMode>>; setThemeMode: React.Dispatch<React.SetStateAction<ThemeMode>>;
pureBlackMode: boolean;
setPureBlackMode: React.Dispatch<React.SetStateAction<boolean>>;
}; };
export const ThemeModeContext = React.createContext<ContextType>({ export const ThemeModeContext = React.createContext<ContextType>({
themeMode: ThemeMode.SYSTEM, themeMode: ThemeMode.SYSTEM,
setThemeMode: (): void => {}, setThemeMode: (): void => {},
pureBlackMode: false,
setPureBlackMode: (): void => {},
}); });

View File

@@ -10,6 +10,7 @@ import useMediaQuery from '@mui/material/useMediaQuery';
import { Breakpoint } from '@mui/material/styles'; import { Breakpoint } from '@mui/material/styles';
import { getCurrentTheme } from '@/theme.ts'; import { getCurrentTheme } from '@/theme.ts';
import { ThemeMode } from '@/components/context/ThemeModeContext.tsx'; import { ThemeMode } from '@/components/context/ThemeModeContext.tsx';
import { AppStorage } from '@/util/AppStorage.ts';
export class MediaQuery { export class MediaQuery {
static useIsTouchDevice(): boolean { static useIsTouchDevice(): boolean {
@@ -29,6 +30,17 @@ export class MediaQuery {
return prefersDarkMode ? ThemeMode.DARK : ThemeMode.LIGHT; return prefersDarkMode ? ThemeMode.DARK : ThemeMode.LIGHT;
} }
static getThemeMode(): Exclude<ThemeMode, 'system'> {
const themeMode = AppStorage.local.getItemParsed<ThemeMode>('themeMode', ThemeMode.SYSTEM);
const isSystemMode = themeMode === ThemeMode.SYSTEM;
if (isSystemMode) {
return this.getSystemThemeMode();
}
return themeMode;
}
static listenToSystemThemeChange(onChange: (themeMode: Exclude<ThemeMode, 'system'>) => void): () => void { static listenToSystemThemeChange(onChange: (themeMode: Exclude<ThemeMode, 'system'>) => void): () => void {
const handleSystemThemeModeChange = (e: MediaQueryListEvent) => { const handleSystemThemeModeChange = (e: MediaQueryListEvent) => {
onChange(e.matches ? ThemeMode.DARK : ThemeMode.LIGHT); onChange(e.matches ? ThemeMode.DARK : ThemeMode.LIGHT);

View File

@@ -13,13 +13,16 @@ import ListItem from '@mui/material/ListItem';
import ListItemText from '@mui/material/ListItemText'; import ListItemText from '@mui/material/ListItemText';
import MenuItem from '@mui/material/MenuItem'; import MenuItem from '@mui/material/MenuItem';
import ListSubheader from '@mui/material/ListSubheader'; import ListSubheader from '@mui/material/ListSubheader';
import Switch from '@mui/material/Switch';
import { NavBarContext } from '@/components/context/NavbarContext.tsx'; import { NavBarContext } from '@/components/context/NavbarContext.tsx';
import { ThemeMode, ThemeModeContext } from '@/components/context/ThemeModeContext.tsx'; import { ThemeMode, ThemeModeContext } from '@/components/context/ThemeModeContext.tsx';
import { Select } from '@/components/atoms/Select.tsx'; import { Select } from '@/components/atoms/Select.tsx';
import { MediaQuery } from '@/lib/ui/MediaQuery.tsx';
export const Appearance = () => { export const Appearance = () => {
const { t } = useTranslation(); const { t } = useTranslation();
const { themeMode, setThemeMode } = useContext(ThemeModeContext); const { themeMode, setThemeMode, pureBlackMode, setPureBlackMode } = useContext(ThemeModeContext);
const isDarkMode = MediaQuery.getThemeMode() === ThemeMode.DARK;
const { setTitle, setAction } = useContext(NavBarContext); const { setTitle, setAction } = useContext(NavBarContext);
useEffect(() => { useEffect(() => {
@@ -54,6 +57,12 @@ export const Appearance = () => {
</MenuItem> </MenuItem>
</Select> </Select>
</ListItem> </ListItem>
{isDarkMode && (
<ListItem>
<ListItemText primary={t('settings.appearance.pure_black_mode')} />
<Switch checked={pureBlackMode} onChange={(_, enabled) => setPureBlackMode(enabled)} />
</ListItem>
)}
</List> </List>
); );
}; };

View File

@@ -31,6 +31,7 @@ export const createTheme = (
const systemMode = MediaQuery.getSystemThemeMode(); const systemMode = MediaQuery.getSystemThemeMode();
const mode = themeMode === ThemeMode.SYSTEM ? systemMode : themeMode; const mode = themeMode === ThemeMode.SYSTEM ? systemMode : themeMode;
const isDarkMode = mode === ThemeMode.DARK; const isDarkMode = mode === ThemeMode.DARK;
const setPureBlackMode = isDarkMode && pureBlackMode;
const baseTheme = createMuiTheme({ const baseTheme = createMuiTheme({
direction, direction,
@@ -55,7 +56,7 @@ export const createTheme = (
default: lighten(baseTheme.palette.primary.dark, 0.85), default: lighten(baseTheme.palette.primary.dark, 0.85),
}; };
const backgroundThemeMode = isDarkMode ? backgroundDark : backgroundLight; const backgroundThemeMode = isDarkMode ? backgroundDark : backgroundLight;
const background = pureBlackMode ? backgroundTrueBlack : backgroundThemeMode; const background = setPureBlackMode ? backgroundTrueBlack : backgroundThemeMode;
const colorTheme = createMuiTheme(baseTheme, { const colorTheme = createMuiTheme(baseTheme, {
palette: { palette: {