迁移好友通知监听脚本
This commit is contained in:
@@ -12,6 +12,7 @@ export { bindChatImageUploadControl } from "./chat-room/image-upload.js";
|
|||||||
export { bindChatComposerControls } from "./chat-room/composer.js";
|
export { bindChatComposerControls } from "./chat-room/composer.js";
|
||||||
export { bindChatToast } from "./chat-room/toast.js";
|
export { bindChatToast } from "./chat-room/toast.js";
|
||||||
export { bindFriendPanelControls, closeFriendPanel, friendSearch, loadFriends, openFriendPanel, quickFriendAction } from "./chat-room/friend-panel.js";
|
export { bindFriendPanelControls, closeFriendPanel, friendSearch, loadFriends, openFriendPanel, quickFriendAction } from "./chat-room/friend-panel.js";
|
||||||
|
export { bindFriendNotificationControls, setupBannerNotification, setupFriendNotification, showFriendBanner } from "./chat-room/friend-notifications.js";
|
||||||
export { closeChatImageLightbox, initChatImageLightboxEvents, openChatImageLightbox } from "./chat-room/lightbox.js";
|
export { closeChatImageLightbox, initChatImageLightboxEvents, openChatImageLightbox } from "./chat-room/lightbox.js";
|
||||||
export {
|
export {
|
||||||
bindMobileDrawerControls,
|
bindMobileDrawerControls,
|
||||||
@@ -93,6 +94,7 @@ import { bindChatImageUploadControl } from "./chat-room/image-upload.js";
|
|||||||
import { bindChatComposerControls } from "./chat-room/composer.js";
|
import { bindChatComposerControls } from "./chat-room/composer.js";
|
||||||
import { bindChatToast } from "./chat-room/toast.js";
|
import { bindChatToast } from "./chat-room/toast.js";
|
||||||
import { bindFriendPanelControls, closeFriendPanel, friendSearch, loadFriends, openFriendPanel, quickFriendAction } from "./chat-room/friend-panel.js";
|
import { bindFriendPanelControls, closeFriendPanel, friendSearch, loadFriends, openFriendPanel, quickFriendAction } from "./chat-room/friend-panel.js";
|
||||||
|
import { bindFriendNotificationControls, setupBannerNotification, setupFriendNotification, showFriendBanner } from "./chat-room/friend-notifications.js";
|
||||||
import { closeChatImageLightbox, initChatImageLightboxEvents, openChatImageLightbox } from "./chat-room/lightbox.js";
|
import { closeChatImageLightbox, initChatImageLightboxEvents, openChatImageLightbox } from "./chat-room/lightbox.js";
|
||||||
import {
|
import {
|
||||||
bindMobileDrawerControls,
|
bindMobileDrawerControls,
|
||||||
@@ -182,11 +184,15 @@ if (typeof window !== "undefined") {
|
|||||||
bindChatComposerControls,
|
bindChatComposerControls,
|
||||||
bindChatToast,
|
bindChatToast,
|
||||||
bindFriendPanelControls,
|
bindFriendPanelControls,
|
||||||
|
bindFriendNotificationControls,
|
||||||
closeFriendPanel,
|
closeFriendPanel,
|
||||||
friendSearch,
|
friendSearch,
|
||||||
loadFriends,
|
loadFriends,
|
||||||
openFriendPanel,
|
openFriendPanel,
|
||||||
quickFriendAction,
|
quickFriendAction,
|
||||||
|
setupBannerNotification,
|
||||||
|
setupFriendNotification,
|
||||||
|
showFriendBanner,
|
||||||
bindMobileDrawerControls,
|
bindMobileDrawerControls,
|
||||||
closeMobileDrawer,
|
closeMobileDrawer,
|
||||||
loadMobileRoomList,
|
loadMobileRoomList,
|
||||||
@@ -265,6 +271,9 @@ if (typeof window !== "undefined") {
|
|||||||
window.friendSearch = friendSearch;
|
window.friendSearch = friendSearch;
|
||||||
window.openFriendPanel = openFriendPanel;
|
window.openFriendPanel = openFriendPanel;
|
||||||
window.quickFriendAction = quickFriendAction;
|
window.quickFriendAction = quickFriendAction;
|
||||||
|
window.setupBannerNotification = setupBannerNotification;
|
||||||
|
window.setupFriendNotification = setupFriendNotification;
|
||||||
|
window.showFriendBanner = showFriendBanner;
|
||||||
window.closeMobileDrawer = closeMobileDrawer;
|
window.closeMobileDrawer = closeMobileDrawer;
|
||||||
window.loadMobileRoomList = loadMobileRoomList;
|
window.loadMobileRoomList = loadMobileRoomList;
|
||||||
window.openMobileDrawer = openMobileDrawer;
|
window.openMobileDrawer = openMobileDrawer;
|
||||||
@@ -305,6 +314,7 @@ if (typeof window !== "undefined") {
|
|||||||
bindChatComposerControls();
|
bindChatComposerControls();
|
||||||
bindChatToast();
|
bindChatToast();
|
||||||
bindFriendPanelControls();
|
bindFriendPanelControls();
|
||||||
|
bindFriendNotificationControls();
|
||||||
bindToolbarControls();
|
bindToolbarControls();
|
||||||
bindUserTargetActions();
|
bindUserTargetActions();
|
||||||
bindAdminMenuControls();
|
bindAdminMenuControls();
|
||||||
|
|||||||
@@ -0,0 +1,188 @@
|
|||||||
|
// 聊天室好友与通用大卡片广播通知监听,集中管理 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<void>}
|
||||||
|
*/
|
||||||
|
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: `<b>${safeUsername}</b> 已将你从好友列表移除。<br><span style="color:#6b7280; font-size:12px;">你的好友列表中仍保留对方,可点击同步移除。</span>`,
|
||||||
|
icon: "👥",
|
||||||
|
color: "#6b7280",
|
||||||
|
duration: 10000,
|
||||||
|
action: {
|
||||||
|
label: `🗑️ 同步移除 ${fromUsername}`,
|
||||||
|
onClick: async () => removeFriendBack(fromUsername),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
window.chatToast?.show?.({
|
||||||
|
title: "好友通知",
|
||||||
|
message: `<b>${safeUsername}</b> 已将你从他的好友列表移除。`,
|
||||||
|
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();
|
||||||
|
}
|
||||||
@@ -1361,153 +1361,4 @@
|
|||||||
{{-- openRewardModal 已迁移到 resources/js/chat-room/reward-modal.js --}}
|
{{-- openRewardModal 已迁移到 resources/js/chat-room/reward-modal.js --}}
|
||||||
|
|
||||||
{{-- ═══════════ 好友系统通知监听 ═══════════ --}}
|
{{-- ═══════════ 好友系统通知监听 ═══════════ --}}
|
||||||
{{-- 监听好友 WebSocket 事件,与好友操作逻辑集中在同一文件维护 --}}
|
{{-- 好友通知与 BannerNotification 监听已迁移到 resources/js/chat-room/friend-notifications.js --}}
|
||||||
<script>
|
|
||||||
// ── 好友系统私有频道监听(仅本人可见) ────────────────
|
|
||||||
/**
|
|
||||||
* 监听当前用户的私有频道 `user.{id}`,
|
|
||||||
* 收到 FriendAdded / FriendRemoved 事件时用弹窗通知。
|
|
||||||
* FriendAdded → 居中大卡弹窗(chatBanner 风格)
|
|
||||||
* FriendRemoved → 右下角 Toast 通知
|
|
||||||
*/
|
|
||||||
function setupFriendNotification() {
|
|
||||||
if (!window.Echo || !window.chatContext) {
|
|
||||||
setTimeout(setupFriendNotification, 500);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const myId = window.chatContext.userId;
|
|
||||||
window.Echo.private(`user.${myId}`)
|
|
||||||
.listen('.FriendAdded', (e) => {
|
|
||||||
showFriendBanner(e.from_username, e.has_added_back);
|
|
||||||
})
|
|
||||||
.listen('.FriendRemoved', (e) => {
|
|
||||||
if (e.had_added_back) {
|
|
||||||
window.chatToast.show({
|
|
||||||
title: '好友通知',
|
|
||||||
message: `<b>${e.from_username}</b> 已将你从好友列表移除。<br><span style="color:#6b7280; font-size:12px;">你的好友列表中仍保留对方,可点击同步移除。</span>`,
|
|
||||||
icon: '👥',
|
|
||||||
color: '#6b7280',
|
|
||||||
duration: 10000,
|
|
||||||
action: {
|
|
||||||
label: `🗑️ 同步移除 ${e.from_username}`,
|
|
||||||
onClick: async () => {
|
|
||||||
const url = `/friend/${encodeURIComponent(e.from_username)}/remove`;
|
|
||||||
const csrf = document.querySelector('meta[name="csrf-token"]')
|
|
||||||
?.content ?? '';
|
|
||||||
await fetch(url, {
|
|
||||||
method: 'DELETE',
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
'X-CSRF-TOKEN': csrf,
|
|
||||||
'Accept': 'application/json'
|
|
||||||
},
|
|
||||||
body: JSON.stringify({
|
|
||||||
room_id: window.chatContext?.roomId
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
window.chatToast.show({
|
|
||||||
title: '好友通知',
|
|
||||||
message: `<b>${e.from_username}</b> 已将你从他的好友列表移除。`,
|
|
||||||
icon: '👥',
|
|
||||||
color: '#9ca3af',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
document.addEventListener('DOMContentLoaded', setupFriendNotification);
|
|
||||||
|
|
||||||
// ── BannerNotification:通用大卡片通知监听 ──────────────────
|
|
||||||
/**
|
|
||||||
* 监听 BannerNotification 事件,渲染 chatBanner 大卡片。
|
|
||||||
* 支持私有用户频道(单推)和房间频道(全员推送)。
|
|
||||||
*
|
|
||||||
* 安全说明:BannerNotification 仅由后端可信代码 broadcast,
|
|
||||||
* 私有频道需鉴权,presence 频道需加入房间,均须服务端验证身份。
|
|
||||||
*/
|
|
||||||
function setupBannerNotification() {
|
|
||||||
if (!window.Echo || !window.chatContext) {
|
|
||||||
setTimeout(setupBannerNotification, 500);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const myId = window.chatContext.userId;
|
|
||||||
const roomId = window.chatContext.roomId;
|
|
||||||
|
|
||||||
// 监听私有用户频道(单独推给某人)
|
|
||||||
window.Echo.private(`user.${myId}`)
|
|
||||||
.listen('.BannerNotification', (e) => {
|
|
||||||
if (e.options && typeof e.options === 'object') {
|
|
||||||
window.chatBanner.show(e.options);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// 监听房间频道(推给房间所有人)
|
|
||||||
if (roomId) {
|
|
||||||
window.Echo.join(`room.${roomId}`)
|
|
||||||
.listen('.BannerNotification', (e) => {
|
|
||||||
if (e.options && typeof e.options === 'object') {
|
|
||||||
window.chatBanner.show(e.options);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
document.addEventListener('DOMContentLoaded', setupBannerNotification);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 显示好友添加居中大卡弹窗(使用 chatBanner 公共组件)。
|
|
||||||
* 互相好友 → 绿色渐变 + 互为好友文案
|
|
||||||
* 单向添加 → 蓝绿渐变 + 提示回加 + [➕ 回加好友] 按钮
|
|
||||||
*
|
|
||||||
* @param {string} fromUsername 添加者用户名
|
|
||||||
* @param {boolean} hasAddedBack 接收方是否已将添加者加为好友
|
|
||||||
*/
|
|
||||||
function showFriendBanner(fromUsername, hasAddedBack) {
|
|
||||||
if (hasAddedBack) {
|
|
||||||
window.chatBanner.show({
|
|
||||||
id: 'friend-banner',
|
|
||||||
icon: '🎉💚🎉',
|
|
||||||
title: '好友通知',
|
|
||||||
name: fromUsername,
|
|
||||||
body: '将你加为好友了!',
|
|
||||||
sub: '<strong style="color:#a7f3d0;">你们现在互为好友 🎊</strong>',
|
|
||||||
gradient: ['#065f46', '#059669', '#10b981'],
|
|
||||||
titleColor: '#a7f3d0',
|
|
||||||
autoClose: 5000,
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
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 (btn, close) => {
|
|
||||||
await window.quickFriendAction?.('add', fromUsername, btn);
|
|
||||||
if (btn.textContent.startsWith('✅')) {
|
|
||||||
setTimeout(close, 1500);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: '稍后再说',
|
|
||||||
color: 'rgba(255,255,255,0.15)',
|
|
||||||
onClick: (btn, close) => close(),
|
|
||||||
},
|
|
||||||
],
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
</script>
|
|
||||||
|
|||||||
Reference in New Issue
Block a user