From 9b455d740d7c667bd04e277309506a4e3f6b8cba Mon Sep 17 00:00:00 2001 From: Slinetrac Date: Sun, 2 Nov 2025 14:22:36 +0800 Subject: [PATCH] refactor(notice): unify showNotice usage --- src/components/home/home-profile-card.tsx | 4 +- src/components/home/proxy-tun-card.tsx | 4 +- src/components/home/system-info-card.tsx | 10 ++- src/components/profile/editor-viewer.tsx | 8 +- .../profile/groups-editor-viewer.tsx | 18 ++-- src/components/profile/profile-item.tsx | 4 +- src/components/profile/profile-more.tsx | 4 +- src/components/profile/profile-viewer.tsx | 4 +- .../profile/proxies-editor-viewer.tsx | 8 +- .../profile/rules-editor-viewer.tsx | 23 +++-- src/components/proxy/provider-button.tsx | 10 +-- .../setting/mods/clash-core-viewer.tsx | 14 +-- .../setting/mods/controller-viewer.tsx | 32 +++---- src/components/setting/mods/dns-viewer.tsx | 19 ++-- .../setting/mods/external-controller-cors.tsx | 6 +- src/components/setting/mods/hotkey-viewer.tsx | 4 +- src/components/setting/mods/layout-viewer.tsx | 4 +- .../setting/mods/lite-mode-viewer.tsx | 4 +- src/components/setting/mods/misc-viewer.tsx | 4 +- .../setting/mods/network-interface-viewer.tsx | 4 +- .../setting/mods/sysproxy-viewer.tsx | 6 +- src/components/setting/mods/theme-viewer.tsx | 4 +- src/components/setting/mods/tun-viewer.tsx | 10 ++- src/components/setting/mods/update-viewer.tsx | 4 +- src/components/setting/mods/web-ui-viewer.tsx | 4 +- src/components/setting/setting-clash.tsx | 6 +- .../setting/setting-verge-advanced.tsx | 4 +- .../shared/ProxyControlSwitches.tsx | 16 ++-- src/components/test/test-item.tsx | 4 +- src/components/test/test-viewer.tsx | 4 +- src/hooks/use-system-state.ts | 12 +-- src/hooks/useServiceInstaller.ts | 9 +- src/hooks/useServiceUninstaller.ts | 9 +- src/locales/ar.json | 4 + src/locales/de.json | 4 + src/locales/en.json | 4 + src/locales/es.json | 4 + src/locales/fa.json | 4 + src/locales/id.json | 4 + src/locales/jp.json | 4 + src/locales/ko.json | 4 + src/locales/ru.json | 4 + src/locales/tr.json | 4 + src/locales/tt.json | 4 + src/locales/zh.json | 4 + src/locales/zhtw.json | 4 + src/pages/_layout/notificationHandlers.ts | 88 +++++++++++++------ src/pages/profiles.tsx | 41 ++++++--- src/pages/settings.tsx | 4 +- src/services/cmds.ts | 12 +-- src/services/noticeService.ts | 19 ++++ 51 files changed, 328 insertions(+), 168 deletions(-) diff --git a/src/components/home/home-profile-card.tsx b/src/components/home/home-profile-card.tsx index 3638f0f0..1da0109b 100644 --- a/src/components/home/home-profile-card.tsx +++ b/src/components/home/home-profile-card.tsx @@ -26,7 +26,7 @@ import { useNavigate } from "react-router"; import { useAppData } from "@/providers/app-data-context"; import { openWebUrl, updateProfile } from "@/services/cmds"; -import { showNotice } from "@/services/noticeService"; +import { createRawNotice, showNotice } from "@/services/noticeService"; import parseTraffic from "@/utils/parse-traffic"; import { EnhancedCard } from "./enhanced-card"; @@ -297,7 +297,7 @@ export const HomeProfileCard = ({ // 刷新首页数据 refreshAll(); } catch (err: any) { - showNotice("error", err.message || err.toString(), 3000); + showNotice("error", createRawNotice(err.message || err.toString()), 3000); } finally { setUpdating(false); } diff --git a/src/components/home/proxy-tun-card.tsx b/src/components/home/proxy-tun-card.tsx index adbf2b87..9d32c442 100644 --- a/src/components/home/proxy-tun-card.tsx +++ b/src/components/home/proxy-tun-card.tsx @@ -21,7 +21,7 @@ import ProxyControlSwitches from "@/components/shared/ProxyControlSwitches"; import { useSystemProxyState } from "@/hooks/use-system-proxy-state"; import { useSystemState } from "@/hooks/use-system-state"; import { useVerge } from "@/hooks/use-verge"; -import { showNotice } from "@/services/noticeService"; +import { createRawNotice, showNotice } from "@/services/noticeService"; const LOCAL_STORAGE_TAB_KEY = "clash-verge-proxy-active-tab"; @@ -148,7 +148,7 @@ export const ProxyTunCard: FC = () => { const { enable_tun_mode } = verge ?? {}; const handleError = (err: Error) => { - showNotice("error", err.message || err.toString()); + showNotice("error", createRawNotice(err.message || err.toString())); }; const handleTabChange = (tab: string) => { diff --git a/src/components/home/system-info-card.tsx b/src/components/home/system-info-card.tsx index e9f483b7..2c398010 100644 --- a/src/components/home/system-info-card.tsx +++ b/src/components/home/system-info-card.tsx @@ -24,7 +24,7 @@ import { useSystemState } from "@/hooks/use-system-state"; import { useVerge } from "@/hooks/use-verge"; import { useServiceInstaller } from "@/hooks/useServiceInstaller"; import { getSystemInfo } from "@/services/cmds"; -import { showNotice } from "@/services/noticeService"; +import { createRawNotice, showNotice } from "@/services/noticeService"; import { checkUpdateSafe as checkUpdate } from "@/services/update"; import { version as appVersion } from "@root/package.json"; @@ -174,13 +174,15 @@ export const SystemInfoCard = () => { try { const info = await checkUpdate(); if (!info?.available) { - showNotice("success", t("Currently on the Latest Version")); + showNotice("success", { + i18nKey: "Currently on the Latest Version", + }); } else { - showNotice("info", t("Update Available"), 2000); + showNotice("info", { i18nKey: "Update Available" }, 2000); goToSettings(); } } catch (err: any) { - showNotice("error", err.message || err.toString()); + showNotice("error", createRawNotice(err.message || err.toString())); } }); diff --git a/src/components/profile/editor-viewer.tsx b/src/components/profile/editor-viewer.tsx index a37af260..ca326147 100644 --- a/src/components/profile/editor-viewer.tsx +++ b/src/components/profile/editor-viewer.tsx @@ -25,7 +25,7 @@ import { useTranslation } from "react-i18next"; import MonacoEditor from "react-monaco-editor"; import pac from "types-pac/pac.d.ts?raw"; -import { showNotice } from "@/services/noticeService"; +import { createRawNotice, showNotice } from "@/services/noticeService"; import { useThemeMode } from "@/services/states"; import debounce from "@/utils/debounce"; import getSystem from "@/utils/get-system"; @@ -133,7 +133,7 @@ export const EditorViewer = (props: Props) => { currData.current = value; onChange?.(prevData.current, currData.current); } catch (err: any) { - showNotice("error", err.message || err.toString()); + showNotice("error", createRawNotice(err.message || err.toString())); } }); @@ -144,7 +144,7 @@ export const EditorViewer = (props: Props) => { } onClose(); } catch (err: any) { - showNotice("error", err.message || err.toString()); + showNotice("error", createRawNotice(err.message || err.toString())); } }); @@ -152,7 +152,7 @@ export const EditorViewer = (props: Props) => { try { onClose(); } catch (err: any) { - showNotice("error", err.message || err.toString()); + showNotice("error", createRawNotice(err.message || err.toString())); } }); diff --git a/src/components/profile/groups-editor-viewer.tsx b/src/components/profile/groups-editor-viewer.tsx index 2c39c04e..6f494d1a 100644 --- a/src/components/profile/groups-editor-viewer.tsx +++ b/src/components/profile/groups-editor-viewer.tsx @@ -55,7 +55,7 @@ import { readProfileFile, saveProfileFile, } from "@/services/cmds"; -import { showNotice } from "@/services/noticeService"; +import { createRawNotice, showNotice } from "@/services/noticeService"; import { useThemeMode } from "@/services/states"; import getSystem from "@/utils/get-system"; @@ -384,12 +384,14 @@ export const GroupsEditorViewer = (props: Props) => { } await saveProfileFile(property, nextData); - showNotice("success", t("components.profile.notifications.saved")); + showNotice("success", { + i18nKey: "components.profile.notifications.saved", + }); setPrevData(nextData); onSave?.(prevData, nextData); onClose(); } catch (err: any) { - showNotice("error", err.toString()); + showNotice("error", createRawNotice(err.toString())); } }); @@ -920,7 +922,10 @@ export const GroupsEditorViewer = (props: Props) => { } setPrependSeq([formIns.getValues(), ...prependSeq]); } catch (err: any) { - showNotice("error", err.message || err.toString()); + showNotice( + "error", + createRawNotice(err.message || err.toString()), + ); } }} > @@ -946,7 +951,10 @@ export const GroupsEditorViewer = (props: Props) => { } setAppendSeq([...appendSeq, formIns.getValues()]); } catch (err: any) { - showNotice("error", err.message || err.toString()); + showNotice( + "error", + createRawNotice(err.message || err.toString()), + ); } }} > diff --git a/src/components/profile/profile-item.tsx b/src/components/profile/profile-item.tsx index 2d1dd4ef..5e5285ae 100644 --- a/src/components/profile/profile-item.tsx +++ b/src/components/profile/profile-item.tsx @@ -34,7 +34,7 @@ import { saveProfileFile, getNextUpdateTime, } from "@/services/cmds"; -import { showNotice } from "@/services/noticeService"; +import { createRawNotice, showNotice } from "@/services/noticeService"; import { useLoadingCache, useSetLoadingCache } from "@/services/states"; import parseTraffic from "@/utils/parse-traffic"; @@ -321,7 +321,7 @@ export const ProfileItem = (props: Props) => { try { await viewProfile(itemData.uid); } catch (err: any) { - showNotice("error", err?.message || err.toString()); + showNotice("error", createRawNotice(err?.message || err.toString())); } }); diff --git a/src/components/profile/profile-more.tsx b/src/components/profile/profile-more.tsx index 8c63a178..82530b24 100644 --- a/src/components/profile/profile-more.tsx +++ b/src/components/profile/profile-more.tsx @@ -14,7 +14,7 @@ import { useTranslation } from "react-i18next"; import { EditorViewer } from "@/components/profile/editor-viewer"; import { viewProfile, readProfileFile, saveProfileFile } from "@/services/cmds"; -import { showNotice } from "@/services/noticeService"; +import { createRawNotice, showNotice } from "@/services/noticeService"; import { LogViewer } from "./log-viewer"; import { ProfileBox } from "./profile-box"; @@ -48,7 +48,7 @@ export const ProfileMore = (props: Props) => { try { await viewProfile(id); } catch (err: any) { - showNotice("error", err?.message || err.toString()); + showNotice("error", createRawNotice(err?.message || err.toString())); } }); diff --git a/src/components/profile/profile-viewer.tsx b/src/components/profile/profile-viewer.tsx index 59c388d5..9540dc29 100644 --- a/src/components/profile/profile-viewer.tsx +++ b/src/components/profile/profile-viewer.tsx @@ -17,7 +17,7 @@ import { useTranslation } from "react-i18next"; import { BaseDialog, Switch } from "@/components/base"; import { useProfiles } from "@/hooks/use-profiles"; import { createProfile, patchProfile } from "@/services/cmds"; -import { showNotice } from "@/services/noticeService"; +import { createRawNotice, showNotice } from "@/services/noticeService"; import { version } from "@root/package.json"; import { FileInput } from "./file-input"; @@ -187,7 +187,7 @@ export function ProfileViewer({ onChange, ref }: ProfileViewerProps) { onChange(isActivating); }, 0); } catch (err: any) { - showNotice("error", err.message || err.toString()); + showNotice("error", createRawNotice(err.message || err.toString())); } finally { setLoading(false); } diff --git a/src/components/profile/proxies-editor-viewer.tsx b/src/components/profile/proxies-editor-viewer.tsx index c7758035..faf67f76 100644 --- a/src/components/profile/proxies-editor-viewer.tsx +++ b/src/components/profile/proxies-editor-viewer.tsx @@ -42,7 +42,7 @@ import { Virtuoso } from "react-virtuoso"; import { ProxyItem } from "@/components/profile/proxy-item"; import { readProfileFile, saveProfileFile } from "@/services/cmds"; -import { showNotice } from "@/services/noticeService"; +import { createRawNotice, showNotice } from "@/services/noticeService"; import { useThemeMode } from "@/services/states"; import getSystem from "@/utils/get-system"; import parseUri from "@/utils/uri-parser"; @@ -263,11 +263,13 @@ export const ProxiesEditorViewer = (props: Props) => { const handleSave = useLockFn(async () => { try { await saveProfileFile(property, currData); - showNotice("success", t("components.profile.notifications.saved")); + showNotice("success", { + i18nKey: "components.profile.notifications.saved", + }); onSave?.(prevData, currData); onClose(); } catch (err: any) { - showNotice("error", err.toString()); + showNotice("error", createRawNotice(err.toString())); } }); diff --git a/src/components/profile/rules-editor-viewer.tsx b/src/components/profile/rules-editor-viewer.tsx index c0c52dbf..f782c7b0 100644 --- a/src/components/profile/rules-editor-viewer.tsx +++ b/src/components/profile/rules-editor-viewer.tsx @@ -45,7 +45,7 @@ import { Virtuoso } from "react-virtuoso"; import { Switch } from "@/components/base"; import { RuleItem } from "@/components/profile/rule-item"; import { readProfileFile, saveProfileFile } from "@/services/cmds"; -import { showNotice } from "@/services/noticeService"; +import { createRawNotice, showNotice } from "@/services/noticeService"; import { useThemeMode } from "@/services/states"; import getSystem from "@/utils/get-system"; @@ -351,7 +351,10 @@ export const RulesEditorViewer = (props: Props) => { ), ); } catch (e: any) { - showNotice("error", e?.message || e?.toString() || "YAML dump error"); + showNotice( + "error", + createRawNotice(e?.message || e?.toString() || "YAML dump error"), + ); } }; let idleId: number | undefined; @@ -479,11 +482,13 @@ export const RulesEditorViewer = (props: Props) => { const handleSave = useLockFn(async () => { try { await saveProfileFile(property, currData); - showNotice("success", t("components.profile.notifications.saved")); + showNotice("success", { + i18nKey: "components.profile.notifications.saved", + }); onSave?.(prevData, currData); onClose(); } catch (err: any) { - showNotice("error", err.toString()); + showNotice("error", createRawNotice(err.toString())); } }); @@ -626,7 +631,10 @@ export const RulesEditorViewer = (props: Props) => { if (prependSeq.includes(raw)) return; setPrependSeq([raw, ...prependSeq]); } catch (err: any) { - showNotice("error", err.message || err.toString()); + showNotice( + "error", + createRawNotice(err.message || err.toString()), + ); } }} > @@ -644,7 +652,10 @@ export const RulesEditorViewer = (props: Props) => { if (appendSeq.includes(raw)) return; setAppendSeq([...appendSeq, raw]); } catch (err: any) { - showNotice("error", err.message || err.toString()); + showNotice( + "error", + createRawNotice(err.message || err.toString()), + ); } }} > diff --git a/src/components/proxy/provider-button.tsx b/src/components/proxy/provider-button.tsx index e7e99a61..c87dc1bb 100644 --- a/src/components/proxy/provider-button.tsx +++ b/src/components/proxy/provider-button.tsx @@ -67,13 +67,13 @@ export const ProviderButton = () => { await refreshProxyProviders(); showNotice("success", { - i18nKey: "components.notices.providers.updateSuccess", + i18nKey: "components.providers.notices.updateSuccess", params: { name }, }); } catch (err: any) { const message = err?.message || err?.toString?.() || String(err); showNotice("error", { - i18nKey: "components.notices.providers.updateFailed", + i18nKey: "components.providers.notices.updateFailed", params: { name, message }, }); } finally { @@ -89,7 +89,7 @@ export const ProviderButton = () => { const allProviders = Object.keys(proxyProviders || {}); if (allProviders.length === 0) { showNotice("info", { - i18nKey: "components.notices.providers.none", + i18nKey: "components.providers.notices.none", }); return; } @@ -121,12 +121,12 @@ export const ProviderButton = () => { await refreshProxyProviders(); showNotice("success", { - i18nKey: "components.notices.providers.allUpdated", + i18nKey: "components.providers.notices.allUpdated", }); } catch (err: any) { const message = err?.message || err?.toString?.() || String(err); showNotice("error", { - i18nKey: "components.notices.providers.genericError", + i18nKey: "components.providers.notices.genericError", params: { message }, }); } finally { diff --git a/src/components/setting/mods/clash-core-viewer.tsx b/src/components/setting/mods/clash-core-viewer.tsx index be951de6..c8288591 100644 --- a/src/components/setting/mods/clash-core-viewer.tsx +++ b/src/components/setting/mods/clash-core-viewer.tsx @@ -21,7 +21,7 @@ import { closeAllConnections, upgradeCore } from "tauri-plugin-mihomo-api"; import { BaseDialog, DialogRef } from "@/components/base"; import { useVerge } from "@/hooks/use-verge"; import { changeClashCore, restartCore } from "@/services/cmds"; -import { showNotice } from "@/services/noticeService"; +import { createRawNotice, showNotice } from "@/services/noticeService"; const VALID_CORE = [ { name: "Mihomo", core: "verge-mihomo", chip: "Release Version" }, @@ -54,7 +54,7 @@ export function ClashCoreViewer({ ref }: { ref?: Ref }) { const errorMsg = await changeClashCore(core); if (errorMsg) { - showNotice("error", errorMsg); + showNotice("error", createRawNotice(errorMsg)); setChangingCore(null); return; } @@ -67,7 +67,7 @@ export function ClashCoreViewer({ ref }: { ref?: Ref }) { }, 500); } catch (err: any) { setChangingCore(null); - showNotice("error", err.message || err.toString()); + showNotice("error", createRawNotice(err.message || err.toString())); } }); @@ -75,11 +75,11 @@ export function ClashCoreViewer({ ref }: { ref?: Ref }) { try { setRestarting(true); await restartCore(); - showNotice("success", t(`Clash Core Restarted`)); + showNotice("success", { i18nKey: "Clash Core Restarted" }); setRestarting(false); } catch (err: any) { setRestarting(false); - showNotice("error", err.message || err.toString()); + showNotice("error", createRawNotice(err.message || err.toString())); } }); @@ -88,14 +88,14 @@ export function ClashCoreViewer({ ref }: { ref?: Ref }) { setUpgrading(true); await upgradeCore(); setUpgrading(false); - showNotice("success", t(`Core Version Updated`)); + showNotice("success", { i18nKey: "Core Version Updated" }); } catch (err: any) { setUpgrading(false); const errMsg = err.response?.data?.message || err.toString(); const showMsg = errMsg.includes("already using latest version") ? "Already Using Latest Core Version" : errMsg; - showNotice("error", t(showMsg)); + showNotice("error", { i18nKey: showMsg, fallback: showMsg }); } }); diff --git a/src/components/setting/mods/controller-viewer.tsx b/src/components/setting/mods/controller-viewer.tsx index cdf84cc0..a20983b8 100644 --- a/src/components/setting/mods/controller-viewer.tsx +++ b/src/components/setting/mods/controller-viewer.tsx @@ -18,7 +18,7 @@ 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/noticeService"; +import { createPrefixedNotice, showNotice } from "@/services/noticeService"; export function ControllerViewer({ ref }: { ref?: Ref }) { const { t } = useTranslation(); @@ -56,20 +56,18 @@ export function ControllerViewer({ ref }: { ref?: Ref }) { // 如果启用了外部控制器,则保存控制器地址和密钥 if (enableController) { if (!controller.trim()) { - showNotice( - "error", - t( + showNotice("error", { + i18nKey: "components.settings.externalController.messages.addressRequired", - ), - ); + }); return; } if (!secret.trim()) { - showNotice( - "error", - t("components.settings.externalController.messages.secretRequired"), - ); + showNotice("error", { + i18nKey: + "components.settings.externalController.messages.secretRequired", + }); return; } @@ -79,12 +77,15 @@ export function ControllerViewer({ ref }: { ref?: Ref }) { await patchInfo({ "external-controller": "" }); } - showNotice("success", t("Configuration saved successfully")); + showNotice("success", { i18nKey: "Configuration saved successfully" }); setOpen(false); } catch (err: any) { + const message = err?.message || err?.toString?.(); showNotice( "error", - err.message || t("Failed to save configuration"), + message + ? createPrefixedNotice(t("Failed to save configuration"), message) + : { i18nKey: "Failed to save configuration" }, 4000, ); } finally { @@ -101,10 +102,9 @@ export function ControllerViewer({ ref }: { ref?: Ref }) { setTimeout(() => setCopySuccess(null)); } catch (err) { console.warn("[ControllerViewer] copy to clipboard failed:", err); - showNotice( - "error", - t("components.settings.externalController.messages.copyFailed"), - ); + showNotice("error", { + i18nKey: "components.settings.externalController.messages.copyFailed", + }); } }, ); diff --git a/src/components/setting/mods/dns-viewer.tsx b/src/components/setting/mods/dns-viewer.tsx index d66d60f6..c0b652ed 100644 --- a/src/components/setting/mods/dns-viewer.tsx +++ b/src/components/setting/mods/dns-viewer.tsx @@ -29,7 +29,11 @@ import MonacoEditor from "react-monaco-editor"; import { BaseDialog, DialogRef, Switch } from "@/components/base"; import { useClash } from "@/hooks/use-clash"; -import { showNotice } from "@/services/noticeService"; +import { + createPrefixedNotice, + createRawNotice, + showNotice, +} from "@/services/noticeService"; import { useThemeMode } from "@/services/states"; import getSystem from "@/utils/get-system"; @@ -424,9 +428,9 @@ export function DnsViewer({ ref }: { ref?: Ref }) { skipYamlSyncRef.current = true; updateValuesFromConfig(parsedYaml); } catch { - showNotice("error", t("Invalid YAML format")); + showNotice("error", { i18nKey: "Invalid YAML format" }); } - }, [yamlContent, t, updateValuesFromConfig]); + }, [yamlContent, updateValuesFromConfig]); useEffect(() => { if (skipYamlSyncRef.current) { @@ -549,7 +553,10 @@ export function DnsViewer({ ref }: { ref?: Ref }) { showNotice( "error", - t("DNS configuration error") + ": " + cleanErrorMsg, + createPrefixedNotice( + `${t("DNS configuration error")}:`, + cleanErrorMsg, + ), ); return; } @@ -561,9 +568,9 @@ export function DnsViewer({ ref }: { ref?: Ref }) { } setOpen(false); - showNotice("success", t("DNS settings saved")); + showNotice("success", { i18nKey: "DNS settings saved" }); } catch (err: any) { - showNotice("error", err.message || err.toString()); + showNotice("error", createRawNotice(err.message || err.toString())); } }); diff --git a/src/components/setting/mods/external-controller-cors.tsx b/src/components/setting/mods/external-controller-cors.tsx index 87e3d050..ed79f2b3 100644 --- a/src/components/setting/mods/external-controller-cors.tsx +++ b/src/components/setting/mods/external-controller-cors.tsx @@ -140,10 +140,12 @@ export const HeaderConfiguration = forwardRef( manual: true, onSuccess: () => { setOpen(false); - showNotice("success", t("Configuration saved successfully")); + showNotice("success", { + i18nKey: "Configuration saved successfully", + }); }, onError: () => { - showNotice("error", t("Failed to save configuration")); + showNotice("error", { i18nKey: "Failed to save configuration" }); }, }, ); diff --git a/src/components/setting/mods/hotkey-viewer.tsx b/src/components/setting/mods/hotkey-viewer.tsx index d034af30..ee0b064b 100644 --- a/src/components/setting/mods/hotkey-viewer.tsx +++ b/src/components/setting/mods/hotkey-viewer.tsx @@ -5,7 +5,7 @@ import { useTranslation } from "react-i18next"; import { BaseDialog, DialogRef, Switch } from "@/components/base"; import { useVerge } from "@/hooks/use-verge"; -import { showNotice } from "@/services/noticeService"; +import { createRawNotice, showNotice } from "@/services/noticeService"; import { HotkeyInput } from "./hotkey-input"; @@ -82,7 +82,7 @@ export const HotkeyViewer = forwardRef((props, ref) => { }); setOpen(false); } catch (err: any) { - showNotice("error", err.message || err.toString()); + showNotice("error", createRawNotice(err.message || err.toString())); } }); diff --git a/src/components/setting/mods/layout-viewer.tsx b/src/components/setting/mods/layout-viewer.tsx index 1fff6303..502f2e36 100644 --- a/src/components/setting/mods/layout-viewer.tsx +++ b/src/components/setting/mods/layout-viewer.tsx @@ -23,7 +23,7 @@ import { DEFAULT_HOVER_DELAY } from "@/components/proxy/proxy-group-navigator"; import { useVerge } from "@/hooks/use-verge"; import { useWindowDecorations } from "@/hooks/use-window"; import { copyIconFile, getAppDir } from "@/services/cmds"; -import { showNotice } from "@/services/noticeService"; +import { createRawNotice, showNotice } from "@/services/noticeService"; import getSystem from "@/utils/get-system"; import { GuardState } from "./guard-state"; @@ -104,7 +104,7 @@ export const LayoutViewer = forwardRef((_, ref) => { const onSwitchFormat = (_e: any, value: boolean) => value; const onError = (err: any) => { - showNotice("error", err.message || err.toString()); + showNotice("error", createRawNotice(err.message || err.toString())); }; const onChangeData = (patch: Partial) => { mutateVerge({ ...verge, ...patch }, false); diff --git a/src/components/setting/mods/lite-mode-viewer.tsx b/src/components/setting/mods/lite-mode-viewer.tsx index d5d3516e..051c83a8 100644 --- a/src/components/setting/mods/lite-mode-viewer.tsx +++ b/src/components/setting/mods/lite-mode-viewer.tsx @@ -15,7 +15,7 @@ import { BaseDialog, DialogRef, Switch } from "@/components/base"; import { TooltipIcon } from "@/components/base/base-tooltip-icon"; import { useVerge } from "@/hooks/use-verge"; import { entry_lightweight_mode } from "@/services/cmds"; -import { showNotice } from "@/services/noticeService"; +import { createRawNotice, showNotice } from "@/services/noticeService"; export function LiteModeViewer({ ref }: { ref?: Ref }) { const { t } = useTranslation(); @@ -46,7 +46,7 @@ export function LiteModeViewer({ ref }: { ref?: Ref }) { }); setOpen(false); } catch (err: any) { - showNotice("error", err.message || err.toString()); + showNotice("error", createRawNotice(err.message || err.toString())); } }); diff --git a/src/components/setting/mods/misc-viewer.tsx b/src/components/setting/mods/misc-viewer.tsx index 4bb38304..5d624a89 100644 --- a/src/components/setting/mods/misc-viewer.tsx +++ b/src/components/setting/mods/misc-viewer.tsx @@ -14,7 +14,7 @@ import { useTranslation } from "react-i18next"; import { BaseDialog, DialogRef, Switch } from "@/components/base"; import { TooltipIcon } from "@/components/base/base-tooltip-icon"; import { useVerge } from "@/hooks/use-verge"; -import { showNotice } from "@/services/noticeService"; +import { createRawNotice, showNotice } from "@/services/noticeService"; export const MiscViewer = forwardRef((props, ref) => { const { t } = useTranslation(); @@ -70,7 +70,7 @@ export const MiscViewer = forwardRef((props, ref) => { }); setOpen(false); } catch (err: any) { - showNotice("error", err.toString()); + showNotice("error", createRawNotice(err.toString())); } }); diff --git a/src/components/setting/mods/network-interface-viewer.tsx b/src/components/setting/mods/network-interface-viewer.tsx index e4e43cda..013d6c9a 100644 --- a/src/components/setting/mods/network-interface-viewer.tsx +++ b/src/components/setting/mods/network-interface-viewer.tsx @@ -109,8 +109,6 @@ const AddressDisplay = ({ label: string; content: string; }) => { - const { t } = useTranslation(); - return ( { await writeText(content); - showNotice("success", t("Copy Success")); + showNotice("success", { i18nKey: "Copy Success" }); }} > diff --git a/src/components/setting/mods/sysproxy-viewer.tsx b/src/components/setting/mods/sysproxy-viewer.tsx index bab1d3b0..5d00bd81 100644 --- a/src/components/setting/mods/sysproxy-viewer.tsx +++ b/src/components/setting/mods/sysproxy-viewer.tsx @@ -36,7 +36,7 @@ import { getSystemProxy, patchVergeConfig, } from "@/services/cmds"; -import { showNotice } from "@/services/noticeService"; +import { createRawNotice, showNotice } from "@/services/noticeService"; import getSystem from "@/utils/get-system"; const sleep = (ms: number) => @@ -161,7 +161,7 @@ export const SysproxyViewer = forwardRef((props, ref) => { ]); } } catch (err: any) { - showNotice("error", err.toString()); + showNotice("error", createRawNotice(err.toString())); } }; @@ -410,7 +410,7 @@ export const SysproxyViewer = forwardRef((props, ref) => { } catch (err: any) { console.error("配置保存失败:", err); mutateVerge(); - showNotice("error", err.toString()); + showNotice("error", createRawNotice(err.toString())); // setOpen(true); } }); diff --git a/src/components/setting/mods/theme-viewer.tsx b/src/components/setting/mods/theme-viewer.tsx index db61da92..13af6f2b 100644 --- a/src/components/setting/mods/theme-viewer.tsx +++ b/src/components/setting/mods/theme-viewer.tsx @@ -16,7 +16,7 @@ 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/noticeService"; +import { createRawNotice, showNotice } from "@/services/noticeService"; export function ThemeViewer(props: { ref?: React.Ref }) { const { ref } = props; @@ -51,7 +51,7 @@ export function ThemeViewer(props: { ref?: React.Ref }) { await patchVerge({ theme_setting: theme }); setOpen(false); } catch (err: any) { - showNotice("error", err.toString()); + showNotice("error", createRawNotice(err.toString())); } }); diff --git a/src/components/setting/mods/tun-viewer.tsx b/src/components/setting/mods/tun-viewer.tsx index 35882f20..27545008 100644 --- a/src/components/setting/mods/tun-viewer.tsx +++ b/src/components/setting/mods/tun-viewer.tsx @@ -15,7 +15,7 @@ import { useTranslation } from "react-i18next"; import { BaseDialog, DialogRef, Switch } from "@/components/base"; import { useClash } from "@/hooks/use-clash"; import { enhanceProfiles } from "@/services/cmds"; -import { showNotice } from "@/services/noticeService"; +import { createRawNotice, showNotice } from "@/services/noticeService"; import getSystem from "@/utils/get-system"; import { StackModeSwitch } from "./stack-mode-switch"; @@ -80,13 +80,15 @@ export function TunViewer({ ref }: { ref?: Ref }) { ); try { await enhanceProfiles(); - showNotice("success", t("components.settings.tun.messages.applied")); + showNotice("success", { + i18nKey: "components.settings.tun.messages.applied", + }); } catch (err: any) { - showNotice("error", err.message || err.toString()); + showNotice("error", createRawNotice(err.message || err.toString())); } setOpen(false); } catch (err: any) { - showNotice("error", err.message || err.toString()); + showNotice("error", createRawNotice(err.message || err.toString())); } }); diff --git a/src/components/setting/mods/update-viewer.tsx b/src/components/setting/mods/update-viewer.tsx index 28021fdb..47e16e58 100644 --- a/src/components/setting/mods/update-viewer.tsx +++ b/src/components/setting/mods/update-viewer.tsx @@ -12,7 +12,7 @@ import useSWR from "swr"; import { BaseDialog, DialogRef } from "@/components/base"; import { useListen } from "@/hooks/use-listen"; import { portableFlag } from "@/pages/_layout"; -import { showNotice } from "@/services/noticeService"; +import { createRawNotice, showNotice } from "@/services/noticeService"; import { useSetUpdateState, useUpdateState } from "@/services/states"; import { checkUpdateSafe as checkUpdate } from "@/services/update"; @@ -95,7 +95,7 @@ export function UpdateViewer({ ref }: { ref?: Ref }) { await updateInfo.downloadAndInstall(); await relaunch(); } catch (err: any) { - showNotice("error", err?.message || err.toString()); + showNotice("error", createRawNotice(err?.message || err.toString())); } finally { setUpdateState(false); if (progressListener) { diff --git a/src/components/setting/mods/web-ui-viewer.tsx b/src/components/setting/mods/web-ui-viewer.tsx index 734a5805..afa1434a 100644 --- a/src/components/setting/mods/web-ui-viewer.tsx +++ b/src/components/setting/mods/web-ui-viewer.tsx @@ -8,7 +8,7 @@ import { BaseDialog, BaseEmpty, DialogRef } from "@/components/base"; import { useClashInfo } from "@/hooks/use-clash"; import { useVerge } from "@/hooks/use-verge"; import { openWebUrl } from "@/services/cmds"; -import { showNotice } from "@/services/noticeService"; +import { createRawNotice, showNotice } from "@/services/noticeService"; import { WebUIItem } from "./web-ui-item"; @@ -92,7 +92,7 @@ export function WebUIViewer({ ref }: { ref?: Ref }) { await openWebUrl(url); } catch (e: any) { - showNotice("error", e.message || e.toString()); + showNotice("error", createRawNotice(e.message || e.toString())); } }); diff --git a/src/components/setting/setting-clash.tsx b/src/components/setting/setting-clash.tsx index a1e280cd..80194871 100644 --- a/src/components/setting/setting-clash.tsx +++ b/src/components/setting/setting-clash.tsx @@ -11,7 +11,7 @@ import { TooltipIcon } from "@/components/base/base-tooltip-icon"; import { useClash } from "@/hooks/use-clash"; import { useVerge } from "@/hooks/use-verge"; import { invoke_uwp_tool } from "@/services/cmds"; -import { showNotice } from "@/services/noticeService"; +import { createRawNotice, showNotice } from "@/services/noticeService"; import { useClashLog } from "@/services/states"; import getSystem from "@/utils/get-system"; @@ -72,7 +72,7 @@ const SettingClash = ({ onError }: Props) => { t("components.settings.clash.messages.geoDataUpdated"), ); } catch (err: any) { - showNotice("error", err.message || err.toString()); + showNotice("error", createRawNotice(err.message || err.toString())); } }; @@ -89,7 +89,7 @@ const SettingClash = ({ onError }: Props) => { } catch (err: any) { setDnsSettingsEnabled(!enable); localStorage.setItem("dns_settings_enabled", String(!enable)); - showNotice("error", err.message || err.toString()); + showNotice("error", createRawNotice(err.message || err.toString())); await patchVerge({ enable_dns_settings: !enable }).catch(() => {}); throw err; } diff --git a/src/components/setting/setting-verge-advanced.tsx b/src/components/setting/setting-verge-advanced.tsx index 0216a4a2..207138ea 100644 --- a/src/components/setting/setting-verge-advanced.tsx +++ b/src/components/setting/setting-verge-advanced.tsx @@ -13,7 +13,7 @@ import { openDevTools, openLogsDir, } from "@/services/cmds"; -import { showNotice } from "@/services/noticeService"; +import { createRawNotice, showNotice } from "@/services/noticeService"; import { checkUpdateSafe as checkUpdate } from "@/services/update"; import { version } from "@root/package.json"; @@ -55,7 +55,7 @@ const SettingVergeAdvanced = ({ onError: _ }: Props) => { updateRef.current?.open(); } } catch (err: any) { - showNotice("error", err.message || err.toString()); + showNotice("error", createRawNotice(err.message || err.toString())); } }; diff --git a/src/components/shared/ProxyControlSwitches.tsx b/src/components/shared/ProxyControlSwitches.tsx index 78df1587..24d00dac 100644 --- a/src/components/shared/ProxyControlSwitches.tsx +++ b/src/components/shared/ProxyControlSwitches.tsx @@ -21,7 +21,7 @@ import { useSystemState } from "@/hooks/use-system-state"; import { useVerge } from "@/hooks/use-verge"; import { useServiceInstaller } from "@/hooks/useServiceInstaller"; import { useServiceUninstaller } from "@/hooks/useServiceUninstaller"; -import { showNotice } from "@/services/noticeService"; +import { createRawNotice, showNotice } from "@/services/noticeService"; interface ProxySwitchProps { label?: string; @@ -126,8 +126,8 @@ const ProxyControlSwitches = ({ const { enable_tun_mode, enable_system_proxy } = verge ?? {}; const showErrorNotice = useCallback( - (msg: string) => showNotice("error", t(msg)), - [t], + (msg: string) => showNotice("error", { i18nKey: msg }), + [], ); const handleTunToggle = async (value: boolean) => { @@ -145,7 +145,10 @@ const ProxyControlSwitches = ({ await installServiceAndRestartCore(); await mutateSystemState(); } catch (err) { - showNotice("error", (err as Error).message || String(err)); + showNotice( + "error", + createRawNotice((err as Error).message || String(err)), + ); } }); @@ -157,7 +160,10 @@ const ProxyControlSwitches = ({ await uninstallServiceAndRestartCore(); await mutateSystemState(); } catch (err) { - showNotice("error", (err as Error).message || String(err)); + showNotice( + "error", + createRawNotice((err as Error).message || String(err)), + ); } }); diff --git a/src/components/test/test-item.tsx b/src/components/test/test-item.tsx index 70c5d011..0a774c86 100644 --- a/src/components/test/test-item.tsx +++ b/src/components/test/test-item.tsx @@ -12,7 +12,7 @@ import { BaseLoading } from "@/components/base"; import { useListen } from "@/hooks/use-listen"; import { cmdTestDelay, downloadIconCache } from "@/services/cmds"; import delayManager from "@/services/delay"; -import { showNotice } from "@/services/noticeService"; +import { createRawNotice, showNotice } from "@/services/noticeService"; import { TestBox } from "./test-box"; @@ -82,7 +82,7 @@ export const TestItem = ({ try { removeTest(uid); } catch (err: any) { - showNotice("error", err.message || err.toString()); + showNotice("error", createRawNotice(err.message || err.toString())); } }); diff --git a/src/components/test/test-viewer.tsx b/src/components/test/test-viewer.tsx index 3dd5d3b6..82e2fe5d 100644 --- a/src/components/test/test-viewer.tsx +++ b/src/components/test/test-viewer.tsx @@ -7,7 +7,7 @@ import { useTranslation } from "react-i18next"; import { BaseDialog } from "@/components/base"; import { useVerge } from "@/hooks/use-verge"; -import { showNotice } from "@/services/noticeService"; +import { createRawNotice, showNotice } from "@/services/noticeService"; interface Props { onChange: (uid: string, patch?: Partial) => void; @@ -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", createRawNotice(err.message || err.toString())); setLoading(false); } }), diff --git a/src/hooks/use-system-state.ts b/src/hooks/use-system-state.ts index 37425270..aaee50d1 100644 --- a/src/hooks/use-system-state.ts +++ b/src/hooks/use-system-state.ts @@ -67,14 +67,16 @@ export function useSystemState() { disablingTunMode = true; patchVerge({ enable_tun_mode: false }) .then(() => { - showNotice( - "info", - t("TUN Mode automatically disabled due to service unavailable"), - ); + showNotice("info", { + i18nKey: + "TUN Mode automatically disabled due to service unavailable", + }); }) .catch((err) => { console.error("[useVerge] 自动关闭TUN模式失败:", err); - showNotice("error", t("Failed to disable TUN Mode automatically")); + showNotice("error", { + i18nKey: "Failed to disable TUN Mode automatically", + }); }) .finally(() => { const tid = setTimeout(() => { diff --git a/src/hooks/useServiceInstaller.ts b/src/hooks/useServiceInstaller.ts index 2f216876..5303e2c0 100644 --- a/src/hooks/useServiceInstaller.ts +++ b/src/hooks/useServiceInstaller.ts @@ -1,8 +1,7 @@ -import { t } from "i18next"; import { useCallback } from "react"; import { installService, restartCore } from "@/services/cmds"; -import { showNotice } from "@/services/noticeService"; +import { createRawNotice, showNotice } from "@/services/noticeService"; import { useSystemState } from "./use-system-state"; @@ -12,14 +11,14 @@ const executeWithErrorHandling = async ( successMessage?: string, ) => { try { - showNotice("info", t(loadingMessage)); + showNotice("info", { i18nKey: loadingMessage }); await operation(); if (successMessage) { - showNotice("success", t(successMessage)); + showNotice("success", { i18nKey: successMessage }); } } catch (err) { const msg = (err as Error)?.message || String(err); - showNotice("error", msg); + showNotice("error", createRawNotice(msg)); throw err; } }; diff --git a/src/hooks/useServiceUninstaller.ts b/src/hooks/useServiceUninstaller.ts index fbcd76a5..d0ce61cf 100644 --- a/src/hooks/useServiceUninstaller.ts +++ b/src/hooks/useServiceUninstaller.ts @@ -1,8 +1,7 @@ -import { t } from "i18next"; import { useCallback } from "react"; import { restartCore, stopCore, uninstallService } from "@/services/cmds"; -import { showNotice } from "@/services/noticeService"; +import { createRawNotice, showNotice } from "@/services/noticeService"; import { useSystemState } from "./use-system-state"; @@ -12,14 +11,14 @@ const executeWithErrorHandling = async ( successMessage?: string, ) => { try { - showNotice("info", t(loadingMessage)); + showNotice("info", { i18nKey: loadingMessage }); await operation(); if (successMessage) { - showNotice("success", t(successMessage)); + showNotice("success", { i18nKey: successMessage }); } } catch (err) { const msg = (err as Error)?.message || String(err); - showNotice("error", msg); + showNotice("error", createRawNotice(msg)); throw err; } }; diff --git a/src/locales/ar.json b/src/locales/ar.json index 6b08dd3d..49d4b94b 100644 --- a/src/locales/ar.json +++ b/src/locales/ar.json @@ -1081,6 +1081,10 @@ "editorModes": { "visualization": "تصور", "advanced": "متقدم" + }, + "notices": { + "raw": "{{message}}", + "prefixedRaw": "{{prefix}} {{message}}" } } } diff --git a/src/locales/de.json b/src/locales/de.json index da067ddc..539fc585 100644 --- a/src/locales/de.json +++ b/src/locales/de.json @@ -1081,6 +1081,10 @@ "editorModes": { "visualization": "Visualisierung", "advanced": "Erweitert" + }, + "notices": { + "raw": "{{message}}", + "prefixedRaw": "{{prefix}} {{message}}" } } } diff --git a/src/locales/en.json b/src/locales/en.json index 5003b798..c7f6fc0c 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -1081,6 +1081,10 @@ "editorModes": { "visualization": "Visualization", "advanced": "Advanced" + }, + "notices": { + "raw": "{{message}}", + "prefixedRaw": "{{prefix}} {{message}}" } } } diff --git a/src/locales/es.json b/src/locales/es.json index 21e22f77..59c0f24e 100644 --- a/src/locales/es.json +++ b/src/locales/es.json @@ -1081,6 +1081,10 @@ "editorModes": { "visualization": "Visualización", "advanced": "Avanzado" + }, + "notices": { + "raw": "{{message}}", + "prefixedRaw": "{{prefix}} {{message}}" } } } diff --git a/src/locales/fa.json b/src/locales/fa.json index aeebb24c..6ad929c2 100644 --- a/src/locales/fa.json +++ b/src/locales/fa.json @@ -1081,6 +1081,10 @@ "editorModes": { "visualization": "تجسم", "advanced": "پیشرفته" + }, + "notices": { + "raw": "{{message}}", + "prefixedRaw": "{{prefix}} {{message}}" } } } diff --git a/src/locales/id.json b/src/locales/id.json index f9b38537..a909f915 100644 --- a/src/locales/id.json +++ b/src/locales/id.json @@ -1081,6 +1081,10 @@ "editorModes": { "visualization": "Visualisasi", "advanced": "Lanjutan" + }, + "notices": { + "raw": "{{message}}", + "prefixedRaw": "{{prefix}} {{message}}" } } } diff --git a/src/locales/jp.json b/src/locales/jp.json index be3171f9..eae8b3be 100644 --- a/src/locales/jp.json +++ b/src/locales/jp.json @@ -1081,6 +1081,10 @@ "editorModes": { "visualization": "可視化", "advanced": "詳細設定" + }, + "notices": { + "raw": "{{message}}", + "prefixedRaw": "{{prefix}} {{message}}" } } } diff --git a/src/locales/ko.json b/src/locales/ko.json index 418788e8..2e22cda2 100644 --- a/src/locales/ko.json +++ b/src/locales/ko.json @@ -1081,6 +1081,10 @@ "editorModes": { "visualization": "시각화", "advanced": "고급" + }, + "notices": { + "raw": "{{message}}", + "prefixedRaw": "{{prefix}} {{message}}" } } } diff --git a/src/locales/ru.json b/src/locales/ru.json index 631e0930..6930fe49 100644 --- a/src/locales/ru.json +++ b/src/locales/ru.json @@ -1081,6 +1081,10 @@ "editorModes": { "visualization": "Визуализация", "advanced": "Дополнительно" + }, + "notices": { + "raw": "{{message}}", + "prefixedRaw": "{{prefix}} {{message}}" } } } diff --git a/src/locales/tr.json b/src/locales/tr.json index 224feff0..4967a615 100644 --- a/src/locales/tr.json +++ b/src/locales/tr.json @@ -1081,6 +1081,10 @@ "editorModes": { "visualization": "Görselleştirme", "advanced": "Gelişmiş" + }, + "notices": { + "raw": "{{message}}", + "prefixedRaw": "{{prefix}} {{message}}" } } } diff --git a/src/locales/tt.json b/src/locales/tt.json index e45026fd..5d51bc45 100644 --- a/src/locales/tt.json +++ b/src/locales/tt.json @@ -1081,6 +1081,10 @@ "editorModes": { "visualization": "Визуализация", "advanced": "Өстәмә" + }, + "notices": { + "raw": "{{message}}", + "prefixedRaw": "{{prefix}} {{message}}" } } } diff --git a/src/locales/zh.json b/src/locales/zh.json index 33ac411b..62670a80 100644 --- a/src/locales/zh.json +++ b/src/locales/zh.json @@ -1081,6 +1081,10 @@ "editorModes": { "visualization": "可视化", "advanced": "高级" + }, + "notices": { + "raw": "{{message}}", + "prefixedRaw": "{{prefix}} {{message}}" } } } diff --git a/src/locales/zhtw.json b/src/locales/zhtw.json index bda2ac88..6bf8df4c 100644 --- a/src/locales/zhtw.json +++ b/src/locales/zhtw.json @@ -1081,6 +1081,10 @@ "editorModes": { "visualization": "視覺化", "advanced": "進階" + }, + "notices": { + "raw": "{{message}}", + "prefixedRaw": "{{prefix}} {{message}}" } } } diff --git a/src/pages/_layout/notificationHandlers.ts b/src/pages/_layout/notificationHandlers.ts index 0f69ce30..3f846b73 100644 --- a/src/pages/_layout/notificationHandlers.ts +++ b/src/pages/_layout/notificationHandlers.ts @@ -1,4 +1,8 @@ -import { showNotice } from "@/services/noticeService"; +import { + createPrefixedNotice, + createRawNotice, + showNotice, +} from "@/services/noticeService"; type NavigateFunction = (path: string, options?: any) => void; type TranslateFunction = (key: string) => string; @@ -12,69 +16,97 @@ export const handleNoticeMessage = ( const handlers: Record void> = { "import_sub_url::ok": () => { navigate("/profile", { state: { current: msg } }); - showNotice("success", t("Import Subscription Successful")); + showNotice("success", { i18nKey: "Import Subscription Successful" }); }, "import_sub_url::error": () => { navigate("/profile"); - showNotice("error", msg); + showNotice("error", createRawNotice(msg)); }, - "set_config::error": () => showNotice("error", msg), + "set_config::error": () => showNotice("error", createRawNotice(msg)), update_with_clash_proxy: () => showNotice( "success", - `${t("Update with Clash proxy successfully")} ${msg}`, + createPrefixedNotice(t("Update with Clash proxy successfully"), msg), ), update_retry_with_clash: () => - showNotice("info", t("Update failed, retrying with Clash proxy...")), + showNotice("info", { + i18nKey: "Update failed, retrying with Clash proxy...", + }), update_failed_even_with_clash: () => showNotice( "error", - `${t("Update failed even with Clash proxy")}: ${msg}`, + createPrefixedNotice( + `${t("Update failed even with Clash proxy")}:`, + msg, + ), ), - update_failed: () => showNotice("error", msg), + update_failed: () => showNotice("error", createRawNotice(msg)), "config_validate::boot_error": () => - showNotice("error", `${t("Boot Config Validation Failed")} ${msg}`), + showNotice( + "error", + createPrefixedNotice(t("Boot Config Validation Failed"), msg), + ), "config_validate::core_change": () => showNotice( "error", - `${t("Core Change Config Validation Failed")} ${msg}`, + createPrefixedNotice(t("Core Change Config Validation Failed"), msg), ), "config_validate::error": () => - showNotice("error", `${t("Config Validation Failed")} ${msg}`), + showNotice( + "error", + createPrefixedNotice(t("Config Validation Failed"), msg), + ), "config_validate::process_terminated": () => - showNotice("error", t("Config Validation Process Terminated")), + showNotice("error", { + i18nKey: "Config Validation Process Terminated", + }), "config_validate::stdout_error": () => - showNotice("error", `${t("Config Validation Failed")} ${msg}`), + showNotice( + "error", + createPrefixedNotice(t("Config Validation Failed"), msg), + ), "config_validate::script_error": () => - showNotice("error", `${t("Script File Error")} ${msg}`), + showNotice("error", createPrefixedNotice(t("Script File Error"), msg)), "config_validate::script_syntax_error": () => - showNotice("error", `${t("Script Syntax Error")} ${msg}`), + showNotice("error", createPrefixedNotice(t("Script Syntax Error"), msg)), "config_validate::script_missing_main": () => - showNotice("error", `${t("Script Missing Main")} ${msg}`), + showNotice("error", createPrefixedNotice(t("Script Missing Main"), msg)), "config_validate::file_not_found": () => - showNotice("error", `${t("File Not Found")} ${msg}`), + showNotice("error", createPrefixedNotice(t("File Not Found"), msg)), "config_validate::yaml_syntax_error": () => - showNotice("error", `${t("YAML Syntax Error")} ${msg}`), + showNotice("error", createPrefixedNotice(t("YAML Syntax Error"), msg)), "config_validate::yaml_read_error": () => - showNotice("error", `${t("YAML Read Error")} ${msg}`), + showNotice("error", createPrefixedNotice(t("YAML Read Error"), msg)), "config_validate::yaml_mapping_error": () => - showNotice("error", `${t("YAML Mapping Error")} ${msg}`), + showNotice("error", createPrefixedNotice(t("YAML Mapping Error"), msg)), "config_validate::yaml_key_error": () => - showNotice("error", `${t("YAML Key Error")} ${msg}`), + showNotice("error", createPrefixedNotice(t("YAML Key Error"), msg)), "config_validate::yaml_error": () => - showNotice("error", `${t("YAML Error")} ${msg}`), + showNotice("error", createPrefixedNotice(t("YAML Error"), msg)), "config_validate::merge_syntax_error": () => - showNotice("error", `${t("Merge File Syntax Error")} ${msg}`), + showNotice( + "error", + createPrefixedNotice(t("Merge File Syntax Error"), msg), + ), "config_validate::merge_mapping_error": () => - showNotice("error", `${t("Merge File Mapping Error")} ${msg}`), + showNotice( + "error", + createPrefixedNotice(t("Merge File Mapping Error"), msg), + ), "config_validate::merge_key_error": () => - showNotice("error", `${t("Merge File Key Error")} ${msg}`), + showNotice("error", createPrefixedNotice(t("Merge File Key Error"), msg)), "config_validate::merge_error": () => - showNotice("error", `${t("Merge File Error")} ${msg}`), + showNotice("error", createPrefixedNotice(t("Merge File Error"), msg)), "config_core::change_success": () => - showNotice("success", `${t("Core Changed Successfully")}: ${msg}`), + showNotice( + "success", + createPrefixedNotice(`${t("Core Changed Successfully")}:`, msg), + ), "config_core::change_error": () => - showNotice("error", `${t("Failed to Change Core")}: ${msg}`), + showNotice( + "error", + createPrefixedNotice(`${t("Failed to Change Core")}:`, msg), + ), }; const handler = handlers[status]; diff --git a/src/pages/profiles.tsx b/src/pages/profiles.tsx index ea06b868..fe59c4e7 100644 --- a/src/pages/profiles.tsx +++ b/src/pages/profiles.tsx @@ -58,7 +58,11 @@ import { reorderProfile, updateProfile, } from "@/services/cmds"; -import { showNotice } from "@/services/noticeService"; +import { + createPrefixedNotice, + createRawNotice, + showNotice, +} from "@/services/noticeService"; import { useSetLoadingCache, useThemeMode } from "@/services/states"; // 记录profile切换状态 @@ -147,7 +151,10 @@ const ProfilePage = () => { setActivatings((prev) => prev.filter((id) => id !== previousSwitching)); showNotice( "info", - `${t("pages.profiles.notifications.switchInterrupted")}: ${previousSwitching} → ${newProfile}`, + createPrefixedNotice( + t("pages.profiles.notifications.switchInterrupted"), + `${previousSwitching} → ${newProfile}`, + ), 3000, ); }, @@ -191,7 +198,9 @@ const ProfilePage = () => { for (const file of paths) { if (!file.endsWith(".yaml") && !file.endsWith(".yml")) { - showNotice("error", t("pages.profiles.errors.onlyYaml")); + showNotice("error", { + i18nKey: "pages.profiles.errors.onlyYaml", + }); continue; } const item = { @@ -283,13 +292,15 @@ const ProfilePage = () => { if (!url) return; // 校验url是否为http/https if (!/^https?:\/\//i.test(url)) { - showNotice("error", t("pages.profiles.errors.invalidUrl")); + showNotice("error", { + i18nKey: "pages.profiles.errors.invalidUrl", + }); return; } setLoading(true); const handleImportSuccess = async (noticeKey: string) => { - showNotice("success", t(noticeKey)); + showNotice("success", { i18nKey: noticeKey }); setUrl(""); await performRobustRefresh(); }; @@ -301,7 +312,9 @@ const ProfilePage = () => { } catch (initialErr) { console.warn("[订阅导入] 首次导入失败:", initialErr); - showNotice("info", t("pages.profiles.notifications.importRetry")); + showNotice("info", { + i18nKey: "pages.profiles.notifications.importRetry", + }); try { // 使用自身代理尝试导入 await importProfile(url, { @@ -524,7 +537,11 @@ const ProfilePage = () => { } console.error(`[Profile] 切换失败:`, err); - showNotice("error", err?.message || err.toString(), 4000); + showNotice( + "error", + createRawNotice(err?.message || err.toString()), + 4000, + ); } finally { // 只有当前profile仍然是正在切换的profile且序列号匹配时才清理状态 if ( @@ -597,7 +614,7 @@ const ProfilePage = () => { ); } } catch (err: any) { - showNotice("error", err.message || err.toString(), 3000); + showNotice("error", createRawNotice(err.message || err.toString()), 3000); } finally { // 保留正在切换的profile,清除其他状态 setActivatings((prev) => @@ -617,7 +634,7 @@ const ProfilePage = () => { await onEnhance(false); } } catch (err: any) { - showNotice("error", err?.message || err.toString()); + showNotice("error", createRawNotice(err?.message || err.toString())); } finally { setActivatings([]); } @@ -733,9 +750,11 @@ const ProfilePage = () => { setSelectedProfiles(new Set()); setBatchMode(false); - showNotice("success", t("pages.profiles.notifications.batchDeleted")); + showNotice("success", { + i18nKey: "pages.profiles.notifications.batchDeleted", + }); } catch (err: any) { - showNotice("error", err?.message || err.toString()); + showNotice("error", createRawNotice(err?.message || err.toString())); } finally { setActivatings([]); } diff --git a/src/pages/settings.tsx b/src/pages/settings.tsx index 41ea15c2..ec0f8e53 100644 --- a/src/pages/settings.tsx +++ b/src/pages/settings.tsx @@ -9,14 +9,14 @@ import SettingSystem from "@/components/setting/setting-system"; import SettingVergeAdvanced from "@/components/setting/setting-verge-advanced"; import SettingVergeBasic from "@/components/setting/setting-verge-basic"; import { openWebUrl } from "@/services/cmds"; -import { showNotice } from "@/services/noticeService"; +import { createRawNotice, showNotice } from "@/services/noticeService"; import { useThemeMode } from "@/services/states"; const SettingPage = () => { const { t } = useTranslation(); const onError = (err: any) => { - showNotice("error", err?.message || err.toString()); + showNotice("error", createRawNotice(err?.message || err.toString())); }; const toGithubRepo = useLockFn(() => { diff --git a/src/services/cmds.ts b/src/services/cmds.ts index e1e686bd..6b9a237a 100644 --- a/src/services/cmds.ts +++ b/src/services/cmds.ts @@ -2,7 +2,7 @@ import { invoke } from "@tauri-apps/api/core"; import dayjs from "dayjs"; import { getProxies, getProxyProviders } from "tauri-plugin-mihomo-api"; -import { showNotice } from "@/services/noticeService"; +import { createRawNotice, showNotice } from "@/services/noticeService"; export async function copyClashEnv() { return invoke("copy_clash_env"); @@ -315,19 +315,19 @@ export async function getAppDir() { export async function openAppDir() { return invoke("open_app_dir").catch((err) => - showNotice("error", err?.message || err.toString()), + showNotice("error", createRawNotice(err?.message || err.toString())), ); } export async function openCoreDir() { return invoke("open_core_dir").catch((err) => - showNotice("error", err?.message || err.toString()), + showNotice("error", createRawNotice(err?.message || err.toString())), ); } export async function openLogsDir() { return invoke("open_logs_dir").catch((err) => - showNotice("error", err?.message || err.toString()), + showNotice("error", createRawNotice(err?.message || err.toString())), ); } @@ -335,7 +335,7 @@ export const openWebUrl = async (url: string) => { try { await invoke("open_web_url", { url }); } catch (err: any) { - showNotice("error", err.toString()); + showNotice("error", createRawNotice(err.toString())); } }; @@ -377,7 +377,7 @@ export async function cmdTestDelay(url: string) { export async function invoke_uwp_tool() { return invoke("invoke_uwp_tool").catch((err) => - showNotice("error", err?.message || err.toString(), 1500), + showNotice("error", createRawNotice(err?.message || err.toString()), 1500), ); } diff --git a/src/services/noticeService.ts b/src/services/noticeService.ts index 580d346c..f91a0939 100644 --- a/src/services/noticeService.ts +++ b/src/services/noticeService.ts @@ -98,6 +98,25 @@ export function showTranslatedNotice( ); } +export const createRawNotice = ( + message: string, + fallback?: string, +): NoticeTranslationDescriptor => ({ + i18nKey: "common.notices.raw", + params: { message }, + fallback: fallback ?? message, +}); + +export const createPrefixedNotice = ( + prefix: string, + message: string, + fallback?: string, +): NoticeTranslationDescriptor => ({ + i18nKey: "common.notices.prefixedRaw", + params: { prefix, message }, + fallback: fallback ?? `${prefix} ${message}`, +}); + // Hides a specific notification by its ID. export function hideNotice(id: number) {