diff --git a/src/hooks/useSettingsState.ts b/src/hooks/useSettingsState.ts index d0946deb..8e540a19 100644 --- a/src/hooks/useSettingsState.ts +++ b/src/hooks/useSettingsState.ts @@ -9,6 +9,7 @@ import { } from "react"; import { SubtitleStyling } from "@/stores/subtitles"; +import { usePreviewThemeStore } from "@/stores/theme"; export function useDerived( initial: T, @@ -56,6 +57,11 @@ export function useSettingsState( const [backendUrlState, setBackendUrl, resetBackendUrl, backendUrlChanged] = useDerived(backendUrl); const [themeState, setTheme, resetTheme, themeChanged] = useDerived(theme); + const setPreviewTheme = usePreviewThemeStore((s) => s.setPreviewTheme); + const resetPreviewTheme = useCallback( + () => setPreviewTheme(theme), + [setPreviewTheme, theme], + ); const [ appLanguageState, setAppLanguage, @@ -81,6 +87,7 @@ export function useSettingsState( function reset() { resetTheme(); + resetPreviewTheme(); resetAppLanguage(); resetSubStyling(); resetProxyUrls(); diff --git a/src/pages/Settings.tsx b/src/pages/Settings.tsx index cebf1ddd..a2efcc2f 100644 --- a/src/pages/Settings.tsx +++ b/src/pages/Settings.tsx @@ -1,5 +1,5 @@ import classNames from "classnames"; -import { useCallback, useEffect, useMemo } from "react"; +import { useCallback, useEffect, useMemo, useRef } from "react"; import { useTranslation } from "react-i18next"; import { useAsyncFn } from "react-use"; @@ -33,7 +33,7 @@ import { AccountWithToken, useAuthStore } from "@/stores/auth"; import { useLanguageStore } from "@/stores/language"; import { usePreferencesStore } from "@/stores/preferences"; import { useSubtitleStore } from "@/stores/subtitles"; -import { useThemeStore } from "@/stores/theme"; +import { usePreviewThemeStore, useThemeStore } from "@/stores/theme"; import { SubPageLayout } from "./layouts/SubPageLayout"; import { PreferencesPart } from "./parts/settings/PreferencesPart"; @@ -102,8 +102,10 @@ export function AccountSettings(props: { export function SettingsPage() { const { t } = useTranslation(); - const activeTheme = useThemeStore((s) => s.theme); + const activeTheme = useThemeStore((s) => s.theme) ?? "default"; const setTheme = useThemeStore((s) => s.setTheme); + const previewTheme = usePreviewThemeStore((s) => s.previewTheme) ?? "default"; + const setPreviewTheme = usePreviewThemeStore((s) => s.setPreviewTheme); const appLanguage = useLanguageStore((s) => s.language); const setAppLanguage = useLanguageStore((s) => s.setLanguage); @@ -144,6 +146,22 @@ export function SettingsPage() { enableThumbnails, ); + // Reset the preview theme when the settings page is unmounted + useEffect( + () => () => { + setPreviewTheme(null); + }, + [setPreviewTheme], + ); + + const setThemeWithPreview = useCallback( + (v: string | null) => { + state.theme.set(v === "default" ? null : v); + setPreviewTheme(v); + }, + [state.theme, setPreviewTheme], + ); + const saveChanges = useCallback(async () => { if (account && backendUrl) { if ( @@ -242,7 +260,11 @@ export function SettingsPage() { />
- +
diff --git a/src/pages/parts/settings/ThemePart.tsx b/src/pages/parts/settings/ThemePart.tsx index 264c5b40..f4972eaf 100644 --- a/src/pages/parts/settings/ThemePart.tsx +++ b/src/pages/parts/settings/ThemePart.tsx @@ -5,6 +5,10 @@ import { Icon, Icons } from "@/components/Icon"; import { Heading1 } from "@/components/utils/Text"; const availableThemes = [ + { + id: "default", + key: "settings.appearance.themes.default", + }, { id: "blue", key: "settings.appearance.themes.blue", @@ -26,6 +30,7 @@ const availableThemes = [ function ThemePreview(props: { selector?: string; active?: boolean; + inUse?: boolean; name: string; onClick?: () => void; }) { @@ -105,7 +110,7 @@ function ThemePreview(props: { {t("settings.appearance.activeTheme")} @@ -117,6 +122,7 @@ function ThemePreview(props: { export function ThemePart(props: { active: string | null; + inUse: string | null; setTheme: (theme: string | null) => void; }) { const { t } = useTranslation(); @@ -126,16 +132,11 @@ export function ThemePart(props: { {t("settings.appearance.title")}
{/* default theme */} - props.setTheme(null)} - /> {availableThemes.map((v) => ( props.setTheme(v.id)} diff --git a/src/stores/theme/index.tsx b/src/stores/theme/index.tsx index c43ec414..a644c626 100644 --- a/src/stores/theme/index.tsx +++ b/src/stores/theme/index.tsx @@ -25,12 +25,31 @@ export const useThemeStore = create( ), ); +export interface PreviewThemeStore { + previewTheme: string | null; + setPreviewTheme(v: string | null): void; +} + +export const usePreviewThemeStore = create( + immer((set) => ({ + previewTheme: null, + setPreviewTheme(v) { + set((s) => { + s.previewTheme = v; + }); + }, + })), +); + export function ThemeProvider(props: { children?: ReactNode; applyGlobal?: boolean; }) { + const previewTheme = usePreviewThemeStore((s) => s.previewTheme); const theme = useThemeStore((s) => s.theme); - const themeSelector = theme ? `theme-${theme}` : undefined; + + const themeToDisplay = previewTheme ?? theme; + const themeSelector = themeToDisplay ? `theme-${themeToDisplay}` : undefined; return (