2026-02-26 21:10:34 +08:00
|
|
|
|
{{--
|
2026-03-09 11:30:11 +08:00
|
|
|
|
文件功能:聊天室核心前端交互脚本(Blade 模板形式)
|
|
|
|
|
|
|
2026-04-27 09:19:49 +00:00
|
|
|
|
已迁移至 Vite 模块(resources/js/chat-room/)的内容:
|
|
|
|
|
|
1. 消息渲染引擎 → message-renderer.js(appendMessage / buildChatMessageContent / 批量渲染 / 裁剪)
|
|
|
|
|
|
2. 在线用户列表 → user-list.js(renderUserList / filterUserList / 徽标轮换)
|
|
|
|
|
|
3. WebSocket 事件监听 → chat-events.js(chat:here / chat:message / chat:muted / Echo 级监听等)
|
|
|
|
|
|
4. 管理操作 → admin-commands.js + admin-menu.js
|
|
|
|
|
|
5. 存点心跳 → heartbeat.js(saveExp / leaveRoom / notifyExpiredLeave)
|
|
|
|
|
|
6. 发送消息 → composer.js(sendMessage / 草稿 / IME 防重 / 神秘箱子暗号)
|
|
|
|
|
|
7. 特效控制 → admin-commands.js + message-utils.js
|
|
|
|
|
|
8. 系统播报屏蔽 → preferences-status.js(屏蔽 / 禁音 / 每日状态 / 偏好)
|
|
|
|
|
|
9. 共享状态 → chat-state.js(DOM 引用 / 在线用户 / 消息队列 / 所有可变状态)
|
|
|
|
|
|
10. 欢迎语菜单 → welcome-menu.js
|
|
|
|
|
|
11. 每日签到 → daily-sign-in.js
|
|
|
|
|
|
12. VIP 进退场 → vip-presence.js
|
|
|
|
|
|
|
|
|
|
|
|
保留在 Blade 内的内容(依赖 Blade 模板语法 或 作为薄兼容桥):
|
|
|
|
|
|
1. switchTab / loadRoomsOnlineStatus(依赖 {{ route('chat.rooms-online-status') }})
|
|
|
|
|
|
2. sendWelcomeTpl(依赖 sendMessage 已迁至 Vite)
|
|
|
|
|
|
3. 点击空白关闭浮层(简单 DOM 事件)
|
|
|
|
|
|
4. 自动滚屏复选框绑定
|
|
|
|
|
|
5. DOMContentLoaded 偏好恢复(调用 Vite 模块函数)
|
|
|
|
|
|
6. 各类 window.* 兼容桥声明
|
2026-03-09 11:30:11 +08:00
|
|
|
|
|
2026-04-27 09:19:49 +00:00
|
|
|
|
通过 @include('chat.partials.scripts') 引入到 frame.blade.php
|
2026-02-28 23:44:38 +08:00
|
|
|
|
|
2026-04-27 09:19:49 +00:00
|
|
|
|
@author ChatRoom Laravel
|
|
|
|
|
|
@version 3.0.0 — 大量逻辑已迁至 Vite 模块,此文件仅保留 Blade 依赖的薄包装
|
|
|
|
|
|
--}}
|
2026-02-28 23:44:38 +08:00
|
|
|
|
|
2026-04-27 09:19:49 +00:00
|
|
|
|
<script>
|
2026-03-12 08:35:21 +08:00
|
|
|
|
/**
|
2026-04-27 09:19:49 +00:00
|
|
|
|
* 聊天室前端交互逻辑(薄包装版本)
|
|
|
|
|
|
* 核心引擎已迁至 Vite 模块 resources/js/chat-room/,此脚本仅保留 Blade 模板依赖的桥接代码。
|
2026-03-12 08:35:21 +08:00
|
|
|
|
*/
|
|
|
|
|
|
|
2026-04-27 09:19:49 +00:00
|
|
|
|
// ── Blade 依赖变量(需要模板语法生成)──
|
|
|
|
|
|
const _currentRoomId = {{ $room->id }};
|
2026-02-27 16:19:21 +08:00
|
|
|
|
|
2026-04-27 09:19:49 +00:00
|
|
|
|
// ── Tab 切换 ──────────────────────────────────────
|
|
|
|
|
|
let _roomsRefreshTimer = null;
|
|
|
|
|
|
let _roomsOnlineStatusCache = null;
|
|
|
|
|
|
let _roomsOnlineStatusCacheAt = 0;
|
|
|
|
|
|
const ROOMS_ONLINE_STATUS_CACHE_TTL = 10000;
|
2026-02-27 14:14:35 +08:00
|
|
|
|
|
|
|
|
|
|
/**
|
2026-04-27 09:19:49 +00:00
|
|
|
|
* 切换右侧面板 Tab(用户列表 / 房间列表)。
|
|
|
|
|
|
* @param {string} tab 'users' | 'rooms'
|
2026-02-27 14:14:35 +08:00
|
|
|
|
*/
|
2026-04-27 09:19:49 +00:00
|
|
|
|
function switchTab(tab) {
|
|
|
|
|
|
['users', 'rooms'].forEach(t => {
|
|
|
|
|
|
document.getElementById('panel-' + t).style.display = t === tab ? 'block' : 'none';
|
|
|
|
|
|
document.getElementById('tab-' + t)?.classList.toggle('active', t === tab);
|
|
|
|
|
|
});
|
|
|
|
|
|
if (tab === 'rooms') {
|
|
|
|
|
|
loadRoomsOnlineStatus();
|
|
|
|
|
|
clearInterval(_roomsRefreshTimer);
|
|
|
|
|
|
_roomsRefreshTimer = setInterval(() => loadRoomsOnlineStatus(true), 30000);
|
2026-04-14 22:48:29 +08:00
|
|
|
|
} else {
|
2026-04-27 09:19:49 +00:00
|
|
|
|
clearInterval(_roomsRefreshTimer);
|
|
|
|
|
|
_roomsRefreshTimer = null;
|
2026-04-19 12:14:10 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-04-27 09:19:49 +00:00
|
|
|
|
window.switchTab = switchTab;
|
2026-04-19 12:14:10 +08:00
|
|
|
|
|
|
|
|
|
|
/**
|
2026-04-27 09:19:49 +00:00
|
|
|
|
* 拉取所有房间在线人数并渲染到右侧面板。
|
|
|
|
|
|
* @param {boolean} forceRefresh
|
2026-04-19 12:14:10 +08:00
|
|
|
|
*/
|
2026-04-27 09:19:49 +00:00
|
|
|
|
function loadRoomsOnlineStatus(forceRefresh = false) {
|
|
|
|
|
|
const container = document.getElementById('rooms-online-list');
|
|
|
|
|
|
if (!container) return;
|
2026-04-12 14:04:18 +08:00
|
|
|
|
|
2026-04-27 09:19:49 +00:00
|
|
|
|
if (!forceRefresh && _roomsOnlineStatusCache && Date.now() - _roomsOnlineStatusCacheAt < ROOMS_ONLINE_STATUS_CACHE_TTL) {
|
|
|
|
|
|
renderRoomsOnlineStatus(_roomsOnlineStatusCache, container);
|
2026-04-12 14:04:18 +08:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-27 09:19:49 +00:00
|
|
|
|
fetch('{{ route('chat.rooms-online-status') }}')
|
|
|
|
|
|
.then(r => r.json())
|
|
|
|
|
|
.then(data => {
|
|
|
|
|
|
_roomsOnlineStatusCache = data;
|
|
|
|
|
|
_roomsOnlineStatusCacheAt = Date.now();
|
|
|
|
|
|
renderRoomsOnlineStatus(data, container);
|
|
|
|
|
|
})
|
|
|
|
|
|
.catch(() => {
|
|
|
|
|
|
container.innerHTML = '<div style="text-align:center;color:#f00;padding:10px;font-size:11px;">加载失败</div>';
|
|
|
|
|
|
});
|
2026-04-19 12:14:10 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
2026-04-27 09:19:49 +00:00
|
|
|
|
* 渲染房间在线状态列表(优先使用 Vite 模块)。
|
2026-04-19 12:14:10 +08:00
|
|
|
|
*/
|
2026-04-27 09:19:49 +00:00
|
|
|
|
function renderRoomsOnlineStatus(data, container) {
|
|
|
|
|
|
if (window.ChatRoomTools?.renderRoomsOnlineStatusToContainer) {
|
|
|
|
|
|
window.ChatRoomTools.renderRoomsOnlineStatusToContainer(data, container, {
|
|
|
|
|
|
currentRoomId: _currentRoomId,
|
|
|
|
|
|
variant: 'desktop',
|
|
|
|
|
|
});
|
2026-04-19 12:14:10 +08:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
2026-04-27 09:19:49 +00:00
|
|
|
|
container.innerHTML = '<div style="text-align:center;color:#bbb;padding:16px 0;font-size:11px;">暂无房间</div>';
|
2026-04-19 12:14:10 +08:00
|
|
|
|
}
|
2026-02-27 14:53:45 +08:00
|
|
|
|
|
2026-04-27 09:19:49 +00:00
|
|
|
|
// ── 发送欢迎语模板 ──────────────────────────────────
|
2026-02-26 21:10:34 +08:00
|
|
|
|
|
|
|
|
|
|
/**
|
2026-04-27 09:19:49 +00:00
|
|
|
|
* 将选中的欢迎语模板填入输入框并自动发送。
|
|
|
|
|
|
* @param {string} tpl 欢迎语模板,含 {name} 占位符
|
2026-02-26 21:10:34 +08:00
|
|
|
|
*/
|
2026-04-27 09:19:49 +00:00
|
|
|
|
function sendWelcomeTpl(tpl) {
|
|
|
|
|
|
const toUser = document.getElementById('to_user')?.value || '大家';
|
2026-04-28 14:40:27 +08:00
|
|
|
|
if (toUser === '大家') {
|
|
|
|
|
|
window.chatDialog?.alert('欢迎语不能对「大家」发送,请先选择具体用户。', '🚫 提示', '#cc4444');
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
const name = toUser;
|
2026-04-27 09:19:49 +00:00
|
|
|
|
const prefix = window.chatContext?.welcomePrefix || window.chatContext?.username || '';
|
|
|
|
|
|
const body = tpl.replace(/\{name\}/g, name);
|
|
|
|
|
|
const msg = `${prefix}:${body}`;
|
2026-03-03 19:29:43 +08:00
|
|
|
|
|
2026-04-27 09:19:49 +00:00
|
|
|
|
const input = document.getElementById('content');
|
|
|
|
|
|
if (input) input.value = msg;
|
2026-03-03 19:29:43 +08:00
|
|
|
|
|
2026-04-27 09:19:49 +00:00
|
|
|
|
const menu = document.getElementById('welcome-menu');
|
|
|
|
|
|
if (menu) menu.style.display = 'none';
|
2026-03-03 19:29:43 +08:00
|
|
|
|
|
2026-04-27 09:19:49 +00:00
|
|
|
|
const actionSel = document.getElementById('action');
|
|
|
|
|
|
const prevAction = actionSel?.value || '';
|
|
|
|
|
|
if (actionSel) actionSel.value = '欢迎';
|
2026-03-03 19:29:43 +08:00
|
|
|
|
|
2026-04-27 09:19:49 +00:00
|
|
|
|
// sendMessage 已由 composer.js(Vite)挂载到 window
|
|
|
|
|
|
window.sendMessage?.(null)?.finally(() => {
|
|
|
|
|
|
if (actionSel) actionSel.value = prevAction;
|
2026-04-19 12:14:10 +08:00
|
|
|
|
});
|
2026-02-26 21:10:34 +08:00
|
|
|
|
}
|
2026-04-27 09:19:49 +00:00
|
|
|
|
window.sendWelcomeTpl = sendWelcomeTpl;
|
2026-02-26 21:10:34 +08:00
|
|
|
|
|
2026-04-27 09:19:49 +00:00
|
|
|
|
// ── 点击空白关闭浮层 ──────────────────────────────
|
|
|
|
|
|
document.addEventListener('click', function(event) {
|
|
|
|
|
|
const clickedInsideFloatingMenu = event.target instanceof Element
|
|
|
|
|
|
&& event.target.closest('[data-chat-welcome-menu], [data-chat-admin-menu], [data-chat-block-menu], [data-chat-feature-menu], [data-chat-feature-menu-toggle], [data-chat-block-menu-toggle], [data-chat-welcome-menu-toggle], [data-chat-admin-menu-toggle]');
|
2026-02-26 21:10:34 +08:00
|
|
|
|
|
2026-04-27 09:19:49 +00:00
|
|
|
|
if (clickedInsideFloatingMenu) return;
|
2026-02-26 22:27:49 +08:00
|
|
|
|
|
2026-04-27 09:19:49 +00:00
|
|
|
|
['welcome-menu', 'admin-menu', 'block-menu', 'feature-menu', 'daily-status-editor-overlay'].forEach(id => {
|
|
|
|
|
|
const el = document.getElementById(id);
|
|
|
|
|
|
if (el) el.style.display = 'none';
|
2026-03-12 07:33:32 +08:00
|
|
|
|
});
|
2026-04-27 09:19:49 +00:00
|
|
|
|
});
|
2026-02-26 23:05:56 +08:00
|
|
|
|
|
2026-04-27 09:19:49 +00:00
|
|
|
|
// ── 自动滚屏复选框绑定 ──────────────────────────────
|
|
|
|
|
|
const autoScrollEl = document.getElementById('auto_scroll');
|
|
|
|
|
|
if (autoScrollEl) {
|
|
|
|
|
|
autoScrollEl.addEventListener('change', function() {
|
|
|
|
|
|
window.setChatAutoScrollEnabled?.(this.checked);
|
2026-03-12 07:33:32 +08:00
|
|
|
|
});
|
2026-02-26 23:05:56 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-27 09:19:49 +00:00
|
|
|
|
// ── 页面加载后从 localStorage 恢复偏好 ──────────────────
|
|
|
|
|
|
document.addEventListener('DOMContentLoaded', () => {
|
|
|
|
|
|
window.ChatRoomTools?.restoreChatFontSize?.();
|
2026-02-28 11:20:34 +08:00
|
|
|
|
|
2026-04-27 09:19:49 +00:00
|
|
|
|
const CHAT_SOUND_MUTED_KEY = window.ChatRoomTools?.CHAT_SOUND_MUTED_STORAGE_KEY || 'chat_sound_muted';
|
|
|
|
|
|
const BLOCKED_SENDERS_KEY = window.ChatRoomTools?.BLOCKED_SYSTEM_SENDERS_STORAGE_KEY || 'chat_blocked_system_senders';
|
2026-04-29 11:35:14 +08:00
|
|
|
|
const BLOCKABLE_SENDERS = window.ChatRoomTools?.BLOCKABLE_SYSTEM_SENDERS || ['钓鱼播报', '猜成语', '星海小博士', '百家乐', '跑马', '神秘箱子'];
|
2026-04-25 19:38:58 +08:00
|
|
|
|
|
2026-04-27 09:19:49 +00:00
|
|
|
|
const initialChatPreferences = window.ChatRoomTools?.normalizeChatPreferences
|
|
|
|
|
|
? window.ChatRoomTools.normalizeChatPreferences(window.chatContext?.chatPreferences || {}, BLOCKABLE_SENDERS)
|
|
|
|
|
|
: { blocked_system_senders: [], sound_muted: false };
|
2026-04-24 21:17:44 +08:00
|
|
|
|
|
2026-04-27 09:19:49 +00:00
|
|
|
|
let storedBlockedSenders = [];
|
|
|
|
|
|
try {
|
|
|
|
|
|
const saved = JSON.parse(localStorage.getItem(BLOCKED_SENDERS_KEY) || '[]');
|
|
|
|
|
|
storedBlockedSenders = Array.isArray(saved)
|
|
|
|
|
|
? saved.filter(s => BLOCKABLE_SENDERS.includes(s))
|
|
|
|
|
|
: [];
|
|
|
|
|
|
} catch (_) { /* ignore */ }
|
2026-04-25 19:45:15 +08:00
|
|
|
|
|
2026-04-27 09:19:49 +00:00
|
|
|
|
const mutedFromLocal = localStorage.getItem(CHAT_SOUND_MUTED_KEY) === '1';
|
2026-02-26 21:10:34 +08:00
|
|
|
|
|
2026-04-27 09:19:49 +00:00
|
|
|
|
const shouldMigrate = window.ChatRoomTools?.shouldMigrateLocalChatPreferences
|
|
|
|
|
|
? window.ChatRoomTools.shouldMigrateLocalChatPreferences(initialChatPreferences, storedBlockedSenders, mutedFromLocal)
|
|
|
|
|
|
: !(initialChatPreferences.blocked_system_senders.length > 0 || initialChatPreferences.sound_muted)
|
|
|
|
|
|
&& (storedBlockedSenders.length > 0 || mutedFromLocal);
|
2026-04-11 22:40:42 +08:00
|
|
|
|
|
2026-04-27 09:19:49 +00:00
|
|
|
|
// 恢复屏蔽发送者
|
|
|
|
|
|
if (shouldMigrate && storedBlockedSenders.length > 0) {
|
|
|
|
|
|
window.chatState.blockedSystemSenders = new Set(storedBlockedSenders);
|
2026-04-28 14:04:07 +08:00
|
|
|
|
} else if (initialChatPreferences.blocked_system_senders.length > 0) {
|
|
|
|
|
|
window.chatState.blockedSystemSenders = new Set(initialChatPreferences.blocked_system_senders);
|
2026-04-11 22:40:42 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-28 14:08:15 +08:00
|
|
|
|
// 恢复后立即隐藏已有消息
|
|
|
|
|
|
if (window.chatState.blockedSystemSenders.size > 0) {
|
|
|
|
|
|
const setRendered = window.setRenderedMessagesVisibilityBySender;
|
|
|
|
|
|
if (typeof setRendered === 'function') {
|
|
|
|
|
|
window.chatState.blockedSystemSenders.forEach(function (sender) {
|
|
|
|
|
|
setRendered(sender, true);
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-27 09:19:49 +00:00
|
|
|
|
// 恢复禁音状态
|
|
|
|
|
|
const muted = shouldMigrate ? mutedFromLocal : initialChatPreferences.sound_muted;
|
|
|
|
|
|
if (window.ChatRoomTools?.setSoundMuted) {
|
|
|
|
|
|
window.ChatRoomTools.setSoundMuted(muted);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
const muteChk = document.getElementById('sound_muted');
|
|
|
|
|
|
if (muteChk) muteChk.checked = muted;
|
2026-02-26 21:10:34 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-27 09:19:49 +00:00
|
|
|
|
// 同步屏蔽复选框
|
|
|
|
|
|
if (typeof window.syncBlockedSystemSenderCheckboxes === 'function') {
|
|
|
|
|
|
window.syncBlockedSystemSenderCheckboxes();
|
2026-03-01 00:12:47 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-27 09:19:49 +00:00
|
|
|
|
if (shouldMigrate && typeof window.saveChatPreferences === 'function') {
|
|
|
|
|
|
void window.saveChatPreferences();
|
|
|
|
|
|
} else if (typeof window.persistChatPreferencesToLocal === 'function') {
|
|
|
|
|
|
window.persistChatPreferencesToLocal();
|
2026-04-11 22:40:42 +08:00
|
|
|
|
}
|
2026-04-27 09:19:49 +00:00
|
|
|
|
});
|
2026-02-26 21:10:34 +08:00
|
|
|
|
|
2026-04-27 09:19:49 +00:00
|
|
|
|
// ── window.* 兼容桥声明 ──────────────────────────────
|
|
|
|
|
|
// 以下函数已全部迁至 Vite 模块,这里仅做 window 引用声明,
|
|
|
|
|
|
// 确保 Blade 内仍可直接调用这些函数名(无需 window. 前缀)。
|
|
|
|
|
|
|
|
|
|
|
|
// 共享状态桥接:将 Vite 的 chatState 暴露为 Blade 兼容的本地引用
|
|
|
|
|
|
const chatState = window.chatState || {};
|
|
|
|
|
|
|
|
|
|
|
|
// 这些函数由 message-renderer.js 挂载到 window
|
|
|
|
|
|
const appendMessage = (...args) => window.appendMessage(...args);
|
|
|
|
|
|
const buildChatMessageContent = (...args) => window.buildChatMessageContent(...args);
|
|
|
|
|
|
const pruneMessageContainer = (...args) => window.pruneMessageContainer(...args);
|
|
|
|
|
|
const enqueueChatMessage = (...args) => window.enqueueChatMessage(...args);
|
|
|
|
|
|
const flushQueuedChatMessages = (...args) => window.flushQueuedChatMessages(...args);
|
|
|
|
|
|
const createChatMessageRenderBatch = (...args) => window.createChatMessageRenderBatch(...args);
|
|
|
|
|
|
const commitChatMessageRenderBatch = (...args) => window.commitChatMessageRenderBatch(...args);
|
|
|
|
|
|
|
|
|
|
|
|
// 这些函数由 user-list.js 挂载到 window
|
|
|
|
|
|
const renderUserList = (...args) => window.renderUserList(...args);
|
|
|
|
|
|
const scheduleRenderUserList = (...args) => window.scheduleRenderUserList(...args);
|
|
|
|
|
|
const filterUserList = (...args) => window.filterUserList(...args);
|
|
|
|
|
|
const scheduleFilterUserList = (...args) => window.scheduleFilterUserList(...args);
|
|
|
|
|
|
const refreshRenderedUserBadges = (...args) => window.refreshRenderedUserBadges(...args);
|
|
|
|
|
|
|
|
|
|
|
|
// 这些函数由 composer.js 挂载到 window
|
|
|
|
|
|
const sendMessage = (...args) => window.sendMessage(...args);
|
|
|
|
|
|
const collectChatComposerState = (...args) => window.collectChatComposerState(...args);
|
|
|
|
|
|
const buildChatMessageFormData = (...args) => window.buildChatMessageFormData(...args);
|
|
|
|
|
|
const handleChatImageSelected = (...args) => window.handleChatImageSelected(...args);
|
|
|
|
|
|
const clearSelectedChatImage = (...args) => window.clearSelectedChatImage(...args);
|
|
|
|
|
|
|
|
|
|
|
|
// 这些函数由 heartbeat.js 挂载到 window
|
|
|
|
|
|
const saveExp = (...args) => window.saveExp(...args);
|
|
|
|
|
|
const leaveRoom = (...args) => window.leaveRoom(...args);
|
|
|
|
|
|
const notifyExpiredLeave = (...args) => window.notifyExpiredLeave(...args);
|
|
|
|
|
|
|
|
|
|
|
|
// 以下函数由 preferences-status.js / message-utils.js / admin-commands.js 挂载
|
|
|
|
|
|
const toggleBlockMenu = (...args) => window.toggleBlockMenu?.(...args);
|
|
|
|
|
|
const toggleFeatureMenu = (...args) => window.toggleFeatureMenu?.(...args);
|
|
|
|
|
|
const closeFeatureMenu = (...args) => window.closeFeatureMenu?.(...args);
|
|
|
|
|
|
const openDailyStatusEditor = (...args) => window.openDailyStatusEditor?.(...args);
|
|
|
|
|
|
const closeDailyStatusEditor = (...args) => window.closeDailyStatusEditor?.(...args);
|
|
|
|
|
|
const toggleBlockedSystemSender = (...args) => window.toggleBlockedSystemSender?.(...args);
|
|
|
|
|
|
const updateDailyStatus = (...args) => window.updateDailyStatus?.(...args);
|
|
|
|
|
|
const clearDailyStatus = (...args) => window.clearDailyStatus?.(...args);
|
|
|
|
|
|
const handleFeatureLocalClear = (...args) => window.handleFeatureLocalClear?.(...args);
|
|
|
|
|
|
const normalizeChatPreferences = (...args) => window.ChatRoomTools?.normalizeChatPreferences?.(...args);
|
|
|
|
|
|
const parseDailyStatusExpiry = (...args) => window.ChatRoomTools?.parseDailyStatusExpiry?.(...args);
|
|
|
|
|
|
const normalizeDailyStatus = (...args) => window.ChatRoomTools?.normalizeDailyStatus?.(...args);
|
|
|
|
|
|
const syncDailyStatusUi = (...args) => window.syncDailyStatusUi?.() || window.ChatRoomTools?.syncDailyStatusUi?.();
|
|
|
|
|
|
const isSoundMuted = (...args) => window.ChatRoomTools?.isSoundMuted?.(...args) || false;
|
|
|
|
|
|
const toggleSoundMute = (...args) => window.toggleSoundMute?.(...args) || window.ChatRoomTools?.toggleSoundMute?.(...args);
|
|
|
|
|
|
const toggleAutoScroll = (...args) => window.ChatRoomTools?.toggleAutoScroll?.(...args);
|
|
|
|
|
|
const localClearScreen = (...args) => window.localClearScreen?.(...args);
|
|
|
|
|
|
const scrollToBottom = (...args) => window.ChatRoomTools?.scrollChatToBottom?.(...args);
|
|
|
|
|
|
|
|
|
|
|
|
// ── 挂载供其他 Blade partials 引用的函数 ────────────
|
|
|
|
|
|
window.toggleBlockMenu = toggleBlockMenu;
|
|
|
|
|
|
window.toggleFeatureMenu = toggleFeatureMenu;
|
|
|
|
|
|
window.closeFeatureMenu = closeFeatureMenu;
|
|
|
|
|
|
window.openDailyStatusEditor = openDailyStatusEditor;
|
|
|
|
|
|
window.closeDailyStatusEditor = closeDailyStatusEditor;
|
|
|
|
|
|
window.toggleBlockedSystemSender = toggleBlockedSystemSender;
|
|
|
|
|
|
window.updateDailyStatus = updateDailyStatus;
|
|
|
|
|
|
window.clearDailyStatus = clearDailyStatus;
|
|
|
|
|
|
window.handleFeatureLocalClear = handleFeatureLocalClear;
|
|
|
|
|
|
window.toggleSoundMute = toggleSoundMute;
|
2026-02-26 21:10:34 +08:00
|
|
|
|
|
2026-04-27 09:19:49 +00:00
|
|
|
|
// 同步每日状态 UI
|
|
|
|
|
|
if (typeof window.syncDailyStatusUi === 'function') {
|
|
|
|
|
|
window.syncDailyStatusUi();
|
2026-02-26 21:10:34 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-27 09:19:49 +00:00
|
|
|
|
// 绑定音效禁音控件
|
|
|
|
|
|
window.ChatRoomTools?.bindSoundMuteControl?.(() => window.saveChatPreferences?.());
|
2026-02-26 21:10:34 +08:00
|
|
|
|
|
2026-03-01 22:20:54 +08:00
|
|
|
|
</script>
|