chore: notice i18n
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import { CloseRounded } from "@mui/icons-material";
|
||||
import { Snackbar, Alert, IconButton, Box } from "@mui/material";
|
||||
import React, { useSyncExternalStore } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
import {
|
||||
subscribeNotices,
|
||||
@@ -9,6 +10,7 @@ import {
|
||||
} from "@/services/noticeService";
|
||||
|
||||
export const NoticeManager: React.FC = () => {
|
||||
const { t } = useTranslation();
|
||||
const currentNotices = useSyncExternalStore(
|
||||
subscribeNotices,
|
||||
getSnapshotNotices,
|
||||
@@ -60,7 +62,12 @@ export const NoticeManager: React.FC = () => {
|
||||
</IconButton>
|
||||
}
|
||||
>
|
||||
{notice.message}
|
||||
{notice.i18n
|
||||
? t(notice.i18n.i18nKey, {
|
||||
defaultValue: notice.i18n.fallback,
|
||||
...(notice.i18n.params ?? {}),
|
||||
})
|
||||
: notice.message}
|
||||
</Alert>
|
||||
</Snackbar>
|
||||
))}
|
||||
|
||||
@@ -66,12 +66,16 @@ export const ProviderButton = () => {
|
||||
await refreshProxy();
|
||||
await refreshProxyProviders();
|
||||
|
||||
showNotice("success", `${name} 更新成功`);
|
||||
showNotice("success", {
|
||||
i18nKey: "notice.provider.updateSuccess",
|
||||
params: { name },
|
||||
});
|
||||
} catch (err: any) {
|
||||
showNotice(
|
||||
"error",
|
||||
`${name} 更新失败: ${err?.message || err.toString()}`,
|
||||
);
|
||||
const message = err?.message || err?.toString?.() || String(err);
|
||||
showNotice("error", {
|
||||
i18nKey: "notice.provider.updateFailed",
|
||||
params: { name, message },
|
||||
});
|
||||
} finally {
|
||||
// 清除更新状态
|
||||
setUpdating((prev) => ({ ...prev, [name]: false }));
|
||||
@@ -84,7 +88,9 @@ export const ProviderButton = () => {
|
||||
// 获取所有provider的名称
|
||||
const allProviders = Object.keys(proxyProviders || {});
|
||||
if (allProviders.length === 0) {
|
||||
showNotice("info", "没有可更新的代理提供者");
|
||||
showNotice("info", {
|
||||
i18nKey: "notice.provider.none",
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -114,9 +120,15 @@ export const ProviderButton = () => {
|
||||
await refreshProxy();
|
||||
await refreshProxyProviders();
|
||||
|
||||
showNotice("success", "全部代理提供者更新成功");
|
||||
showNotice("success", {
|
||||
i18nKey: "notice.provider.allUpdated",
|
||||
});
|
||||
} catch (err: any) {
|
||||
showNotice("error", `更新失败: ${err?.message || err.toString()}`);
|
||||
const message = err?.message || err?.toString?.() || String(err);
|
||||
showNotice("error", {
|
||||
i18nKey: "notice.provider.genericError",
|
||||
params: { message },
|
||||
});
|
||||
} finally {
|
||||
// 清除所有更新状态
|
||||
setUpdating({});
|
||||
|
||||
@@ -58,12 +58,16 @@ export const ProviderButton = () => {
|
||||
await refreshRules();
|
||||
await refreshRuleProviders();
|
||||
|
||||
showNotice("success", `${name} 更新成功`);
|
||||
showNotice("success", {
|
||||
i18nKey: "notice.provider.updateSuccess",
|
||||
params: { name },
|
||||
});
|
||||
} catch (err: any) {
|
||||
showNotice(
|
||||
"error",
|
||||
`${name} 更新失败: ${err?.message || err.toString()}`,
|
||||
);
|
||||
const message = err?.message || err?.toString?.() || String(err);
|
||||
showNotice("error", {
|
||||
i18nKey: "notice.provider.updateFailed",
|
||||
params: { name, message },
|
||||
});
|
||||
} finally {
|
||||
// 清除更新状态
|
||||
setUpdating((prev) => ({ ...prev, [name]: false }));
|
||||
@@ -76,7 +80,9 @@ export const ProviderButton = () => {
|
||||
// 获取所有provider的名称
|
||||
const allProviders = Object.keys(ruleProviders || {});
|
||||
if (allProviders.length === 0) {
|
||||
showNotice("info", "没有可更新的规则提供者");
|
||||
showNotice("info", {
|
||||
i18nKey: "notice.provider.none",
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -106,9 +112,15 @@ export const ProviderButton = () => {
|
||||
await refreshRules();
|
||||
await refreshRuleProviders();
|
||||
|
||||
showNotice("success", "全部规则提供者更新成功");
|
||||
showNotice("success", {
|
||||
i18nKey: "notice.provider.allUpdated",
|
||||
});
|
||||
} catch (err: any) {
|
||||
showNotice("error", `更新失败: ${err?.message || err.toString()}`);
|
||||
const message = err?.message || err?.toString?.() || String(err);
|
||||
showNotice("error", {
|
||||
i18nKey: "notice.provider.genericError",
|
||||
params: { message },
|
||||
});
|
||||
} finally {
|
||||
// 清除所有更新状态
|
||||
setUpdating({});
|
||||
|
||||
@@ -203,6 +203,13 @@
|
||||
"Close Connection": "Close Connection",
|
||||
"Rules": "Rules",
|
||||
"Rule Provider": "Rule Provider",
|
||||
"notice.forceRefreshCompleted": "Force refresh completed",
|
||||
"notice.emergencyRefreshFailed": "Emergency refresh failed: {{message}}",
|
||||
"notice.provider.updateSuccess": "{{name}} updated successfully",
|
||||
"notice.provider.updateFailed": "Failed to update {{name}}: {{message}}",
|
||||
"notice.provider.genericError": "Update failed: {{message}}",
|
||||
"notice.provider.none": "No providers available to update",
|
||||
"notice.provider.allUpdated": "All providers updated successfully",
|
||||
"Logs": "Logs",
|
||||
"Pause": "Pause",
|
||||
"Resume": "Resume",
|
||||
|
||||
@@ -203,6 +203,13 @@
|
||||
"Close Connection": "关闭连接",
|
||||
"Rules": "规则",
|
||||
"Rule Provider": "规则集合",
|
||||
"notice.forceRefreshCompleted": "数据已强制刷新",
|
||||
"notice.emergencyRefreshFailed": "紧急刷新失败: {{message}}",
|
||||
"notice.provider.updateSuccess": "{{name}} 更新成功",
|
||||
"notice.provider.updateFailed": "{{name}} 更新失败: {{message}}",
|
||||
"notice.provider.genericError": "更新失败: {{message}}",
|
||||
"notice.provider.none": "没有可更新的提供者",
|
||||
"notice.provider.allUpdated": "全部提供者更新成功",
|
||||
"Logs": "日志",
|
||||
"Pause": "暂停",
|
||||
"Resume": "继续",
|
||||
|
||||
@@ -345,10 +345,18 @@ const ProfilePage = () => {
|
||||
await new Promise((resolve) => setTimeout(resolve, 500));
|
||||
await onEnhance(false);
|
||||
|
||||
showNotice("success", "数据已强制刷新", 2000);
|
||||
showNotice("success", { i18nKey: "notice.forceRefreshCompleted" }, 2000);
|
||||
} catch (error: any) {
|
||||
console.error("[紧急刷新] 失败:", error);
|
||||
showNotice("error", `紧急刷新失败: ${error.message}`, 4000);
|
||||
const message = error?.message || String(error);
|
||||
showNotice(
|
||||
"error",
|
||||
{
|
||||
i18nKey: "notice.emergencyRefreshFailed",
|
||||
params: { message },
|
||||
},
|
||||
4000,
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -1,9 +1,20 @@
|
||||
import { ReactNode } from "react";
|
||||
|
||||
type NoticeType = "success" | "error" | "info";
|
||||
|
||||
export interface NoticeTranslationDescriptor {
|
||||
i18nKey: string;
|
||||
params?: Record<string, unknown>;
|
||||
fallback?: string;
|
||||
}
|
||||
|
||||
type NoticeMessage = ReactNode | NoticeTranslationDescriptor;
|
||||
|
||||
interface NoticeItem {
|
||||
id: number;
|
||||
type: "success" | "error" | "info";
|
||||
message: ReactNode;
|
||||
type: NoticeType;
|
||||
message?: ReactNode;
|
||||
i18n?: NoticeTranslationDescriptor;
|
||||
duration: number;
|
||||
timerId?: ReturnType<typeof setTimeout>;
|
||||
}
|
||||
@@ -18,11 +29,25 @@ function notifyListeners() {
|
||||
listeners.forEach((listener) => listener([...notices])); // Pass a copy
|
||||
}
|
||||
|
||||
function isTranslationDescriptor(
|
||||
message: NoticeMessage,
|
||||
): message is NoticeTranslationDescriptor {
|
||||
if (
|
||||
typeof message === "object" &&
|
||||
message !== null &&
|
||||
Object.prototype.hasOwnProperty.call(message, "i18nKey")
|
||||
) {
|
||||
const descriptor = message as NoticeTranslationDescriptor;
|
||||
return typeof descriptor.i18nKey === "string";
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Shows a notification.
|
||||
|
||||
export function showNotice(
|
||||
type: "success" | "error" | "info",
|
||||
message: ReactNode,
|
||||
type: NoticeType,
|
||||
message: NoticeMessage,
|
||||
duration?: number,
|
||||
): number {
|
||||
const id = nextId++;
|
||||
@@ -32,10 +57,15 @@ export function showNotice(
|
||||
const newNotice: NoticeItem = {
|
||||
id,
|
||||
type,
|
||||
message,
|
||||
duration: effectiveDuration,
|
||||
};
|
||||
|
||||
if (isTranslationDescriptor(message)) {
|
||||
newNotice.i18n = message;
|
||||
} else {
|
||||
newNotice.message = message;
|
||||
}
|
||||
|
||||
// Auto-hide timer (only if duration is not null/0)
|
||||
if (effectiveDuration > 0) {
|
||||
newNotice.timerId = setTimeout(() => {
|
||||
@@ -48,6 +78,26 @@ export function showNotice(
|
||||
return id;
|
||||
}
|
||||
|
||||
export function showTranslatedNotice(
|
||||
type: NoticeType,
|
||||
i18nKey: string,
|
||||
options?: {
|
||||
params?: Record<string, unknown>;
|
||||
fallback?: string;
|
||||
},
|
||||
duration?: number,
|
||||
) {
|
||||
return showNotice(
|
||||
type,
|
||||
{
|
||||
i18nKey,
|
||||
params: options?.params,
|
||||
fallback: options?.fallback,
|
||||
},
|
||||
duration,
|
||||
);
|
||||
}
|
||||
|
||||
// Hides a specific notification by its ID.
|
||||
|
||||
export function hideNotice(id: number) {
|
||||
|
||||
Reference in New Issue
Block a user