2026-04-25 19:27:39 +08:00
|
|
|
// 聊天消息工具函数,承接主消息渲染脚本里可独立复用的判断逻辑。
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 判断图片消息是否已经超过前端允许展示的保留期。
|
|
|
|
|
*
|
|
|
|
|
* @param {Record<string, any>|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;
|
|
|
|
|
}
|
2026-04-25 19:38:58 +08:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 只清理当前浏览器的聊天窗口,并记录本地清屏的最大消息 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 = `<span style="color: #64748b; font-weight: bold;">🧹 您已执行本地清屏</span><span class="msg-time">(${timeText})</span>`;
|
|
|
|
|
|
|
|
|
|
if (publicPane) {
|
|
|
|
|
publicPane.appendChild(notice);
|
|
|
|
|
publicPane.scrollTop = publicPane.scrollHeight;
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-04-25 19:45:15 +08:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 同步自动滚屏复选框与状态文字。
|
|
|
|
|
*
|
|
|
|
|
* @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;
|
|
|
|
|
}
|
2026-04-25 19:47:26 +08:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 在允许自动滚屏时,把聊天容器滚动到最新消息。
|
|
|
|
|
* 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;
|
|
|
|
|
}
|