diff --git a/src/components/common/traffic-error-boundary.tsx b/src/components/common/traffic-error-boundary.tsx index c665e2a9..9f52f575 100644 --- a/src/components/common/traffic-error-boundary.tsx +++ b/src/components/common/traffic-error-boundary.tsx @@ -193,7 +193,8 @@ const TrafficErrorFallback: React.FC = ({ - Error: {error?.message || "Unknown error"} + Error:{" "} + {error instanceof Error ? error.message : "Unknown error"} {retryCount > 0 && ( diff --git a/src/components/home/home-profile-card.tsx b/src/components/home/home-profile-card.tsx index fa1e01e3..40ad3442 100644 --- a/src/components/home/home-profile-card.tsx +++ b/src/components/home/home-profile-card.tsx @@ -296,8 +296,8 @@ export const HomeProfileCard = ({ // 刷新首页数据 refreshAll(); - } catch (err: any) { - showNotice.error(err.message || err.toString(), 3000); + } catch (err) { + showNotice.error(err, 3000); } finally { setUpdating(false); } diff --git a/src/components/home/ip-info-card.tsx b/src/components/home/ip-info-card.tsx index 585460dc..abfe3ac3 100644 --- a/src/components/home/ip-info-card.tsx +++ b/src/components/home/ip-info-card.tsx @@ -68,8 +68,8 @@ export const IpInfoCard = () => { const data = await getIpInfo(); setIpInfo(data); setCountdown(IP_REFRESH_SECONDS); - } catch (err: any) { - setError(err.message || t("Failed to get IP info")); + } catch (err) { + setError(err instanceof Error ? err.message : t("Failed to get IP info")); } finally { setLoading(false); } diff --git a/src/components/home/proxy-tun-card.tsx b/src/components/home/proxy-tun-card.tsx index c775f87a..179d3606 100644 --- a/src/components/home/proxy-tun-card.tsx +++ b/src/components/home/proxy-tun-card.tsx @@ -147,8 +147,8 @@ export const ProxyTunCard: FC = () => { const { enable_tun_mode } = verge ?? {}; - const handleError = (err: Error) => { - showNotice.error(err.message || err.toString()); + const handleError = (err: unknown) => { + showNotice.error(err); }; const handleTabChange = (tab: string) => { diff --git a/src/components/home/system-info-card.tsx b/src/components/home/system-info-card.tsx index 74a52957..80394495 100644 --- a/src/components/home/system-info-card.tsx +++ b/src/components/home/system-info-card.tsx @@ -179,8 +179,8 @@ export const SystemInfoCard = () => { showNotice.info("Update Available", 2000); goToSettings(); } - } catch (err: any) { - showNotice.error(err.message || err.toString()); + } catch (err) { + showNotice.error(err); } }); diff --git a/src/components/profile/profile-viewer.tsx b/src/components/profile/profile-viewer.tsx index b872ec07..e38f4d25 100644 --- a/src/components/profile/profile-viewer.tsx +++ b/src/components/profile/profile-viewer.tsx @@ -145,7 +145,7 @@ export function ProfileViewer({ onChange, ref }: ProfileViewerProps) { } catch { // 首次创建/更新失败,尝试使用自身代理 showNotice.info( - t("components.profile.viewer.notifications.creationRetry"), + "components.profile.viewer.notifications.creationRetry", ); // 使用自身代理的配置 @@ -170,7 +170,7 @@ export function ProfileViewer({ onChange, ref }: ProfileViewerProps) { } showNotice.success( - t("components.profile.viewer.notifications.creationSuccess"), + "components.profile.viewer.notifications.creationSuccess", ); } } diff --git a/src/components/profile/proxies-editor-viewer.tsx b/src/components/profile/proxies-editor-viewer.tsx index 7aa41576..ba7ad167 100644 --- a/src/components/profile/proxies-editor-viewer.tsx +++ b/src/components/profile/proxies-editor-viewer.tsx @@ -163,11 +163,11 @@ export const ProxiesEditorViewer = (props: Props) => { proxies.push(proxy); names.push(proxy.name); } - } catch (err: any) { + } catch (err) { console.warn( "[ProxiesEditorViewer] parseUri failed for line:", uri, - err?.message || err, + err, ); // 不阻塞主流程 } diff --git a/src/components/proxy/provider-button.tsx b/src/components/proxy/provider-button.tsx index 3a1a969e..a2ef21ce 100644 --- a/src/components/proxy/provider-button.tsx +++ b/src/components/proxy/provider-button.tsx @@ -66,15 +66,13 @@ export const ProviderButton = () => { await refreshProxy(); await refreshProxyProviders(); - showNotice.success({ - i18nKey: "components.providers.notices.updateSuccess", - params: { name }, + showNotice.success("components.providers.notices.updateSuccess", { + name, }); - } catch (err: any) { - const message = err?.message || err?.toString?.() || String(err); - showNotice.error({ - i18nKey: "components.providers.notices.updateFailed", - params: { name, message }, + } catch (err) { + showNotice.error("components.providers.notices.updateFailed", { + name, + message: String(err), }); } finally { // 清除更新状态 @@ -88,9 +86,7 @@ export const ProviderButton = () => { // 获取所有provider的名称 const allProviders = Object.keys(proxyProviders || {}); if (allProviders.length === 0) { - showNotice.info({ - i18nKey: "components.providers.notices.none", - }); + showNotice.info("components.providers.notices.none"); return; } @@ -120,14 +116,10 @@ export const ProviderButton = () => { await refreshProxy(); await refreshProxyProviders(); - showNotice.success({ - i18nKey: "components.providers.notices.allUpdated", - }); - } catch (err: any) { - const message = err?.message || err?.toString?.() || String(err); - showNotice.error({ - i18nKey: "components.providers.notices.genericError", - params: { message }, + showNotice.success("components.providers.notices.allUpdated"); + } catch (err) { + showNotice.error("components.providers.notices.genericError", { + message: String(err), }); } finally { // 清除所有更新状态 diff --git a/src/components/rule/provider-button.tsx b/src/components/rule/provider-button.tsx index f0857f53..3c472e84 100644 --- a/src/components/rule/provider-button.tsx +++ b/src/components/rule/provider-button.tsx @@ -58,15 +58,13 @@ export const ProviderButton = () => { await refreshRules(); await refreshRuleProviders(); - showNotice.success({ - i18nKey: "components.notices.providers.updateSuccess", - params: { name }, + showNotice.success("components.notices.providers.updateSuccess", { + name, }); - } catch (err: any) { - const message = err?.message || err?.toString?.() || String(err); - showNotice.error({ - i18nKey: "components.notices.providers.updateFailed", - params: { name, message }, + } catch (err) { + showNotice.error("components.notices.providers.updateFailed", { + name, + message: String(err), }); } finally { // 清除更新状态 @@ -80,9 +78,7 @@ export const ProviderButton = () => { // 获取所有provider的名称 const allProviders = Object.keys(ruleProviders || {}); if (allProviders.length === 0) { - showNotice.info({ - i18nKey: "components.notices.providers.none", - }); + showNotice.info("components.notices.providers.none"); return; } @@ -112,14 +108,10 @@ export const ProviderButton = () => { await refreshRules(); await refreshRuleProviders(); - showNotice.success({ - i18nKey: "components.notices.providers.allUpdated", - }); - } catch (err: any) { - const message = err?.message || err?.toString?.() || String(err); - showNotice.error({ - i18nKey: "components.notices.providers.genericError", - params: { message }, + showNotice.success("components.notices.providers.allUpdated"); + } catch (err) { + showNotice.error("components.notices.providers.genericError", { + message: String(err), }); } finally { // 清除所有更新状态 diff --git a/src/components/setting/mods/backup-config-viewer.tsx b/src/components/setting/mods/backup-config-viewer.tsx index a174747e..14ac7c54 100644 --- a/src/components/setting/mods/backup-config-viewer.tsx +++ b/src/components/setting/mods/backup-config-viewer.tsx @@ -84,18 +84,16 @@ export const BackupConfigViewer = memo( if (!url) { urlRef.current?.focus(); - showNotice( - "error", - t("components.settings.backup.messages.webdavUrlRequired"), + showNotice.error( + "components.settings.backup.messages.webdavUrlRequired", ); throw new Error( t("components.settings.backup.messages.webdavUrlRequired"), ); } else if (!isValidUrl(url)) { urlRef.current?.focus(); - showNotice( - "error", - t("components.settings.backup.messages.invalidWebdavUrl"), + showNotice.error( + "components.settings.backup.messages.invalidWebdavUrl", ); throw new Error( t("components.settings.backup.messages.invalidWebdavUrl"), @@ -103,9 +101,8 @@ export const BackupConfigViewer = memo( } if (!username) { usernameRef.current?.focus(); - showNotice( - "error", - t("components.settings.backup.messages.usernameRequired"), + showNotice.error( + "components.settings.backup.messages.usernameRequired", ); throw new Error( t("components.settings.backup.messages.usernameRequired"), @@ -113,9 +110,8 @@ export const BackupConfigViewer = memo( } if (!password) { passwordRef.current?.focus(); - showNotice( - "error", - t("components.settings.backup.messages.passwordRequired"), + showNotice.error( + "components.settings.backup.messages.passwordRequired", ); throw new Error( t("components.settings.backup.messages.passwordRequired"), @@ -132,18 +128,15 @@ export const BackupConfigViewer = memo( data.username.trim(), data.password, ).then(() => { - showNotice( - "success", - t("components.settings.backup.messages.webdavConfigSaved"), + showNotice.success( + "components.settings.backup.messages.webdavConfigSaved", ); onSaveSuccess(); }); } catch (error) { - showNotice( - "error", - t("components.settings.backup.messages.webdavConfigSaveFailed", { - error, - }), + showNotice.error( + "components.settings.backup.messages.webdavConfigSaveFailed", + { error }, 3000, ); } finally { @@ -156,17 +149,15 @@ export const BackupConfigViewer = memo( try { setLoading(true); await createWebdavBackup().then(async () => { - showNotice( - "success", - t("components.settings.backup.messages.backupCreated"), + showNotice.success( + "components.settings.backup.messages.backupCreated", ); await onBackupSuccess(); }); } catch (error) { - showNotice( - "error", - t("components.settings.backup.messages.backupFailed", { error }), - ); + showNotice.error("components.settings.backup.messages.backupFailed", { + error, + }); } finally { setLoading(false); } diff --git a/src/components/setting/mods/backup-table-viewer.tsx b/src/components/setting/mods/backup-table-viewer.tsx index 8aa46996..a0d65d59 100644 --- a/src/components/setting/mods/backup-table-viewer.tsx +++ b/src/components/setting/mods/backup-table-viewer.tsx @@ -75,9 +75,8 @@ export const BackupTableViewer = memo( const handleRestore = useLockFn(async (filename: string) => { await onRestore(filename).then(() => { - showNotice( - "success", - t("components.settings.backup.messages.restoreSuccess"), + showNotice.success( + "components.settings.backup.messages.restoreSuccess", ); }); await restartApp(); @@ -95,15 +94,13 @@ export const BackupTableViewer = memo( return; } await onExport(filename, savePath); - showNotice( - "success", - t("components.settings.backup.messages.localBackupExported"), + showNotice.success( + "components.settings.backup.messages.localBackupExported", ); } catch (error) { console.error(error); - showNotice( - "error", - t("components.settings.backup.messages.localBackupExportFailed"), + showNotice.error( + "components.settings.backup.messages.localBackupExportFailed", ); } }); diff --git a/src/components/setting/mods/clash-core-viewer.tsx b/src/components/setting/mods/clash-core-viewer.tsx index 8c87667f..9cfb9d29 100644 --- a/src/components/setting/mods/clash-core-viewer.tsx +++ b/src/components/setting/mods/clash-core-viewer.tsx @@ -75,7 +75,7 @@ export function ClashCoreViewer({ ref }: { ref?: Ref }) { try { setRestarting(true); await restartCore(); - showNotice.success({ i18nKey: "Clash Core Restarted" }); + showNotice.success("Clash Core Restarted"); setRestarting(false); } catch (err) { setRestarting(false); @@ -88,10 +88,10 @@ export function ClashCoreViewer({ ref }: { ref?: Ref }) { setUpgrading(true); await upgradeCore(); setUpgrading(false); - showNotice.success({ i18nKey: "Core Version Updated" }); + showNotice.success("Core Version Updated"); } catch (err: any) { setUpgrading(false); - const errMsg = err.response?.data?.message || err.toString(); + const errMsg = err?.response?.data?.message ?? String(err); const showMsg = errMsg.includes("already using latest version") ? "Already Using Latest Core Version" : errMsg; diff --git a/src/components/setting/mods/clash-port-viewer.tsx b/src/components/setting/mods/clash-port-viewer.tsx index f3e2ed17..cecd41c9 100644 --- a/src/components/setting/mods/clash-port-viewer.tsx +++ b/src/components/setting/mods/clash-port-viewer.tsx @@ -69,16 +69,10 @@ export const ClashPortViewer = forwardRef((_, ref) => { manual: true, onSuccess: () => { setOpen(false); - showNotice( - "success", - t("components.settings.clash.port.messages.saved"), - ); + showNotice.success("components.settings.clash.port.messages.saved"); }, onError: () => { - showNotice( - "error", - t("components.settings.clash.port.messages.saveFailed"), - ); + showNotice.error("components.settings.clash.port.messages.saveFailed"); }, }, ); diff --git a/src/components/setting/mods/controller-viewer.tsx b/src/components/setting/mods/controller-viewer.tsx index 02d205dd..47bbd412 100644 --- a/src/components/setting/mods/controller-viewer.tsx +++ b/src/components/setting/mods/controller-viewer.tsx @@ -56,18 +56,16 @@ export function ControllerViewer({ ref }: { ref?: Ref }) { // 如果启用了外部控制器,则保存控制器地址和密钥 if (enableController) { if (!controller.trim()) { - showNotice.error({ - i18nKey: - "components.settings.externalController.messages.addressRequired", - }); + showNotice.error( + "components.settings.externalController.messages.addressRequired", + ); return; } if (!secret.trim()) { - showNotice.error({ - i18nKey: - "components.settings.externalController.messages.secretRequired", - }); + showNotice.error( + "components.settings.externalController.messages.secretRequired", + ); return; } @@ -77,15 +75,10 @@ export function ControllerViewer({ ref }: { ref?: Ref }) { await patchInfo({ "external-controller": "" }); } - showNotice.success({ i18nKey: "Configuration saved successfully" }); + showNotice.success("Configuration saved successfully"); setOpen(false); - } catch (err: any) { - const message = err?.message || err?.toString?.(); - if (message) { - showNotice.error(t("Failed to save configuration"), message, 4000); - } else { - showNotice.error("Failed to save configuration", 4000); - } + } catch (err) { + showNotice.error("Failed to save configuration", err, 4000); } finally { setIsSaving(false); } @@ -100,9 +93,9 @@ export function ControllerViewer({ ref }: { ref?: Ref }) { setTimeout(() => setCopySuccess(null)); } catch (err) { console.warn("[ControllerViewer] copy to clipboard failed:", err); - showNotice.error({ - i18nKey: "components.settings.externalController.messages.copyFailed", - }); + showNotice.error( + "components.settings.externalController.messages.copyFailed", + ); } }, ); diff --git a/src/components/setting/mods/dns-viewer.tsx b/src/components/setting/mods/dns-viewer.tsx index 109888bc..d6878b22 100644 --- a/src/components/setting/mods/dns-viewer.tsx +++ b/src/components/setting/mods/dns-viewer.tsx @@ -424,7 +424,7 @@ export function DnsViewer({ ref }: { ref?: Ref }) { skipYamlSyncRef.current = true; updateValuesFromConfig(parsedYaml); } catch { - showNotice.error({ i18nKey: "Invalid YAML format" }); + showNotice.error("Invalid YAML format"); } }, [yamlContent, updateValuesFromConfig]); @@ -547,7 +547,7 @@ export function DnsViewer({ ref }: { ref?: Ref }) { } } - showNotice.error(`${t("DNS configuration error")}:`, cleanErrorMsg); + showNotice.error("DNS configuration error:", cleanErrorMsg); return; } @@ -558,7 +558,7 @@ export function DnsViewer({ ref }: { ref?: Ref }) { } setOpen(false); - showNotice.success({ i18nKey: "DNS settings saved" }); + showNotice.success("DNS settings saved"); } catch (err) { showNotice.error(err); } diff --git a/src/components/setting/mods/external-controller-cors.tsx b/src/components/setting/mods/external-controller-cors.tsx index b8631ac6..1e82f926 100644 --- a/src/components/setting/mods/external-controller-cors.tsx +++ b/src/components/setting/mods/external-controller-cors.tsx @@ -140,12 +140,10 @@ export const HeaderConfiguration = forwardRef( manual: true, onSuccess: () => { setOpen(false); - showNotice.success({ - i18nKey: "Configuration saved successfully", - }); + showNotice.success("Configuration saved successfully"); }, onError: () => { - showNotice.error({ i18nKey: "Failed to save configuration" }); + showNotice.error("Failed to save configuration"); }, }, ); diff --git a/src/components/setting/mods/local-backup-actions.tsx b/src/components/setting/mods/local-backup-actions.tsx index db8abbb6..0ce7ca62 100644 --- a/src/components/setting/mods/local-backup-actions.tsx +++ b/src/components/setting/mods/local-backup-actions.tsx @@ -20,16 +20,14 @@ export const LocalBackupActions = memo( try { setLoading(true); await createLocalBackup(); - showNotice( - "success", - t("components.settings.backup.messages.localBackupCreated"), + showNotice.success( + "components.settings.backup.messages.localBackupCreated", ); await onBackupSuccess(); } catch (error) { console.error(error); - showNotice( - "error", - t("components.settings.backup.messages.localBackupFailed"), + showNotice.error( + "components.settings.backup.messages.localBackupFailed", ); } finally { setLoading(false); diff --git a/src/components/setting/mods/network-interface-viewer.tsx b/src/components/setting/mods/network-interface-viewer.tsx index 9244e0aa..a187c973 100644 --- a/src/components/setting/mods/network-interface-viewer.tsx +++ b/src/components/setting/mods/network-interface-viewer.tsx @@ -133,7 +133,7 @@ const AddressDisplay = ({ size="small" onClick={async () => { await writeText(content); - showNotice.success({ i18nKey: "Copy Success" }); + showNotice.success("Copy Success"); }} > diff --git a/src/components/setting/mods/sysproxy-viewer.tsx b/src/components/setting/mods/sysproxy-viewer.tsx index a021fefd..81790adc 100644 --- a/src/components/setting/mods/sysproxy-viewer.tsx +++ b/src/components/setting/mods/sysproxy-viewer.tsx @@ -278,14 +278,12 @@ export const SysproxyViewer = forwardRef((props, ref) => { const onSave = useLockFn(async () => { if (value.duration < 1) { showNotice.error( - t("components.settings.sysproxy.messages.durationTooShort"), + "components.settings.sysproxy.messages.durationTooShort", ); return; } if (value.bypass && !validReg.test(value.bypass)) { - showNotice.error( - t("components.settings.sysproxy.messages.invalidBypass"), - ); + showNotice.error("components.settings.sysproxy.messages.invalidBypass"); return; } @@ -303,7 +301,7 @@ export const SysproxyViewer = forwardRef((props, ref) => { !hostnameRegex.test(value.proxy_host) ) { showNotice.error( - t("components.settings.sysproxy.messages.invalidProxyHost"), + "components.settings.sysproxy.messages.invalidProxyHost", ); return; } diff --git a/src/components/setting/mods/update-viewer.tsx b/src/components/setting/mods/update-viewer.tsx index c4d59c1c..8672549a 100644 --- a/src/components/setting/mods/update-viewer.tsx +++ b/src/components/setting/mods/update-viewer.tsx @@ -58,14 +58,12 @@ export function UpdateViewer({ ref }: { ref?: Ref }) { const onUpdate = useLockFn(async () => { if (portableFlag) { - showNotice.error(t("components.settings.update.messages.portableError")); + showNotice.error("components.settings.update.messages.portableError"); return; } if (!updateInfo?.body) return; if (breakChangeFlag) { - showNotice.error( - t("components.settings.update.messages.breakChangeError"), - ); + showNotice.error("components.settings.update.messages.breakChangeError"); return; } if (updateState) return; diff --git a/src/components/setting/setting-clash.tsx b/src/components/setting/setting-clash.tsx index c2537239..c6545a0d 100644 --- a/src/components/setting/setting-clash.tsx +++ b/src/components/setting/setting-clash.tsx @@ -67,9 +67,7 @@ const SettingClash = ({ onError }: Props) => { const onUpdateGeo = async () => { try { await updateGeo(); - showNotice.success( - t("components.settings.clash.messages.geoDataUpdated"), - ); + showNotice.success("components.settings.clash.messages.geoDataUpdated"); } catch (err: any) { showNotice.error(err); } diff --git a/src/components/setting/setting-system.tsx b/src/components/setting/setting-system.tsx index f73fe9fb..6e4f950e 100644 --- a/src/components/setting/setting-system.tsx +++ b/src/components/setting/setting-system.tsx @@ -78,9 +78,8 @@ const SettingSystem = ({ onError }: Props) => { }} onGuard={async (e) => { if (isAdminMode) { - showNotice( - "info", - t("components.settings.system.tooltips.autoLaunchAdmin"), + showNotice.info( + "components.settings.system.tooltips.autoLaunchAdmin", ); } diff --git a/src/components/setting/setting-verge-advanced.tsx b/src/components/setting/setting-verge-advanced.tsx index 46a3aafd..b1159798 100644 --- a/src/components/setting/setting-verge-advanced.tsx +++ b/src/components/setting/setting-verge-advanced.tsx @@ -48,7 +48,7 @@ const SettingVergeAdvanced = ({ onError: _ }: Props) => { const info = await checkUpdate(); if (!info?.available) { showNotice.success( - t("components.settings.verge.advanced.notifications.latestVersion"), + "components.settings.verge.advanced.notifications.latestVersion", ); } else { updateRef.current?.open(); @@ -61,19 +61,19 @@ const SettingVergeAdvanced = ({ onError: _ }: Props) => { const onExportDiagnosticInfo = useCallback(async () => { await exportDiagnosticInfo(); showNotice.success( - t("components.settings.common.notifications.copySuccess"), + "components.settings.common.notifications.copySuccess", 1000, ); - }, [t]); + }, []); const copyVersion = useCallback(() => { navigator.clipboard.writeText(`v${version}`).then(() => { showNotice.success( - t("components.settings.verge.advanced.notifications.versionCopied"), + "components.settings.verge.advanced.notifications.versionCopied", 1000, ); }); - }, [t]); + }, []); return ( diff --git a/src/components/setting/setting-verge-basic.tsx b/src/components/setting/setting-verge-basic.tsx index a764de17..05b4448a 100644 --- a/src/components/setting/setting-verge-basic.tsx +++ b/src/components/setting/setting-verge-basic.tsx @@ -76,12 +76,11 @@ const SettingVergeBasic = ({ onError }: Props) => { const onCopyClashEnv = useCallback(async () => { await copyClashEnv(); - showNotice( - "success", - t("components.settings.common.notifications.copySuccess"), + showNotice.success( + "components.settings.common.notifications.copySuccess", 1000, ); - }, [t]); + }, []); return ( diff --git a/src/components/test/test-item.tsx b/src/components/test/test-item.tsx index 6407cc18..36f6371c 100644 --- a/src/components/test/test-item.tsx +++ b/src/components/test/test-item.tsx @@ -82,7 +82,7 @@ export const TestItem = ({ try { removeTest(uid); } catch (err: any) { - showNotice.error(err.message || err.toString()); + showNotice.error(err); } }); diff --git a/src/components/test/test-viewer.tsx b/src/components/test/test-viewer.tsx index 42386a4c..244465f4 100644 --- a/src/components/test/test-viewer.tsx +++ b/src/components/test/test-viewer.tsx @@ -101,7 +101,7 @@ export const TestViewer = forwardRef((props, ref) => { setLoading(false); setTimeout(() => formIns.reset(), 500); } catch (err: any) { - showNotice.error(err.message || err.toString()); + showNotice.error(err); setLoading(false); } }), diff --git a/src/hooks/use-system-state.ts b/src/hooks/use-system-state.ts index 44d54088..844ad236 100644 --- a/src/hooks/use-system-state.ts +++ b/src/hooks/use-system-state.ts @@ -1,5 +1,4 @@ import { useEffect } from "react"; -import { useTranslation } from "react-i18next"; import useSWR from "swr"; import { getRunningMode, isAdmin, isServiceAvailable } from "@/services/cmds"; @@ -26,7 +25,6 @@ let disablingTunMode = false; * 包括运行模式、管理员状态、系统服务是否可用 */ export function useSystemState() { - const { t } = useTranslation(); const { verge, patchVerge } = useVerge(); const { @@ -67,16 +65,13 @@ export function useSystemState() { disablingTunMode = true; patchVerge({ enable_tun_mode: false }) .then(() => { - showNotice.info({ - i18nKey: - "TUN Mode automatically disabled due to service unavailable", - }); + showNotice.info( + "TUN Mode automatically disabled due to service unavailable", + ); }) .catch((err) => { console.error("[useVerge] 自动关闭TUN模式失败:", err); - showNotice.error({ - i18nKey: "Failed to disable TUN Mode automatically", - }); + showNotice.error("Failed to disable TUN Mode automatically"); }) .finally(() => { const tid = setTimeout(() => { @@ -86,7 +81,7 @@ export function useSystemState() { }, 1000); }); } - }, [enable_tun_mode, isTunModeAvailable, patchVerge, isLoading, t]); + }, [enable_tun_mode, isTunModeAvailable, patchVerge, isLoading]); return { runningMode: systemState.runningMode, diff --git a/src/hooks/useServiceInstaller.ts b/src/hooks/useServiceInstaller.ts index 39d56443..6f325254 100644 --- a/src/hooks/useServiceInstaller.ts +++ b/src/hooks/useServiceInstaller.ts @@ -17,8 +17,7 @@ const executeWithErrorHandling = async ( showNotice.success(successMessage); } } catch (err) { - const msg = (err as Error)?.message || String(err); - showNotice.error(msg); + showNotice.error(err); throw err; } }; diff --git a/src/hooks/useServiceUninstaller.ts b/src/hooks/useServiceUninstaller.ts index 546c3f59..10418da6 100644 --- a/src/hooks/useServiceUninstaller.ts +++ b/src/hooks/useServiceUninstaller.ts @@ -17,8 +17,7 @@ const executeWithErrorHandling = async ( showNotice.success(successMessage); } } catch (err) { - const msg = (err as Error)?.message || String(err); - showNotice.error(msg); + showNotice.error(err); throw err; } }; diff --git a/src/locales/en.json b/src/locales/en.json index c7f6fc0c..4c017b08 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -373,6 +373,7 @@ "Service Administrator Prompt": "Clash Verge requires administrator privileges to reinstall the system service", "DNS Settings": "DNS Settings", "DNS settings saved": "DNS settings saved", + "DNS configuration error:": "DNS configuration error:", "DNS Overwrite": "DNS Overwrite", "DNS Settings Warning": "If you are not familiar with these settings, please do not modify them and keep DNS Overwrite enabled", "Enable DNS": "Enable DNS", diff --git a/src/locales/zh.json b/src/locales/zh.json index 62670a80..d8cc1689 100644 --- a/src/locales/zh.json +++ b/src/locales/zh.json @@ -373,6 +373,7 @@ "Service Administrator Prompt": "Clash Verge 需要管理员权限安装系统服务", "DNS Settings": "DNS 设置", "DNS settings saved": "DNS 设置已保存", + "DNS configuration error:": "DNS 配置错误:", "DNS Overwrite": "DNS 覆写", "DNS Settings Warning": "如果你不清楚这里的设置请不要修改,并保持 DNS 覆写开启", "Enable DNS": "启用 DNS", diff --git a/src/locales/zhtw.json b/src/locales/zhtw.json index 6bf8df4c..1fc9ea76 100644 --- a/src/locales/zhtw.json +++ b/src/locales/zhtw.json @@ -373,6 +373,7 @@ "Service Administrator Prompt": "Clash Verge 需要管理員權限安裝系統服務", "DNS Settings": "DNS 設定", "DNS settings saved": "DNS 設定已儲存", + "DNS configuration error:": "DNS 設定錯誤:", "DNS Overwrite": "DNS 覆寫", "DNS Settings Warning": "如果你不清楚這裡的設定請不要修改,並保持 DNS 覆寫開啟", "Enable DNS": "啟用 DNS", diff --git a/src/pages/_layout/notificationHandlers.ts b/src/pages/_layout/notificationHandlers.ts index 78596d81..82ae6b29 100644 --- a/src/pages/_layout/notificationHandlers.ts +++ b/src/pages/_layout/notificationHandlers.ts @@ -20,51 +20,51 @@ export const handleNoticeMessage = ( }, "set_config::error": () => showNotice.error(msg), update_with_clash_proxy: () => - showNotice.success(t("Update with Clash proxy successfully"), msg), + showNotice.success("Update with Clash proxy successfully", msg), update_retry_with_clash: () => showNotice.info("Update failed, retrying with Clash proxy..."), update_failed_even_with_clash: () => - showNotice.error(`${t("Update failed even with Clash proxy")}:`, msg), + showNotice.error("Update failed even with Clash proxy", msg), update_failed: () => showNotice.error(msg), "config_validate::boot_error": () => - showNotice.error(t("Boot Config Validation Failed"), msg), + showNotice.error("Boot Config Validation Failed", msg), "config_validate::core_change": () => - showNotice.error(t("Core Change Config Validation Failed"), msg), + showNotice.error("Core Change Config Validation Failed", msg), "config_validate::error": () => - showNotice.error(t("Config Validation Failed"), msg), + showNotice.error("Config Validation Failed", msg), "config_validate::process_terminated": () => showNotice.error("Config Validation Process Terminated"), "config_validate::stdout_error": () => - showNotice.error(t("Config Validation Failed"), msg), + showNotice.error("Config Validation Failed", msg), "config_validate::script_error": () => - showNotice.error(t("Script File Error"), msg), + showNotice.error("Script File Error", msg), "config_validate::script_syntax_error": () => - showNotice.error(t("Script Syntax Error"), msg), + showNotice.error("Script Syntax Error", msg), "config_validate::script_missing_main": () => - showNotice.error(t("Script Missing Main"), msg), + showNotice.error("Script Missing Main", msg), "config_validate::file_not_found": () => - showNotice.error(t("File Not Found"), msg), + showNotice.error("File Not Found", msg), "config_validate::yaml_syntax_error": () => - showNotice.error(t("YAML Syntax Error"), msg), + showNotice.error("YAML Syntax Error", msg), "config_validate::yaml_read_error": () => - showNotice.error(t("YAML Read Error"), msg), + showNotice.error("YAML Read Error", msg), "config_validate::yaml_mapping_error": () => - showNotice.error(t("YAML Mapping Error"), msg), + showNotice.error("YAML Mapping Error", msg), "config_validate::yaml_key_error": () => - showNotice.error(t("YAML Key Error"), msg), - "config_validate::yaml_error": () => showNotice.error(t("YAML Error"), msg), + showNotice.error("YAML Key Error", msg), + "config_validate::yaml_error": () => showNotice.error("YAML Error", msg), "config_validate::merge_syntax_error": () => - showNotice.error(t("Merge File Syntax Error"), msg), + showNotice.error("Merge File Syntax Error", msg), "config_validate::merge_mapping_error": () => - showNotice.error(t("Merge File Mapping Error"), msg), + showNotice.error("Merge File Mapping Error", msg), "config_validate::merge_key_error": () => - showNotice.error(t("Merge File Key Error"), msg), + showNotice.error("Merge File Key Error", msg), "config_validate::merge_error": () => - showNotice.error(t("Merge File Error"), msg), + showNotice.error("Merge File Error", msg), "config_core::change_success": () => - showNotice.success(`${t("Core Changed Successfully")}:`, msg), + showNotice.success("Core Changed Successfully", msg), "config_core::change_error": () => - showNotice.error(`${t("Failed to Change Core")}:`, msg), + showNotice.error("Failed to Change Core", msg), }; const handler = handlers[status]; diff --git a/src/pages/profiles.tsx b/src/pages/profiles.tsx index 4dc7fece..9e17de32 100644 --- a/src/pages/profiles.tsx +++ b/src/pages/profiles.tsx @@ -146,12 +146,12 @@ const ProfilePage = () => { setActivatings((prev) => prev.filter((id) => id !== previousSwitching)); showNotice.info( - t("pages.profiles.notifications.switchInterrupted"), + "pages.profiles.notifications.switchInterrupted", `${previousSwitching} → ${newProfile}`, 3000, ); }, - [t], + [], ); // 清理切换状态 @@ -240,12 +240,11 @@ const ProfilePage = () => { await onEnhance(false); showNotice.success("pages.profiles.notices.forceRefreshCompleted", 2000); - } catch (error: any) { + } catch (error) { console.error("[紧急刷新] 失败:", error); - const message = error?.message || String(error); showNotice.error( "pages.profiles.notices.emergencyRefreshFailed", - { message }, + { message: String(error) }, 4000, ); } @@ -302,11 +301,11 @@ const ProfilePage = () => { self_proxy: true, }); await handleImportSuccess("Profile Imported with Clash proxy"); - } catch (retryErr: any) { + } catch (retryErr) { // 回退导入也失败 - const retryErrmsg = retryErr?.message || retryErr.toString(); showNotice.error( - `${t("pages.profiles.notifications.importFail")}: ${retryErrmsg}`, + "pages.profiles.notifications.importFail", + String(retryErr), ); } } finally { @@ -353,13 +352,10 @@ const ProfilePage = () => { // 清除SWR缓存并重新获取 await mutate("getProfiles", getProfiles(), { revalidate: true }); await onEnhance(false); - showNotice.error( - t("pages.profiles.notifications.importNeedsRefresh"), - 3000, - ); + showNotice.error("pages.profiles.notifications.importNeedsRefresh", 3000); } catch (finalError) { console.error(`[导入刷新] 最终刷新尝试失败:`, finalError); - showNotice.error(t("pages.profiles.notifications.importSuccess"), 5000); + showNotice.error("pages.profiles.notifications.importSuccess", 5000); } }; @@ -477,7 +473,7 @@ const ProfilePage = () => { if (notifySuccess && success) { showNotice.success( - t("pages.profiles.notifications.profileSwitched"), + "pages.profiles.notifications.profileSwitched", 1000, ); } @@ -531,7 +527,6 @@ const ProfilePage = () => { profiles, patchProfiles, mutateLogs, - t, executeBackgroundTasks, handleProfileInterrupt, cleanupSwitchState, @@ -577,7 +572,7 @@ const ProfilePage = () => { mutateLogs(); if (notifySuccess) { showNotice.success( - t("pages.profiles.notifications.profileReactivated"), + "pages.profiles.notifications.profileReactivated", 1000, ); } @@ -718,9 +713,7 @@ const ProfilePage = () => { setSelectedProfiles(new Set()); setBatchMode(false); - showNotice.success({ - i18nKey: "pages.profiles.notifications.batchDeleted", - }); + showNotice.success("pages.profiles.notifications.batchDeleted"); } catch (err: any) { showNotice.error(err); } finally { @@ -729,8 +722,8 @@ const ProfilePage = () => { }); const mode = useThemeMode(); - const islight = mode === "light" ? true : false; - const dividercolor = islight + const isLight = mode === "light"; + const dividercolor = isLight ? "rgba(0, 0, 0, 0.06)" : "rgba(255, 255, 255, 0.06)"; diff --git a/src/pages/settings.tsx b/src/pages/settings.tsx index 7392b27b..a19503ee 100644 --- a/src/pages/settings.tsx +++ b/src/pages/settings.tsx @@ -16,7 +16,7 @@ const SettingPage = () => { const { t } = useTranslation(); const onError = (err: any) => { - showNotice.error(err?.message || err.toString()); + showNotice.error(err); }; const toGithubRepo = useLockFn(() => { diff --git a/src/pages/unlock.tsx b/src/pages/unlock.tsx index 1993ceba..ebdb2dec 100644 --- a/src/pages/unlock.tsx +++ b/src/pages/unlock.tsx @@ -173,10 +173,7 @@ const UnlockPage = () => { setIsCheckingAll(false); } catch (err: any) { setIsCheckingAll(false); - showNotice( - "error", - err?.message || err?.toString() || t("Detection timeout or failed"), - ); + showNotice.error("Detection timeout or failed", err); console.error("Failed to check media unlock:", err); } }); @@ -206,12 +203,7 @@ const UnlockPage = () => { setLoadingItems((prev) => prev.filter((item) => item !== name)); } catch (err: any) { setLoadingItems((prev) => prev.filter((item) => item !== name)); - showNotice( - "error", - err?.message || - err?.toString() || - t("Detection failed for {name}").replace("{name}", name), - ); + showNotice.error("Detection failed for {name}", { name }, err); console.error(`Failed to check ${name}:`, err); } }); diff --git a/src/services/noticeService.ts b/src/services/noticeService.ts index f86c5b46..a6a91da9 100644 --- a/src/services/noticeService.ts +++ b/src/services/noticeService.ts @@ -3,17 +3,11 @@ import { ReactNode, isValidElement } from "react"; type NoticeType = "success" | "error" | "info"; -/** - * Descriptor used when the notice content should be resolved through i18n. - */ export interface NoticeTranslationDescriptor { key: string; params?: Record; } -/** - * Runtime representation of a notice entry queued for rendering. - */ interface NoticeItem { readonly id: number; readonly type: NoticeType; @@ -69,6 +63,7 @@ function parseNoticeExtras(extras: NoticeExtra[]): ParsedNoticeExtras { let raw: unknown; let duration: number | undefined; + // Prioritize objects as translation params, then as raw payloads, while the first number wins as duration. for (const extra of extras) { if (extra === undefined) continue; @@ -182,13 +177,9 @@ function extractDisplayText(input: unknown): string | undefined { if (input instanceof Error) { return input.message || input.name; } - if ( - typeof input === "object" && - input !== null && - "message" in input && - typeof (input as { message?: unknown }).message === "string" - ) { - return (input as { message?: string }).message; + if (typeof input === "object" && input !== null) { + const maybeMessage = (input as { message?: unknown }).message; + if (typeof maybeMessage === "string") return maybeMessage; } try { return JSON.stringify(input); @@ -250,6 +241,7 @@ function normalizeNoticeMessage( }, }; } + // Prefer showing the original string while still surfacing the raw details below. return { i18n: { key: "common.notices.prefixedRaw", @@ -285,9 +277,6 @@ function normalizeNoticeMessage( return { i18n: createRawDescriptor("") }; } -/** - * Imperative entry point for users to display new notices. - */ const baseShowNotice = ( type: NoticeType, message: NoticeContent, @@ -300,6 +289,7 @@ const baseShowNotice = ( effectiveDuration > 0 ? setTimeout(() => hideNotice(id), effectiveDuration) : undefined; + const normalizedMessage = normalizeNoticeMessage(message, params, raw); const notice = buildNotice( id, @@ -342,11 +332,3 @@ export function subscribeNotices(subscriber: NoticeSubscriber) { export function getSnapshotNotices() { return notices; } - -export function clearAllNotices() { - notices.forEach((notice) => { - if (notice.timerId) clearTimeout(notice.timerId); - }); - notices = []; - notifySubscribers(); -}