From 451ded82c58cdfd4d8941533db50df61f448b292 Mon Sep 17 00:00:00 2001 From: Dawn <2439646234@qq.com> Date: Sat, 28 Dec 2024 02:37:32 +0800 Subject: [PATCH] =?UTF-8?q?perf(component):=20:zap:=20=E4=BC=98=E5=8C=96?= =?UTF-8?q?=E6=92=A4=E5=9B=9E=E6=B6=88=E6=81=AF=E9=87=8D=E6=96=B0=E7=BC=96?= =?UTF-8?q?=E8=BE=91=E5=88=A4=E5=AE=9A=20(#128)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/codecov.yml | 6 --- src/components/rightBox/chatBox/ChatMain.vue | 45 +++++++++++++---- src/components/rightBox/emoji/index.vue | 9 ++-- src/hooks/useCommon.ts | 10 ++++ src/hooks/useLogin.ts | 6 +-- src/hooks/useMessage.ts | 1 - src/layout/left/model.tsx | 3 +- src/stores/chat.ts | 53 +++++++++++++++++--- src/styles/scss/global/variable.scss | 4 +- src/typings/components.d.ts | 1 + src/views/LockScreen.vue | 2 +- src/views/homeWindow/message/index.vue | 35 ++++++++++++- 12 files changed, 140 insertions(+), 35 deletions(-) diff --git a/.github/workflows/codecov.yml b/.github/workflows/codecov.yml index db2acaef..d86549de 100644 --- a/.github/workflows/codecov.yml +++ b/.github/workflows/codecov.yml @@ -43,12 +43,6 @@ jobs: - name: Run tests with coverage run: pnpm coverage - - name: Upload coverage to Codecov - uses: ./ - with: - file: ./coverage/coverage-final.json - flags: unittest - - name: Upload results to Codecov uses: codecov/codecov-action@v5 with: diff --git a/src/components/rightBox/chatBox/ChatMain.vue b/src/components/rightBox/chatBox/ChatMain.vue index 62aed85a..b588ac4a 100644 --- a/src/components/rightBox/chatBox/ChatMain.vue +++ b/src/components/rightBox/chatBox/ChatMain.vue @@ -16,8 +16,37 @@ + + + + + + + + + + + + + + + + + + + + + + + (msgId: number) => { const message = chatStore.getMessage(msgId) if (!recalledMsg || !message) return false - // 判断是否是当前用户的撤回消息且在2分钟内 - return ( - message.fromUser.uid === userUid.value && - !isDiffNow({ - time: recalledMsg.recallTime, - unit: 'minute', - diff: 2 - }) - ) + // 只需要判断是否是当前用户的消息,时间检查已经在 getRecalledMessage 中处理 + return message.fromUser.uid === userUid.value }) const handleReEdit = (msgId: number) => { @@ -767,6 +789,9 @@ onUnmounted(() => { } hoverBubble.value.key = -1 window.removeEventListener('click', closeMenu, true) + + // 清理所有过期定时器 + chatStore.clearAllExpirationTimers() }) diff --git a/src/components/rightBox/emoji/index.vue b/src/components/rightBox/emoji/index.vue index 7cda1c92..68828ce6 100644 --- a/src/components/rightBox/emoji/index.vue +++ b/src/components/rightBox/emoji/index.vue @@ -302,16 +302,17 @@ onMounted(async () => { background-color: var(--emoji-hover); cursor: pointer; } + + &.active { + background-color: var(--emoji-active-color) !important; + } + &:last-child { margin-right: 0; } } } -.active { - background-color: #13987f; -} - .emoji-content { position: relative; width: 100%; diff --git a/src/hooks/useCommon.ts b/src/hooks/useCommon.ts index b0c9432c..b258d57a 100644 --- a/src/hooks/useCommon.ts +++ b/src/hooks/useCommon.ts @@ -214,6 +214,7 @@ export const useCommon = () => { const divNode = document.createElement('div') divNode.id = 'replyDiv' // 设置id为replyDiv divNode.contentEditable = 'false' // 设置为不可编辑 + divNode.tabIndex = -1 // 防止被focus divNode.style.cssText = ` background-color: var(--reply-bg); font-size: 12px; @@ -223,7 +224,9 @@ export const useCommon = () => { border-radius: 8px; margin-bottom: 2px; user-select: none; + pointer-events: none; /* 防止鼠标事件 */ cursor: default; + outline: none; /* 移除focus时的轮廓 */ ` // 把dom中的value值作为回复信息的作者,dom中的content作为回复信息的内容 const author = dom.accountName + ':' @@ -244,6 +247,8 @@ export const useCommon = () => { padding: 0 4px; color: rgba(19, 152, 127); cursor: default; + user-select: none; + pointer-events: none; ` headerNode.appendChild(document.createTextNode(author)) // 创建一个div标签节点包裹正文内容 @@ -276,6 +281,7 @@ export const useCommon = () => { border-radius: 4px; cursor: default; user-select: none; + pointer-events: none; ` // 将img标签节点插入到div标签节点中 divNode.appendChild(contentBox) @@ -298,6 +304,8 @@ export const useCommon = () => { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; + user-select: none; + pointer-events: none; ` contentBox.appendChild(document.createTextNode(content)) } @@ -312,6 +320,8 @@ export const useCommon = () => { cursor: pointer; margin-left: 10px; flex-shrink: 0; + user-select: none; + pointer-events: auto; /* 确保关闭按钮可以点击 */ ` closeBtn.textContent = '关闭' closeBtn.addEventListener('click', () => { diff --git a/src/hooks/useLogin.ts b/src/hooks/useLogin.ts index f0ca77a9..ad8289dd 100644 --- a/src/hooks/useLogin.ts +++ b/src/hooks/useLogin.ts @@ -20,9 +20,9 @@ export const useLogin = () => { const logout = async () => { const { createWebviewWindow } = useWindow() // todo 退出账号 需要关闭其他的全部窗口 - await createWebviewWindow('登录', 'login', 320, 448, 'home', false, 320, 448).then(() => { - emit(EventEnum.LOGOUT) - emitTo('tray', 'logout_success') + await createWebviewWindow('登录', 'login', 320, 448, 'home', false, 320, 448).then(async () => { + await emit(EventEnum.LOGOUT) + await emitTo('tray', 'logout_success') }) } diff --git a/src/hooks/useMessage.ts b/src/hooks/useMessage.ts index 0b973d2e..8d8f6fff 100644 --- a/src/hooks/useMessage.ts +++ b/src/hooks/useMessage.ts @@ -124,7 +124,6 @@ export const useMessage = () => { // 如果没有会话就把右侧消息框关闭 if (updatedSessions.length === 0) { - console.log('没有会话了') useMitt.emit(MittEnum.MSG_BOX_SHOW, { item: -1 }) return } diff --git a/src/layout/left/model.tsx b/src/layout/left/model.tsx index d1a114f1..7c802bc3 100644 --- a/src/layout/left/model.tsx +++ b/src/layout/left/model.tsx @@ -24,6 +24,7 @@ import { check } from '@tauri-apps/plugin-updater' import { relaunch } from '@tauri-apps/plugin-process' import { useUserStore } from '@/stores/user.ts' import { useSettingStore } from '@/stores/setting.ts' +import { AvatarUtils } from '@/utils/avatarUtils.ts' const formRef = ref() const formValue = ref({ @@ -83,7 +84,7 @@ export const LockScreen = defineComponent(() => { 锁定屏幕 - +

{userStore.userInfo.name}

diff --git a/src/stores/chat.ts b/src/stores/chat.ts index fec8b8bb..6ff20420 100644 --- a/src/stores/chat.ts +++ b/src/stores/chat.ts @@ -13,11 +13,20 @@ import { useUserStore } from '@/stores/user.ts' import { renderReplyContent } from '@/utils/RenderReplyContent.ts' import { sendNotification } from '@tauri-apps/plugin-notification' +type RecalledMessage = { + messageId: number + content: string + recallTime: number +} + // 定义每页加载的消息数量 export const pageSize = 20 // 标识是否第一次请求 let isFirstInit = false +// 撤回消息的过期时间 +const RECALL_EXPIRATION_TIME = 2 * 60 * 1000 // 2分钟,单位毫秒 + export const useChatStore = defineStore( 'chat', () => { @@ -43,7 +52,7 @@ export const useChatStore = defineStore( ) // 消息加载状态 const replyMapping = reactive>>(new Map([[currentRoomId.value, new Map()]])) // 回复消息的映射关系 // 存储撤回的消息内容和时间 - const recalledMessages = reactive>(new Map()) + const recalledMessages = reactive>(new Map()) // 当前聊天室的消息Map计算属性 const currentMessageMap = computed({ @@ -334,7 +343,7 @@ export const useChatStore = defineStore( }) } - // tab 在后台获得新消息,就开始闪烁! + // tab 在台获得新消息,就开始闪烁! // if (document.hidden && !shakeTitle.isShaking) { // shakeTitle.start() // } @@ -403,17 +412,32 @@ export const useChatStore = defineStore( } } + // 存储每条撤回消息的过期定时器 + const expirationTimers = new Map() + // 更新消息撤回状态 const updateRecallStatus = (data: RevokedMsgType) => { const { msgId } = data const message = currentMessageMap.value?.get(msgId) if (message && typeof data.recallUid === 'number') { // 存储撤回的消息内容和时间 + const recallTime = Date.now() recalledMessages.set(msgId, { + messageId: msgId, content: message.message.body.content, - recallTime: Date.now() + recallTime }) + // 为这条消息设置过期定时器 TODO: setTimeout会有精度问题 + const timeoutId = window.setTimeout(() => { + recalledMessages.delete(msgId) + expirationTimers.delete(msgId) + triggerMessageMapUpdate() + }, RECALL_EXPIRATION_TIME) + + // 存储定时器ID以便清理 + expirationTimers.set(msgId, timeoutId) + message.message.type = MsgEnum.RECALL const cacheUser = cachedStore.userCachedList[data.recallUid] // 如果撤回者的 id 不等于消息发送人的 id, 或者你本人就是管理员,那么显示管理员撤回的。 @@ -436,11 +460,27 @@ export const useChatStore = defineStore( } } - // 获取撤回的消息内容 - const getRecalledMessage = (msgId: number) => { + // 添加一个工具函数来触发消息列表更新 + const triggerMessageMapUpdate = () => { + if (currentMessageMap.value) { + const newMap = new Map(currentMessageMap.value) + currentMessageMap.value = newMap + } + } + + // 获取撤回消息 + const getRecalledMessage = (msgId: number): RecalledMessage | undefined => { return recalledMessages.get(msgId) } + // 清理所有定时器 + const clearAllExpirationTimers = () => { + expirationTimers.forEach((timerId) => { + clearTimeout(timerId) + }) + expirationTimers.clear() + } + // 删除消息 const deleteMsg = (msgId: number) => { currentMessageMap.value?.delete(msgId) @@ -526,7 +566,8 @@ export const useChatStore = defineStore( getMessage, removeContact, getRecalledMessage, - recalledMessages + recalledMessages, + clearAllExpirationTimers } }, { diff --git a/src/styles/scss/global/variable.scss b/src/styles/scss/global/variable.scss index 312644fd..bf02d317 100644 --- a/src/styles/scss/global/variable.scss +++ b/src/styles/scss/global/variable.scss @@ -8,7 +8,7 @@ html, body, svg { outline: none; - overscroll-behavior: none; // 禁止mac的“触底反弹”效果 + overscroll-behavior: none; // 禁止mac的"触底反弹"效果 } /**! 主题变量 */ /* todo: 这里只需要定义单个颜色的变量即可无需重复定义组件的颜色 */ @@ -68,6 +68,7 @@ svg { // 表情框样式 --bg-emoji: rgba(255, 255, 255, 0.6); --emoji-hover: #e3e3e3; + --emoji-active-color: #13987f; // 回复消息样式 --reply-color: #909090; --reply-hover: #505050; @@ -149,6 +150,7 @@ html[data-theme='dark'] { // 表情框样式 --bg-emoji: rgba(27, 27, 27, 0.8); --emoji-hover: #3b3b3b; + --emoji-active-color: #13987f; // 回复消息样式 --reply-color: #e3e3e3; --reply-hover: #b1b1b1; diff --git a/src/typings/components.d.ts b/src/typings/components.d.ts index de7061ac..a658e3ef 100644 --- a/src/typings/components.d.ts +++ b/src/typings/components.d.ts @@ -54,6 +54,7 @@ declare module 'vue' { NPopselect: typeof import('naive-ui')['NPopselect'] NQrCode: typeof import('naive-ui')['NQrCode'] NRadio: typeof import('naive-ui')['NRadio'] + NResult: typeof import('naive-ui')['NResult'] NScrollbar: typeof import('naive-ui')['NScrollbar'] NSelect: typeof import('naive-ui')['NSelect'] NSkeleton: typeof import('naive-ui')['NSkeleton'] diff --git a/src/views/LockScreen.vue b/src/views/LockScreen.vue index 9549a0d4..f8433573 100644 --- a/src/views/LockScreen.vue +++ b/src/views/LockScreen.vue @@ -3,7 +3,7 @@
+ class="lock-bg overflow-y-hidden rounded-8px select-none absolute top-0 left-0 w-full h-full z-9999 transition-all duration-300 ease-in-out"> diff --git a/src/views/homeWindow/message/index.vue b/src/views/homeWindow/message/index.vue index 96cc7b9f..861c5e03 100644 --- a/src/views/homeWindow/message/index.vue +++ b/src/views/homeWindow/message/index.vue @@ -2,7 +2,7 @@ -
+
+ + + + + + + + + + + + + + + + + + + + + + + +