From b17846f2643a4fccb0a2ee237dd2bd46b5e10482 Mon Sep 17 00:00:00 2001 From: codytseng Date: Sat, 18 Oct 2025 17:54:28 +0800 Subject: [PATCH] feat: pure black --- src/PageManager.tsx | 23 +++++++-- src/components/AccountManager/index.tsx | 2 +- src/components/ui/sonner.tsx | 4 +- src/i18n/locales/ar.ts | 4 +- src/i18n/locales/de.ts | 4 +- src/i18n/locales/en.ts | 4 +- src/i18n/locales/es.ts | 4 +- src/i18n/locales/fa.ts | 4 +- src/i18n/locales/fr.ts | 4 +- src/i18n/locales/hi.ts | 4 +- src/i18n/locales/it.ts | 4 +- src/i18n/locales/ja.ts | 4 +- src/i18n/locales/ko.ts | 4 +- src/i18n/locales/pl.ts | 4 +- src/i18n/locales/pt-BR.ts | 4 +- src/i18n/locales/pt-PT.ts | 4 +- src/i18n/locales/ru.ts | 4 +- src/i18n/locales/th.ts | 4 +- src/i18n/locales/zh.ts | 4 +- src/index.css | 6 +++ src/lib/link.ts | 1 + .../AppearanceSettingsPage/index.tsx | 48 +++++++++++++++++++ .../secondary/GeneralSettingsPage/index.tsx | 17 ------- src/pages/secondary/SettingsPage/index.tsx | 9 ++++ src/providers/ThemeProvider.tsx | 39 +++++++-------- src/routes.tsx | 2 + src/types/index.d.ts | 4 +- 27 files changed, 156 insertions(+), 63 deletions(-) create mode 100644 src/pages/secondary/AppearanceSettingsPage/index.tsx diff --git a/src/PageManager.tsx b/src/PageManager.tsx index df7b64ff..541df6f3 100644 --- a/src/PageManager.tsx +++ b/src/PageManager.tsx @@ -28,6 +28,7 @@ import RelayPage from './pages/primary/RelayPage' import SearchPage from './pages/primary/SearchPage' import { NotificationProvider } from './providers/NotificationProvider' import { useScreenSize } from './providers/ScreenSizeProvider' +import { useTheme } from './providers/ThemeProvider' import { routes } from './routes' import modalManager from './services/modal-manager.service' @@ -104,6 +105,7 @@ export function PageManager({ maxStackSize = 5 }: { maxStackSize?: number }) { ]) const [secondaryStack, setSecondaryStack] = useState([]) const { isSmallScreen } = useScreenSize() + const { themeSetting } = useTheme() const ignorePopStateRef = useRef(false) useEffect(() => { @@ -356,8 +358,18 @@ export function PageManager({ maxStackSize = 5 }: { maxStackSize?: number }) { }} > -
-
+
+
{primaryPages.map(({ name, element, props }) => (
))}
-
+
{secondaryStack.map((item, index) => (
{ if (!nostrLogin) return diff --git a/src/components/ui/sonner.tsx b/src/components/ui/sonner.tsx index e0739b62..2b6da19e 100644 --- a/src/components/ui/sonner.tsx +++ b/src/components/ui/sonner.tsx @@ -1,16 +1,14 @@ -// import { useTheme } from "next-themes" import { useTheme } from '@/providers/ThemeProvider' import { Toaster as Sonner } from 'sonner' type ToasterProps = React.ComponentProps const Toaster = ({ ...props }: ToasterProps) => { - // const { theme = "system" } = useTheme() const { themeSetting } = useTheme() return ( { export const toWallet = () => '/settings/wallet' export const toPostSettings = () => '/settings/posts' export const toGeneralSettings = () => '/settings/general' +export const toAppearanceSettings = () => '/settings/appearance' export const toTranslation = () => '/settings/translation' export const toProfileEditor = () => '/profile-editor' export const toRelay = (url: string) => `/relays/${encodeURIComponent(url)}` diff --git a/src/pages/secondary/AppearanceSettingsPage/index.tsx b/src/pages/secondary/AppearanceSettingsPage/index.tsx new file mode 100644 index 00000000..f818299b --- /dev/null +++ b/src/pages/secondary/AppearanceSettingsPage/index.tsx @@ -0,0 +1,48 @@ +import { Label } from '@/components/ui/label' +import SecondaryPageLayout from '@/layouts/SecondaryPageLayout' +import { cn } from '@/lib/utils' +import { useTheme } from '@/providers/ThemeProvider' +import { Monitor, Moon, Sun } from 'lucide-react' +import { forwardRef } from 'react' +import { useTranslation } from 'react-i18next' + +const THEMES = [ + { key: 'system', label: 'System', icon: }, + { key: 'light', label: 'Light', icon: }, + { key: 'dark', label: 'Dark', icon: }, + { key: 'pure-black', label: 'Pure Black', icon: } +] as const + +const AppearanceSettingsPage = forwardRef(({ index }: { index?: number }, ref) => { + const { t } = useTranslation() + const { themeSetting, setThemeSetting } = useTheme() + + return ( + +
+
+ +
+ {THEMES.map(({ key, label, icon }) => ( + + ))} +
+
+
+
+ ) +}) +AppearanceSettingsPage.displayName = 'AppearanceSettingsPage' +export default AppearanceSettingsPage diff --git a/src/pages/secondary/GeneralSettingsPage/index.tsx b/src/pages/secondary/GeneralSettingsPage/index.tsx index 33cdf140..a4c0f665 100644 --- a/src/pages/secondary/GeneralSettingsPage/index.tsx +++ b/src/pages/secondary/GeneralSettingsPage/index.tsx @@ -6,7 +6,6 @@ import { LocalizedLanguageNames, TLanguage } from '@/i18n' import SecondaryPageLayout from '@/layouts/SecondaryPageLayout' import { cn, isSupportCheckConnectionType } from '@/lib/utils' import { useContentPolicy } from '@/providers/ContentPolicyProvider' -import { useTheme } from '@/providers/ThemeProvider' import { useUserPreferences } from '@/providers/UserPreferencesProvider' import { useUserTrust } from '@/providers/UserTrustProvider' import { TMediaAutoLoadPolicy } from '@/types' @@ -18,7 +17,6 @@ import { useTranslation } from 'react-i18next' const GeneralSettingsPage = forwardRef(({ index }: { index?: number }, ref) => { const { t, i18n } = useTranslation() const [language, setLanguage] = useState(i18n.language as TLanguage) - const { themeSetting, setThemeSetting } = useTheme() const { autoplay, setAutoplay, @@ -57,21 +55,6 @@ const GeneralSettingsPage = forwardRef(({ index }: { index?: number }, ref) => { - - - -
+ push(toAppearanceSettings())}> +
+ +
{t('Appearance')}
+
+ +
push(toRelaySettings())}>
diff --git a/src/providers/ThemeProvider.tsx b/src/providers/ThemeProvider.tsx index f9faa581..6ee05fc8 100644 --- a/src/providers/ThemeProvider.tsx +++ b/src/providers/ThemeProvider.tsx @@ -2,14 +2,8 @@ import storage from '@/services/local-storage.service' import { TTheme, TThemeSetting } from '@/types' import { createContext, useContext, useEffect, useState } from 'react' -type ThemeProviderProps = { - children: React.ReactNode - defaultTheme?: TTheme -} - type ThemeProviderState = { themeSetting: TThemeSetting - theme: TTheme setThemeSetting: (themeSetting: TThemeSetting) => Promise } @@ -19,7 +13,7 @@ function getSystemTheme() { const ThemeProviderContext = createContext(undefined) -export function ThemeProvider({ children, ...props }: ThemeProviderProps) { +export function ThemeProvider({ children }: { children: React.ReactNode }) { const [themeSetting, setThemeSetting] = useState( (localStorage.getItem('themeSetting') as TThemeSetting | null) ?? 'system' ) @@ -39,7 +33,10 @@ export function ThemeProvider({ children, ...props }: ThemeProviderProps) { }, []) useEffect(() => { - if (themeSetting !== 'system') return + if (themeSetting !== 'system') { + setTheme(themeSetting) + return + } const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)') const handleChange = (e: MediaQueryListEvent) => { @@ -57,27 +54,27 @@ export function ThemeProvider({ children, ...props }: ThemeProviderProps) { const updateTheme = async () => { const root = window.document.documentElement root.classList.remove('light', 'dark') - root.classList.add(theme) - localStorage.setItem('theme', theme) + root.classList.add(theme === 'pure-black' ? 'dark' : theme) + + if (theme === 'pure-black') { + root.classList.add('pure-black') + } else { + root.classList.remove('pure-black') + } } updateTheme() }, [theme]) + const updateThemeSetting = async (themeSetting: TThemeSetting) => { + storage.setThemeSetting(themeSetting) + setThemeSetting(themeSetting) + } + return ( { - storage.setThemeSetting(themeSetting) - setThemeSetting(themeSetting) - if (themeSetting === 'system') { - setTheme(getSystemTheme()) - return - } - setTheme(themeSetting) - } + setThemeSetting: updateThemeSetting }} > {children} diff --git a/src/routes.tsx b/src/routes.tsx index 414c5eab..db4f745f 100644 --- a/src/routes.tsx +++ b/src/routes.tsx @@ -1,5 +1,6 @@ import { match } from 'path-to-regexp' import { isValidElement } from 'react' +import AppearanceSettingsPage from './pages/secondary/AppearanceSettingsPage' import FollowingListPage from './pages/secondary/FollowingListPage' import GeneralSettingsPage from './pages/secondary/GeneralSettingsPage' import MuteListPage from './pages/secondary/MuteListPage' @@ -34,6 +35,7 @@ const ROUTES = [ { path: '/settings/wallet', element: }, { path: '/settings/posts', element: }, { path: '/settings/general', element: }, + { path: '/settings/appearance', element: }, { path: '/settings/translation', element: }, { path: '/profile-editor', element: }, { path: '/mutes', element: }, diff --git a/src/types/index.d.ts b/src/types/index.d.ts index e833de42..82d52804 100644 --- a/src/types/index.d.ts +++ b/src/types/index.d.ts @@ -71,8 +71,8 @@ export type TConfig = { theme: TThemeSetting } -export type TThemeSetting = 'light' | 'dark' | 'system' -export type TTheme = 'light' | 'dark' +export type TThemeSetting = 'light' | 'dark' | 'system' | 'pure-black' +export type TTheme = 'light' | 'dark' | 'pure-black' export type TDraftEvent = Pick