import { ContentCopy } from '@mui/icons-material' import { Alert, Box, CircularProgress, IconButton, List, ListItem, ListItemText, Snackbar, TextField, Tooltip, } from '@mui/material' import { useLockFn } from 'ahooks' import { useImperativeHandle, useState, type Ref } from 'react' import { useTranslation } from 'react-i18next' import { BaseDialog, DialogRef, Switch } from '@/components/base' import { useClashInfo } from '@/hooks/use-clash' import { useVerge } from '@/hooks/use-verge' import { showNotice } from '@/services/notice-service' export function ControllerViewer({ ref }: { ref?: Ref }) { const { t } = useTranslation() const [open, setOpen] = useState(false) const [copySuccess, setCopySuccess] = useState(null) const [isSaving, setIsSaving] = useState(false) const { clashInfo, patchInfo } = useClashInfo() const { verge, patchVerge } = useVerge() const [controller, setController] = useState(clashInfo?.server || '') const [secret, setSecret] = useState(clashInfo?.secret || '') const [enableController, setEnableController] = useState( verge?.enable_external_controller ?? false, ) // 对话框打开时初始化配置 useImperativeHandle(ref, () => ({ open: async () => { setOpen(true) setController(clashInfo?.server || '') setSecret(clashInfo?.secret || '') setEnableController(verge?.enable_external_controller ?? false) }, close: () => setOpen(false), })) // 保存配置 const onSave = useLockFn(async () => { try { setIsSaving(true) // 先保存 enable_external_controller 设置 await patchVerge({ enable_external_controller: enableController }) // 如果启用了外部控制器,则保存控制器地址和密钥 if (enableController) { if (!controller.trim()) { showNotice.error( 'settings.sections.externalController.messages.addressRequired', ) return } if (!secret.trim()) { showNotice.error( 'settings.sections.externalController.messages.secretRequired', ) return } await patchInfo({ 'external-controller': controller, secret }) } else { // 如果禁用了外部控制器,则清空控制器地址 await patchInfo({ 'external-controller': '' }) } showNotice.success('shared.feedback.notifications.common.saveSuccess') setOpen(false) } catch (err) { showNotice.error( 'shared.feedback.notifications.common.saveFailed', err, 4000, ) } finally { setIsSaving(false) } }) // 复制到剪贴板 const handleCopyToClipboard = useLockFn( async (text: string, type: string) => { try { await navigator.clipboard.writeText(text) setCopySuccess(type) setTimeout(() => setCopySuccess(null)) } catch (err) { console.warn('[ControllerViewer] copy to clipboard failed:', err) showNotice.error( 'settings.sections.externalController.messages.copyFailed', ) } }, ) return ( {t('shared.statuses.saving')} ) : ( t('shared.actions.save') ) } cancelBtn={t('shared.actions.cancel')} onClose={() => setOpen(false)} onCancel={() => setOpen(false)} onOk={onSave} > setEnableController(e.target.checked)} disabled={isSaving} /> setController(e.target.value)} disabled={isSaving || !enableController} /> handleCopyToClipboard(controller, 'controller')} color="primary" disabled={isSaving || !enableController} > setSecret(e.target.value)} disabled={isSaving || !enableController} /> handleCopyToClipboard(secret, 'secret')} color="primary" disabled={isSaving || !enableController} > {copySuccess === 'controller' ? t( 'settings.sections.externalController.messages.controllerCopied', ) : t('settings.sections.externalController.messages.secretCopied')} ) }