495efdf9e0
- 输入框输入 / 弹出命令菜单,当前支持 /拍一拍 - 选择对象后输入 /拍一拍 发送拍一拍通知 - 所有在线用户屏幕抖动 + 正常聊天样式显示消息 - 命令注册表可扩展,后续新增命令只需 push 到数组
155 lines
5.2 KiB
JavaScript
155 lines
5.2 KiB
JavaScript
// 拍一拍功能模块
|
|
// 拦截输入框中的 /拍一拍 命令,向所选对象发送拍一拍通知并触发屏幕抖动。
|
|
|
|
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 = '<img src="' + headImgSrc + '" style="display:inline;width:16px;height:16px;vertical-align:middle;margin-right:2px;mix-blend-mode: multiply;" onerror="this.src=\'/images/headface/1.gif\'">';
|
|
|
|
// 可点击用户名(与正常消息一致)
|
|
const fromHtml = '<span class="msg-user" data-chat-message-user data-u="' + fromUserSafe + '" style="color: #000099; cursor: pointer;">' + fromUserSafe + '</span>';
|
|
const toHtml = '<span class="msg-user" data-chat-message-user data-u="' + targetUserSafe + '" style="color: #000099; cursor: pointer;">' + targetUserSafe + '</span>';
|
|
|
|
const div = document.createElement("div");
|
|
div.className = "msg-line";
|
|
if (fromUserSafe) {
|
|
div.dataset.fromUser = fromUserSafe;
|
|
}
|
|
|
|
div.innerHTML = headImg + fromHtml + "对" + toHtml + "说:<span class=\"msg-content\" style=\"color: #000000\">👋 我刚拍了拍你</span> <span class=\"msg-time\">(" + timeStr + ")</span>";
|
|
|
|
container.appendChild(div);
|
|
pruneMessageContainer(container, 600);
|
|
if (state?.autoScroll) {
|
|
container.scrollTop = container.scrollHeight;
|
|
}
|
|
|
|
// 同时在包厢窗口(say2)也显示
|
|
const container2 = state?.container2;
|
|
if (container2) {
|
|
const div2 = div.cloneNode(true);
|
|
container2.appendChild(div2);
|
|
pruneMessageContainer(container2, 300);
|
|
if (state?.autoScroll) {
|
|
container2.scrollTop = container2.scrollHeight;
|
|
}
|
|
}
|
|
}
|
|
|
|
// ── 导出 ──
|
|
export { isPatCommand, executePat, triggerPatShake, appendPatMessage };
|
|
|
|
// 挂载到 window 供其他模块使用
|
|
window.isPatCommand = isPatCommand;
|
|
window.executePat = executePat;
|
|
window.triggerPatShake = triggerPatShake;
|
|
window.appendPatMessage = appendPatMessage;
|