优化手机输入及钓鱼
This commit is contained in:
@@ -245,6 +245,11 @@
|
||||
return '星海小博士';
|
||||
}
|
||||
|
||||
// 兼容旧版自动钓鱼卡购买通知:历史上该消息曾以“系统传音”发送,但正文里带有“钓鱼播报”字样。
|
||||
if ((fromUser === '系统传音' || fromUser === '系统') && (content.includes('钓鱼播报') || content.includes('自动钓鱼模式'))) {
|
||||
return '钓鱼播报';
|
||||
}
|
||||
|
||||
if ((fromUser === '系统传音' || fromUser === '系统') && content.includes('神秘箱子')) {
|
||||
return '神秘箱子';
|
||||
}
|
||||
@@ -1739,19 +1744,93 @@
|
||||
// ── 发送消息(Enter 发送,防 IME 输入法重复触发)────────
|
||||
// 用 isComposing 标记中文输入法的组词状态,组词期间过滤掉 Enter
|
||||
let _imeComposing = false;
|
||||
let _isSending = false; // 发送中防重入标记
|
||||
let _sendStartedAt = 0; // 记录发送开始时间,用于页面恢复后释放异常锁
|
||||
const _contentInput = document.getElementById('content');
|
||||
const CHAT_DRAFT_STORAGE_KEY = `chat_draft_${window.chatContext?.roomId ?? '0'}_${window.chatContext?.userId ?? '0'}`;
|
||||
|
||||
/**
|
||||
* 更新底部图片选择状态提示。
|
||||
* 将当前输入框内容保存到会话级草稿缓存。
|
||||
*/
|
||||
function updateChatImageSelectionLabel(filename = '') {
|
||||
const nameEl = document.getElementById('chat-image-name');
|
||||
if (!nameEl) {
|
||||
return;
|
||||
function persistChatDraft(value = null) {
|
||||
try {
|
||||
const draft = value ?? _contentInput?.value ?? '';
|
||||
if (draft === '') {
|
||||
sessionStorage.removeItem(CHAT_DRAFT_STORAGE_KEY);
|
||||
return;
|
||||
}
|
||||
|
||||
sessionStorage.setItem(CHAT_DRAFT_STORAGE_KEY, draft);
|
||||
} catch (_) {
|
||||
// 会话存储不可用时静默降级,不影响聊天主流程
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 从会话缓存中恢复聊天草稿。
|
||||
*
|
||||
* @returns {string}
|
||||
*/
|
||||
function loadChatDraft() {
|
||||
try {
|
||||
return sessionStorage.getItem(CHAT_DRAFT_STORAGE_KEY) || '';
|
||||
} catch (_) {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将当前输入区状态整理为一份稳定快照,避免直接序列化整张表单。
|
||||
*
|
||||
* @returns {Object}
|
||||
*/
|
||||
function collectChatComposerState() {
|
||||
const contentInput = document.getElementById('content');
|
||||
const submitBtn = document.getElementById('send-btn');
|
||||
const imageInput = document.getElementById('chat_image');
|
||||
const toUserSelect = document.getElementById('to_user');
|
||||
const actionSelect = document.getElementById('action');
|
||||
const fontColorInput = document.getElementById('font_color');
|
||||
const secretCheckbox = document.getElementById('is_secret');
|
||||
const contentRaw = contentInput?.value ?? '';
|
||||
const selectedImage = imageInput?.files?.[0] ?? null;
|
||||
|
||||
return {
|
||||
contentInput,
|
||||
submitBtn,
|
||||
imageInput,
|
||||
contentRaw,
|
||||
content: contentRaw.trim(),
|
||||
selectedImage,
|
||||
toUser: toUserSelect?.value || '大家',
|
||||
action: actionSelect?.value || '',
|
||||
fontColor: fontColorInput?.value || '',
|
||||
isSecret: Boolean(secretCheckbox?.checked),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 基于当前聊天快照构造稳定的 multipart 请求体。
|
||||
*
|
||||
* @param {Object} composerState
|
||||
* @returns {FormData}
|
||||
*/
|
||||
function buildChatMessageFormData(composerState) {
|
||||
const formData = new FormData();
|
||||
formData.append('content', composerState.contentRaw);
|
||||
formData.append('to_user', composerState.toUser);
|
||||
formData.append('action', composerState.action);
|
||||
formData.append('font_color', composerState.fontColor);
|
||||
|
||||
if (composerState.isSecret) {
|
||||
formData.append('is_secret', '1');
|
||||
}
|
||||
|
||||
nameEl.textContent = filename || '未选择图片';
|
||||
nameEl.style.color = filename ? '#0f766e' : '#64748b';
|
||||
if (composerState.selectedImage) {
|
||||
formData.append('image', composerState.selectedImage);
|
||||
}
|
||||
|
||||
return formData;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1760,12 +1839,9 @@
|
||||
function handleChatImageSelected(input) {
|
||||
const file = input?.files?.[0] ?? null;
|
||||
if (!file) {
|
||||
updateChatImageSelectionLabel('');
|
||||
return;
|
||||
}
|
||||
|
||||
updateChatImageSelectionLabel(file.name);
|
||||
|
||||
// 用户选择图片后,立即触发自动发送
|
||||
sendMessage(null);
|
||||
}
|
||||
@@ -1779,8 +1855,39 @@
|
||||
if (resetInput && imageInput) {
|
||||
imageInput.value = '';
|
||||
}
|
||||
}
|
||||
|
||||
updateChatImageSelectionLabel('');
|
||||
/**
|
||||
* 页面从后台恢复后,同步草稿、图片提示和发送锁状态。
|
||||
*/
|
||||
function syncChatComposerAfterResume() {
|
||||
if (!_contentInput) {
|
||||
return;
|
||||
}
|
||||
|
||||
const savedDraft = loadChatDraft();
|
||||
if (_contentInput.value === '' && savedDraft !== '') {
|
||||
_contentInput.value = savedDraft;
|
||||
} else if (_contentInput.value !== '') {
|
||||
persistChatDraft(_contentInput.value);
|
||||
}
|
||||
|
||||
const imageInput = document.getElementById('chat_image');
|
||||
if (!imageInput?.files?.length) {
|
||||
clearSelectedChatImage();
|
||||
}
|
||||
|
||||
_imeComposing = false;
|
||||
|
||||
if (_isSending && Date.now() - _sendStartedAt > 15000) {
|
||||
const submitBtn = document.getElementById('send-btn');
|
||||
if (submitBtn) {
|
||||
submitBtn.disabled = false;
|
||||
}
|
||||
|
||||
_isSending = false;
|
||||
_sendStartedAt = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1832,36 +1939,52 @@
|
||||
window.handleChatImageSelected = handleChatImageSelected;
|
||||
window.openChatImageLightbox = openChatImageLightbox;
|
||||
window.closeChatImageLightbox = closeChatImageLightbox;
|
||||
updateChatImageSelectionLabel();
|
||||
syncChatComposerAfterResume();
|
||||
|
||||
// 中文/日文等 IME 组词开始
|
||||
_contentInput.addEventListener('compositionstart', () => {
|
||||
_imeComposing = true;
|
||||
});
|
||||
// 组词结束(确认候选词完成),给 10ms 缓冲让 keydown 先被过滤掉
|
||||
_contentInput.addEventListener('compositionend', () => {
|
||||
setTimeout(() => {
|
||||
_imeComposing = false;
|
||||
}, 10);
|
||||
});
|
||||
if (_contentInput) {
|
||||
_contentInput.addEventListener('input', function() {
|
||||
persistChatDraft(this.value);
|
||||
});
|
||||
|
||||
_contentInput.addEventListener('keydown', function(e) {
|
||||
if (e.key === 'Enter' && !e.shiftKey) {
|
||||
e.preventDefault();
|
||||
// IME 正在组词时(如选候选汉字),不触发发送
|
||||
if (_imeComposing) return;
|
||||
sendMessage(e);
|
||||
// 中文/日文等 IME 组词开始
|
||||
_contentInput.addEventListener('compositionstart', () => {
|
||||
_imeComposing = true;
|
||||
});
|
||||
// 组词结束(确认候选词完成),给 10ms 缓冲让 keydown 先被过滤掉
|
||||
_contentInput.addEventListener('compositionend', () => {
|
||||
setTimeout(() => {
|
||||
_imeComposing = false;
|
||||
}, 10);
|
||||
});
|
||||
|
||||
_contentInput.addEventListener('keydown', function(e) {
|
||||
if (e.key === 'Enter' && !e.shiftKey) {
|
||||
e.preventDefault();
|
||||
// IME 正在组词时(如选候选汉字),不触发发送
|
||||
if (_imeComposing) return;
|
||||
sendMessage(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
window.addEventListener('pageshow', syncChatComposerAfterResume);
|
||||
document.addEventListener('visibilitychange', function() {
|
||||
if (document.visibilityState === 'visible') {
|
||||
syncChatComposerAfterResume();
|
||||
}
|
||||
});
|
||||
window.addEventListener('focus', function() {
|
||||
setTimeout(syncChatComposerAfterResume, 0);
|
||||
});
|
||||
|
||||
/**
|
||||
* 发送聊天消息(内带防重入锁,避免快速连按 Enter 重复提交)
|
||||
*/
|
||||
let _isSending = false; // 发送中防重入标记
|
||||
async function sendMessage(e) {
|
||||
if (e) e.preventDefault();
|
||||
if (_isSending) return; // 上一次还没结束,忽略
|
||||
_isSending = true;
|
||||
_sendStartedAt = Date.now();
|
||||
|
||||
// 前端禁言检查
|
||||
if (isMutedUntil > Date.now()) {
|
||||
@@ -1878,25 +2001,28 @@
|
||||
container2.scrollTop = container2.scrollHeight;
|
||||
}
|
||||
_isSending = false;
|
||||
_sendStartedAt = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
const form = document.getElementById('chat-form');
|
||||
const formData = new FormData(form);
|
||||
const contentInput = document.getElementById('content');
|
||||
const submitBtn = document.getElementById('send-btn');
|
||||
const imageInput = document.getElementById('chat_image');
|
||||
const selectedImage = imageInput?.files?.[0] ?? null;
|
||||
const composerState = collectChatComposerState();
|
||||
const {
|
||||
contentInput,
|
||||
submitBtn,
|
||||
content,
|
||||
contentRaw,
|
||||
selectedImage,
|
||||
toUser,
|
||||
} = composerState;
|
||||
|
||||
const content = String(formData.get('content') || '').trim();
|
||||
if (!content && !selectedImage) {
|
||||
contentInput.focus();
|
||||
_isSending = false;
|
||||
_sendStartedAt = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
// 如果发言对象是 AI 小助手,也发送一份给专用机器人 API,不打断正常的发消息流程
|
||||
const toUser = formData.get('to_user');
|
||||
if (toUser === 'AI小班长' && content) {
|
||||
sendToChatBot(content); // 异步调用,不阻塞全局发送
|
||||
}
|
||||
@@ -1907,6 +2033,7 @@
|
||||
const passcodePattern = /^[A-Z0-9]{4,8}$/;
|
||||
if (!selectedImage && passcodePattern.test(content.trim())) {
|
||||
_isSending = false;
|
||||
_sendStartedAt = 0;
|
||||
|
||||
try {
|
||||
const claimRes = await fetch('/mystery-box/claim', {
|
||||
@@ -1925,6 +2052,7 @@
|
||||
if (claimData.ok) {
|
||||
// ✅ 领取成功:清空输入框,不发送普通消息
|
||||
contentInput.value = '';
|
||||
persistChatDraft('');
|
||||
contentInput.focus();
|
||||
|
||||
// 清除活跃箱子全局标志
|
||||
@@ -1955,6 +2083,10 @@
|
||||
}
|
||||
|
||||
submitBtn.disabled = true;
|
||||
const formData = buildChatMessageFormData({
|
||||
...composerState,
|
||||
contentRaw,
|
||||
});
|
||||
|
||||
try {
|
||||
const response = await fetch(window.chatContext.sendUrl, {
|
||||
@@ -1970,6 +2102,7 @@
|
||||
const data = await response.json();
|
||||
if (response.ok && data.status === 'success') {
|
||||
contentInput.value = '';
|
||||
persistChatDraft('');
|
||||
clearSelectedChatImage(true);
|
||||
contentInput.focus();
|
||||
} else {
|
||||
@@ -1982,6 +2115,7 @@
|
||||
} finally {
|
||||
submitBtn.disabled = false;
|
||||
_isSending = false; // 释放发送锁,允许下次发送
|
||||
_sendStartedAt = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user