add feature nickname

This commit is contained in:
unknown
2025-10-16 16:49:04 +08:00
parent 276dbe9817
commit 3d6544d51c
4 changed files with 405 additions and 45 deletions

View File

@ -2,6 +2,10 @@ const ipc = window.electronAPI;
let userInfo = {};
let trcConfig = {}
// 会话状态跟踪 - 用于控制滚动行为
let sessionStates = new Map(); // 存储会话状态 {userId: {isFirstOpen: boolean, scrollPosition: number}}
let isAppFirstLoad = true; // 标记应用是否首次加载
// 安全的 localStorage 访问工具
const safeLocalStorage = {
getItem: (key) => {
@ -50,7 +54,8 @@ const getCurrentUserId = (item) => {
return peerId;
}
const onlineStatusCheck = ()=> {
setInterval(async () => {
// 定义检测函数
const checkStatus = async () => {
// 安全地检查 localStorage
const element = safeLocalStorage.getItem('dc1_auth_key');
@ -82,7 +87,13 @@ const onlineStatusCheck = ()=> {
ipc.loginNotify(args);
userInfo = args;
}
}, 60000); // 每隔5000毫秒3秒调用一次
};
// 立即执行一次检测
checkStatus();
// 然后每5秒检测一次更快速地检测登录状态
setInterval(checkStatus, 5000);
}
const getUserInfo = () => {
return new Promise((resolve, reject) => {
@ -589,18 +600,189 @@ const updateConfigInfo = async () => {
}
}
// 滚动到聊天底部的函数
const scrollToBottom = (force = false) => {
try {
console.log('Telegram: 开始执行滚动到底部操作');
// Telegram 聊天消息容器选择器
const possibleSelectors = [
'#MiddleColumn .messages-container',
'#MiddleColumn [class*="messages-container"]',
'#MiddleColumn .Transition',
'#MiddleColumn [class*="Transition"]',
'#MiddleColumn > div > div',
'.MessageList',
'[class*="MessageList"]'
];
let chatContainer = null;
for (const selector of possibleSelectors) {
const element = document.querySelector(selector);
if (element) {
// 检查元素是否真的可以滚动
const style = window.getComputedStyle(element);
if (style.overflowY === 'auto' || style.overflowY === 'scroll' ||
element.scrollHeight > element.clientHeight) {
chatContainer = element;
console.log(`Telegram: 找到可滚动聊天容器,使用选择器: ${selector}`);
console.log(`Telegram: 容器信息 - scrollHeight: ${element.scrollHeight}, clientHeight: ${element.clientHeight}, scrollTop: ${element.scrollTop}`);
break;
}
}
}
// 如果没找到特定容器,尝试查找包含消息的父容器
if (!chatContainer) {
console.log('Telegram: 未找到预定义容器,尝试通过消息元素查找');
const messageElements = document.querySelectorAll('.Message, [class*="Message"]');
console.log(`Telegram: 找到 ${messageElements.length} 个消息元素`);
if (messageElements.length > 0) {
// 从最后一个消息元素开始向上查找可滚动容器
const lastMessage = messageElements[messageElements.length - 1];
let parent = lastMessage.parentElement;
while (parent && parent !== document.body) {
const style = window.getComputedStyle(parent);
if (style.overflowY === 'auto' || style.overflowY === 'scroll' ||
parent.scrollHeight > parent.clientHeight) {
chatContainer = parent;
console.log('Telegram: 通过消息元素找到滚动容器');
console.log(`Telegram: 容器信息 - scrollHeight: ${parent.scrollHeight}, clientHeight: ${parent.clientHeight}, scrollTop: ${parent.scrollTop}`);
break;
}
parent = parent.parentElement;
}
}
}
if (chatContainer) {
// 记录滚动前的状态
const beforeScrollTop = chatContainer.scrollTop;
const maxScrollTop = chatContainer.scrollHeight - chatContainer.clientHeight;
console.log(`Telegram: 滚动前状态 - scrollTop: ${beforeScrollTop}, maxScrollTop: ${maxScrollTop}`);
// 使用最可靠的滚动方法
const scrollMethods = [
() => {
// 方法1: 直接设置为最大滚动值
chatContainer.scrollTop = chatContainer.scrollHeight;
console.log(`Telegram: 方法1执行后 scrollTop: ${chatContainer.scrollTop}`);
},
() => {
// 方法2: 使用scrollTo方法滚动到底部
chatContainer.scrollTo({
top: chatContainer.scrollHeight,
behavior: 'auto'
});
console.log(`Telegram: 方法2执行后 scrollTop: ${chatContainer.scrollTop}`);
},
() => {
// 方法3: 查找最后一条消息并滚动到它
const lastMessage = chatContainer.querySelector('.Message:last-child, [class*="Message"]:last-child');
if (lastMessage) {
lastMessage.scrollIntoView({
behavior: 'auto',
block: 'end',
inline: 'nearest'
});
console.log(`Telegram: 方法3执行后 scrollTop: ${chatContainer.scrollTop}`);
}
}
];
// 执行滚动方法
scrollMethods.forEach((method, index) => {
try {
method();
console.log(`Telegram: 执行滚动方法 ${index + 1} 完成`);
} catch (error) {
console.warn(`Telegram: 滚动方法 ${index + 1} 失败:`, error);
}
});
// 最终验证和强制滚动
setTimeout(() => {
const finalScrollTop = chatContainer.scrollTop;
const finalMaxScrollTop = chatContainer.scrollHeight - chatContainer.clientHeight;
const isAtBottom = finalScrollTop >= (finalMaxScrollTop - 10);
console.log(`Telegram: 滚动完成验证 - scrollTop: ${finalScrollTop}, maxScrollTop: ${finalMaxScrollTop}, 是否在底部: ${isAtBottom}`);
// 如果还没到底部,执行最终强制滚动
if (!isAtBottom) {
chatContainer.scrollTop = chatContainer.scrollHeight;
console.log(`Telegram: 执行最终强制滚动新scrollTop: ${chatContainer.scrollTop}`);
}
}, 200);
console.log('Telegram: 滚动到聊天底部操作执行完成');
return true;
} else {
console.warn('Telegram: 未找到聊天容器,无法滚动');
return false;
}
} catch (error) {
console.error('Telegram: 滚动到底部时出错:', error);
return false;
}
};
//会话列表切换触发函数
const sessionChange = async () => {
const currentUserId = getCurrentUserId();
const args = {platform: 'Telegram', userId: currentUserId};
console.log('会话切换 sessionChange args', args);
// 检查是否为新打开的会话
const isNewSession = isAppFirstLoad || !sessionStates.has(currentUserId);
// 先更新配置信息
ipc.infoUpdate(args)
await updateConfigInfo()
const myNode = document.getElementById('custom-translate-textarea')
if (!myNode) {
addTranslatePreview();
addTranslateListener();
}
styledTextarea.initData()
// 处理滚动逻辑
if (isNewSession) {
console.log(`Telegram: 新打开会话 ${currentUserId},将滚动到底部`);
// 新打开的会话,滚动到底部 - 使用多次延迟确保完全加载
// 第一次滚动 - 早期尝试
setTimeout(() => {
scrollToBottom();
}, 300);
// 第二次滚动 - 确保DOM完全加载
setTimeout(() => {
scrollToBottom();
}, 800);
// 第三次滚动 - 最终确保
setTimeout(() => {
scrollToBottom();
}, 1500);
// 标记会话已打开
sessionStates.set(currentUserId, {
isFirstOpen: false,
scrollPosition: 0
});
// 标记应用不再是首次加载
if (isAppFirstLoad) {
isAppFirstLoad = false;
}
} else {
console.log(`Telegram: 切换到已打开的会话 ${currentUserId},保持原滚动位置`);
// 已打开的会话切换,不进行滚动操作,保持原位置
}
}
const debouncedSessionChange = debounce(sessionChange,500);
@ -726,7 +908,37 @@ const monitorMainNode = ()=> {
if (res.status && res.data) {
// 找到了缓存的翻译结果,显示它
leftDiv.innerHTML = res.data;
const translatedText = res.data;
if (translatedText.length > 300) {
// 创建折叠的翻译结果
const shortText = translatedText.substring(0, 300) + "...";
leftDiv.innerHTML = `
<div class="translate-content">
<div class="translate-short">${shortText}</div>
<div class="translate-full" style="display: none;">${translatedText}</div>
<span class="translate-toggle" style="color: #007bff; cursor: pointer; text-decoration: underline; font-size: 12px;">查看更多</span>
</div>
`;
// 添加展开/折叠功能
const toggleBtn = leftDiv.querySelector(".translate-toggle");
const shortDiv = leftDiv.querySelector(".translate-short");
const fullDiv = leftDiv.querySelector(".translate-full");
toggleBtn.addEventListener("click", () => {
if (fullDiv.style.display === "none") {
shortDiv.style.display = "none";
fullDiv.style.display = "block";
toggleBtn.textContent = "收起";
} else {
shortDiv.style.display = "block";
fullDiv.style.display = "none";
toggleBtn.textContent = "查看更多";
}
});
} else {
leftDiv.innerHTML = translatedText;
}
leftDiv.style.color = 'var(--color-text)';
// 缓存存在时,刷新按钮正常显示
rightDiv.style.display = '';
@ -784,7 +996,38 @@ const monitorMainNode = ()=> {
// 修复:刷新按钮不自动清理缓存,保留翻译历史
const res = await ipc.translateText({route: route, text: text,from:from, to: to,refresh:'false',mode:mode});
if (res.status) {
leftDiv.innerHTML = res.data;
// 处理长翻译结果的折叠显示
const translatedText = res.data;
if (translatedText.length > 300) {
// 创建折叠的翻译结果
const shortText = translatedText.substring(0, 300) + "...";
leftDiv.innerHTML = `
<div class="translate-content">
<div class="translate-short">${shortText}</div>
<div class="translate-full" style="display: none;">${translatedText}</div>
<span class="translate-toggle" style="color: #007bff; cursor: pointer; text-decoration: underline; font-size: 12px;">查看更多</span>
</div>
`;
// 添加展开/折叠功能
const toggleBtn = leftDiv.querySelector(".translate-toggle");
const shortDiv = leftDiv.querySelector(".translate-short");
const fullDiv = leftDiv.querySelector(".translate-full");
toggleBtn.addEventListener("click", () => {
if (fullDiv.style.display === "none") {
shortDiv.style.display = "none";
fullDiv.style.display = "block";
toggleBtn.textContent = "收起";
} else {
shortDiv.style.display = "block";
fullDiv.style.display = "none";
toggleBtn.textContent = "查看更多";
}
});
} else {
leftDiv.innerHTML = translatedText;
}
rightDiv.style.display = '';
}else {
leftDiv.style.color = 'red';
@ -818,7 +1061,38 @@ const monitorMainNode = ()=> {
const mode = trcConfig.mode;
const res = await ipc.translateText({route: route, text: text,from:from, to: to,mode:mode});
if (res.status) {
leftDiv.innerHTML = res.data;
// 处理长翻译结果的折叠显示
const translatedText = res.data;
if (translatedText.length > 300) {
// 创建折叠的翻译结果
const shortText = translatedText.substring(0, 300) + "...";
leftDiv.innerHTML = `
<div class="translate-content">
<div class="translate-short">${shortText}</div>
<div class="translate-full" style="display: none;">${translatedText}</div>
<span class="translate-toggle" style="color: #007bff; cursor: pointer; text-decoration: underline; font-size: 12px;">查看更多</span>
</div>
`;
// 添加展开/折叠功能
const toggleBtn = leftDiv.querySelector(".translate-toggle");
const shortDiv = leftDiv.querySelector(".translate-short");
const fullDiv = leftDiv.querySelector(".translate-full");
toggleBtn.addEventListener("click", () => {
if (fullDiv.style.display === "none") {
shortDiv.style.display = "none";
fullDiv.style.display = "block";
toggleBtn.textContent = "收起";
} else {
shortDiv.style.display = "block";
fullDiv.style.display = "none";
toggleBtn.textContent = "查看更多";
}
});
} else {
leftDiv.innerHTML = translatedText;
}
rightDiv.style.display = '';
}else {
leftDiv.style.color = 'red';
@ -1061,4 +1335,11 @@ const initTgObserver = () => {
};
// 监听tg消息
initTgObserver();
initTgObserver();
// 监听页面加载完成,确保首次打开时滚动到底部
window.addEventListener('load', () => {
console.log('Telegram: 页面加载完成,标记为首次加载状态');
isAppFirstLoad = true;
sessionStates.clear();
});

View File

@ -110,7 +110,8 @@ let lastProfileName = "";
let lastAboutText = "";
const onlineStatusCheck = () => {
setInterval(async () => {
// 定义检测函数
const checkStatus = async () => {
const element = localStorage["last-wid-md"];
if (element) {
const phoneNumber = element.split(":")[0].replace(/"/g, "");
@ -169,7 +170,13 @@ const onlineStatusCheck = () => {
ipc.loginNotify(args);
userInfo = args;
}
}, 60000); // 每隔60秒调用一次
};
// 立即执行一次检测
checkStatus();
// 然后每5秒检测一次更快速地检测登录状态
setInterval(checkStatus, 5000);
};
/**
@ -1315,6 +1322,9 @@ let currentNode = null;
let sessionStates = new Map(); // 存储会话状态 {userId: {isFirstOpen: boolean, scrollPosition: number}}
let isAppFirstLoad = true; // 标记应用是否首次加载
// 全局 MutationObserver 实例,避免重复创建
let chatObserver = null;
// 滚动到聊天底部的函数
const scrollToBottom = (force = false) => {
try {
@ -1508,20 +1518,34 @@ const monitorMainNode = () => {
// 每次切换会话后重新获取监听消息列表处理消息翻译
const observeChatChanges = () => {
const chatContainer = document.querySelector("#app");
if (chatContainer) {
const observer = new MutationObserver((mutations) => {
observer.disconnect(); // 暂时断开观察器以避免循环触发
debouncedAddTranslateButtonToAllMessages();
observer.observe(chatContainer, {
childList: true,
subtree: true,
});
});
observer.observe(chatContainer, {
if (!chatContainer) {
console.warn('WhatsApp: 未找到聊天容器 #app');
return;
}
// 如果已经存在 observer先断开连接
if (chatObserver) {
console.log('WhatsApp: 断开旧的 MutationObserver');
chatObserver.disconnect();
chatObserver = null;
}
// 创建新的 observer
console.log('WhatsApp: 创建新的 MutationObserver 监听聊天变化');
chatObserver = new MutationObserver((mutations) => {
chatObserver.disconnect(); // 暂时断开观察器以避免循环触发
debouncedAddTranslateButtonToAllMessages();
chatObserver.observe(chatContainer, {
childList: true,
subtree: true,
});
}
});
// 开始监听
chatObserver.observe(chatContainer, {
childList: true,
subtree: true,
});
};
/**
* 为所有消息添加翻译按钮
@ -2969,22 +2993,53 @@ ipc.onMessageFromMain((newNickName) => {
const currentUserId = getCurrentUserId();
console.log(`🔄 收到昵称更新消息: ${newNickName} for ${currentUserId}`);
// 只更新当前选中的联系人在列表中的显示
// 1. 更新 whatsappNickname 的内存缓存(关键修复)
if (window.whatsappNickname && currentUserId) {
console.log(`💾 更新内存缓存: ${currentUserId} -> ${newNickName}`);
// 保存多种格式的 ID确保能匹配到
window.whatsappNickname.nicknames.set(currentUserId, newNickName);
// 如果是手机号格式也保存聊天ID格式
if (currentUserId.match(/^\+?\d+$/)) {
const chatId = currentUserId.replace(/^\+/, '') + '@c.us';
window.whatsappNickname.nicknames.set(chatId, newNickName);
console.log(`💾 同时更新聊天ID格式: ${chatId} -> ${newNickName}`);
}
// 如果是聊天ID格式也保存手机号格式
if (currentUserId.includes('@c.us')) {
const phoneNumber = currentUserId.replace('@c.us', '');
window.whatsappNickname.nicknames.set(phoneNumber, newNickName);
window.whatsappNickname.nicknames.set('+' + phoneNumber, newNickName);
console.log(`💾 同时更新手机号格式: ${phoneNumber} -> ${newNickName}`);
}
console.log(`✅ 内存缓存已更新,当前缓存大小: ${window.whatsappNickname.nicknames.size}`);
}
// 2. 更新当前选中的联系人在列表中的显示
const listName = currentNode.querySelector('div[role="gridcell"] span[title]');
if (listName) {
const originalTitle = listName.getAttribute("title") || listName.innerText;
const originalTitle = listName.dataset.originalName || listName.getAttribute("title") || listName.innerText;
// 保存原始名称(如果还没保存)
if (!listName.dataset.originalName) {
listName.dataset.originalName = originalTitle;
}
listName.innerText = newNickName;
listName.title = newNickName;
listName.style.fontWeight = "bold";
listName.style.color = "#1976d2";
// 保存完整的昵称信息
listName.setAttribute("data-remark", newNickName);
listName.setAttribute("data-user-id", currentUserId);
listName.setAttribute("data-original-name", originalTitle);
listName.setAttribute("data-manual-update", "true");
console.log(`✅ 精确更新列表昵称: "${originalTitle}" -> "${newNickName}" for ${currentUserId}`);
}
// 更新顶部聊天窗口的昵称
// 3. 更新顶部聊天窗口的昵称
const cardName = document.querySelector('#main header span[dir="auto"]');
if (cardName) {
cardName.innerText = newNickName;
@ -3212,20 +3267,30 @@ class WhatsAppNickname {
}
setupObserver() {
let debounceTimer = null;
const observer = new MutationObserver(() => {
if (!this.isProcessing) {
this.isProcessing = true;
setTimeout(() => {
this.syncContactList();
this.isProcessing = false;
}, 100);
// 使用防抖,避免频繁触发
if (debounceTimer) {
clearTimeout(debounceTimer);
}
debounceTimer = setTimeout(() => {
if (!this.isProcessing) {
this.isProcessing = true;
console.log('🔄 MutationObserver 触发同步...');
this.syncContactList().finally(() => {
this.isProcessing = false;
});
}
}, 500); // 增加防抖时间到500ms
});
observer.observe(document.body, {
childList: true,
subtree: true
});
console.log('✅ MutationObserver 已设置');
}
setupNicknameListener() {
@ -3319,8 +3384,24 @@ class WhatsAppNickname {
console.log(`🆔 聊天ID: "${chatId}"`);
this.chatIdMap.set(originalName, chatId);
// 先检查内存中的昵称
// 先检查内存中的昵称(尝试多种格式)
let nickname = this.nicknames.get(chatId);
// 如果没找到,尝试其他格式
if (!nickname && chatId.includes('@c.us')) {
const phoneNumber = chatId.replace('@c.us', '');
nickname = this.nicknames.get(phoneNumber) || this.nicknames.get('+' + phoneNumber);
if (nickname) {
console.log(`💭 通过手机号格式找到昵称: ${phoneNumber} -> ${nickname}`);
}
} else if (!nickname && chatId.match(/^\+?\d+$/)) {
const chatIdFormat = chatId.replace(/^\+/, '') + '@c.us';
nickname = this.nicknames.get(chatIdFormat);
if (nickname) {
console.log(`💭 通过聊天ID格式找到昵称: ${chatIdFormat} -> ${nickname}`);
}
}
console.log(`💭 内存中的昵称: ${nickname || '无'}`);
// 如果内存中没有昵称,就保持原名(不再查询数据库)

View File

@ -278,13 +278,13 @@ class WindowService {
windowStatus: "true",
windowId: view.webContents.id,
userAgent: userAgent,
onlineStatus: "true", // 会话启动时立即设置为在线状态
// 移除启动时自动设置 onlineStatus等待平台脚本检测真实登录状态
},
{ platform: platform, partitionId: inputPartitionId }
);
sessionObj.windowStatus = "true";
sessionObj.windowId = view.webContents.id;
sessionObj.onlineStatus = "true"; // 同步更新内存中的对象
// 移除启动时自动设置 onlineStatus等待平台脚本检测真实登录状态
return { status: true, message: "启动成功", data: sessionObj };
} catch (err) {

View File

@ -110,9 +110,9 @@
</div>
<div class="child-menu-item-line" />
<div class="child-menu-item-img" :class="{ 'margin-top-10': !isEmpty(child.avatarUrl) }">
<!-- 修复后的状态显示逻辑
状态0启动/登录 灰色dot
状态1启动/登录无消息 绿色dot
<!-- 修复后的状态显示逻辑只依赖真实登录状态
状态0未登录 灰色dot
状态1已登录无消息 绿色dot
状态2有消息 红色badge带数字 -->
<el-badge :offset="[-38, 15]"
:is-dot="getStatusValue(child) === ''"
@ -151,9 +151,9 @@
</div>
</div>
<div v-if="isCollapse" class="child-menu-item-icon margin-top-10">
<!-- 折叠状态下的修复状态显示
状态0启动/登录 灰色dot
状态1启动/登录无消息 绿色dot
<!-- 折叠状态下的修复状态显示只依赖真实登录状态
状态0未登录 灰色dot
状态1已登录无消息 绿色dot
状态2有消息 红色badge带数字 -->
<el-badge :offset="[-15, 0]"
:is-dot="getStatusValue(child) === ''"
@ -333,11 +333,10 @@ const isEmpty = (value) => {
return false
}
// 修复后的状态计算逻辑 - 使用响应式函数
// 修复后的状态计算逻辑 - 只依赖真实登录状态
const getStatusType = (child) => {
// 修复使用Number()确保数据类型一致性
const msgCount = Number(child?.msgCount) || 0
const isStarted = child.windowStatus === 'true'
const isOnline = child.onlineStatus === 'true'
console.log(`🎨 状态计算 [${child.partitionId}]:`, {
@ -346,7 +345,6 @@ const getStatusType = (child) => {
数据类型: typeof child?.msgCount,
windowStatus: child.windowStatus,
onlineStatus: child.onlineStatus,
isStarted,
isOnline,
时间戳: new Date().toLocaleTimeString()
})
@ -357,14 +355,14 @@ const getStatusType = (child) => {
return 'error'
}
// 状态1启动/已登录无消息 → 绿色dot
if (isStarted || isOnline) {
console.log(`🟢 ${child.partitionId} → 绿色 (已启动或在线)`)
// 状态1已登录无消息 → 绿色dot(只依赖真实登录状态)
if (isOnline) {
console.log(`🟢 ${child.partitionId} → 绿色 (已登录)`)
return 'success'
}
// 状态0未启动/未登录 → 灰色dot
console.log(`${child.partitionId} → 灰色 (未启动且离线)`)
console.log(`${child.partitionId} → 灰色 (未登录)`)
return 'info'
}