// 聊天消息工具函数,承接主消息渲染脚本里可独立复用的判断逻辑。 /** * 判断图片消息是否已经超过前端允许展示的保留期。 * * @param {Record|null|undefined} message 聊天消息 * @param {number} retentionDays 图片保留天数 * @param {number} nowTimestamp 当前时间戳 * @returns {boolean} */ export function isExpiredChatImageMessage( message, retentionDays = Number.parseInt(window.chatContext?.chatImageRetentionDays || 3, 10), nowTimestamp = Date.now(), ) { if (!message) { return false; } if (message.message_type === "expired_image") { return true; } if (message.message_type !== "image") { return false; } if (!message.image_url || !message.image_thumb_url) { return true; } const sentAtText = String(message.sent_at || "").replace(" ", "T"); const sentAt = sentAtText ? new Date(sentAtText) : null; if (!sentAt || Number.isNaN(sentAt.getTime())) { return false; } return nowTimestamp >= sentAt.getTime() + retentionDays * 24 * 60 * 60 * 1000; } /** * 只清理当前浏览器的聊天窗口,并记录本地清屏的最大消息 ID。 * * @param {number|string} roomId 房间 ID * @param {number|string} maxMessageId 当前已接收最大消息 ID * @returns {void} */ export function localClearScreen(roomId = window.chatContext?.roomId, maxMessageId = 0) { const publicPane = document.getElementById("chat-messages-container"); const privatePane = document.getElementById("chat-messages-container2"); if (publicPane) { publicPane.innerHTML = ""; } if (privatePane) { privatePane.innerHTML = ""; } // 刷新页面时只恢复本地清屏点之后的新消息,避免旧消息重新回流。 localStorage.setItem(`local_clear_msg_id_${roomId}`, maxMessageId); const notice = document.createElement("div"); notice.className = "msg-line"; const now = new Date(); const timeText = [ now.getHours().toString().padStart(2, "0"), now.getMinutes().toString().padStart(2, "0"), now.getSeconds().toString().padStart(2, "0"), ].join(":"); notice.innerHTML = `🧹 您已执行本地清屏(${timeText})`; if (publicPane) { publicPane.appendChild(notice); publicPane.scrollTop = publicPane.scrollHeight; } } /** * 同步自动滚屏复选框与状态文字。 * * @param {boolean} enabled 是否开启自动滚屏 * @returns {void} */ export function syncAutoScrollControls(enabled) { const checkbox = document.getElementById("auto_scroll"); const status = document.getElementById("scroll-status"); if (checkbox) { checkbox.checked = enabled; } if (status) { status.textContent = enabled ? "开" : "关"; } } /** * 切换自动滚屏状态。 * 真实状态仍由 Blade 大脚本闭包保存,这里通过 getter/setter 桥接,避免 Vite 模块持有第二份状态。 * * @param {() => boolean} getCurrent 读取当前状态 * @param {(enabled:boolean) => void} setCurrent 写回当前状态 * @returns {boolean} */ export function toggleAutoScroll(getCurrent, setCurrent) { const nextEnabled = !Boolean(typeof getCurrent === "function" ? getCurrent() : true); if (typeof setCurrent === "function") { setCurrent(nextEnabled); } syncAutoScrollControls(nextEnabled); return nextEnabled; } /** * 在允许自动滚屏时,把聊天容器滚动到最新消息。 * Blade 仍负责传入真实容器和开关读取器,避免工具函数直接依赖大脚本闭包。 * * @param {HTMLElement|null|undefined} container 聊天消息容器 * @param {() => boolean} shouldScroll 判断当前是否允许自动滚屏 * @returns {void} */ export function scrollChatToBottom(container, shouldScroll) { if (!container || !(typeof shouldScroll === "function" ? shouldScroll() : true)) { return; } container.scrollTop = container.scrollHeight; }