Add pure black mode option
This commit is contained in:
@@ -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": {
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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 => {},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -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: {
|
||||||
|
|||||||
Reference in New Issue
Block a user