feat: 错误监控面板、生成通知、社区搜索、任务队列优化
- AdminMonitor: admin用户可见的客户端错误实时监控面板,右下角浮窗 - generationNotifier: 生成完成浏览器通知 + 站内Toast - CommunityPage: 新增搜索框,标题/描述/标签模糊匹配,防抖300ms - App.tsx: 全局unhandled error/rejection监听上报 - WorkbenchPage: 任务并发提示改为显示当前任务数 - serverConnection: 后端client-errors路由注册 - WelcomeSplash: 欢迎按钮全程显示 Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,50 @@
|
||||
/**
|
||||
* Browser notification + in-app toast for generation task completions.
|
||||
* Falls back gracefully when Notification API is unavailable.
|
||||
*/
|
||||
|
||||
let permissionGranted = false;
|
||||
|
||||
async function requestPermission(): Promise<boolean> {
|
||||
if (permissionGranted) return true;
|
||||
if (typeof Notification === "undefined") return false;
|
||||
if (Notification.permission === "granted") { permissionGranted = true; return true; }
|
||||
if (Notification.permission === "denied") return false;
|
||||
try {
|
||||
const result = await Notification.requestPermission();
|
||||
permissionGranted = result === "granted";
|
||||
return permissionGranted;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export function notifyTaskCompleted(label: string, mode: "image" | "video" = "image") {
|
||||
const emoji = mode === "video" ? "🎬" : "🖼️";
|
||||
const title = `${emoji} ${label}生成完成`;
|
||||
const body = "点击返回查看生成结果";
|
||||
|
||||
// Browser notification (background tab)
|
||||
if (typeof Notification !== "undefined" && Notification.permission === "granted") {
|
||||
try { new Notification(title, { body, icon: "/favicon.ico", tag: "gen-complete" }); } catch { /* */ }
|
||||
}
|
||||
|
||||
// In-app toast
|
||||
dispatchGenToast(title);
|
||||
}
|
||||
|
||||
// Use the existing toast system for in-app notifications
|
||||
function dispatchGenToast(msg: string) {
|
||||
try {
|
||||
import("../components/toast/toastStore").then((m) => m.toast(msg, "success"));
|
||||
} catch { /* toast system not loaded */ }
|
||||
}
|
||||
|
||||
/** Call once on app init to pre-warm permission. */
|
||||
export async function initNotificationPermission() {
|
||||
if (typeof Notification === "undefined") return;
|
||||
if (Notification.permission === "default") {
|
||||
// Don't prompt immediately — wait for first user interaction
|
||||
document.addEventListener("click", () => requestPermission(), { once: true });
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user