diff --git a/src/main/main.ts b/src/main/main.ts index 0f307a007..ffc8de619 100644 --- a/src/main/main.ts +++ b/src/main/main.ts @@ -1,4 +1,4 @@ -import { app, globalShortcut, ipcMain as ipc, nativeTheme } from 'electron'; +import { app, globalShortcut, ipcMain as ipc } from 'electron'; import log from 'electron-log'; import { menubar } from 'menubar'; @@ -92,20 +92,6 @@ app.whenReady().then(async () => { }); }); - nativeTheme.on('updated', () => { - if (nativeTheme.shouldUseDarkColors) { - mb.window.webContents.send( - namespacedEvent('update-theme'), - 'DARK_DEFAULT', - ); - } else { - mb.window.webContents.send( - namespacedEvent('update-theme'), - 'LIGHT_DEFAULT', - ); - } - }); - /** * Gitify custom IPC events */ diff --git a/src/renderer/App.tsx b/src/renderer/App.tsx index eda622c32..d3ad42080 100644 --- a/src/renderer/App.tsx +++ b/src/renderer/App.tsx @@ -21,10 +21,6 @@ import { NotificationsRoute } from './routes/Notifications'; import { SettingsRoute } from './routes/Settings'; import './App.css'; -import { - DEFAULT_DAY_COLOR_SCHEME, - DEFAULT_NIGHT_COLOR_SCHEME, -} from './utils/theme'; function RequireAuth({ children }) { const { isLoggedIn } = useContext(AppContext); @@ -39,11 +35,7 @@ function RequireAuth({ children }) { export const App = () => { return ( - + diff --git a/src/renderer/components/settings/AppearanceSettings.tsx b/src/renderer/components/settings/AppearanceSettings.tsx index a2cfb3210..5d58ed82d 100644 --- a/src/renderer/components/settings/AppearanceSettings.tsx +++ b/src/renderer/components/settings/AppearanceSettings.tsx @@ -1,5 +1,5 @@ -import { ipcRenderer, webFrame } from 'electron'; -import { type FC, useContext, useEffect, useState } from 'react'; +import { webFrame } from 'electron'; +import { type FC, useContext, useState } from 'react'; import { CheckIcon, @@ -21,19 +21,11 @@ import { Select, Stack, Text, - useTheme, } from '@primer/react'; -import { namespacedEvent } from '../../../shared/events'; import { AppContext } from '../../context/App'; import { Size, Theme } from '../../types'; import { hasMultipleAccounts } from '../../utils/auth/utils'; -import { - DEFAULT_DAY_COLOR_SCHEME, - DEFAULT_NIGHT_COLOR_SCHEME, - isDayScheme, - setScrollbarTheme, -} from '../../utils/theme'; import { zoomLevelToPercentage, zoomPercentageToLevel } from '../../utils/zoom'; import { Checkbox } from '../fields/Checkbox'; import { FieldLabel } from '../fields/FieldLabel'; @@ -43,27 +35,11 @@ let timeout: NodeJS.Timeout; const DELAY = 200; export const AppearanceSettings: FC = () => { - const { setColorMode, setDayScheme, setNightScheme } = useTheme(); const { auth, settings, updateSetting } = useContext(AppContext); const [zoomPercentage, setZoomPercentage] = useState( zoomLevelToPercentage(webFrame.getZoomLevel()), ); - useEffect(() => { - ipcRenderer.on( - namespacedEvent('update-theme'), - (_, updatedTheme: Theme) => { - if (settings.theme === Theme.SYSTEM) { - const mode = isDayScheme(updatedTheme) ? 'day' : 'night'; - setColorMode('auto'); - setDayScheme(DEFAULT_DAY_COLOR_SCHEME); - setNightScheme(DEFAULT_NIGHT_COLOR_SCHEME); - setScrollbarTheme(mode); - } - }, - ); - }, [settings.theme, setColorMode, setDayScheme, setNightScheme]); - window.addEventListener('resize', () => { // clear the timeout clearTimeout(timeout); diff --git a/src/renderer/context/App.tsx b/src/renderer/context/App.tsx index bc477aba5..6aadc648b 100644 --- a/src/renderer/context/App.tsx +++ b/src/renderer/context/App.tsx @@ -56,9 +56,8 @@ import { clearState, loadState, saveState } from '../utils/storage'; import { DEFAULT_DAY_COLOR_SCHEME, DEFAULT_NIGHT_COLOR_SCHEME, - isDayScheme, + mapThemeModeToColorMode, mapThemeModeToColorScheme, - setScrollbarTheme, } from '../utils/theme'; import { zoomPercentageToLevel } from '../utils/zoom'; @@ -155,17 +154,12 @@ export const AppProvider = ({ children }: { children: ReactNode }) => { }, []); useEffect(() => { + const colorMode = mapThemeModeToColorMode(settings.theme); const colorScheme = mapThemeModeToColorScheme(settings.theme); - if (isDayScheme(settings.theme)) { - setDayScheme(colorScheme ?? DEFAULT_DAY_COLOR_SCHEME); - setColorMode('day'); - setScrollbarTheme('day'); - } else { - setNightScheme(colorScheme ?? DEFAULT_NIGHT_COLOR_SCHEME); - setColorMode('night'); - setScrollbarTheme('night'); - } + setColorMode(colorMode); + setDayScheme(colorScheme ?? DEFAULT_DAY_COLOR_SCHEME); + setNightScheme(colorScheme ?? DEFAULT_NIGHT_COLOR_SCHEME); }, [settings.theme, setColorMode, setDayScheme, setNightScheme]); // biome-ignore lint/correctness/useExhaustiveDependencies: We only want fetchNotifications to be called for account changes diff --git a/src/renderer/routes/Notifications.tsx b/src/renderer/routes/Notifications.tsx index 6b9b8b354..407549a3c 100644 --- a/src/renderer/routes/Notifications.tsx +++ b/src/renderer/routes/Notifications.tsx @@ -2,6 +2,7 @@ import { type FC, useContext, useMemo } from 'react'; import { AllRead } from '../components/AllRead'; import { Oops } from '../components/Oops'; +import { Page } from '../components/layout/Page'; import { AccountNotifications } from '../components/notifications/AccountNotifications'; import { AppContext } from '../context/App'; import { getAccountUUID } from '../utils/auth/utils'; @@ -35,7 +36,7 @@ export const NotificationsRoute: FC = () => { } return ( - <> + {notifications.map((accountNotifications) => ( { showAccountHeader={hasMultipleAccounts || settings.showAccountHeader} /> ))} - + ); }; diff --git a/src/renderer/routes/__snapshots__/Notifications.test.tsx.snap b/src/renderer/routes/__snapshots__/Notifications.test.tsx.snap index a8bb76bc4..05d667890 100644 --- a/src/renderer/routes/__snapshots__/Notifications.test.tsx.snap +++ b/src/renderer/routes/__snapshots__/Notifications.test.tsx.snap @@ -419,15 +419,25 @@ exports[`renderer/routes/Notifications.tsx should render itself & its children ( "asFragment": [Function], "baseElement":
+
+

+ AccountNotifications +

+
+
+ , + "container":
+

AccountNotifications

- , - "container":
-

- AccountNotifications -

, "debug": [Function], "findAllByAltText": [Function], @@ -488,6 +498,24 @@ exports[`renderer/routes/Notifications.tsx should render itself & its children ( "asFragment": [Function], "baseElement":
+
+

+ AccountNotifications +

+

+ AccountNotifications +

+
+
+ , + "container":
+

AccountNotifications

@@ -495,14 +523,6 @@ exports[`renderer/routes/Notifications.tsx should render itself & its children ( AccountNotifications

- , - "container":
-

- AccountNotifications -

-

- AccountNotifications -

, "debug": [Function], "findAllByAltText": [Function], diff --git a/src/renderer/utils/theme.test.ts b/src/renderer/utils/theme.test.ts index e353d13f7..26e313518 100644 --- a/src/renderer/utils/theme.test.ts +++ b/src/renderer/utils/theme.test.ts @@ -1,50 +1,21 @@ import { Theme } from '../types'; -import { - getTheme, - mapThemeModeToColorScheme, - setScrollbarTheme, -} from './theme'; +import { mapThemeModeToColorMode, mapThemeModeToColorScheme } from './theme'; describe('renderer/utils/theme.ts', () => { - const htmlElement = document.createElement('html'); - - beforeEach(() => { - document.querySelector = jest.fn(() => htmlElement); - }); - - it('should change to light mode', () => { - setScrollbarTheme('day'); - expect(getTheme()).toBe(Theme.LIGHT); - }); - - it('should change to dark mode', () => { - setScrollbarTheme('night'); - expect(getTheme()).toBe(Theme.DARK); - }); - - it("should use the system's mode - light", () => { - Object.defineProperty(window, 'matchMedia', { - writable: true, - value: jest.fn().mockImplementation((_query) => ({ - matches: false, - })), - }); - setScrollbarTheme(); - expect(getTheme()).toBe(Theme.LIGHT); - }); - - it("should use the system's mode - dark", () => { - Object.defineProperty(window, 'matchMedia', { - writable: true, - value: jest.fn().mockImplementation((_query) => ({ - matches: true, - })), - }); - setScrollbarTheme(); - expect(getTheme()).toBe(Theme.DARK); + it('should map theme mode to github primer color mode', () => { + expect(mapThemeModeToColorMode(Theme.LIGHT)).toBe('day'); + expect(mapThemeModeToColorMode(Theme.LIGHT_HIGH_CONTRAST)).toBe('day'); + expect(mapThemeModeToColorMode(Theme.LIGHT_COLORBLIND)).toBe('day'); + expect(mapThemeModeToColorMode(Theme.LIGHT_TRITANOPIA)).toBe('day'); + expect(mapThemeModeToColorMode(Theme.DARK)).toBe('night'); + expect(mapThemeModeToColorMode(Theme.DARK_HIGH_CONTRAST)).toBe('night'); + expect(mapThemeModeToColorMode(Theme.DARK_COLORBLIND)).toBe('night'); + expect(mapThemeModeToColorMode(Theme.DARK_TRITANOPIA)).toBe('night'); + expect(mapThemeModeToColorMode(Theme.DARK_DIMMED)).toBe('night'); + expect(mapThemeModeToColorMode(Theme.SYSTEM)).toBe('auto'); }); - it('should map theme mode to github primer provider', () => { + it('should map theme mode to github primer color scheme', () => { expect(mapThemeModeToColorScheme(Theme.LIGHT)).toBe('light'); expect(mapThemeModeToColorScheme(Theme.LIGHT_HIGH_CONTRAST)).toBe( 'light_high_contrast', diff --git a/src/renderer/utils/theme.ts b/src/renderer/utils/theme.ts index 70f28a8d0..b1fffb656 100644 --- a/src/renderer/utils/theme.ts +++ b/src/renderer/utils/theme.ts @@ -4,63 +4,21 @@ import { Theme } from '../types'; export const DEFAULT_DAY_COLOR_SCHEME = 'light'; export const DEFAULT_NIGHT_COLOR_SCHEME = 'dark'; -/** - * @deprecated - */ -export function getTheme(): Theme { - if (document.querySelector('html').classList.contains('dark')) { - return Theme.DARK; - } - - return Theme.LIGHT; -} - -/** - * @deprecated - */ -export function setLightMode() { - document.querySelector('html').classList.remove('dark'); -} - -/** - * @deprecated - */ -export function setDarkMode() { - document.querySelector('html').classList.add('dark'); -} - -/** - * TODO find a way to set scrollbar colors based on GitHub Primer Theme Provider / Design Tokens - * @deprecated - */ -export function setScrollbarTheme(mode?: ColorModeWithAuto) { - switch (mode) { - case 'day': - case 'light': - setLightMode(); - break; - case 'night': - case 'dark': - setDarkMode(); - break; - default: - if (window.matchMedia?.('(prefers-color-scheme: dark)').matches) { - setDarkMode(); - } else { - setLightMode(); - } - } -} - -export function isDayScheme(themeMode: Theme) { +export function mapThemeModeToColorMode(themeMode: Theme): ColorModeWithAuto { switch (themeMode) { case Theme.LIGHT: case Theme.LIGHT_HIGH_CONTRAST: case Theme.LIGHT_COLORBLIND: case Theme.LIGHT_TRITANOPIA: - return true; + return 'day'; + case Theme.DARK: + case Theme.DARK_HIGH_CONTRAST: + case Theme.DARK_COLORBLIND: + case Theme.DARK_TRITANOPIA: + case Theme.DARK_DIMMED: + return 'night'; default: - return false; + return 'auto'; } } diff --git a/tailwind.config.ts b/tailwind.config.ts index 32c091d19..fdadf86f7 100644 --- a/tailwind.config.ts +++ b/tailwind.config.ts @@ -5,7 +5,7 @@ const sidebarWidth = '2.5rem'; // 40px const config: Config = { content: ['./src/**/*.js', './src/**/*.ts', './src/**/*.tsx'], - darkMode: 'class', + darkMode: ['class', '[data-color-mode="dark"]'], // GitHub Primer Theme Provider color mode custom selector theme: { extend: { fontSize: { @@ -65,19 +65,16 @@ const config: Config = { }, }, }, - variants: { - extend: {}, - }, plugins: [ ({ addBase }) => { - // TODO - ideally we would move this to be controlled by GitHub Primer Design Tokens, colors and color schemes + // TODO - ideally we would use GitHub Primer Design Tokens instead of TailwindCSS addBase({ - ':root': { + '[data-color-mode="light"]': { '--gitify-scrollbar-track': colors.gray[100], '--gitify-scrollbar-thumb': colors.gray[300], '--gitify-scrollbar-thumb-hover': colors.gray[400], }, - '.dark': { + '[data-color-mode="dark"]': { '--gitify-scrollbar-track': colors.gray[900], '--gitify-scrollbar-thumb': colors.gray[700], '--gitify-scrollbar-thumb-hover': colors.gray[600],