Files
chatroom/resources/js/chat-room/right-panel.js
T

275 lines
8.2 KiB
JavaScript
Raw Normal View History

2026-04-25 03:42:54 +08:00
// 聊天室右侧名单/房间面板事件绑定,逐步替代 Blade 内联事件。
let rightPanelEventsBound = false;
2026-04-25 10:36:59 +08:00
// 用户名单由 Blade 主脚本动态重建,点击/双击状态必须放在模块层避免 DOM 重建后残留闭包。
const userListClickTimers = new WeakMap();
const userListLastTapTimes = new WeakMap();
2026-04-25 11:03:40 +08:00
const messageUserClickTimers = new WeakMap();
const messageUserLastTapTimes = new WeakMap();
2026-04-25 10:36:59 +08:00
const USER_LIST_CLICK_DELAY = 250;
const USER_LIST_DOUBLE_TAP_DELAY = 300;
2026-04-25 11:03:40 +08:00
const MESSAGE_USER_CLICK_DELAY = 250;
const MESSAGE_USER_DOUBLE_TAP_DELAY = 300;
2026-04-25 03:42:54 +08:00
/**
2026-04-25 10:36:59 +08:00
* 选择聊天目标并聚焦输入框。
*
* @param {string} username 目标用户名
* @returns {void}
*/
function selectChatTarget(username) {
const toUserSelect = document.getElementById("to_user");
if (toUserSelect instanceof HTMLSelectElement) {
toUserSelect.value = username;
}
// 手机抽屉里的名单点击后需要收起抽屉,桌面端没有该函数时自然跳过。
window.closeMobileDrawer?.();
document.getElementById("content")?.focus();
}
/**
* 打开用户资料卡并取消待执行的单击选择。
*
* @param {HTMLElement} item 用户列表项
* @returns {void}
*/
function openUserCardFromList(item) {
const username = item.dataset.username || "";
const clickTimer = userListClickTimers.get(item);
if (!username) {
return;
}
if (clickTimer) {
window.clearTimeout(clickTimer);
userListClickTimers.delete(item);
}
window.closeMobileDrawer?.();
window.openUserCard?.(username);
}
2026-04-25 11:03:40 +08:00
/**
* 读取消息区用户名,过滤掉系统播报和游戏标签等不可交互内容。
*
* @param {HTMLElement} item 消息区用户名节点
* @returns {string}
*/
function getMessageUsername(item) {
return (item.dataset.u || "").trim();
}
/**
* 消息区用户名单击选择聊天目标,保留旧 switchTarget 的离线用户兜底逻辑。
*
* @param {string} username 目标用户名
* @returns {void}
*/
function selectMessageChatTarget(username) {
if (!username) {
return;
}
if (typeof window.switchTarget === "function") {
window.switchTarget(username);
return;
}
selectChatTarget(username);
}
/**
* 消息区用户名双击打开名片,并取消待执行的单击选择。
*
* @param {HTMLElement} item 消息区用户名节点
* @returns {void}
*/
function openUserCardFromMessage(item) {
const username = getMessageUsername(item);
const clickTimer = messageUserClickTimers.get(item);
if (!username) {
return;
}
if (clickTimer) {
window.clearTimeout(clickTimer);
messageUserClickTimers.delete(item);
}
window.openUserCard?.(username);
}
/**
* 调度消息区用户名单击选择,延迟执行用于和双击打开名片互斥。
*
* @param {HTMLElement} item 消息区用户名节点
* @returns {void}
*/
function scheduleMessageUserSelection(item) {
const username = getMessageUsername(item);
if (!username || messageUserClickTimers.has(item)) {
return;
}
const clickTimer = window.setTimeout(() => {
messageUserClickTimers.delete(item);
selectMessageChatTarget(username);
}, MESSAGE_USER_CLICK_DELAY);
messageUserClickTimers.set(item, clickTimer);
}
2026-04-25 10:36:59 +08:00
/**
* 调度用户列表单击选择,延迟执行用于和双击打开名片互斥。
*
* @param {HTMLElement} item 用户列表项
* @returns {void}
*/
function scheduleUserListSelection(item) {
const username = item.dataset.username || "";
if (!username || userListClickTimers.has(item)) {
return;
}
const clickTimer = window.setTimeout(() => {
userListClickTimers.delete(item);
selectChatTarget(username);
}, USER_LIST_CLICK_DELAY);
userListClickTimers.set(item, clickTimer);
}
/**
* 绑定右侧在线面板的 tab、刷新、排序、搜索与用户列表选择事件。
2026-04-25 03:42:54 +08:00
*
* @returns {void}
*/
export function bindChatRightPanelControls() {
if (rightPanelEventsBound || typeof document === "undefined") {
return;
}
rightPanelEventsBound = true;
document.addEventListener("click", (event) => {
if (!(event.target instanceof Element)) {
return;
}
2026-04-25 10:04:30 +08:00
// 右侧名单和房间 tab 仍由 Blade 主脚本维护状态,这里只转发点击入口。
2026-04-25 03:42:54 +08:00
const tabButton = event.target.closest("[data-chat-right-tab]");
if (tabButton) {
event.preventDefault();
window.switchTab?.(tabButton.dataset.chatRightTab);
return;
}
2026-04-25 10:04:30 +08:00
// 在线名单刷新函数仍在主聊天脚本中,后续渲染整体迁移后再收口到模块内。
2026-04-25 03:42:54 +08:00
const refreshButton = event.target.closest("[data-chat-user-list-refresh]");
if (refreshButton) {
event.preventDefault();
window.renderUserList?.();
2026-04-25 10:36:59 +08:00
return;
}
const everyoneItem = event.target.closest("[data-user-list-everyone]");
if (everyoneItem) {
event.preventDefault();
selectChatTarget("大家");
return;
}
const userItem = event.target.closest("[data-user-list-entry]");
if (userItem instanceof HTMLElement) {
event.preventDefault();
scheduleUserListSelection(userItem);
2026-04-25 11:03:40 +08:00
return;
}
const messageUser = event.target.closest("[data-chat-message-user]");
if (messageUser instanceof HTMLElement) {
event.preventDefault();
scheduleMessageUserSelection(messageUser);
2026-04-25 03:42:54 +08:00
}
});
2026-04-25 10:36:59 +08:00
document.addEventListener("dblclick", (event) => {
if (!(event.target instanceof Element)) {
return;
}
const userItem = event.target.closest("[data-user-list-entry]");
if (userItem instanceof HTMLElement) {
event.preventDefault();
openUserCardFromList(userItem);
2026-04-25 11:03:40 +08:00
return;
}
const messageUser = event.target.closest("[data-chat-message-user]");
if (messageUser instanceof HTMLElement) {
event.preventDefault();
openUserCardFromMessage(messageUser);
2026-04-25 10:36:59 +08:00
}
});
document.addEventListener("touchend", (event) => {
if (!(event.target instanceof Element)) {
return;
}
const userItem = event.target.closest("[data-user-list-entry]");
if (!(userItem instanceof HTMLElement)) {
2026-04-25 11:03:40 +08:00
const messageUser = event.target.closest("[data-chat-message-user]");
if (!(messageUser instanceof HTMLElement)) {
return;
}
// 消息区用户名由 Blade 渲染,移动端双触发统一在 Vite 模块内处理。
const now = Date.now();
const lastTapTime = messageUserLastTapTimes.get(messageUser) || 0;
if (now - lastTapTime < MESSAGE_USER_DOUBLE_TAP_DELAY) {
event.preventDefault();
messageUserLastTapTimes.delete(messageUser);
openUserCardFromMessage(messageUser);
return;
}
messageUserLastTapTimes.set(messageUser, now);
2026-04-25 10:36:59 +08:00
return;
}
// 移动端双触发沿用旧的 300ms 口径,单触摸仍交给 click 事件选择聊天目标。
const now = Date.now();
const lastTapTime = userListLastTapTimes.get(userItem) || 0;
if (now - lastTapTime < USER_LIST_DOUBLE_TAP_DELAY) {
event.preventDefault();
userListLastTapTimes.delete(userItem);
openUserCardFromList(userItem);
return;
}
userListLastTapTimes.set(userItem, now);
}, { passive: false });
2026-04-25 03:42:54 +08:00
document.addEventListener("change", (event) => {
if (!(event.target instanceof HTMLSelectElement) || event.target.id !== "user-sort-select") {
return;
}
window.renderUserList?.();
});
document.addEventListener("input", (event) => {
if (!(event.target instanceof HTMLInputElement) || event.target.id !== "user-search-input") {
return;
}
2026-04-25 10:04:30 +08:00
// 搜索输入高频触发,继续复用主脚本里的 RAF 节流渲染。
2026-04-25 03:42:54 +08:00
window.scheduleFilterUserList?.();
});
}