Dark Theme: Auto-change when system theme changes
This commit is contained in:
parent
479106c9eb
commit
93fd353d1d
@ -7,7 +7,9 @@ import { GlobalActions } from '../../../global/types';
|
||||
import { LeftColumnContent, ISettings } from '../../../types';
|
||||
import { ApiChat } from '../../../api/types';
|
||||
|
||||
import { APP_NAME, APP_VERSION, FEEDBACK_URL } from '../../../config';
|
||||
import {
|
||||
ANIMATION_LEVEL_MAX, APP_NAME, APP_VERSION, FEEDBACK_URL,
|
||||
} from '../../../config';
|
||||
import { IS_SINGLE_COLUMN_LAYOUT } from '../../../util/environment';
|
||||
import buildClassName from '../../../util/buildClassName';
|
||||
import { pick } from '../../../util/iteratees';
|
||||
@ -137,7 +139,7 @@ const LeftMainHeader: FC<OwnProps & StateProps & DispatchProps> = ({
|
||||
|
||||
setSettingOption({ theme: newTheme });
|
||||
setSettingOption({ shouldUseSystemTheme: false });
|
||||
switchTheme(newTheme, animationLevel > 0);
|
||||
switchTheme(newTheme, animationLevel === ANIMATION_LEVEL_MAX);
|
||||
}, [animationLevel, setSettingOption, theme]);
|
||||
|
||||
const handleAnimationLevelChange = useCallback((e: React.SyntheticEvent<HTMLElement>) => {
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import { addReducer } from '../../../lib/teact/teactn';
|
||||
import { addReducer, getGlobal, setGlobal } from '../../../lib/teact/teactn';
|
||||
|
||||
import { ANIMATION_LEVEL_MAX } from '../../../config';
|
||||
import {
|
||||
IS_ANDROID, IS_IOS, IS_SAFARI, IS_TOUCH_ENV,
|
||||
} from '../../../util/environment';
|
||||
@ -7,6 +8,8 @@ import { setLanguage } from '../../../util/langProvider';
|
||||
import switchTheme from '../../../util/switchTheme';
|
||||
import { selectTheme } from '../../selectors';
|
||||
|
||||
subscribeToSystemThemeChange();
|
||||
|
||||
addReducer('init', (global) => {
|
||||
const { animationLevel, messageTextSize, language } = global.settings.byKey;
|
||||
const theme = selectTheme(global);
|
||||
@ -17,7 +20,7 @@ addReducer('init', (global) => {
|
||||
document.body.classList.add('initial');
|
||||
document.body.classList.add(`animation-level-${animationLevel}`);
|
||||
document.body.classList.add(IS_TOUCH_ENV ? 'is-touch-env' : 'is-pointer-env');
|
||||
switchTheme(theme, animationLevel > 0);
|
||||
switchTheme(theme, animationLevel === ANIMATION_LEVEL_MAX);
|
||||
|
||||
if (IS_SAFARI) {
|
||||
document.body.classList.add('is-safari');
|
||||
@ -64,3 +67,26 @@ addReducer('clearAuthError', (global) => {
|
||||
authError: undefined,
|
||||
};
|
||||
});
|
||||
|
||||
function subscribeToSystemThemeChange() {
|
||||
function handleSystemThemeChange() {
|
||||
const currentThemeMatch = document.documentElement.className.match(/theme-(\w+)/);
|
||||
const currentTheme = currentThemeMatch ? currentThemeMatch[1] : 'light';
|
||||
const global = getGlobal();
|
||||
const nextTheme = selectTheme(global);
|
||||
const { animationLevel } = global.settings.byKey;
|
||||
|
||||
if (nextTheme !== currentTheme) {
|
||||
switchTheme(nextTheme, animationLevel === ANIMATION_LEVEL_MAX);
|
||||
// Force-update component containers
|
||||
setGlobal({ ...global });
|
||||
}
|
||||
}
|
||||
|
||||
const mql = window.matchMedia('(prefers-color-scheme: dark)');
|
||||
if (typeof mql.addEventListener === 'function') {
|
||||
mql.addEventListener('change', handleSystemThemeChange);
|
||||
} else if (typeof mql.addListener === 'function') {
|
||||
mql.addListener(handleSystemThemeChange);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { GlobalState } from '../../global/types';
|
||||
import { RightColumnContent } from '../../types';
|
||||
|
||||
import { IS_SINGLE_COLUMN_LAYOUT, SYSTEM_THEME } from '../../util/environment';
|
||||
import { getSystemTheme, IS_SINGLE_COLUMN_LAYOUT } from '../../util/environment';
|
||||
import { selectCurrentMessageList, selectIsPollResultsOpen } from './messages';
|
||||
import { selectCurrentTextSearch } from './localSearch';
|
||||
import { selectCurrentStickerSearch, selectCurrentGifSearch } from './symbols';
|
||||
@ -57,5 +57,5 @@ export function selectIsRightColumnShown(global: GlobalState) {
|
||||
export function selectTheme(global: GlobalState) {
|
||||
const { theme, shouldUseSystemTheme } = global.settings.byKey;
|
||||
|
||||
return shouldUseSystemTheme ? SYSTEM_THEME : theme;
|
||||
return shouldUseSystemTheme ? getSystemTheme() : theme;
|
||||
}
|
||||
|
||||
@ -21,6 +21,7 @@ export interface IAlbum {
|
||||
}
|
||||
|
||||
export type ThemeKey = 'light' | 'dark';
|
||||
|
||||
export interface IThemeSettings {
|
||||
background?: string;
|
||||
backgroundColor?: string;
|
||||
|
||||
@ -5,6 +5,10 @@ import {
|
||||
IS_TEST,
|
||||
} from '../config';
|
||||
|
||||
export * from './environmentWebp';
|
||||
|
||||
export * from './environmentSystemTheme';
|
||||
|
||||
export function getPlatform() {
|
||||
const { userAgent, platform } = window.navigator;
|
||||
const macosPlatforms = ['Macintosh', 'MacIntel', 'MacPPC', 'Mac68K'];
|
||||
@ -55,30 +59,3 @@ export const IS_CANVAS_FILTER_SUPPORTED = (
|
||||
export const DPR = window.devicePixelRatio || 1;
|
||||
|
||||
export const MASK_IMAGE_DISABLED = true;
|
||||
|
||||
export const SYSTEM_THEME = (
|
||||
window && window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches
|
||||
) ? 'dark' : 'light';
|
||||
|
||||
let isWebpSupportedCache: boolean | undefined;
|
||||
|
||||
export function isWebpSupported() {
|
||||
return Boolean(isWebpSupportedCache);
|
||||
}
|
||||
|
||||
function testWebp(): Promise<boolean> {
|
||||
return new Promise((resolve) => {
|
||||
const webp = new Image();
|
||||
// eslint-disable-next-line max-len
|
||||
webp.src = 'data:image/webp;base64,UklGRjoAAABXRUJQVlA4IC4AAACyAgCdASoCAAIALmk0mk0iIiIiIgBoSygABc6WWgAA/veff/0PP8bA//LwYAAA';
|
||||
const handleLoadOrError = () => {
|
||||
resolve(webp.height === 2);
|
||||
};
|
||||
webp.onload = handleLoadOrError;
|
||||
webp.onerror = handleLoadOrError;
|
||||
});
|
||||
}
|
||||
|
||||
testWebp().then((hasWebp) => {
|
||||
isWebpSupportedCache = hasWebp;
|
||||
});
|
||||
|
||||
20
src/util/environmentSystemTheme.ts
Normal file
20
src/util/environmentSystemTheme.ts
Normal file
@ -0,0 +1,20 @@
|
||||
import { ThemeKey } from '../types';
|
||||
|
||||
let systemThemeCache: ThemeKey = (
|
||||
window && window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches
|
||||
) ? 'dark' : 'light';
|
||||
|
||||
export function getSystemTheme() {
|
||||
return systemThemeCache;
|
||||
}
|
||||
|
||||
function handleSystemThemeChange(e: MediaQueryListEventMap['change']) {
|
||||
systemThemeCache = e.matches ? 'dark' : 'light';
|
||||
}
|
||||
|
||||
const mql = window.matchMedia('(prefers-color-scheme: dark)');
|
||||
if (typeof mql.addEventListener === 'function') {
|
||||
mql.addEventListener('change', handleSystemThemeChange);
|
||||
} else if (typeof mql.addListener === 'function') {
|
||||
mql.addListener(handleSystemThemeChange);
|
||||
}
|
||||
22
src/util/environmentWebp.ts
Normal file
22
src/util/environmentWebp.ts
Normal file
@ -0,0 +1,22 @@
|
||||
let isWebpSupportedCache: boolean | undefined;
|
||||
|
||||
export function isWebpSupported() {
|
||||
return Boolean(isWebpSupportedCache);
|
||||
}
|
||||
|
||||
function testWebp(): Promise<boolean> {
|
||||
return new Promise((resolve) => {
|
||||
const webp = new Image();
|
||||
// eslint-disable-next-line max-len
|
||||
webp.src = 'data:image/webp;base64,UklGRjoAAABXRUJQVlA4IC4AAACyAgCdASoCAAIALmk0mk0iIiIiIgBoSygABc6WWgAA/veff/0PP8bA//LwYAAA';
|
||||
const handleLoadOrError = () => {
|
||||
resolve(webp.height === 2);
|
||||
};
|
||||
webp.onload = handleLoadOrError;
|
||||
webp.onerror = handleLoadOrError;
|
||||
});
|
||||
}
|
||||
|
||||
testWebp().then((hasWebp) => {
|
||||
isWebpSupportedCache = hasWebp;
|
||||
});
|
||||
Loading…
x
Reference in New Issue
Block a user