From 19796d92201e28e15cb9d1729362fcf204700a32 Mon Sep 17 00:00:00 2001 From: Innei Date: Fri, 12 Jul 2024 00:19:23 +0800 Subject: [PATCH] fix: web app dark mode (#131) * fix: web app dark mode Signed-off-by: Innei * update electron logic Signed-off-by: Innei --------- Signed-off-by: Innei --- src/main/tipc.ts | 9 ++- src/renderer/src/hooks/common/useDark.ts | 68 +++++++++++++++---- .../src/modules/settings/tabs/apperance.tsx | 10 ++- 3 files changed, 72 insertions(+), 15 deletions(-) diff --git a/src/main/tipc.ts b/src/main/tipc.ts index edcc3b31a..b5dad2897 100644 --- a/src/main/tipc.ts +++ b/src/main/tipc.ts @@ -177,7 +177,14 @@ export const router = { setAppearance: t.procedure .input<"light" | "dark" | "system">() .action(async ({ input }) => { - nativeTheme.themeSource = input + // NOTE: Temporarily changing to system to get the color mode that system is in at the moment may cause a bit of a problem. + // On macos, there is a bug, traffic lights flicker + nativeTheme.themeSource = "system" + const systemColorMode = nativeTheme.shouldUseDarkColors ? + "dark" : + "light" + + nativeTheme.themeSource = systemColorMode === input ? "system" : input }), setMacOSBadge: t.procedure.input().action(async ({ input }) => { if (app.dock) { diff --git a/src/renderer/src/hooks/common/useDark.ts b/src/renderer/src/hooks/common/useDark.ts index 8a9ed627b..0a9c03c13 100644 --- a/src/renderer/src/hooks/common/useDark.ts +++ b/src/renderer/src/hooks/common/useDark.ts @@ -1,24 +1,68 @@ import { jotaiStore } from "@renderer/lib/jotai" +import { getStorageNS } from "@renderer/lib/ns" import { atom, useAtomValue } from "jotai" -import { useLayoutEffect } from "react" +import { atomWithStorage } from "jotai/utils" +import { useCallback, useLayoutEffect } from "react" import { useMediaQuery } from "usehooks-ts" -const darkAtom = atom(false) -export function useDark() { - return useAtomValue(darkAtom) +const useDarkQuery = () => useMediaQuery("(prefers-color-scheme: dark)") +type ColorMode = "light" | "dark" | "system" +const darkAtom = !window.electron ? + atomWithStorage( + getStorageNS("color-mode"), + "system" as ColorMode, + undefined, + { + getOnInit: true, + }, + ) : + atom("system" as ColorMode) +function useDarkElectron() { + return useAtomValue(darkAtom) === "dark" } +function useDarkWebApp() { + const systemIsDark = useDarkQuery() + const mode = useAtomValue(darkAtom) + return mode === "dark" || (mode === "system" && systemIsDark) +} +export const useDark = window.electron ? useDarkElectron : useDarkWebApp -export const useSyncDark = () => { - const isDark = useMediaQuery("(prefers-color-scheme: dark)") +const useSyncDarkElectron = () => { + const appIsDark = useDarkQuery() useLayoutEffect(() => { - jotaiStore.set(darkAtom, isDark) + document.documentElement.dataset.theme = appIsDark ? "dark" : "light" + disableTransition(["[role=switch]>*"]) - document.documentElement.dataset.theme = isDark ? "dark" : "light" - disableTransition([ - "[role=switch]>*", - ]) - }, [isDark]) + jotaiStore.set(darkAtom, appIsDark ? "dark" : "light") + }, [appIsDark]) +} + +const useSyncDarkWebApp = () => { + const colorMode = useAtomValue(darkAtom) + const systemIsDark = useDarkQuery() + useLayoutEffect(() => { + const realColorMode: Exclude = + colorMode === "system" ? (systemIsDark ? "dark" : "light") : colorMode + document.documentElement.dataset.theme = realColorMode + disableTransition(["[role=switch]>*"]) + }, [colorMode, systemIsDark]) +} + +export const useSyncDark = window.electron ? + useSyncDarkElectron : + useSyncDarkWebApp + +export const useSetDarkInWebApp = () => { + const systemColorMode = useDarkQuery() ? "dark" : "light" + return useCallback( + (colorMode: Exclude) => + jotaiStore.set( + darkAtom, + colorMode === systemColorMode ? "system" : colorMode, + ), + [systemColorMode], + ) } function disableTransition(disableTransitionExclude: string[] = []) { diff --git a/src/renderer/src/modules/settings/tabs/apperance.tsx b/src/renderer/src/modules/settings/tabs/apperance.tsx index 84bab3bd4..93e500368 100644 --- a/src/renderer/src/modules/settings/tabs/apperance.tsx +++ b/src/renderer/src/modules/settings/tabs/apperance.tsx @@ -11,7 +11,7 @@ import { SelectTrigger, SelectValue, } from "@renderer/components/ui/select" -import { useDark } from "@renderer/hooks/common" +import { useDark, useSetDarkInWebApp } from "@renderer/hooks/common" import { tipcClient } from "@renderer/lib/client" import { getOS } from "@renderer/lib/utils" import { useQuery } from "@tanstack/react-query" @@ -26,6 +26,8 @@ const SettingBuilder = createSettingBuilder(useUISettingValue) export const SettingAppearance = () => { const isDark = useDark() + const setDarkInWebApp = useSetDarkInWebApp() + return ( <> @@ -41,7 +43,11 @@ export const SettingAppearance = () => { label="Dark Mode" checked={isDark} onCheckedChange={(e) => { - tipcClient?.setAppearance(e ? "dark" : "light") + if (window.electron) { + tipcClient?.setAppearance(e ? "dark" : "light") + } else { + setDarkInWebApp(e ? "dark" : "light") + } }} />, {