diff --git a/src/components/left/settings/SettingsGeneral.tsx b/src/components/left/settings/SettingsGeneral.tsx index b06d6f424..4962a64fa 100644 --- a/src/components/left/settings/SettingsGeneral.tsx +++ b/src/components/left/settings/SettingsGeneral.tsx @@ -8,18 +8,19 @@ import type { ISettings, TimeFormat } from '../../../types'; import { SettingsScreens } from '../../../types'; import { - getSystemTheme, IS_ANDROID, IS_IOS, IS_MAC_OS, + IS_ANDROID, IS_IOS, IS_MAC_OS, } from '../../../util/windowEnvironment'; +import { getSystemTheme } from '../../../util/systemTheme'; import { pick } from '../../../util/iteratees'; import { setTimeFormat } from '../../../util/langProvider'; import useLang from '../../../hooks/useLang'; import useHistoryBack from '../../../hooks/useHistoryBack'; +import useAppLayout from '../../../hooks/useAppLayout'; import ListItem from '../../ui/ListItem'; import RangeSlider from '../../ui/RangeSlider'; import type { IRadioOption } from '../../ui/RadioGroup'; import RadioGroup from '../../ui/RadioGroup'; -import useAppLayout from '../../../hooks/useAppLayout'; type OwnProps = { isActive?: boolean; diff --git a/src/components/middle/MiddleColumn.tsx b/src/components/middle/MiddleColumn.tsx index a683fc4a6..3f37909bb 100644 --- a/src/components/middle/MiddleColumn.tsx +++ b/src/components/middle/MiddleColumn.tsx @@ -18,8 +18,6 @@ import { MIN_SCREEN_WIDTH_FOR_STATIC_RIGHT_COLUMN, SAFE_SCREEN_WIDTH_FOR_STATIC_RIGHT_COLUMN, ANIMATION_END_DELAY, - DARK_THEME_BG_COLOR, - LIGHT_THEME_BG_COLOR, SUPPORTED_IMAGE_CONTENT_TYPES, GENERAL_TOPIC_ID, TMP_CHAT_ID, @@ -60,6 +58,7 @@ import { import calculateMiddleFooterTransforms from './helpers/calculateMiddleFooterTransforms'; import captureEscKeyListener from '../../util/captureEscKeyListener'; import buildClassName from '../../util/buildClassName'; +import buildStyle from '../../util/buildStyle'; import useLastCallback from '../../hooks/useLastCallback'; import useCustomBackground from '../../hooks/useCustomBackground'; @@ -458,17 +457,16 @@ function MiddleColumn({ id="MiddleColumn" className={className} onTransitionEnd={handleCssTransitionEnd} - style={` - --composer-hidden-scale: ${composerHiddenScale}; - --toolbar-hidden-scale: ${toolbarHiddenScale}; - --unpin-hidden-scale: ${unpinHiddenScale}; - --toolbar-unpin-hidden-scale: ${toolbarForUnpinHiddenScale}; - --composer-translate-x: ${composerTranslateX}px; - --toolbar-translate-x: ${toolbarTranslateX}px; - --pattern-color: ${patternColor}; - --theme-background-color: - ${backgroundColor || (theme === 'dark' ? DARK_THEME_BG_COLOR : LIGHT_THEME_BG_COLOR)}; - `} + style={buildStyle( + `--composer-hidden-scale: ${composerHiddenScale}`, + `--toolbar-hidden-scale: ${toolbarHiddenScale}`, + `--unpin-hidden-scale: ${unpinHiddenScale}`, + `--toolbar-unpin-hidden-scale: ${toolbarForUnpinHiddenScale},`, + `--composer-translate-x: ${composerTranslateX}px`, + `--toolbar-translate-x: ${toolbarTranslateX}px`, + `--pattern-color: ${patternColor}`, + backgroundColor && `--theme-background-color: ${backgroundColor}`, + )} onClick={(isTablet && isLeftColumnShown) ? handleTabletFocus : undefined} > {isDesktop && ( diff --git a/src/global/actions/ui/initial.ts b/src/global/actions/ui/initial.ts index 0e5eb17a7..865f72eec 100644 --- a/src/global/actions/ui/initial.ts +++ b/src/global/actions/ui/initial.ts @@ -8,6 +8,7 @@ import { } from '../../../util/windowEnvironment'; import { setLanguage } from '../../../util/langProvider'; import switchTheme from '../../../util/switchTheme'; +import { getSystemTheme, setSystemThemeChangeCallback } from '../../../util/systemTheme'; import { selectTabState, selectNotifySettings, selectTheme, selectPerformanceSettings, selectCanAnimateInterface, } from '../../selectors'; @@ -21,10 +22,18 @@ import type { ActionReturnType, GlobalState } from '../../types'; import { updateTabState } from '../../reducers/tabs'; import { getCurrentTabId } from '../../../util/establishMultitabRole'; import { applyPerformanceSettings } from '../../../util/perfomanceSettings'; +import { replaceSettings } from '../../reducers'; const HISTORY_ANIMATION_DURATION = 450; -subscribeToSystemThemeChange(); +setSystemThemeChangeCallback((theme) => { + let global = getGlobal(); + + if (!global.settings.byKey.shouldUseSystemTheme) return; + + global = replaceSettings(global, { theme }); + setGlobal(global); +}); addActionHandler('switchMultitabRole', async (global, actions, payload): Promise => { const { isMasterTab, tabId = getCurrentTabId() } = payload; @@ -115,7 +124,11 @@ addCallback((global: GlobalState) => { }, tabState.id); const { messageTextSize, language } = global.settings.byKey; - const theme = selectTheme(global); + + const globalTheme = selectTheme(global); + const systemTheme = getSystemTheme(); + const theme = global.settings.byKey.shouldUseSystemTheme ? systemTheme : globalTheme; + const performanceType = selectPerformanceSettings(global); void setLanguage(language, undefined, true); @@ -150,7 +163,11 @@ addCallback((global: GlobalState) => { } }); - switchTheme(theme, selectCanAnimateInterface(global)); + const canAnimate = selectCanAnimateInterface(global); + + switchTheme(theme, canAnimate); + // Make sure global has the latest theme. Will cause `switchTheme` on change + global = replaceSettings(global, { theme }); startWebsync(); @@ -223,27 +240,3 @@ addActionHandler('disableHistoryAnimations', (global, actions, payload): ActionR }, tabId); setGlobal(global, { forceSyncOnIOs: true }); }); - -function subscribeToSystemThemeChange() { - function handleSystemThemeChange() { - const currentThemeMatch = document.documentElement.className.match(/theme-(\w+)/); - const currentTheme = currentThemeMatch ? currentThemeMatch[1] : 'light'; - // eslint-disable-next-line eslint-multitab-tt/no-immediate-global - let global = getGlobal(); - const nextTheme = selectTheme(global); - - if (nextTheme !== currentTheme) { - switchTheme(nextTheme, selectCanAnimateInterface(global)); - // Force-update component containers - global = { ...global }; - 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); - } -} diff --git a/src/global/actions/ui/settings.ts b/src/global/actions/ui/settings.ts index fa3573afa..62974e4ee 100644 --- a/src/global/actions/ui/settings.ts +++ b/src/global/actions/ui/settings.ts @@ -20,15 +20,15 @@ addCallback((global: GlobalState) => { // eslint-disable-next-line eslint-multitab-tt/no-getactions-in-actions const { updatePageTitle } = getActions(); - const settings = global.settings.byKey; - const prevSettings = prevGlobal?.settings.byKey; - const performance = global.settings.performance; - const prevPerformance = prevGlobal?.settings.performance; + const oldGlobal = prevGlobal; prevGlobal = global; - if (!prevSettings) { - return; - } + if (!oldGlobal) return; + + const settings = global.settings.byKey; + const prevSettings = oldGlobal.settings.byKey; + const performance = global.settings.performance; + const prevPerformance = oldGlobal.settings.performance; if (performance !== prevPerformance) { requestMutation(() => { diff --git a/src/global/selectors/ui.ts b/src/global/selectors/ui.ts index 76301b94b..879d635eb 100644 --- a/src/global/selectors/ui.ts +++ b/src/global/selectors/ui.ts @@ -3,7 +3,6 @@ import type { PerformanceTypeKey } from '../../types'; import type { ApiMessage } from '../../api/types'; import { NewChatMembersProgress, RightColumnContent } from '../../types'; -import { getSystemTheme } from '../../util/windowEnvironment'; import { selectCurrentMessageList, selectIsCreateTopicPanelOpen, selectIsEditTopicPanelOpen, selectIsPollResultsOpen, } from './messages'; @@ -62,9 +61,9 @@ export function selectIsRightColumnShown( } export function selectTheme(global: T) { - const { theme, shouldUseSystemTheme } = global.settings.byKey; + const { theme } = global.settings.byKey; - return shouldUseSystemTheme ? getSystemTheme() : theme; + return theme; } export function selectIsForumPanelOpen( diff --git a/src/util/switchTheme.ts b/src/util/switchTheme.ts index 6f63965e6..b59640330 100644 --- a/src/util/switchTheme.ts +++ b/src/util/switchTheme.ts @@ -16,6 +16,7 @@ type RGBAColor = { let isInitialized = false; +const DECIMAL_PLACES = 3; const HEX_COLOR_REGEX = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})?$/i; const DURATION_MS = 200; const ENABLE_ANIMATION_DELAY_MS = 500; @@ -116,9 +117,9 @@ function applyColorAnimationStep(startIndex: number, endIndex: number, interpola ? Math.round(lerp(propertyColors[startIndex].a!, propertyColors[endIndex].a!, interpolationRatio)) : undefined; - document.documentElement.style.setProperty(property, a !== undefined - ? `rgba(${r},${g},${b},${a / 255})` - : `rgb(${r},${g},${b})`); + const roundedA = a !== undefined ? Math.round((a / 255) * 10 ** DECIMAL_PLACES) / 10 ** DECIMAL_PLACES : undefined; + + document.documentElement.style.setProperty(property, `rgb(${r},${g},${b}${roundedA ? `,${roundedA}` : ''})`); if (RGB_VARIABLES.has(property)) { document.documentElement.style.setProperty(`${property}-rgb`, `${r},${g},${b}`); diff --git a/src/util/environmentSystemTheme.ts b/src/util/systemTheme.ts similarity index 72% rename from src/util/environmentSystemTheme.ts rename to src/util/systemTheme.ts index 03b67154b..28e676600 100644 --- a/src/util/environmentSystemTheme.ts +++ b/src/util/systemTheme.ts @@ -4,12 +4,20 @@ let systemThemeCache: ThemeKey = ( window.matchMedia?.('(prefers-color-scheme: dark)').matches ) ? 'dark' : 'light'; +let themeChangeCallback: ((newTheme: ThemeKey) => void) | undefined; + export function getSystemTheme() { return systemThemeCache; } function handleSystemThemeChange(e: MediaQueryListEventMap['change']) { systemThemeCache = e.matches ? 'dark' : 'light'; + + themeChangeCallback?.(systemThemeCache); +} + +export function setSystemThemeChangeCallback(callback: (newTheme: ThemeKey) => void) { + themeChangeCallback = callback; } const mql = window.matchMedia('(prefers-color-scheme: dark)'); diff --git a/src/util/windowEnvironment.ts b/src/util/windowEnvironment.ts index 2d91d180f..b725a94b9 100644 --- a/src/util/windowEnvironment.ts +++ b/src/util/windowEnvironment.ts @@ -9,8 +9,6 @@ import { export * from './environmentWebp'; -export * from './environmentSystemTheme'; - export function getPlatform() { const { userAgent, platform } = window.navigator; const macosPlatforms = ['Macintosh', 'MacIntel', 'MacPPC', 'Mac68K'];