Skip to content
Snippets Groups Projects
Unverified Commit b856c487 authored by Tim's avatar Tim Committed by GitHub
Browse files

add option to select dark theme for "System default" (#3325)

* add option to select dark theme for "System default"

* add release notes

* fix release notes

* update visual tests
parent 407e3143
No related branches found
No related tags found
No related merge requests found
Showing
with 111 additions and 17 deletions
File suppressed by a .gitattributes entry or the file's encoding is unsupported.
File suppressed by a .gitattributes entry or the file's encoding is unsupported.
File suppressed by a .gitattributes entry or the file's encoding is unsupported.
File suppressed by a .gitattributes entry or the file's encoding is unsupported.
File suppressed by a .gitattributes entry or the file's encoding is unsupported.
File suppressed by a .gitattributes entry or the file's encoding is unsupported.
import React from 'react';
import React, { type ReactNode } from 'react';
import { type Theme } from 'loot-core/types/prefs';
import { type DarkTheme, type Theme } from 'loot-core/types/prefs';
import { themeOptions, useTheme, theme as themeStyle } from '../../style';
import {
themeOptions,
useTheme,
theme as themeStyle,
usePreferredDarkTheme,
darkThemeOptions,
} from '../../style';
import { tokens } from '../../tokens';
import { Select } from '../common/Select';
import { Text } from '../common/Text';
import { View } from '../common/View';
import { useSidebar } from '../sidebar/SidebarProvider';
import { Setting } from './UI';
function Column({ title, children }: { title: string; children: ReactNode }) {
return (
<View
style={{
alignItems: 'flex-start',
flexGrow: 1,
gap: '0.5em',
width: '100%',
}}
>
<Text style={{ fontWeight: 500 }}>{title}</Text>
<View style={{ alignItems: 'flex-start', gap: '1em' }}>{children}</View>
</View>
);
}
export function ThemeSettings() {
const sidebar = useSidebar();
const [theme, switchTheme] = useTheme();
const [darkTheme, switchDarkTheme] = usePreferredDarkTheme();
return (
<Setting
primaryAction={
<Select<Theme>
onChange={value => {
switchTheme(value);
}}
value={theme}
options={themeOptions}
buttonStyle={{
':hover': {
backgroundColor: themeStyle.buttonNormalBackgroundHover,
<View
style={{
flexDirection: 'column',
gap: '1em',
width: '100%',
[`@media (min-width: ${
sidebar.floating
? tokens.breakpoint_small
: tokens.breakpoint_medium
})`]: {
flexDirection: 'row',
},
}}
/>
>
<Column title="Theme">
<Select<Theme>
onChange={value => {
switchTheme(value);
}}
value={theme}
options={themeOptions}
buttonStyle={{
':hover': {
backgroundColor: themeStyle.buttonNormalBackgroundHover,
},
}}
/>
</Column>
{theme === 'auto' && (
<Column title="Dark theme">
<Select<DarkTheme>
onChange={value => {
switchDarkTheme(value);
}}
value={darkTheme}
options={darkThemeOptions}
buttonStyle={{
':hover': {
backgroundColor: themeStyle.buttonNormalBackgroundHover,
},
}}
/>
</Column>
)}
</View>
}
>
<Text>
......
......@@ -2,7 +2,7 @@
import { useEffect, useState } from 'react';
import { isNonProductionEnvironment } from 'loot-core/src/shared/environment';
import type { Theme } from 'loot-core/src/types/prefs';
import type { DarkTheme, Theme } from 'loot-core/src/types/prefs';
import { useGlobalPref } from '../hooks/useGlobalPref';
......@@ -25,13 +25,25 @@ export const themeOptions = Object.entries(themes).map(
([key, { name }]) => [key, name] as [Theme, string],
);
export const darkThemeOptions = Object.entries({
dark: themes.dark,
midnight: themes.midnight,
}).map(([key, { name }]) => [key, name] as [DarkTheme, string]);
export function useTheme() {
const [theme = 'light', setThemePref] = useGlobalPref('theme');
return [theme, setThemePref] as const;
}
export function usePreferredDarkTheme() {
const [darkTheme = 'dark', setDarkTheme] =
useGlobalPref('preferredDarkTheme');
return [darkTheme, setDarkTheme] as const;
}
export function ThemeStyle() {
const [theme] = useTheme();
const [darkThemePreference] = usePreferredDarkTheme();
const [themeColors, setThemeColors] = useState<
| typeof lightTheme
| typeof darkTheme
......@@ -42,9 +54,11 @@ export function ThemeStyle() {
useEffect(() => {
if (theme === 'auto') {
const darkTheme = themes[darkThemePreference];
function darkThemeMediaQueryListener(event: MediaQueryListEvent) {
if (event.matches) {
setThemeColors(themes['dark'].colors);
setThemeColors(darkTheme.colors);
} else {
setThemeColors(themes['light'].colors);
}
......@@ -59,7 +73,7 @@ export function ThemeStyle() {
);
if (darkThemeMediaQuery.matches) {
setThemeColors(themes['dark'].colors);
setThemeColors(darkTheme.colors);
} else {
setThemeColors(themes['light'].colors);
}
......@@ -73,7 +87,7 @@ export function ThemeStyle() {
} else {
setThemeColors(themes[theme].colors);
}
}, [theme]);
}, [theme, darkThemePreference]);
if (!themeColors) return null;
......
......@@ -1256,6 +1256,12 @@ handlers['save-global-prefs'] = async function (prefs) {
if ('theme' in prefs) {
await asyncStorage.setItem('theme', prefs.theme);
}
if ('preferredDarkTheme' in prefs) {
await asyncStorage.setItem(
'preferred-dark-theme',
prefs.preferredDarkTheme,
);
}
if ('serverSelfSignedCert' in prefs) {
await asyncStorage.setItem(
'server-self-signed-cert',
......@@ -1272,12 +1278,14 @@ handlers['load-global-prefs'] = async function () {
[, documentDir],
[, encryptKey],
[, theme],
[, preferredDarkTheme],
] = await asyncStorage.multiGet([
'floating-sidebar',
'max-months',
'document-dir',
'encrypt-key',
'theme',
'preferred-dark-theme',
]);
return {
floatingSidebar: floatingSidebar === 'true' ? true : false,
......@@ -1292,6 +1300,10 @@ handlers['load-global-prefs'] = async function () {
theme === 'midnight'
? theme
: 'auto',
preferredDarkTheme:
preferredDarkTheme === 'dark' || preferredDarkTheme === 'midnight'
? preferredDarkTheme
: 'dark',
};
};
......
......@@ -83,11 +83,13 @@ export type LocalPrefs = SyncedPrefs &
}>;
export type Theme = 'light' | 'dark' | 'auto' | 'midnight' | 'development';
export type DarkTheme = 'dark' | 'midnight';
export type GlobalPrefs = Partial<{
floatingSidebar: boolean;
maxMonths: number;
keyId?: string;
theme: Theme;
preferredDarkTheme: DarkTheme;
documentDir: string; // Electron only
serverSelfSignedCert: string; // Electron only
}>;
---
category: Features
authors: [tim-smart]
---
Add setting to set preferred dark theme for "System default" mode
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment