import { EditRounded } from '@mui/icons-material' import { Button, List, ListItem, ListItemText, styled, TextField, useTheme, } from '@mui/material' import { useLockFn } from 'ahooks' import { useEffect, useMemo, useRef, useState } from 'react' import { useTranslation } from 'react-i18next' import { BaseDialog, DialogRef } from '@/components/base' import { EditorViewer } from '@/components/profile/editor-viewer' import { useVerge } from '@/hooks/use-verge' import { defaultDarkTheme, defaultTheme } from '@/pages/_theme' import { showNotice } from '@/services/notice-service' import { useSettingDialog } from '../settings-search-context' export function ThemeViewer(props: { ref?: React.Ref }) { const { ref } = props const { t } = useTranslation() const [open, setOpen] = useState(false) const [editorOpen, setEditorOpen] = useState(false) const [cssEditorValue, setCssEditorValue] = useState('') const [cssEditorSavedValue, setCssEditorSavedValue] = useState('') const { verge, patchVerge } = useVerge() const { theme_setting } = verge ?? {} const [theme, setTheme] = useState(theme_setting || {}) // Latest theme ref to avoid stale closures when saving CSS const themeRef = useRef(theme) useEffect(() => { themeRef.current = theme }, [theme]) useSettingDialog(ref, 'theme-viewer', open, () => ({ open: () => { setOpen(true) setTheme({ ...theme_setting }) }, close: () => setOpen(false), })) const textProps = { size: 'small', autoComplete: 'off', sx: { width: 135 }, } as const const handleChange = (field: keyof typeof theme) => (e: any) => { setTheme((t) => ({ ...t, [field]: e.target.value })) } const onSave = useLockFn(async () => { try { await patchVerge({ theme_setting: theme }) setOpen(false) } catch (err) { showNotice.error(err) } }) const { palette } = useTheme() const dt = palette.mode === 'light' ? defaultTheme : defaultDarkTheme type ThemeKey = keyof typeof theme & keyof typeof defaultTheme const fieldDefinitions: Array<{ labelKey: string; key: ThemeKey }> = useMemo( () => [ { labelKey: 'settings.components.verge.theme.fields.primaryColor', key: 'primary_color', }, { labelKey: 'settings.components.verge.theme.fields.secondaryColor', key: 'secondary_color', }, { labelKey: 'settings.components.verge.theme.fields.primaryText', key: 'primary_text', }, { labelKey: 'settings.components.verge.theme.fields.secondaryText', key: 'secondary_text', }, { labelKey: 'settings.components.verge.theme.fields.infoColor', key: 'info_color', }, { labelKey: 'settings.components.verge.theme.fields.warningColor', key: 'warning_color', }, { labelKey: 'settings.components.verge.theme.fields.errorColor', key: 'error_color', }, { labelKey: 'settings.components.verge.theme.fields.successColor', key: 'success_color', }, ], [], ) const openCssEditor = () => { const nextCss = themeRef.current?.css_injection ?? '' setCssEditorValue(nextCss) setCssEditorSavedValue(nextCss) setEditorOpen(true) } const handleSaveCss = useLockFn(async () => { const prevTheme = themeRef.current || {} setTheme({ ...prevTheme, css_injection: cssEditorValue }) setCssEditorSavedValue(cssEditorValue) }) const renderItem = (labelKey: string, key: ThemeKey) => { const label = t(labelKey) return ( e.key === 'Enter' && onSave()} /> ) } return ( setOpen(false)} onCancel={() => setOpen(false)} onOk={onSave} > {fieldDefinitions.map((field) => renderItem(field.labelKey, field.key))} e.key === 'Enter' && onSave()} /> {editorOpen && ( { setEditorOpen(false) }} /> )} ) } const Item = styled(ListItem)(() => ({ padding: '5px 2px', })) const Round = styled('div')(() => ({ width: '24px', height: '24px', borderRadius: '18px', display: 'inline-block', marginRight: '8px', }))