feat: pure black

This commit is contained in:
codytseng
2025-10-18 17:54:28 +08:00
parent 057de9595b
commit b17846f264
27 changed files with 156 additions and 63 deletions

View File

@@ -28,6 +28,7 @@ import RelayPage from './pages/primary/RelayPage'
import SearchPage from './pages/primary/SearchPage' import SearchPage from './pages/primary/SearchPage'
import { NotificationProvider } from './providers/NotificationProvider' import { NotificationProvider } from './providers/NotificationProvider'
import { useScreenSize } from './providers/ScreenSizeProvider' import { useScreenSize } from './providers/ScreenSizeProvider'
import { useTheme } from './providers/ThemeProvider'
import { routes } from './routes' import { routes } from './routes'
import modalManager from './services/modal-manager.service' import modalManager from './services/modal-manager.service'
@@ -104,6 +105,7 @@ export function PageManager({ maxStackSize = 5 }: { maxStackSize?: number }) {
]) ])
const [secondaryStack, setSecondaryStack] = useState<TStackItem[]>([]) const [secondaryStack, setSecondaryStack] = useState<TStackItem[]>([])
const { isSmallScreen } = useScreenSize() const { isSmallScreen } = useScreenSize()
const { themeSetting } = useTheme()
const ignorePopStateRef = useRef(false) const ignorePopStateRef = useRef(false)
useEffect(() => { useEffect(() => {
@@ -356,8 +358,18 @@ export function PageManager({ maxStackSize = 5 }: { maxStackSize?: number }) {
}} }}
> >
<Sidebar /> <Sidebar />
<div className="grid grid-cols-2 gap-2 w-full pr-2 py-2"> <div
<div className="rounded-lg shadow-lg bg-background overflow-hidden"> className={cn(
'grid grid-cols-2 w-full',
themeSetting === 'pure-black' ? '' : 'gap-2 pr-2 py-2'
)}
>
<div
className={cn(
'bg-background overflow-hidden',
themeSetting === 'pure-black' ? 'border-l' : 'rounded-lg shadow-lg'
)}
>
{primaryPages.map(({ name, element, props }) => ( {primaryPages.map(({ name, element, props }) => (
<div <div
key={name} key={name}
@@ -370,7 +382,12 @@ export function PageManager({ maxStackSize = 5 }: { maxStackSize?: number }) {
</div> </div>
))} ))}
</div> </div>
<div className="rounded-lg shadow-lg bg-background overflow-hidden"> <div
className={cn(
'bg-background overflow-hidden',
themeSetting === 'pure-black' ? 'border-l' : 'rounded-lg shadow-lg'
)}
>
{secondaryStack.map((item, index) => ( {secondaryStack.map((item, index) => (
<div <div
key={item.index} key={item.index}

View File

@@ -80,7 +80,7 @@ function AccountManagerNav({
const wizard = new NstartModal({ const wizard = new NstartModal({
baseUrl: 'https://nstart.me', baseUrl: 'https://nstart.me',
an: 'Jumble', an: 'Jumble',
am: themeSetting, am: themeSetting === 'pure-black' ? 'dark' : themeSetting,
al: i18n.language.slice(0, 2), al: i18n.language.slice(0, 2),
onComplete: ({ nostrLogin }) => { onComplete: ({ nostrLogin }) => {
if (!nostrLogin) return if (!nostrLogin) return

View File

@@ -1,16 +1,14 @@
// import { useTheme } from "next-themes"
import { useTheme } from '@/providers/ThemeProvider' import { useTheme } from '@/providers/ThemeProvider'
import { Toaster as Sonner } from 'sonner' import { Toaster as Sonner } from 'sonner'
type ToasterProps = React.ComponentProps<typeof Sonner> type ToasterProps = React.ComponentProps<typeof Sonner>
const Toaster = ({ ...props }: ToasterProps) => { const Toaster = ({ ...props }: ToasterProps) => {
// const { theme = "system" } = useTheme()
const { themeSetting } = useTheme() const { themeSetting } = useTheme()
return ( return (
<Sonner <Sonner
theme={themeSetting} theme={themeSetting === 'pure-black' ? 'dark' : themeSetting}
className="toaster group" className="toaster group"
richColors richColors
mobileOffset={64} mobileOffset={64}

View File

@@ -455,6 +455,8 @@ export default {
'Unpinned!': 'تم إلغاء التثبيت!', 'Unpinned!': 'تم إلغاء التثبيت!',
'Failed to unpin: {{error}}': 'فشل في إلغاء التثبيت: {{error}}', 'Failed to unpin: {{error}}': 'فشل في إلغاء التثبيت: {{error}}',
'Unpin from profile': 'إلغاء التثبيت من الملف الشخصي', 'Unpin from profile': 'إلغاء التثبيت من الملف الشخصي',
'Pin to profile': 'تثبيت في الملف الشخصي' 'Pin to profile': 'تثبيت في الملف الشخصي',
Appearance: 'المظهر',
'Pure Black': 'أسود نقي'
} }
} }

View File

@@ -469,6 +469,8 @@ export default {
'Unpinned!': 'Anheften aufgehoben!', 'Unpinned!': 'Anheften aufgehoben!',
'Failed to unpin: {{error}}': 'Fehler beim Anheften aufheben: {{error}}', 'Failed to unpin: {{error}}': 'Fehler beim Anheften aufheben: {{error}}',
'Unpin from profile': 'Vom Profil lösen', 'Unpin from profile': 'Vom Profil lösen',
'Pin to profile': 'An Profil anheften' 'Pin to profile': 'An Profil anheften',
Appearance: 'Aussehen',
'Pure Black': 'Reines Schwarz'
} }
} }

View File

@@ -454,6 +454,8 @@ export default {
'Unpinned!': 'Unpinned!', 'Unpinned!': 'Unpinned!',
'Failed to unpin: {{error}}': 'Failed to unpin: {{error}}', 'Failed to unpin: {{error}}': 'Failed to unpin: {{error}}',
'Unpin from profile': 'Unpin from profile', 'Unpin from profile': 'Unpin from profile',
'Pin to profile': 'Pin to profile' 'Pin to profile': 'Pin to profile',
Appearance: 'Appearance',
'Pure Black': 'Pure Black'
} }
} }

View File

@@ -463,6 +463,8 @@ export default {
'Unpinned!': '¡Desfijado!', 'Unpinned!': '¡Desfijado!',
'Failed to unpin: {{error}}': 'Error al desfijar: {{error}}', 'Failed to unpin: {{error}}': 'Error al desfijar: {{error}}',
'Unpin from profile': 'Desfijar del perfil', 'Unpin from profile': 'Desfijar del perfil',
'Pin to profile': 'Fijar al perfil' 'Pin to profile': 'Fijar al perfil',
Appearance: 'Apariencia',
'Pure Black': 'Negro Puro'
} }
} }

View File

@@ -458,6 +458,8 @@ export default {
'Unpinned!': 'لغو پین شد!', 'Unpinned!': 'لغو پین شد!',
'Failed to unpin: {{error}}': 'لغو پین ناموفق بود: {{error}}', 'Failed to unpin: {{error}}': 'لغو پین ناموفق بود: {{error}}',
'Unpin from profile': 'لغو پین از پروفایل', 'Unpin from profile': 'لغو پین از پروفایل',
'Pin to profile': 'پین به پروفایل' 'Pin to profile': 'پین به پروفایل',
Appearance: 'ظاهر',
'Pure Black': 'سیاه خالص'
} }
} }

View File

@@ -468,6 +468,8 @@ export default {
'Unpinned!': 'Retrait de lépingle effectué !', 'Unpinned!': 'Retrait de lépingle effectué !',
'Failed to unpin: {{error}}': 'Échec du retrait de lépingle : {{error}}', 'Failed to unpin: {{error}}': 'Échec du retrait de lépingle : {{error}}',
'Unpin from profile': 'Retirer lépingle du profil', 'Unpin from profile': 'Retirer lépingle du profil',
'Pin to profile': 'Épingler au profil' 'Pin to profile': 'Épingler au profil',
Appearance: 'Apparence',
'Pure Black': 'Noir pur'
} }
} }

View File

@@ -460,6 +460,8 @@ export default {
'Unpinned!': 'पिन हटा दिया गया!', 'Unpinned!': 'पिन हटा दिया गया!',
'Failed to unpin: {{error}}': 'पिन हटाने में असफल: {{error}}', 'Failed to unpin: {{error}}': 'पिन हटाने में असफल: {{error}}',
'Unpin from profile': 'प्रोफ़ाइल से पिन हटाएं', 'Unpin from profile': 'प्रोफ़ाइल से पिन हटाएं',
'Pin to profile': 'प्रोफ़ाइल पर पिन करें' 'Pin to profile': 'प्रोफ़ाइल पर पिन करें',
Appearance: 'दिखावट',
'Pure Black': 'शुद्ध काला'
} }
} }

View File

@@ -463,6 +463,8 @@ export default {
'Unpinned!': 'Rimosso fissaggio!', 'Unpinned!': 'Rimosso fissaggio!',
'Failed to unpin: {{error}}': 'Impossibile rimuovere il fissaggio: {{error}}', 'Failed to unpin: {{error}}': 'Impossibile rimuovere il fissaggio: {{error}}',
'Unpin from profile': 'Rimuovi fissaggio dal profilo', 'Unpin from profile': 'Rimuovi fissaggio dal profilo',
'Pin to profile': 'Fissa al profilo' 'Pin to profile': 'Fissa al profilo',
Appearance: 'Aspetto',
'Pure Black': 'Nero Puro'
} }
} }

View File

@@ -459,6 +459,8 @@ export default {
'Unpinned!': '固定が解除されました!', 'Unpinned!': '固定が解除されました!',
'Failed to unpin: {{error}}': '固定解除に失敗しました: {{error}}', 'Failed to unpin: {{error}}': '固定解除に失敗しました: {{error}}',
'Unpin from profile': 'プロフィールから固定解除', 'Unpin from profile': 'プロフィールから固定解除',
'Pin to profile': 'プロフィールに固定' 'Pin to profile': 'プロフィールに固定',
Appearance: '外観',
'Pure Black': '純黒'
} }
} }

View File

@@ -459,6 +459,8 @@ export default {
'Unpinned!': '고정 해제됨!', 'Unpinned!': '고정 해제됨!',
'Failed to unpin: {{error}}': '고정 해제 실패: {{error}}', 'Failed to unpin: {{error}}': '고정 해제 실패: {{error}}',
'Unpin from profile': '프로필에서 고정 해제', 'Unpin from profile': '프로필에서 고정 해제',
'Pin to profile': '프로필에 고정' 'Pin to profile': '프로필에 고정',
Appearance: '외관',
'Pure Black': '순수한 검은색'
} }
} }

View File

@@ -463,6 +463,8 @@ export default {
'Unpinned!': 'Odpięte!', 'Unpinned!': 'Odpięte!',
'Failed to unpin: {{error}}': 'Nie udało się przypiąć: {{error}}', 'Failed to unpin: {{error}}': 'Nie udało się przypiąć: {{error}}',
'Unpin from profile': 'Odpiń z profilu', 'Unpin from profile': 'Odpiń z profilu',
'Pin to profile': 'Przypnij do profilu' 'Pin to profile': 'Przypnij do profilu',
Appearance: 'Wygląd',
'Pure Black': 'Czysta Czerń'
} }
} }

View File

@@ -460,6 +460,8 @@ export default {
'Unpinned!': 'Desafixado!', 'Unpinned!': 'Desafixado!',
'Failed to unpin: {{error}}': 'Falha ao desafixar: {{error}}', 'Failed to unpin: {{error}}': 'Falha ao desafixar: {{error}}',
'Unpin from profile': 'Desafixar do perfil', 'Unpin from profile': 'Desafixar do perfil',
'Pin to profile': 'Fixar no perfil' 'Pin to profile': 'Fixar no perfil',
Appearance: 'Aparência',
'Pure Black': 'Preto Puro'
} }
} }

View File

@@ -463,6 +463,8 @@ export default {
'Unpinned!': 'Desafixado!', 'Unpinned!': 'Desafixado!',
'Failed to unpin: {{error}}': 'Falha ao desafixar: {{error}}', 'Failed to unpin: {{error}}': 'Falha ao desafixar: {{error}}',
'Unpin from profile': 'Desafixar do perfil', 'Unpin from profile': 'Desafixar do perfil',
'Pin to profile': 'Fixar no perfil' 'Pin to profile': 'Fixar no perfil',
Appearance: 'Aparência',
'Pure Black': 'Preto Puro'
} }
} }

View File

@@ -465,6 +465,8 @@ export default {
'Unpinned!': 'Откреплено!', 'Unpinned!': 'Откреплено!',
'Failed to unpin: {{error}}': 'Не удалось открепить: {{error}}', 'Failed to unpin: {{error}}': 'Не удалось открепить: {{error}}',
'Unpin from profile': 'Открепить из профиля', 'Unpin from profile': 'Открепить из профиля',
'Pin to profile': 'Закрепить в профиле' 'Pin to profile': 'Закрепить в профиле',
Appearance: 'Внешний вид',
'Pure Black': 'Чистый Черный'
} }
} }

View File

@@ -453,6 +453,8 @@ export default {
'Unpinned!': 'ยกเลิกปักหมุดแล้ว!', 'Unpinned!': 'ยกเลิกปักหมุดแล้ว!',
'Failed to unpin: {{error}}': 'ไม่สามารถยกเลิกปักหมุดได้: {{error}}', 'Failed to unpin: {{error}}': 'ไม่สามารถยกเลิกปักหมุดได้: {{error}}',
'Unpin from profile': 'ยกเลิกปักหมุดจากโปรไฟล์', 'Unpin from profile': 'ยกเลิกปักหมุดจากโปรไฟล์',
'Pin to profile': 'ปักหมุดไปที่โปรไฟล์' 'Pin to profile': 'ปักหมุดไปที่โปรไฟล์',
Appearance: 'รูปลักษณ์',
'Pure Black': 'สีดำล้วน'
} }
} }

View File

@@ -451,6 +451,8 @@ export default {
'Unpinned!': '已取消置顶!', 'Unpinned!': '已取消置顶!',
'Failed to unpin: {{error}}': '取消置顶失败: {{error}}', 'Failed to unpin: {{error}}': '取消置顶失败: {{error}}',
'Unpin from profile': '从个人资料取消置顶', 'Unpin from profile': '从个人资料取消置顶',
'Pin to profile': '置顶到个人资料' 'Pin to profile': '置顶到个人资料',
Appearance: '外观',
'Pure Black': '纯黑'
} }
} }

View File

@@ -134,6 +134,12 @@
--chart-4: 280 65% 60%; --chart-4: 280 65% 60%;
--chart-5: 340 75% 55%; --chart-5: 340 75% 55%;
} }
.dark.pure-black {
--surface-background: 0 0% 0%;
--background: 0 0% 0%;
--card: 0 0% 0%;
--popover: 0 0% 0%;
}
.dark input[type='datetime-local']::-webkit-calendar-picker-indicator { .dark input[type='datetime-local']::-webkit-calendar-picker-indicator {
filter: invert(1) brightness(1.5); filter: invert(1) brightness(1.5);

View File

@@ -69,6 +69,7 @@ export const toRelaySettings = (tag?: 'mailbox' | 'favorite-relays') => {
export const toWallet = () => '/settings/wallet' export const toWallet = () => '/settings/wallet'
export const toPostSettings = () => '/settings/posts' export const toPostSettings = () => '/settings/posts'
export const toGeneralSettings = () => '/settings/general' export const toGeneralSettings = () => '/settings/general'
export const toAppearanceSettings = () => '/settings/appearance'
export const toTranslation = () => '/settings/translation' export const toTranslation = () => '/settings/translation'
export const toProfileEditor = () => '/profile-editor' export const toProfileEditor = () => '/profile-editor'
export const toRelay = (url: string) => `/relays/${encodeURIComponent(url)}` export const toRelay = (url: string) => `/relays/${encodeURIComponent(url)}`

View File

@@ -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: <Monitor className="w-5 h-5" /> },
{ key: 'light', label: 'Light', icon: <Sun className="w-5 h-5" /> },
{ key: 'dark', label: 'Dark', icon: <Moon className="w-5 h-5" /> },
{ key: 'pure-black', label: 'Pure Black', icon: <Moon className="w-5 h-5 fill-current" /> }
] as const
const AppearanceSettingsPage = forwardRef(({ index }: { index?: number }, ref) => {
const { t } = useTranslation()
const { themeSetting, setThemeSetting } = useTheme()
return (
<SecondaryPageLayout ref={ref} index={index} title={t('Appearance')}>
<div className="space-y-4 mt-3">
<div className="flex flex-col gap-2 px-4">
<Label className="text-base">{t('Theme')}</Label>
<div className="grid grid-cols-2 md:grid-cols-4 gap-4 w-full">
{THEMES.map(({ key, label, icon }) => (
<button
key={key}
onClick={() => {
setThemeSetting(key)
}}
className={cn(
'flex flex-col items-center gap-2 py-4 rounded-lg border-2 transition-all',
themeSetting === key ? 'border-primary' : 'border-border hover:border-primary/60'
)}
>
<div className="flex items-center justify-center w-8 h-8">{icon}</div>
<span className="text-xs font-medium">{t(label)}</span>
</button>
))}
</div>
</div>
</div>
</SecondaryPageLayout>
)
})
AppearanceSettingsPage.displayName = 'AppearanceSettingsPage'
export default AppearanceSettingsPage

View File

@@ -6,7 +6,6 @@ import { LocalizedLanguageNames, TLanguage } from '@/i18n'
import SecondaryPageLayout from '@/layouts/SecondaryPageLayout' import SecondaryPageLayout from '@/layouts/SecondaryPageLayout'
import { cn, isSupportCheckConnectionType } from '@/lib/utils' import { cn, isSupportCheckConnectionType } from '@/lib/utils'
import { useContentPolicy } from '@/providers/ContentPolicyProvider' import { useContentPolicy } from '@/providers/ContentPolicyProvider'
import { useTheme } from '@/providers/ThemeProvider'
import { useUserPreferences } from '@/providers/UserPreferencesProvider' import { useUserPreferences } from '@/providers/UserPreferencesProvider'
import { useUserTrust } from '@/providers/UserTrustProvider' import { useUserTrust } from '@/providers/UserTrustProvider'
import { TMediaAutoLoadPolicy } from '@/types' import { TMediaAutoLoadPolicy } from '@/types'
@@ -18,7 +17,6 @@ import { useTranslation } from 'react-i18next'
const GeneralSettingsPage = forwardRef(({ index }: { index?: number }, ref) => { const GeneralSettingsPage = forwardRef(({ index }: { index?: number }, ref) => {
const { t, i18n } = useTranslation() const { t, i18n } = useTranslation()
const [language, setLanguage] = useState<TLanguage>(i18n.language as TLanguage) const [language, setLanguage] = useState<TLanguage>(i18n.language as TLanguage)
const { themeSetting, setThemeSetting } = useTheme()
const { const {
autoplay, autoplay,
setAutoplay, setAutoplay,
@@ -57,21 +55,6 @@ const GeneralSettingsPage = forwardRef(({ index }: { index?: number }, ref) => {
</SelectContent> </SelectContent>
</Select> </Select>
</SettingItem> </SettingItem>
<SettingItem>
<Label htmlFor="theme" className="text-base font-normal">
{t('Theme')}
</Label>
<Select defaultValue="system" value={themeSetting} onValueChange={setThemeSetting}>
<SelectTrigger id="theme" className="w-48">
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value="system">{t('System')}</SelectItem>
<SelectItem value="light">{t('Light')}</SelectItem>
<SelectItem value="dark">{t('Dark')}</SelectItem>
</SelectContent>
</Select>
</SettingItem>
<SettingItem> <SettingItem>
<Label htmlFor="notification-list-style" className="text-base font-normal"> <Label htmlFor="notification-list-style" className="text-base font-normal">
<div>{t('Notification list style')}</div> <div>{t('Notification list style')}</div>

View File

@@ -2,6 +2,7 @@ import AboutInfoDialog from '@/components/AboutInfoDialog'
import Donation from '@/components/Donation' import Donation from '@/components/Donation'
import SecondaryPageLayout from '@/layouts/SecondaryPageLayout' import SecondaryPageLayout from '@/layouts/SecondaryPageLayout'
import { import {
toAppearanceSettings,
toGeneralSettings, toGeneralSettings,
toPostSettings, toPostSettings,
toRelaySettings, toRelaySettings,
@@ -18,6 +19,7 @@ import {
Info, Info,
KeyRound, KeyRound,
Languages, Languages,
Palette,
PencilLine, PencilLine,
Server, Server,
Settings2, Settings2,
@@ -42,6 +44,13 @@ const SettingsPage = forwardRef(({ index }: { index?: number }, ref) => {
</div> </div>
<ChevronRight /> <ChevronRight />
</SettingItem> </SettingItem>
<SettingItem className="clickable" onClick={() => push(toAppearanceSettings())}>
<div className="flex items-center gap-4">
<Palette />
<div>{t('Appearance')}</div>
</div>
<ChevronRight />
</SettingItem>
<SettingItem className="clickable" onClick={() => push(toRelaySettings())}> <SettingItem className="clickable" onClick={() => push(toRelaySettings())}>
<div className="flex items-center gap-4"> <div className="flex items-center gap-4">
<Server /> <Server />

View File

@@ -2,14 +2,8 @@ import storage from '@/services/local-storage.service'
import { TTheme, TThemeSetting } from '@/types' import { TTheme, TThemeSetting } from '@/types'
import { createContext, useContext, useEffect, useState } from 'react' import { createContext, useContext, useEffect, useState } from 'react'
type ThemeProviderProps = {
children: React.ReactNode
defaultTheme?: TTheme
}
type ThemeProviderState = { type ThemeProviderState = {
themeSetting: TThemeSetting themeSetting: TThemeSetting
theme: TTheme
setThemeSetting: (themeSetting: TThemeSetting) => Promise<void> setThemeSetting: (themeSetting: TThemeSetting) => Promise<void>
} }
@@ -19,7 +13,7 @@ function getSystemTheme() {
const ThemeProviderContext = createContext<ThemeProviderState | undefined>(undefined) const ThemeProviderContext = createContext<ThemeProviderState | undefined>(undefined)
export function ThemeProvider({ children, ...props }: ThemeProviderProps) { export function ThemeProvider({ children }: { children: React.ReactNode }) {
const [themeSetting, setThemeSetting] = useState<TThemeSetting>( const [themeSetting, setThemeSetting] = useState<TThemeSetting>(
(localStorage.getItem('themeSetting') as TThemeSetting | null) ?? 'system' (localStorage.getItem('themeSetting') as TThemeSetting | null) ?? 'system'
) )
@@ -39,7 +33,10 @@ export function ThemeProvider({ children, ...props }: ThemeProviderProps) {
}, []) }, [])
useEffect(() => { useEffect(() => {
if (themeSetting !== 'system') return if (themeSetting !== 'system') {
setTheme(themeSetting)
return
}
const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)') const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)')
const handleChange = (e: MediaQueryListEvent) => { const handleChange = (e: MediaQueryListEvent) => {
@@ -57,27 +54,27 @@ export function ThemeProvider({ children, ...props }: ThemeProviderProps) {
const updateTheme = async () => { const updateTheme = async () => {
const root = window.document.documentElement const root = window.document.documentElement
root.classList.remove('light', 'dark') root.classList.remove('light', 'dark')
root.classList.add(theme) root.classList.add(theme === 'pure-black' ? 'dark' : theme)
localStorage.setItem('theme', theme)
if (theme === 'pure-black') {
root.classList.add('pure-black')
} else {
root.classList.remove('pure-black')
}
} }
updateTheme() updateTheme()
}, [theme]) }, [theme])
const updateThemeSetting = async (themeSetting: TThemeSetting) => {
storage.setThemeSetting(themeSetting)
setThemeSetting(themeSetting)
}
return ( return (
<ThemeProviderContext.Provider <ThemeProviderContext.Provider
{...props}
value={{ value={{
themeSetting: themeSetting, themeSetting: themeSetting,
theme: theme, setThemeSetting: updateThemeSetting
setThemeSetting: async (themeSetting: TThemeSetting) => {
storage.setThemeSetting(themeSetting)
setThemeSetting(themeSetting)
if (themeSetting === 'system') {
setTheme(getSystemTheme())
return
}
setTheme(themeSetting)
}
}} }}
> >
{children} {children}

View File

@@ -1,5 +1,6 @@
import { match } from 'path-to-regexp' import { match } from 'path-to-regexp'
import { isValidElement } from 'react' import { isValidElement } from 'react'
import AppearanceSettingsPage from './pages/secondary/AppearanceSettingsPage'
import FollowingListPage from './pages/secondary/FollowingListPage' import FollowingListPage from './pages/secondary/FollowingListPage'
import GeneralSettingsPage from './pages/secondary/GeneralSettingsPage' import GeneralSettingsPage from './pages/secondary/GeneralSettingsPage'
import MuteListPage from './pages/secondary/MuteListPage' import MuteListPage from './pages/secondary/MuteListPage'
@@ -34,6 +35,7 @@ const ROUTES = [
{ path: '/settings/wallet', element: <WalletPage /> }, { path: '/settings/wallet', element: <WalletPage /> },
{ path: '/settings/posts', element: <PostSettingsPage /> }, { path: '/settings/posts', element: <PostSettingsPage /> },
{ path: '/settings/general', element: <GeneralSettingsPage /> }, { path: '/settings/general', element: <GeneralSettingsPage /> },
{ path: '/settings/appearance', element: <AppearanceSettingsPage /> },
{ path: '/settings/translation', element: <TranslationPage /> }, { path: '/settings/translation', element: <TranslationPage /> },
{ path: '/profile-editor', element: <ProfileEditorPage /> }, { path: '/profile-editor', element: <ProfileEditorPage /> },
{ path: '/mutes', element: <MuteListPage /> }, { path: '/mutes', element: <MuteListPage /> },

View File

@@ -71,8 +71,8 @@ export type TConfig = {
theme: TThemeSetting theme: TThemeSetting
} }
export type TThemeSetting = 'light' | 'dark' | 'system' export type TThemeSetting = 'light' | 'dark' | 'system' | 'pure-black'
export type TTheme = 'light' | 'dark' export type TTheme = 'light' | 'dark' | 'pure-black'
export type TDraftEvent = Pick<Event, 'content' | 'created_at' | 'kind' | 'tags'> export type TDraftEvent = Pick<Event, 'content' | 'created_at' | 'kind' | 'tags'>