// 拍一拍功能模块 // 拦截输入框中的 /拍一拍 命令,向所选对象发送拍一拍通知并触发屏幕抖动。 import { pruneMessageContainer } from "./message-renderer.js"; function csrf() { return document.querySelector('meta[name="csrf-token"]')?.content ?? ""; } /** * 判断输入是否为 /拍一拍 命令。 */ function isPatCommand(text) { return /^\/拍一拍\s*$/.test(text); } /** * 获取当前选中的聊天对象。 */ function getSelectedTarget() { const toUserSelect = document.getElementById("to_user"); if (!toUserSelect) return null; const val = toUserSelect.value?.trim(); return val || null; } /** * 执行拍一拍请求。 */ async function executePat() { const targetUser = getSelectedTarget(); if (!targetUser || targetUser === "大家") { window.chatDialog?.alert("请先选择一个聊天对象(不能为大家),再进行拍一拍。", "拍一拍", "#f472b6"); return false; } const roomId = window.chatContext?.roomId; if (!roomId) return false; try { const response = await fetch(`/room/${roomId}/pat`, { method: "POST", headers: { "X-CSRF-TOKEN": csrf(), "Content-Type": "application/json", "Accept": "application/json", }, body: JSON.stringify({ target_user: targetUser }), }); const data = await response.json(); if (data.status === "success") { // 清空输入并触发本机抖动 const contentInput = document.getElementById("content"); if (contentInput) { contentInput.value = ""; if (typeof window.persistChatDraft === "function") { window.persistChatDraft(""); } contentInput.focus(); } triggerPatShake(); return true; } window.chatDialog?.alert(data.message || "拍一拍失败", "拍一拍", "#f472b6"); return false; } catch (error) { console.error("拍一拍请求失败:", error); window.chatDialog?.alert("网络错误,拍一拍发送失败。", "拍一拍", "#f472b6"); return false; } } /** * 触发屏幕抖动动画。 */ function triggerPatShake() { const layout = document.querySelector(".chat-layout"); if (!layout) return; layout.classList.remove("chat-shake"); // 强制回流后重新添加动画 void layout.offsetWidth; layout.classList.add("chat-shake"); // 动画结束后移除 class setTimeout(() => { layout.classList.remove("chat-shake"); }, 500); } /** * 添加拍一拍消息到聊天窗口(使用正常聊天样式渲染)。 */ function appendPatMessage(displayText, fromUserHeadface, fromUser, targetUser) { const state = window.chatState; const container = state?.container; if (!container) return; const now = new Date(); const timeStr = now.getHours().toString().padStart(2, "0") + ":" + now.getMinutes().toString().padStart(2, "0") + ":" + now.getSeconds().toString().padStart(2, "0"); const fromUserSafe = fromUser || ""; const targetUserSafe = targetUser || ""; // 获取发送者的在线数据,获取正确头像 const senderInfo = state.onlineUsers[fromUserSafe]; const senderHead = (senderInfo && senderInfo.headface) || "1.gif"; let headImgSrc = senderHead.startsWith("storage/") ? "/" + senderHead : "/images/headface/" + senderHead; const headImg = ''; // 可点击用户名(与正常消息一致) const fromHtml = '' + fromUserSafe + ''; const toHtml = '' + targetUserSafe + ''; const div = document.createElement("div"); div.className = "msg-line"; if (fromUserSafe) { div.dataset.fromUser = fromUserSafe; } div.innerHTML = headImg + fromHtml + "对" + toHtml + "说:👋 我刚拍了拍你 (" + timeStr + ")"; // 路由规则:发送者和被拍者在包厢看到,其他用户在公屏看到 const currentUser = window.chatContext?.username || ""; const isRelatedToMe = fromUser === currentUser || targetUser === currentUser; if (isRelatedToMe) { const container2 = state?.container2; if (container2) { container2.appendChild(div); pruneMessageContainer(container2, 300); if (state?.autoScroll) { container2.scrollTop = container2.scrollHeight; } } } else { container.appendChild(div); pruneMessageContainer(container, 600); if (state?.autoScroll) { container.scrollTop = container.scrollHeight; } } } // ── 导出 ── export { isPatCommand, executePat, triggerPatShake, appendPatMessage }; // 挂载到 window 供其他模块使用 window.isPatCommand = isPatCommand; window.executePat = executePat; window.triggerPatShake = triggerPatShake; window.appendPatMessage = appendPatMessage;