// 聊天室好友与通用大卡片广播通知监听,集中管理 Echo 订阅和弹窗渲染。 import { escapeHtml } from "./html.js"; /** * 读取页面 CSRF Token。 * * @returns {string} */ function getCsrfToken() { return document.querySelector('meta[name="csrf-token"]')?.content ?? ""; } /** * 等待 Echo 与聊天上下文就绪后再订阅频道。 * * @param {Function} callback * @returns {void} */ function whenEchoReady(callback) { if (!window.Echo || !window.chatContext) { window.setTimeout(() => whenEchoReady(callback), 500); return; } callback(); } /** * 同步移除对方好友关系。 * * @param {string} username * @returns {Promise} */ async function removeFriendBack(username) { await fetch(`/friend/${encodeURIComponent(username)}/remove`, { method: "DELETE", headers: { "Content-Type": "application/json", "X-CSRF-TOKEN": getCsrfToken(), "Accept": "application/json", }, body: JSON.stringify({ room_id: window.chatContext?.roomId, }), }); } /** * 显示好友添加居中大卡弹窗。 * * @param {string} fromUsername * @param {boolean} hasAddedBack * @returns {void} */ export function showFriendBanner(fromUsername, hasAddedBack) { if (!window.chatBanner) { return; } if (hasAddedBack) { window.chatBanner.show({ id: "friend-banner", icon: "🎉💚🎉", title: "好友通知", name: fromUsername, body: "将你加为好友了!", sub: "你们现在互为好友 🎊", gradient: ["#065f46", "#059669", "#10b981"], titleColor: "#a7f3d0", autoClose: 5000, }); return; } window.chatBanner.show({ id: "friend-banner", icon: "💚📩", title: "好友申请", name: fromUsername, body: "将你加为好友了!", sub: "但你还没有回加对方为好友", gradient: ["#1e3a5f", "#1d4ed8", "#0891b2"], titleColor: "#bae6fd", autoClose: 0, buttons: [ { label: "➕ 回加好友", color: "#10b981", onClick: async (button, close) => { await window.quickFriendAction?.("add", fromUsername, button); if (button.textContent.startsWith("✅")) { window.setTimeout(close, 1500); } }, }, { label: "稍后再说", color: "rgba(255,255,255,0.15)", onClick: (button, close) => close(), }, ], }); } /** * 订阅好友私有频道通知。 * * @returns {void} */ export function setupFriendNotification() { whenEchoReady(() => { const myId = window.chatContext.userId; window.Echo.private(`user.${myId}`) .listen(".FriendAdded", (event) => { showFriendBanner(event.from_username, event.has_added_back); }) .listen(".FriendRemoved", (event) => { const fromUsername = String(event.from_username ?? ""); const safeUsername = escapeHtml(fromUsername); if (event.had_added_back) { window.chatToast?.show?.({ title: "好友通知", message: `${safeUsername} 已将你从好友列表移除。
你的好友列表中仍保留对方,可点击同步移除。`, icon: "👥", color: "#6b7280", duration: 10000, action: { label: `🗑️ 同步移除 ${fromUsername}`, onClick: async () => removeFriendBack(fromUsername), }, }); return; } window.chatToast?.show?.({ title: "好友通知", message: `${safeUsername} 已将你从他的好友列表移除。`, icon: "👥", color: "#9ca3af", }); }); }); } /** * 订阅通用大卡片通知广播。 * * @returns {void} */ export function setupBannerNotification() { whenEchoReady(() => { const myId = window.chatContext.userId; const roomId = window.chatContext.roomId; const showBanner = (event) => { if (event.options && typeof event.options === "object") { window.chatBanner?.show?.(event.options); } }; // 私有频道只推送给指定用户,房间频道推送给当前房间在线用户。 window.Echo.private(`user.${myId}`).listen(".BannerNotification", showBanner); if (roomId) { window.Echo.join(`room.${roomId}`).listen(".BannerNotification", showBanner); } }); } /** * 绑定好友与大卡片通知监听,并暴露旧全局函数。 * * @returns {void} */ export function bindFriendNotificationControls() { if (typeof window === "undefined") { return; } window.showFriendBanner = showFriendBanner; window.setupFriendNotification = setupFriendNotification; window.setupBannerNotification = setupBannerNotification; setupFriendNotification(); setupBannerNotification(); }