feat(dns): remember override state per profile

Save the DNS override switch and session confirmation per profile so automatic protection only disables the affected profile. Preserve the legacy switch as the initial preference and show the profile scope beside the settings gear and in the DNS editor.

Refs #7993
This commit is contained in:
wonfen
2026-09-22 10:54:55 +08:00
parent 6a85d0344e
commit ff1ce92fac
25 changed files with 237 additions and 64 deletions
+12 -1
View File
@@ -1,5 +1,6 @@
import { RestartAltRounded } from '@mui/icons-material'
import {
Alert,
Box,
Button,
FormControl,
@@ -33,6 +34,7 @@ import {
Switch,
} from '@/components/base'
import { useClash } from '@/hooks/use-clash'
import { useProfiles } from '@/hooks/use-profiles'
import { useVerge } from '@/hooks/use-verge'
import { showNotice } from '@/services/notice-service'
import { useThemeMode } from '@/services/states'
@@ -186,6 +188,12 @@ export function DnsViewer({ ref }: { ref?: Ref<DialogRef> }) {
const { t } = useTranslation()
const { mutateClash } = useClash()
const { verge } = useVerge()
const { current: currentProfile } = useProfiles()
const dnsEnabled = currentProfile
? (verge?.profile_dns_settings?.[currentProfile.uid]?.enabled ??
verge?.enable_dns_settings ??
false)
: false
const themeMode = useThemeMode()
const [open, setOpen] = useState(false)
@@ -550,7 +558,7 @@ export function DnsViewer({ ref }: { ref?: Ref<DialogRef> }) {
return
}
if (verge?.enable_dns_settings) {
if (dnsEnabled) {
await invoke('apply_dns_config', { apply: true })
mutateClash()
}
@@ -638,6 +646,9 @@ export function DnsViewer({ ref }: { ref?: Ref<DialogRef> }) {
onCancel={() => setOpen(false)}
onOk={onSave}
>
<Alert severity="info" sx={{ mb: 2 }}>
{t('settings.modals.dns.dialog.profileScope')}
</Alert>
<Typography
variant="body2"
color="warning.main"
+37 -11
View File
@@ -9,6 +9,7 @@ import { BaseDialog, DialogRef, Switch, TooltipIcon } from '@/components/base'
import { useClash } from '@/hooks/use-clash'
import { useClashLog } from '@/hooks/use-clash-log'
import { useDisplayedMixedPort } from '@/hooks/use-displayed-mixed-port'
import { useProfiles } from '@/hooks/use-profiles'
import { useVerge } from '@/hooks/use-verge'
import { invoke_uwp_tool, setDnsOverride } from '@/services/cmds'
import { showNotice } from '@/services/notice-service'
@@ -36,6 +37,12 @@ const SettingClash = ({ onError }: Props) => {
const { clash, version, mutateClash, patchClash } = useClash()
const { verge, mutateVerge } = useVerge()
const { current: currentProfile } = useProfiles()
const dnsEnabled = currentProfile
? (verge?.profile_dns_settings?.[currentProfile.uid]?.enabled ??
verge?.enable_dns_settings ??
false)
: false
const displayedMixedPort = useDisplayedMixedPort()
const [, setClashLog] = useClashLog()
@@ -46,7 +53,10 @@ const SettingClash = ({ onError }: Props) => {
'unified-delay': unifiedDelay,
} = clash ?? {}
const [dnsConfirmation, setDnsConfirmation] = useState<string | null>(null)
const [dnsConfirmation, setDnsConfirmation] = useState<{
profileUid: string
source: string
} | null>(null)
const [dnsUpdating, setDnsUpdating] = useState(false)
const webRef = useRef<DialogRef>(null)
@@ -72,12 +82,17 @@ const SettingClash = ({ onError }: Props) => {
}
const handleDnsToggle = useLockFn(
async (enable: boolean, confirmation?: string) => {
async (
enable: boolean,
confirmation?: string,
profileUid = currentProfile?.uid,
) => {
if (!profileUid) return
setDnsUpdating(true)
try {
const outcome = await setDnsOverride(enable, confirmation)
const outcome = await setDnsOverride(profileUid, enable, confirmation)
if (outcome.status === 'confirmation_required') {
setDnsConfirmation(outcome.source)
setDnsConfirmation({ profileUid, source: outcome.source })
} else {
setDnsConfirmation(null)
}
@@ -114,7 +129,12 @@ const SettingClash = ({ onError }: Props) => {
onCancel={closeDnsConfirmation}
onClose={closeDnsConfirmation}
onOk={() => {
if (dnsConfirmation) void handleDnsToggle(true, dnsConfirmation)
if (dnsConfirmation)
void handleDnsToggle(
true,
dnsConfirmation.source,
dnsConfirmation.profileUid,
)
}}
>
<Typography variant="body2">
@@ -151,16 +171,22 @@ const SettingClash = ({ onError }: Props) => {
<SettingItem
label={t('settings.sections.clash.form.fields.dnsOverwrite')}
extra={
<TooltipIcon
icon={SettingsRounded}
onClick={() => dnsRef.current?.open()}
/>
<>
<TooltipIcon
icon={SettingsRounded}
onClick={() => dnsRef.current?.open()}
/>
<TooltipIcon
title={t('settings.modals.dns.dialog.profileScope')}
sx={{ opacity: '0.7' }}
/>
</>
}
>
<Switch
edge="end"
checked={verge?.enable_dns_settings ?? false}
disabled={dnsUpdating || dnsConfirmation !== null}
checked={dnsEnabled}
disabled={!currentProfile || dnsUpdating || dnsConfirmation !== null}
onChange={(_, checked) => handleDnsToggle(checked)}
/>
</SettingItem>