mirror of
https://github.com/clash-verge-rev/clash-verge-rev.git
synced 2026-11-14 14:52:08 +08:00
refactor(settings): route settings-domain commands through the mutate funnel
Ports port saving, webdav/local backup operations, core change/restart/ upgrade, service install/uninstall/repair flows, the system-proxy toggle queue, the DNS override toggle, and app lifecycle commands onto the single funnel. Bespoke flows keep their own error handling via errorNotice:false; simple flows gain the funnel's default error toast and revalidation.
This commit is contained in:
@@ -12,6 +12,7 @@ import {
|
|||||||
restartCore,
|
restartCore,
|
||||||
type ServiceInstallOutcome,
|
type ServiceInstallOutcome,
|
||||||
} from '@/services/cmds'
|
} from '@/services/cmds'
|
||||||
|
import { mutate } from '@/services/mutate'
|
||||||
import { showNotice } from '@/services/notice-service'
|
import { showNotice } from '@/services/notice-service'
|
||||||
import { useAppDispatch, useRunState } from '@/store/app-store-context'
|
import { useAppDispatch, useRunState } from '@/store/app-store-context'
|
||||||
|
|
||||||
@@ -74,11 +75,23 @@ export const ServiceMigrationDialog = () => {
|
|||||||
let outcome: ServiceInstallOutcome | undefined
|
let outcome: ServiceInstallOutcome | undefined
|
||||||
try {
|
try {
|
||||||
if (remedy === 'install') {
|
if (remedy === 'install') {
|
||||||
outcome = await installService()
|
const result = await mutate(() => installService(), {
|
||||||
|
id: 'install-service',
|
||||||
|
errorNotice: false,
|
||||||
|
})
|
||||||
|
if (result.ok) outcome = result.value
|
||||||
} else if (remedy === 'repair') {
|
} else if (remedy === 'repair') {
|
||||||
outcome = await repairService()
|
const result = await mutate(() => repairService(), {
|
||||||
|
id: 'repair-service',
|
||||||
|
errorNotice: false,
|
||||||
|
})
|
||||||
|
if (result.ok) outcome = result.value
|
||||||
} else {
|
} else {
|
||||||
outcome = await reinstallService()
|
const result = await mutate(() => reinstallService(), {
|
||||||
|
id: 'reinstall-service',
|
||||||
|
errorNotice: false,
|
||||||
|
})
|
||||||
|
if (result.ok) outcome = result.value
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
showNotice.error(
|
showNotice.error(
|
||||||
@@ -114,8 +127,11 @@ export const ServiceMigrationDialog = () => {
|
|||||||
|
|
||||||
let restartSucceeded = false
|
let restartSucceeded = false
|
||||||
try {
|
try {
|
||||||
await restartCore()
|
const result = await mutate(() => restartCore(), {
|
||||||
restartSucceeded = true
|
id: 'restart-core',
|
||||||
|
errorNotice: false,
|
||||||
|
})
|
||||||
|
restartSucceeded = result.ok
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
showNotice.error(
|
showNotice.error(
|
||||||
'layout.components.serviceMigration.errors.restartFailed',
|
'layout.components.serviceMigration.errors.restartFailed',
|
||||||
@@ -145,7 +161,10 @@ export const ServiceMigrationDialog = () => {
|
|||||||
setWorkflowIncomplete(true)
|
setWorkflowIncomplete(true)
|
||||||
let startupError: unknown
|
let startupError: unknown
|
||||||
try {
|
try {
|
||||||
await continueWithSidecar()
|
await mutate(() => continueWithSidecar(), {
|
||||||
|
id: 'continue-with-sidecar',
|
||||||
|
errorNotice: false,
|
||||||
|
})
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
startupError = error
|
startupError = error
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import {
|
|||||||
type PendingFailure,
|
type PendingFailure,
|
||||||
type ServiceInstallOutcome,
|
type ServiceInstallOutcome,
|
||||||
} from '@/services/cmds'
|
} from '@/services/cmds'
|
||||||
|
import { mutate } from '@/services/mutate'
|
||||||
import { showNotice } from '@/services/notice-service'
|
import { showNotice } from '@/services/notice-service'
|
||||||
import {
|
import {
|
||||||
clearServiceRequest,
|
clearServiceRequest,
|
||||||
@@ -103,10 +104,18 @@ export const SysproxyPrivilegeDialog = () => {
|
|||||||
let outcome: ServiceInstallOutcome | undefined
|
let outcome: ServiceInstallOutcome | undefined
|
||||||
if (remedy === 'reinstallAndRestart') {
|
if (remedy === 'reinstallAndRestart') {
|
||||||
setStep('installing')
|
setStep('installing')
|
||||||
outcome = await reinstallService()
|
const result = await mutate(() => reinstallService(), {
|
||||||
|
id: 'reinstall-service',
|
||||||
|
errorNotice: false,
|
||||||
|
})
|
||||||
|
if (result.ok) outcome = result.value
|
||||||
} else if (remedy === 'installAndRestart') {
|
} else if (remedy === 'installAndRestart') {
|
||||||
setStep('installing')
|
setStep('installing')
|
||||||
outcome = await installService()
|
const result = await mutate(() => installService(), {
|
||||||
|
id: 'install-service',
|
||||||
|
errorNotice: false,
|
||||||
|
})
|
||||||
|
if (result.ok) outcome = result.value
|
||||||
}
|
}
|
||||||
if (outcome?.status === 'sidecar') {
|
if (outcome?.status === 'sidecar') {
|
||||||
showNotice.warning(
|
showNotice.warning(
|
||||||
@@ -118,7 +127,10 @@ export const SysproxyPrivilegeDialog = () => {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
setStep('restarting')
|
setStep('restarting')
|
||||||
await restartCore()
|
await mutate(() => restartCore(), {
|
||||||
|
id: 'restart-core',
|
||||||
|
errorNotice: false,
|
||||||
|
})
|
||||||
|
|
||||||
const runState = await getRuntimeState()
|
const runState = await getRuntimeState()
|
||||||
const usingAdminFallback =
|
const usingAdminFallback =
|
||||||
@@ -131,7 +143,10 @@ export const SysproxyPrivilegeDialog = () => {
|
|||||||
if (runState.mode === 'Service' || usingAdminFallback) {
|
if (runState.mode === 'Service' || usingAdminFallback) {
|
||||||
if (restoring !== undefined) {
|
if (restoring !== undefined) {
|
||||||
setStep('applying')
|
setStep('applying')
|
||||||
await patchVergeConfig(restoring)
|
await mutate(() => patchVergeConfig(restoring), {
|
||||||
|
id: 'sysproxy-privilege-restore',
|
||||||
|
errorNotice: false,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
if (!usingAdminFallback) {
|
if (!usingAdminFallback) {
|
||||||
showNotice.success(
|
showNotice.success(
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import { useNavigate } from 'react-router'
|
|||||||
import { useSystemState } from '@/hooks/use-system-state'
|
import { useSystemState } from '@/hooks/use-system-state'
|
||||||
import { useAppRefreshers } from '@/providers/app-data-context'
|
import { useAppRefreshers } from '@/providers/app-data-context'
|
||||||
import { openLogsDir, restartCore } from '@/services/cmds'
|
import { openLogsDir, restartCore } from '@/services/cmds'
|
||||||
import { showNotice } from '@/services/notice-service'
|
import { mutate } from '@/services/mutate'
|
||||||
|
|
||||||
import type { ProxyEmptyStateReason } from './proxy-empty-state-model'
|
import type { ProxyEmptyStateReason } from './proxy-empty-state-model'
|
||||||
|
|
||||||
@@ -25,10 +25,10 @@ export const ProxyEmptyState = ({ reason }: Props) => {
|
|||||||
const handleRestart = useLockFn(async () => {
|
const handleRestart = useLockFn(async () => {
|
||||||
setIsRestarting(true)
|
setIsRestarting(true)
|
||||||
try {
|
try {
|
||||||
await restartCore()
|
await mutate(() => restartCore(), { id: 'restart-core' })
|
||||||
await Promise.all([refreshProxy(), mutateSystemState()])
|
await Promise.all([refreshProxy(), mutateSystemState()])
|
||||||
} catch (error) {
|
} catch {
|
||||||
showNotice.error(error)
|
// the funnel already notified
|
||||||
} finally {
|
} finally {
|
||||||
setIsRestarting(false)
|
setIsRestarting(false)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import { useTranslation } from 'react-i18next'
|
|||||||
|
|
||||||
import { useVerge } from '@/hooks/use-verge'
|
import { useVerge } from '@/hooks/use-verge'
|
||||||
import { saveWebdavConfig, createWebdavBackup } from '@/services/cmds'
|
import { saveWebdavConfig, createWebdavBackup } from '@/services/cmds'
|
||||||
|
import { mutate } from '@/services/mutate'
|
||||||
import { errorDetail, showNotice } from '@/services/notice-service'
|
import { errorDetail, showNotice } from '@/services/notice-service'
|
||||||
import {
|
import {
|
||||||
buildWebdavSignature,
|
buildWebdavSignature,
|
||||||
@@ -119,7 +120,13 @@ export const BackupConfigViewer = memo(
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
setLoading(true)
|
setLoading(true)
|
||||||
await saveWebdavConfig(trimmedUrl, trimmedUsername, data.password)
|
await mutate(
|
||||||
|
() => saveWebdavConfig(trimmedUrl, trimmedUsername, data.password),
|
||||||
|
{
|
||||||
|
id: 'save-webdav-config',
|
||||||
|
errorNotice: false,
|
||||||
|
},
|
||||||
|
)
|
||||||
await mutateVerge(
|
await mutateVerge(
|
||||||
(current) =>
|
(current) =>
|
||||||
current
|
current
|
||||||
@@ -156,7 +163,10 @@ export const BackupConfigViewer = memo(
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
setLoading(true)
|
setLoading(true)
|
||||||
await createWebdavBackup().then(async () => {
|
await mutate(() => createWebdavBackup(), {
|
||||||
|
id: 'create-webdav-backup',
|
||||||
|
errorNotice: false,
|
||||||
|
}).then(async () => {
|
||||||
showNotice.success('settings.modals.backup.messages.backupCreated')
|
showNotice.success('settings.modals.backup.messages.backupCreated')
|
||||||
await onBackupSuccess()
|
await onBackupSuccess()
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ import {
|
|||||||
restoreLocalBackup,
|
restoreLocalBackup,
|
||||||
restoreWebDavBackup,
|
restoreWebDavBackup,
|
||||||
} from '@/services/cmds'
|
} from '@/services/cmds'
|
||||||
|
import { mutate } from '@/services/mutate'
|
||||||
import { showNotice } from '@/services/notice-service'
|
import { showNotice } from '@/services/notice-service'
|
||||||
import {
|
import {
|
||||||
buildWebdavSignature,
|
buildWebdavSignature,
|
||||||
@@ -228,17 +229,29 @@ export const BackupHistoryViewer = ({
|
|||||||
try {
|
try {
|
||||||
if (action === 'delete') {
|
if (action === 'delete') {
|
||||||
if (actionIsLocal) {
|
if (actionIsLocal) {
|
||||||
await deleteLocalBackup(filename)
|
await mutate(() => deleteLocalBackup(filename), {
|
||||||
|
id: 'delete-local-backup',
|
||||||
|
errorNotice: false,
|
||||||
|
})
|
||||||
} else {
|
} else {
|
||||||
await deleteWebdavBackup(filename)
|
await mutate(() => deleteWebdavBackup(filename), {
|
||||||
|
id: 'delete-webdav-backup',
|
||||||
|
errorNotice: false,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
setPendingConfirmation(null)
|
setPendingConfirmation(null)
|
||||||
await fetchRows()
|
await fetchRows()
|
||||||
} else {
|
} else {
|
||||||
if (actionIsLocal) {
|
if (actionIsLocal) {
|
||||||
await restoreLocalBackup(filename)
|
await mutate(() => restoreLocalBackup(filename), {
|
||||||
|
id: 'restore-local-backup',
|
||||||
|
errorNotice: false,
|
||||||
|
})
|
||||||
} else {
|
} else {
|
||||||
await restoreWebDavBackup(filename)
|
await mutate(() => restoreWebDavBackup(filename), {
|
||||||
|
id: 'restore-webdav-backup',
|
||||||
|
errorNotice: false,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
setPendingConfirmation(null)
|
setPendingConfirmation(null)
|
||||||
showNotice.success('settings.modals.backup.messages.restoreSuccess')
|
showNotice.success('settings.modals.backup.messages.restoreSuccess')
|
||||||
@@ -265,8 +278,11 @@ export const BackupHistoryViewer = ({
|
|||||||
const savePath = await save({ defaultPath: filename })
|
const savePath = await save({ defaultPath: filename })
|
||||||
if (!savePath || Array.isArray(savePath)) return
|
if (!savePath || Array.isArray(savePath)) return
|
||||||
try {
|
try {
|
||||||
await exportLocalBackup(filename, savePath)
|
await mutate(() => exportLocalBackup(filename, savePath), {
|
||||||
showNotice.success('settings.modals.backup.messages.localBackupExported')
|
id: 'export-local-backup',
|
||||||
|
errorNotice: false,
|
||||||
|
successNotice: 'settings.modals.backup.messages.localBackupExported',
|
||||||
|
})
|
||||||
} catch (ignoreError: unknown) {
|
} catch (ignoreError: unknown) {
|
||||||
showNotice.error(
|
showNotice.error(
|
||||||
'settings.modals.backup.messages.localBackupExportFailed',
|
'settings.modals.backup.messages.localBackupExportFailed',
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import {
|
|||||||
createWebdavBackup,
|
createWebdavBackup,
|
||||||
importLocalBackup,
|
importLocalBackup,
|
||||||
} from '@/services/cmds'
|
} from '@/services/cmds'
|
||||||
|
import { mutate } from '@/services/mutate'
|
||||||
import { errorDetail, showNotice } from '@/services/notice-service'
|
import { errorDetail, showNotice } from '@/services/notice-service'
|
||||||
import { buildWebdavSignature, setWebdavStatus } from '@/services/webdav-status'
|
import { buildWebdavSignature, setWebdavStatus } from '@/services/webdav-status'
|
||||||
|
|
||||||
@@ -55,12 +56,18 @@ export function BackupViewer({ ref }: { ref?: Ref<DialogRef> }) {
|
|||||||
try {
|
try {
|
||||||
setBusyAction(target)
|
setBusyAction(target)
|
||||||
if (target === 'local') {
|
if (target === 'local') {
|
||||||
await createLocalBackup()
|
await mutate(() => createLocalBackup(), {
|
||||||
showNotice.success('settings.modals.backup.messages.localBackupCreated')
|
id: 'create-local-backup',
|
||||||
|
errorNotice: false,
|
||||||
|
successNotice: 'settings.modals.backup.messages.localBackupCreated',
|
||||||
|
})
|
||||||
} else {
|
} else {
|
||||||
await createWebdavBackup()
|
await mutate(() => createWebdavBackup(), {
|
||||||
showNotice.success('settings.modals.backup.messages.backupCreated')
|
id: 'create-webdav-backup',
|
||||||
setWebdavStatus(webdavSignature, 'ready')
|
errorNotice: false,
|
||||||
|
successNotice: 'settings.modals.backup.messages.backupCreated',
|
||||||
|
onFulfilled: () => setWebdavStatus(webdavSignature, 'ready'),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error)
|
console.error(error)
|
||||||
@@ -86,8 +93,11 @@ export function BackupViewer({ ref }: { ref?: Ref<DialogRef> }) {
|
|||||||
if (!selected || Array.isArray(selected)) return
|
if (!selected || Array.isArray(selected)) return
|
||||||
try {
|
try {
|
||||||
setLocalImporting(true)
|
setLocalImporting(true)
|
||||||
await importLocalBackup(selected)
|
await mutate(() => importLocalBackup(selected), {
|
||||||
showNotice.success('settings.modals.backup.messages.localBackupImported')
|
id: 'import-local-backup',
|
||||||
|
errorNotice: false,
|
||||||
|
successNotice: 'settings.modals.backup.messages.localBackupImported',
|
||||||
|
})
|
||||||
openHistory('local')
|
openHistory('local')
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error)
|
console.error(error)
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ import { BaseDialog, DialogRef } from '@/components/base'
|
|||||||
import { useClash, useClashInfo } from '@/hooks/use-clash'
|
import { useClash, useClashInfo } from '@/hooks/use-clash'
|
||||||
import { useVerge } from '@/hooks/use-verge'
|
import { useVerge } from '@/hooks/use-verge'
|
||||||
import { changeClashCore, restartCore, upgradeClashCore } from '@/services/cmds'
|
import { changeClashCore, restartCore, upgradeClashCore } from '@/services/cmds'
|
||||||
|
import { mutate } from '@/services/mutate'
|
||||||
import { showNotice } from '@/services/notice-service'
|
import { showNotice } from '@/services/notice-service'
|
||||||
|
|
||||||
const VALID_CORE = [
|
const VALID_CORE = [
|
||||||
@@ -60,8 +61,16 @@ export function ClashCoreViewer({ ref }: { ref?: Ref<DialogRef> }) {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
setChangingCore(core)
|
setChangingCore(core)
|
||||||
closeAllConnections()
|
void mutate(() => closeAllConnections(), {
|
||||||
const errorMsg = await changeClashCore(core)
|
id: 'close-all-connections',
|
||||||
|
errorNotice: false,
|
||||||
|
})
|
||||||
|
const result = await mutate(() => changeClashCore(core), {
|
||||||
|
id: 'change-clash-core',
|
||||||
|
errorNotice: false,
|
||||||
|
})
|
||||||
|
if (!result.ok) return
|
||||||
|
const errorMsg = result.value
|
||||||
|
|
||||||
if (errorMsg) {
|
if (errorMsg) {
|
||||||
showNotice.error(errorMsg)
|
showNotice.error(errorMsg)
|
||||||
@@ -83,28 +92,33 @@ export function ClashCoreViewer({ ref }: { ref?: Ref<DialogRef> }) {
|
|||||||
const onRestart = useLockFn(async () => {
|
const onRestart = useLockFn(async () => {
|
||||||
try {
|
try {
|
||||||
setRestarting(true)
|
setRestarting(true)
|
||||||
await restartCore()
|
await mutate(() => restartCore(), {
|
||||||
showNotice.success(
|
id: 'restart-core',
|
||||||
t('settings.feedback.notifications.clash.restartSuccess'),
|
successNotice: t(
|
||||||
)
|
'settings.feedback.notifications.clash.restartSuccess',
|
||||||
|
),
|
||||||
|
})
|
||||||
setRestarting(false)
|
setRestarting(false)
|
||||||
} catch (err) {
|
} catch {
|
||||||
setRestarting(false)
|
setRestarting(false)
|
||||||
showNotice.error(err)
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const onUpgrade = useLockFn(async () => {
|
const onUpgrade = useLockFn(async () => {
|
||||||
try {
|
try {
|
||||||
setUpgrading(true)
|
setUpgrading(true)
|
||||||
const report = await upgradeClashCore()
|
await mutate(() => upgradeClashCore(), {
|
||||||
showNotice.success(
|
id: 'upgrade-clash-core',
|
||||||
report.upgraded
|
onFulfilled: (report) => {
|
||||||
? t('settings.feedback.notifications.clash.versionUpdated')
|
showNotice.success(
|
||||||
: t('settings.feedback.notifications.clash.alreadyLatestVersion'),
|
report.upgraded
|
||||||
)
|
? t('settings.feedback.notifications.clash.versionUpdated')
|
||||||
} catch (err) {
|
: t('settings.feedback.notifications.clash.alreadyLatestVersion'),
|
||||||
showNotice.error(err)
|
)
|
||||||
|
},
|
||||||
|
})
|
||||||
|
} catch {
|
||||||
|
// the funnel already notified
|
||||||
} finally {
|
} finally {
|
||||||
setUpgrading(false)
|
setUpgrading(false)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,7 +15,8 @@ import { useTranslation } from 'react-i18next'
|
|||||||
import { BaseDialog, Switch } from '@/components/base'
|
import { BaseDialog, Switch } from '@/components/base'
|
||||||
import { useDisplayedMixedPort } from '@/hooks/use-displayed-mixed-port'
|
import { useDisplayedMixedPort } from '@/hooks/use-displayed-mixed-port'
|
||||||
import { useVerge } from '@/hooks/use-verge'
|
import { useVerge } from '@/hooks/use-verge'
|
||||||
import { saveProxyPorts } from '@/services/cmds'
|
import { saveProxyPorts, type ProxyPortSettings } from '@/services/cmds'
|
||||||
|
import { mutate } from '@/services/mutate'
|
||||||
import { showNotice } from '@/services/notice-service'
|
import { showNotice } from '@/services/notice-service'
|
||||||
import getSystem from '@/utils/get-system'
|
import getSystem from '@/utils/get-system'
|
||||||
|
|
||||||
@@ -57,22 +58,31 @@ export const ClashPortViewer = forwardRef<ClashPortViewerRef>((_, ref) => {
|
|||||||
)
|
)
|
||||||
|
|
||||||
// 添加保存请求,防止GUI卡死
|
// 添加保存请求,防止GUI卡死
|
||||||
const { loading, runAsync: saveSettings } = useRequest(saveProxyPorts, {
|
const { loading, runAsync: saveSettings } = useRequest(
|
||||||
manual: true,
|
async (settings: ProxyPortSettings) =>
|
||||||
onSuccess: (outcome) => {
|
mutate(() => saveProxyPorts(settings), {
|
||||||
if (outcome.status === 'conflict') {
|
id: 'save-proxy-ports',
|
||||||
showNotice.error('settings.modals.clashPort.messages.portInUse', {
|
errorNotice: false,
|
||||||
port: outcome.port,
|
}),
|
||||||
})
|
{
|
||||||
return
|
manual: true,
|
||||||
}
|
onSuccess: (result) => {
|
||||||
setOpen(false)
|
if (!result.ok) return
|
||||||
showNotice.success('settings.modals.clashPort.messages.saved')
|
const outcome = result.value
|
||||||
|
if (outcome.status === 'conflict') {
|
||||||
|
showNotice.error('settings.modals.clashPort.messages.portInUse', {
|
||||||
|
port: outcome.port,
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
setOpen(false)
|
||||||
|
showNotice.success('settings.modals.clashPort.messages.saved')
|
||||||
|
},
|
||||||
|
onError: (error) => {
|
||||||
|
showNotice.error('settings.modals.clashPort.messages.saveFailed', error)
|
||||||
|
},
|
||||||
},
|
},
|
||||||
onError: (error) => {
|
)
|
||||||
showNotice.error('settings.modals.clashPort.messages.saveFailed', error)
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
useImperativeHandle(ref, () => ({
|
useImperativeHandle(ref, () => ({
|
||||||
open: () => {
|
open: () => {
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import { useTranslation } from 'react-i18next'
|
|||||||
import { BaseDialog, Switch } from '@/components/base'
|
import { BaseDialog, Switch } from '@/components/base'
|
||||||
import { useClash } from '@/hooks/use-clash'
|
import { useClash } from '@/hooks/use-clash'
|
||||||
import { restartCore } from '@/services/cmds'
|
import { restartCore } from '@/services/cmds'
|
||||||
|
import { mutate } from '@/services/mutate'
|
||||||
import { showNotice } from '@/services/notice-service'
|
import { showNotice } from '@/services/notice-service'
|
||||||
|
|
||||||
// Development origins must never be persisted into production configuration.
|
// Development origins must never be persisted into production configuration.
|
||||||
@@ -135,7 +136,10 @@ export const HeaderConfiguration = forwardRef<ClashHeaderConfigingRef>(
|
|||||||
),
|
),
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
await restartCore()
|
await mutate(() => restartCore(), {
|
||||||
|
id: 'restart-core',
|
||||||
|
errorNotice: false,
|
||||||
|
})
|
||||||
await mutateClash()
|
await mutateClash()
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import type { Options as ReactMarkdownOptions } from 'react-markdown'
|
|||||||
import { BaseDialog, DialogRef } from '@/components/base'
|
import { BaseDialog, DialogRef } from '@/components/base'
|
||||||
import { useUpdate } from '@/hooks/use-update'
|
import { useUpdate } from '@/hooks/use-update'
|
||||||
import { restartApp } from '@/services/cmds'
|
import { restartApp } from '@/services/cmds'
|
||||||
|
import { mutate } from '@/services/mutate'
|
||||||
import { showNotice } from '@/services/notice-service'
|
import { showNotice } from '@/services/notice-service'
|
||||||
import { useSetUpdateState, useUpdateState } from '@/services/states'
|
import { useSetUpdateState, useUpdateState } from '@/services/states'
|
||||||
import { openExternalUrl } from '@/utils/open-external-url'
|
import { openExternalUrl } from '@/utils/open-external-url'
|
||||||
@@ -270,8 +271,14 @@ export function UpdateViewer({ ref }: { ref?: Ref<DialogRef> }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await updateInfo.downloadAndInstall(onDownloadEvent)
|
await mutate(() => updateInfo.downloadAndInstall(onDownloadEvent), {
|
||||||
await restartApp()
|
id: 'install-app-update',
|
||||||
|
errorNotice: false,
|
||||||
|
})
|
||||||
|
await mutate(() => restartApp(), {
|
||||||
|
id: 'restart-app',
|
||||||
|
errorNotice: false,
|
||||||
|
})
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
showNotice.error(err)
|
showNotice.error(err)
|
||||||
} finally {
|
} finally {
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import { useDisplayedMixedPort } from '@/hooks/use-displayed-mixed-port'
|
|||||||
import { useProfiles } from '@/hooks/use-profiles'
|
import { useProfiles } from '@/hooks/use-profiles'
|
||||||
import { useVerge } from '@/hooks/use-verge'
|
import { useVerge } from '@/hooks/use-verge'
|
||||||
import { invoke_uwp_tool, setDnsOverride } from '@/services/cmds'
|
import { invoke_uwp_tool, setDnsOverride } from '@/services/cmds'
|
||||||
|
import { mutate } from '@/services/mutate'
|
||||||
import { showNotice } from '@/services/notice-service'
|
import { showNotice } from '@/services/notice-service'
|
||||||
import getSystem from '@/utils/get-system'
|
import getSystem from '@/utils/get-system'
|
||||||
|
|
||||||
@@ -74,7 +75,10 @@ const SettingClash = ({ onError }: Props) => {
|
|||||||
}
|
}
|
||||||
const onUpdateGeo = async () => {
|
const onUpdateGeo = async () => {
|
||||||
try {
|
try {
|
||||||
await updateGeo()
|
await mutate(() => updateGeo(), {
|
||||||
|
id: 'update-geo',
|
||||||
|
errorNotice: false,
|
||||||
|
})
|
||||||
showNotice.success('settings.feedback.notifications.clash.geoDataUpdated')
|
showNotice.success('settings.feedback.notifications.clash.geoDataUpdated')
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
showNotice.error(err)
|
showNotice.error(err)
|
||||||
@@ -90,7 +94,15 @@ const SettingClash = ({ onError }: Props) => {
|
|||||||
if (!profileUid) return
|
if (!profileUid) return
|
||||||
setDnsUpdating(true)
|
setDnsUpdating(true)
|
||||||
try {
|
try {
|
||||||
const outcome = await setDnsOverride(profileUid, enable, confirmation)
|
const result = await mutate(
|
||||||
|
() => setDnsOverride(profileUid, enable, confirmation),
|
||||||
|
{
|
||||||
|
id: 'set-dns-override',
|
||||||
|
errorNotice: false,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
if (!result.ok) return
|
||||||
|
const outcome = result.value
|
||||||
if (outcome.status === 'confirmation_required') {
|
if (outcome.status === 'confirmation_required') {
|
||||||
setDnsConfirmation({ profileUid, source: outcome.source })
|
setDnsConfirmation({ profileUid, source: outcome.source })
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import {
|
|||||||
openDevTools,
|
openDevTools,
|
||||||
openLogsDir,
|
openLogsDir,
|
||||||
} from '@/services/cmds'
|
} from '@/services/cmds'
|
||||||
|
import { mutate } from '@/services/mutate'
|
||||||
import { showNotice } from '@/services/notice-service'
|
import { showNotice } from '@/services/notice-service'
|
||||||
import { checkUpdateSafe as checkUpdate } from '@/services/update'
|
import { checkUpdateSafe as checkUpdate } from '@/services/update'
|
||||||
import { version } from '@root/package.json'
|
import { version } from '@root/package.json'
|
||||||
@@ -61,7 +62,10 @@ const SettingVergeAdvanced = ({ onError: _ }: Props) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const onExportDiagnosticInfo = useCallback(async () => {
|
const onExportDiagnosticInfo = useCallback(async () => {
|
||||||
await exportDiagnosticInfo()
|
await mutate(() => exportDiagnosticInfo(), {
|
||||||
|
id: 'export-diagnostic-info',
|
||||||
|
errorNotice: false,
|
||||||
|
})
|
||||||
showNotice.success('shared.feedback.notifications.common.copySuccess', 1000)
|
showNotice.success('shared.feedback.notifications.common.copySuccess', 1000)
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
@@ -128,7 +132,12 @@ const SettingVergeAdvanced = ({ onError: _ }: Props) => {
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<SettingItem
|
<SettingItem
|
||||||
onClick={openDevTools}
|
onClick={() => {
|
||||||
|
void mutate(() => openDevTools(), {
|
||||||
|
id: 'open-devtools',
|
||||||
|
errorNotice: false,
|
||||||
|
}).catch(() => {})
|
||||||
|
}}
|
||||||
label={t('settings.components.verge.advanced.fields.openDevTools')}
|
label={t('settings.components.verge.advanced.fields.openDevTools')}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
@@ -145,7 +154,10 @@ const SettingVergeAdvanced = ({ onError: _ }: Props) => {
|
|||||||
|
|
||||||
<SettingItem
|
<SettingItem
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
exitApp()
|
void mutate(() => exitApp(), {
|
||||||
|
id: 'exit-app',
|
||||||
|
errorNotice: false,
|
||||||
|
}).catch(() => {})
|
||||||
}}
|
}}
|
||||||
label={t('settings.components.verge.advanced.fields.exit')}
|
label={t('settings.components.verge.advanced.fields.exit')}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { useCallback } from 'react'
|
import { useCallback } from 'react'
|
||||||
|
|
||||||
import { installService, restartCore } from '@/services/cmds'
|
import { installService, restartCore } from '@/services/cmds'
|
||||||
|
import { mutate } from '@/services/mutate'
|
||||||
import { showNotice } from '@/services/notice-service'
|
import { showNotice } from '@/services/notice-service'
|
||||||
|
|
||||||
const executeWithErrorHandling = async <T>(
|
const executeWithErrorHandling = async <T>(
|
||||||
@@ -23,14 +24,19 @@ const executeWithErrorHandling = async <T>(
|
|||||||
|
|
||||||
export const useServiceInstaller = () => {
|
export const useServiceInstaller = () => {
|
||||||
const installServiceAndRestartCore = useCallback(async () => {
|
const installServiceAndRestartCore = useCallback(async () => {
|
||||||
const outcome = await executeWithErrorHandling(
|
const installResult = await executeWithErrorHandling(
|
||||||
() => installService(),
|
() =>
|
||||||
|
mutate(() => installService(), {
|
||||||
|
id: 'install-service',
|
||||||
|
errorNotice: false,
|
||||||
|
}),
|
||||||
'settings.statuses.clashService.installing',
|
'settings.statuses.clashService.installing',
|
||||||
)
|
)
|
||||||
if (outcome.status === 'sidecar') {
|
if (!installResult.ok) return
|
||||||
|
if (installResult.value.status === 'sidecar') {
|
||||||
showNotice.warning(
|
showNotice.warning(
|
||||||
'settings.feedback.notifications.clashService.permissionFallback',
|
'settings.feedback.notifications.clashService.permissionFallback',
|
||||||
{ reason: outcome.reason },
|
{ reason: installResult.value.reason },
|
||||||
0,
|
0,
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
@@ -40,7 +46,11 @@ export const useServiceInstaller = () => {
|
|||||||
)
|
)
|
||||||
|
|
||||||
await executeWithErrorHandling(
|
await executeWithErrorHandling(
|
||||||
() => restartCore(),
|
() =>
|
||||||
|
mutate(() => restartCore(), {
|
||||||
|
id: 'restart-core',
|
||||||
|
errorNotice: false,
|
||||||
|
}),
|
||||||
'settings.statuses.clash.restarting',
|
'settings.statuses.clash.restarting',
|
||||||
'settings.feedback.notifications.clash.restartSuccess',
|
'settings.feedback.notifications.clash.restartSuccess',
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { useCallback } from 'react'
|
import { useCallback } from 'react'
|
||||||
|
|
||||||
import { uninstallService } from '@/services/cmds'
|
import { uninstallService } from '@/services/cmds'
|
||||||
|
import { mutate } from '@/services/mutate'
|
||||||
import { showNotice } from '@/services/notice-service'
|
import { showNotice } from '@/services/notice-service'
|
||||||
|
|
||||||
import { useSystemState } from './use-system-state'
|
import { useSystemState } from './use-system-state'
|
||||||
@@ -12,10 +13,15 @@ export const useServiceUninstaller = () => {
|
|||||||
let uninstallError: unknown
|
let uninstallError: unknown
|
||||||
showNotice.info('settings.statuses.clashService.uninstalling')
|
showNotice.info('settings.statuses.clashService.uninstalling')
|
||||||
try {
|
try {
|
||||||
await uninstallService()
|
const result = await mutate(() => uninstallService(), {
|
||||||
showNotice.success(
|
id: 'uninstall-service',
|
||||||
'settings.feedback.notifications.clashService.uninstallSuccess',
|
errorNotice: false,
|
||||||
)
|
})
|
||||||
|
if (result.ok) {
|
||||||
|
showNotice.success(
|
||||||
|
'settings.feedback.notifications.clashService.uninstallSuccess',
|
||||||
|
)
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
uninstallError = error
|
uninstallError = error
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import {
|
|||||||
getEmbeddedServerPort,
|
getEmbeddedServerPort,
|
||||||
patchVergeConfig,
|
patchVergeConfig,
|
||||||
} from '@/services/cmds'
|
} from '@/services/cmds'
|
||||||
|
import { mutate } from '@/services/mutate'
|
||||||
import {
|
import {
|
||||||
removeCacheData,
|
removeCacheData,
|
||||||
revalidateQueries,
|
revalidateQueries,
|
||||||
@@ -64,10 +65,16 @@ export const useSystemProxyState = () => {
|
|||||||
while (pendingRef.current !== null) {
|
while (pendingRef.current !== null) {
|
||||||
const target = pendingRef.current
|
const target = pendingRef.current
|
||||||
pendingRef.current = null
|
pendingRef.current = null
|
||||||
await patchVergeConfig({ enable_system_proxy: target })
|
await mutate(() => patchVergeConfig({ enable_system_proxy: target }), {
|
||||||
|
id: 'toggle-system-proxy',
|
||||||
|
errorNotice: false,
|
||||||
|
})
|
||||||
confirmed = target
|
confirmed = target
|
||||||
if (!target && verge?.auto_close_connection) {
|
if (!target && verge?.auto_close_connection) {
|
||||||
await closeAllConnections().catch(() => {})
|
await mutate(() => closeAllConnections(), {
|
||||||
|
id: 'close-all-connections',
|
||||||
|
errorNotice: false,
|
||||||
|
}).catch(() => {})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
Reference in New Issue
Block a user