refactor(notice): unify showNotice usage

This commit is contained in:
Slinetrac
2025-11-02 16:11:48 +08:00
Unverified
parent 5fb67cf921
commit 29c93b0d99
37 changed files with 159 additions and 248 deletions

View File

@@ -193,7 +193,8 @@ const TrafficErrorFallback: React.FC<TrafficErrorFallbackProps> = ({
<Alert severity="error" sx={{ mb: 2, maxWidth: 400 }}>
<Typography variant="body2">
<strong>Error:</strong> {error?.message || "Unknown error"}
<strong>Error:</strong>{" "}
{error instanceof Error ? error.message : "Unknown error"}
</Typography>
{retryCount > 0 && (
<Typography variant="caption" display="block" sx={{ mt: 1 }}>

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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) => {

View File

@@ -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);
}
});

View File

@@ -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",
);
}
}

View File

@@ -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,
);
// 不阻塞主流程
}

View File

@@ -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 {
// 清除所有更新状态

View File

@@ -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 {
// 清除所有更新状态

View File

@@ -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);
}

View File

@@ -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",
);
}
});

View File

@@ -75,7 +75,7 @@ export function ClashCoreViewer({ ref }: { ref?: Ref<DialogRef> }) {
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<DialogRef> }) {
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;

View File

@@ -69,16 +69,10 @@ export const ClashPortViewer = forwardRef<ClashPortViewerRef>((_, 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");
},
},
);

View File

@@ -56,18 +56,16 @@ export function ControllerViewer({ ref }: { ref?: Ref<DialogRef> }) {
// 如果启用了外部控制器,则保存控制器地址和密钥
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<DialogRef> }) {
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<DialogRef> }) {
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",
);
}
},
);

View File

@@ -424,7 +424,7 @@ export function DnsViewer({ ref }: { ref?: Ref<DialogRef> }) {
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<DialogRef> }) {
}
}
showNotice.error(`${t("DNS configuration error")}:`, cleanErrorMsg);
showNotice.error("DNS configuration error:", cleanErrorMsg);
return;
}
@@ -558,7 +558,7 @@ export function DnsViewer({ ref }: { ref?: Ref<DialogRef> }) {
}
setOpen(false);
showNotice.success({ i18nKey: "DNS settings saved" });
showNotice.success("DNS settings saved");
} catch (err) {
showNotice.error(err);
}

View File

@@ -140,12 +140,10 @@ export const HeaderConfiguration = forwardRef<ClashHeaderConfigingRef>(
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");
},
},
);

View File

@@ -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);

View File

@@ -133,7 +133,7 @@ const AddressDisplay = ({
size="small"
onClick={async () => {
await writeText(content);
showNotice.success({ i18nKey: "Copy Success" });
showNotice.success("Copy Success");
}}
>
<ContentCopyRounded sx={{ fontSize: "18px" }} />

View File

@@ -278,14 +278,12 @@ export const SysproxyViewer = forwardRef<DialogRef>((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<DialogRef>((props, ref) => {
!hostnameRegex.test(value.proxy_host)
) {
showNotice.error(
t("components.settings.sysproxy.messages.invalidProxyHost"),
"components.settings.sysproxy.messages.invalidProxyHost",
);
return;
}

View File

@@ -58,14 +58,12 @@ export function UpdateViewer({ ref }: { ref?: Ref<DialogRef> }) {
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;

View File

@@ -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);
}

View File

@@ -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",
);
}

View File

@@ -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 (
<SettingList title={t("components.settings.verge.advanced.title")}>

View File

@@ -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 (
<SettingList title={t("components.settings.verge.basic.title")}>

View File

@@ -82,7 +82,7 @@ export const TestItem = ({
try {
removeTest(uid);
} catch (err: any) {
showNotice.error(err.message || err.toString());
showNotice.error(err);
}
});

View File

@@ -101,7 +101,7 @@ export const TestViewer = forwardRef<TestViewerRef, Props>((props, ref) => {
setLoading(false);
setTimeout(() => formIns.reset(), 500);
} catch (err: any) {
showNotice.error(err.message || err.toString());
showNotice.error(err);
setLoading(false);
}
}),

View File

@@ -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,

View File

@@ -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;
}
};

View File

@@ -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;
}
};

View File

@@ -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",

View File

@@ -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",

View File

@@ -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",

View File

@@ -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];

View File

@@ -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)";

View File

@@ -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(() => {

View File

@@ -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);
}
});

View File

@@ -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<string, unknown>;
}
/**
* 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();
}