| ¶Ô±ÈÐÂÎļþ |
| | |
| | | import type { Message } from '../types' |
| | | |
| | | import type { ImChannelMessageApi } from '#/api/im/message/channel' |
| | | import type { ImGroupMessageApi } from '#/api/im/message/group' |
| | | import type { ImPrivateMessageApi } from '#/api/im/message/private' |
| | | |
| | | import { watch } from 'vue' |
| | | |
| | | import { pullChannelMessageList as apiPullChannelMessageList } from '#/api/im/message/channel' |
| | | import { pullGroupMessageList as apiPullGroupMessageList } from '#/api/im/message/group' |
| | | import { getPrivateMaxReadMessageId as apiGetPrivateMaxReadMessageId, pullPrivateMessageList as apiPullPrivateMessageList } from '#/api/im/message/private' |
| | | import { getCurrentUserId } from '#/views/im/utils/auth' |
| | | |
| | | import { buildChannelConversationStub } from '../../utils/channel' |
| | | import { |
| | | MESSAGE_GROUP_PULL_SIZE, |
| | | MESSAGE_PRIVATE_PULL_SIZE, |
| | | MESSAGE_PRIVATE_READ_ENABLED |
| | | } from '../../utils/config' |
| | | import { |
| | | ImContentType, |
| | | ImConversationType, |
| | | ImMessageStatus, |
| | | isFriendChatTip, |
| | | isFriendNotification |
| | | } from '../../utils/constants' |
| | | import { generateClientMessageId, getPrivateMessagePeerId } from '../../utils/message' |
| | | import { runMinIdPull } from '../../utils/pull' |
| | | import { getFriendDisplayName, getGroupDisplayName } from '../../utils/user' |
| | | import { useConversationStore } from '../store/conversationStore' |
| | | import { useFriendStore } from '../store/friendStore' |
| | | import { useGroupRequestStore } from '../store/groupRequestStore' |
| | | import { useGroupStore } from '../store/groupStore' |
| | | import { type PulledMessage, useMessageStore } from '../store/messageStore' |
| | | import { useRtcStore } from '../store/rtcStore' |
| | | import { useImWebSocketStore } from '../store/websocketStore' |
| | | |
| | | /** ä¸ç±»æ¶æ¯ pull æ¥å£è¿åçåå§ VO èåç±»åï¼runMinIdPull åªé id æ¨è¿æ¸¸æ ï¼å
·ä½ååå¨ applyPage å
æç±»å cast */ |
| | | type PulledRawMessage = ImChannelMessageApi.ChannelMessageRespVO | ImGroupMessageApi.GroupMessageRespVO | ImPrivateMessageApi.PrivateMessageRespVO |
| | | |
| | | /** |
| | | * æ¶æ¯å¢éæåï¼ç»å½åå页æå离线æé´çæ°æ¶æ¯ |
| | | * |
| | | * 设计è¦ç¹ï¼ |
| | | * 1. åæ¶æåç§è + 群èï¼ä½¿ç¨åèªç `minId` 游æ ï¼privateMessageMaxId / groupMessageMaxIdï¼ |
| | | * 2. åç«¯ä¸æ¬¡æå¤è¿å size æ¡ï¼å端æ minId æç»ç¿»é¡µï¼ç´å°æ¥å£è¿å空åè¡¨ä¸ºæ¢ |
| | | * 3. æåæé´ conversationStore.loading=trueï¼ |
| | | * - conversationStore è·³è¿æ¹éæä¹
åï¼é¿å
é¢ç¹åå
¥å¡é¡¿ |
| | | * - websocketStore ææ°æ¥ç WS æ®éæ¶æ¯ä¸¢è¿ç¼å²åºï¼ç循ç¯ç»æåç»ä¸åæ¾ |
| | | * 4. WebSocket éè¿åä¼å触å䏿¬¡æåï¼è¡¥é½æç½æé´éè¿çæ¶æ¯ |
| | | */ |
| | | export const useMessagePuller = () => { |
| | | const conversationStore = useConversationStore() |
| | | const messageStore = useMessageStore() |
| | | const wsStore = useImWebSocketStore() |
| | | const friendStore = useFriendStore() |
| | | const groupStore = useGroupStore() |
| | | const groupRequestStore = useGroupRequestStore() |
| | | const rtcStore = useRtcStore() |
| | | const currentUserId = getCurrentUserId() |
| | | |
| | | /** å¤æè¯·æ±æ¯å¦è¢«ä¸»å¨åæ¶ */ |
| | | const isAbortError = (e: unknown): boolean => { |
| | | const error = e as { code?: string; message?: string; name?: string; } |
| | | return ( |
| | | error?.name === 'CanceledError' || |
| | | error?.code === 'ERR_CANCELED' || |
| | | error?.message === 'canceled' |
| | | ) |
| | | } |
| | | |
| | | /** ç§èä¼è¯å½å±ï¼èªå·±åçç®"åç» receiverId çä¼è¯"ï¼å¦åç®"åéæ¹çä¼è¯"ï¼curry currentUserId è¿éå
åå° 3 å¤è°ç¨æ¹çæ ·æ¿ */ |
| | | const getPrivatePeerId = (message: ImPrivateMessageApi.PrivateMessageRespVO) => |
| | | getPrivateMessagePeerId(message, currentUserId) |
| | | |
| | | /** æå¡ç«¯ç§èæ¶æ¯ -> æ¬å° Messageï¼targetId æ¯ä¼è¯ä¸»é®ï¼å¯¹ç«¯ userIdï¼ */ |
| | | const convertPrivateMessage = (message: ImPrivateMessageApi.PrivateMessageRespVO): Message => { |
| | | return { |
| | | id: message.id, |
| | | clientMessageId: message.clientMessageId || generateClientMessageId(), |
| | | type: message.type, |
| | | content: message.content, |
| | | status: message.status, |
| | | receiptStatus: message.receiptStatus, |
| | | sendTime: new Date(message.sendTime).getTime(), |
| | | senderId: message.senderId, |
| | | targetId: getPrivatePeerId(message), |
| | | selfSend: message.senderId === currentUserId |
| | | } |
| | | } |
| | | |
| | | /** æå¡ç«¯ç¾¤èæ¶æ¯ -> æ¬å° Message */ |
| | | const convertGroupMessage = (message: ImGroupMessageApi.GroupMessageRespVO): Message => { |
| | | return { |
| | | id: message.id, |
| | | clientMessageId: message.clientMessageId || generateClientMessageId(), |
| | | type: message.type, |
| | | content: message.content, |
| | | status: message.status, |
| | | sendTime: new Date(message.sendTime).getTime(), |
| | | senderId: message.senderId, |
| | | targetId: message.groupId, |
| | | selfSend: message.senderId === currentUserId, |
| | | atUserIds: message.atUserIds || [], |
| | | receiverUserIds: message.receiverUserIds || [], |
| | | receiptStatus: message.receiptStatus, |
| | | readCount: message.readCount |
| | | } |
| | | } |
| | | |
| | | /** æå¡ç«¯é¢éæ¶æ¯ -> æ¬å° Message */ |
| | | const convertChannelMessage = (message: ImChannelMessageApi.ChannelMessageRespVO): Message => { |
| | | return { |
| | | id: message.id, |
| | | clientMessageId: message.clientMessageId || generateClientMessageId(), |
| | | type: message.type, |
| | | content: message.content, |
| | | status: ImMessageStatus.NORMAL, // é¢éæ æ¤åï¼æä¸ºæ£å¸¸ |
| | | receiptStatus: message.receiptStatus, // é¢é已读æï¼DONE 已读 / PENDING æªè¯» |
| | | sendTime: new Date(message.sendTime).getTime(), |
| | | senderId: 0, // ç³»ç»ä¸åï¼æ åé人 |
| | | targetId: message.channelId, // ä¼è¯å½å±å°é¢éç¼å· |
| | | selfSend: false, |
| | | materialId: message.materialId // 详æ
页æå¯ææ¬ç¨ |
| | | } |
| | | } |
| | | |
| | | /** é¢éï¼ä¼è¯å½å±å° channelIdï¼name / avatar æç¨å ä½ï¼å°æ¥æ¥å
¥ channelStore ååå¡«çå¼ */ |
| | | const convertChannelConversation = (message: ImChannelMessageApi.ChannelMessageRespVO) => |
| | | buildChannelConversationStub(message.channelId) |
| | | |
| | | /** ç§èï¼ä¼è¯å½å±å°å¯¹ç«¯ userId */ |
| | | const convertPrivateConversation = (message: ImPrivateMessageApi.PrivateMessageRespVO) => { |
| | | const targetId = getPrivatePeerId(message) |
| | | const friend = friendStore.getFriend(targetId) |
| | | return { |
| | | type: ImConversationType.PRIVATE, |
| | | targetId, |
| | | name: friend ? getFriendDisplayName(friend) : String(targetId), // ä¼è¯å表 / 顶鍿 é¢å±ç¤ºï¼å¥½å夿³¨ > ç宿µç§° |
| | | avatar: friend?.avatar || '', |
| | | silent: friend?.silent |
| | | } |
| | | } |
| | | |
| | | /** 群èï¼ä¼è¯å½å±å° groupId */ |
| | | const convertGroupConversation = (message: ImGroupMessageApi.GroupMessageRespVO) => { |
| | | const group = groupStore.getGroup(message.groupId) |
| | | return { |
| | | type: ImConversationType.GROUP, |
| | | targetId: message.groupId, |
| | | name: group ? getGroupDisplayName(group) : String(message.groupId), |
| | | avatar: group?.avatar || '', |
| | | silent: group?.silent |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * åç±»åæåç¦»çº¿æ¶æ¯ï¼ç¿»é¡µ / minId æ¸¸æ æ¨è¿ / 空页åç± runMinIdPull è´è´£ï¼è¿éåªåæ¥å£åæ¯ + éæ¡ä¸å¡åå |
| | | * ï¼æ¤å / 好åéç¥ / æ®éæ¶æ¯ï¼+ å
¥åºã |
| | | * |
| | | * åæ¶è¯ä¹ä¸¤å±å®å«ï¼ç» isActive ä¼ å
¥ runMinIdPullï¼ä»»ä¸ä¸çå³ä¸¢å¼æ¬æ¹ä¸å
¥åºãåæ¢ç¿»é¡µï¼é¿å
æ§ session ååºè½å°æ° storeï¼ |
| | | * 1. startEpochï¼cancelPull() éå¢ pullEpochï¼ç¦»å¼ IM / åè´¦å·æ¶è·³åº |
| | | * 2. startUserIdï¼æ¯æ¹ await 忝坹å½åç»å½ userIdï¼é²å¾¡ logout / å¤ tab ä¸ç¨æ·å·²åä½ cancelPull æªè§¦å |
| | | */ |
| | | const pullByType = async ( |
| | | conversationType: number, |
| | | startMinId: number, |
| | | startEpoch: number, |
| | | startUserId: number, |
| | | signal: AbortSignal |
| | | ) => { |
| | | // ç§è / 群è / é¢éåèªä¸å¥æ¥å£ï¼æ conversationType 忝è°åº¦ã翻页æºå¶ï¼minId 游æ / ç©ºé¡µå¤æ / 鲿»ç¿»ï¼äº¤ç» runMinIdPull |
| | | const isPrivate = conversationType === ImConversationType.PRIVATE |
| | | const isChannel = conversationType === ImConversationType.CHANNEL |
| | | const size = isPrivate ? MESSAGE_PRIVATE_PULL_SIZE : MESSAGE_GROUP_PULL_SIZE |
| | | const isStillValid = () => |
| | | !signal.aborted && pullEpoch === startEpoch && getCurrentUserId() === startUserId |
| | | await runMinIdPull<PulledRawMessage>({ |
| | | initialMinId: startMinId, |
| | | pageSize: size, |
| | | isActive: isStillValid, |
| | | fetchPage: ({ minId, size }) => { |
| | | if (isPrivate) { |
| | | return apiPullPrivateMessageList({ minId, size }, signal) |
| | | } |
| | | if (isChannel) { |
| | | return apiPullChannelMessageList({ minId, size }, signal) |
| | | } |
| | | return apiPullGroupMessageList({ minId, size }, signal) |
| | | }, |
| | | applyPage: async (list, nextMinId) => { |
| | | const pulledMessages: PulledMessage[] = [] |
| | | // éæ¡ dispatchï¼åæ¶æ¯èµ°æ¹é insertï¼RECALL ä¿¡å·èµ°æ¹é recall æåæ¹å
å·² insert çåæ¶æ¯æ´æ°ä¸ºæ¤åæç¤ºã |
| | | // å端æ id ååºè¿åï¼ä¸ä¿¡å· id ä¸å® > åæ¶æ¯ idï¼å
æ´æ° status åæä¿¡å·ï¼ï¼æä»¥åæ¶æ¯ä¸å®å
å°ãrecallMessage æ¾å¾å° |
| | | for (const raw of list) { |
| | | if (isChannel) { |
| | | const message = raw as ImChannelMessageApi.ChannelMessageRespVO |
| | | pulledMessages.push({ |
| | | kind: 'insert', |
| | | conversationInfo: convertChannelConversation(message), |
| | | message: convertChannelMessage(message) |
| | | }) |
| | | continue |
| | | } |
| | | if (isPrivate) { |
| | | const message = raw as ImPrivateMessageApi.PrivateMessageRespVO |
| | | // ç¹æ®ï¼æ¤åæ¶æ¯çå¤ç |
| | | if (message.type === ImContentType.RECALL) { |
| | | pulledMessages.push({ |
| | | kind: 'recall', |
| | | conversationType: ImConversationType.PRIVATE, |
| | | targetId: getPrivatePeerId(message), |
| | | recallSignalContent: message.content |
| | | }) |
| | | continue |
| | | } |
| | | // ç¹æ®ï¼åå²å¥½åäºä»¶åªè¿åèå¤©æ°æ³¡ï¼å¥½åä¸»æ°æ®ç±å¥½åå¢éè¡¥å¿åæ¥ |
| | | // ä»
FRIEND_ADD / FRIEND_DELETE æä½ä¸ºä¼è¯æ°æ³¡å
¥æ¶æ¯å表 |
| | | if (isFriendNotification(message.type) && !isFriendChatTip(message.type)) { |
| | | continue |
| | | } |
| | | // å
¶å®æ¶æ¯æ£å¸¸å
¥ä¼è¯æ¶æ¯å表 |
| | | pulledMessages.push({ |
| | | kind: 'insert', |
| | | conversationInfo: convertPrivateConversation(message), |
| | | message: convertPrivateMessage(message) |
| | | }) |
| | | } else { |
| | | const message = raw as ImGroupMessageApi.GroupMessageRespVO |
| | | // ç¹æ®ï¼æ¤åæ¶æ¯çå¤ç |
| | | if (message.type === ImContentType.RECALL) { |
| | | pulledMessages.push({ |
| | | kind: 'recall', |
| | | conversationType: ImConversationType.GROUP, |
| | | targetId: message.groupId, |
| | | recallSignalContent: message.content |
| | | }) |
| | | continue |
| | | } |
| | | pulledMessages.push({ |
| | | kind: 'insert', |
| | | conversationInfo: convertGroupConversation(message), |
| | | message: convertGroupMessage(message) |
| | | }) |
| | | } |
| | | } |
| | | // å
¥åº + æ¨è¿ messageMaxIdï¼nextMinId ä¸ºç©ºï¼æ¬æ¹æ ææ idï¼æ¶ä¸æ¨è¿æ¸¸æ ï¼ä¸æ§é»è¾ä¸è´ |
| | | await messageStore.applyPulledMessageList(pulledMessages, conversationType, nextMinId) |
| | | } |
| | | }) |
| | | } |
| | | |
| | | /** å䏿¶å»åªå
è®¸ä¸æ¬¡ pullï¼index.vue çæå¨è°ç¨ä¸éè¿ watch 触åå¯è½å¹¶åï¼å
±ç¨åä¸ä¸ª promise å³å¯å»é */ |
| | | let pullPromise: null | Promise<void> = null |
| | | let pullAbortController: AbortController | null = null |
| | | |
| | | /** |
| | | * 馿¬¡ pull æ¯å¦å·²å®æãä»
å¨ç½® true åï¼isConnected watch æä¼è§¦å pullã |
| | | * 鲿¢ socket onopen æ¯ friendStore/groupStore 颿å
å°è¾¾æ¶ï¼watcher æ¢è·é ææ¶æ¯æå
¥æ©äºä¼è¯å
æ°æ®å¯è§ |
| | | */ |
| | | let initialPulled = false |
| | | |
| | | /** |
| | | * pull 轮次计æ°ï¼åè´¦å· / ç¦»å¼ IM æ¶ cancelPull() éå¢ï¼æ§ pullByType å¾ªç¯æ epoch èªæ£åè·³åº |
| | | * é¿å
æ§ session çæ¥å£ååºå¨æ° session è½å°ï¼é æè·¨è´¦å·æ¶æ¯æ³æ¼ |
| | | * |
| | | * 注æï¼æ®éæè¿ï¼WS çæï¼ä¸åæ¶ pullââç½ç»æå¨ / æå¡ç«¯éå¯é½å±äºæ¬è´¦å·æ£å¸¸çå½å¨æï¼ |
| | | * åæ¶ä¼å¯¼è´é¦æè¢«ä¸æå initialPulled æ°¸è¿åå¨ falseï¼åç»éè¿ watcher ä¸åè¡¥æ |
| | | */ |
| | | let pullEpoch = 0 |
| | | |
| | | /** æ¾å¼åæ¶ï¼ä»
ç± index.vue onUnmountedï¼ç¦»å¼ IM / åè´¦å· / è·¯ç±è·³åºï¼è°ç¨ */ |
| | | const cancelPull = () => { |
| | | pullEpoch++ |
| | | pullAbortController?.abort() |
| | | pullAbortController = null |
| | | // æ§ promise ä»å¨ finally é¶æ®µè·ï¼ä½ epoch å®å«å·²é»æåç»å¯ä½ç¨ï¼è¿éç«å»è®© pullPromise = null 让æ°ä¸è½®å¯éå
¥ |
| | | pullPromise = null |
| | | // åæ¥ä¸¢å¼ WS ç¼å²å¸§ï¼æ§ pull å·²ä¸ä¼ flushBufferï¼è¥ä¸æ¸
䏿¬¡è¿ IM ç¬¬ä¸æ¬¡ pullOnce ä¼ææ§ session çå¸§åæ¾è¿æ° store |
| | | wsStore.discardBuffer() |
| | | } |
| | | |
| | | /** |
| | | * ç¶æäºä»¶è¡¥å¿ï¼å¥½å / 好åç³è¯·èµ°å¢éï¼ç¾¤å表å群ç³è¯·çº¢ç¹èµ°å¿«ç
§å·æ° |
| | | * |
| | | * é¦ç»ä¸»æ°æ®ç± index.vue 驱å¨ï¼éè¿æ¶å store 已就ä½ï¼å¤è·¯ allSettled å¹¶åäºä¸å½±åï¼å路失败ä»
è®°æ¥å¿ã |
| | | * 群æåä¸åå
¨å±å¢é忥ï¼éè¿åªæ è®°æ¬å°ç¾¤æå cache è¿æï¼è¿å
¥ç¾¤ä¼è¯ææåå表æ¶åæ groupId å·æ°ã |
| | | */ |
| | | const pullStateEvents = async (): Promise<void> => { |
| | | // 1. æ¸
çè¿æ¥çº§ç¼å |
| | | messageStore.clearPrivateReadMaxIdCache() |
| | | rtcStore.clearGroupCallCache() |
| | | groupStore.markAllGroupActiveCallsExpired() |
| | | groupStore.markAllGroupInfoExpired() |
| | | groupStore.markAllGroupMembersExpired() |
| | | // 2. å¹¶åè¡¥å¿è¿ç«¯ç¶æ |
| | | const results = await Promise.allSettled([ |
| | | friendStore.pullFriends(), |
| | | friendStore.pullFriendRequests(), |
| | | conversationStore.pullConversationReads(), |
| | | groupStore.fetchGroupList(true), |
| | | groupRequestStore.pullGroupRequests(), |
| | | groupRequestStore.fetchUnhandledGroupRequestList() |
| | | ]) |
| | | for (const result of results) { |
| | | if (result.status === 'rejected') { |
| | | console.warn('[IM] ç¶æäºä»¶å¢éè¡¥å¿å¤±è´¥', result.reason) |
| | | } |
| | | } |
| | | } |
| | | |
| | | /** æ§è¡ä¸æ¬¡å
¨éå¢éæåï¼éå
¥å®å
¨ï¼è¿è¡ä¸å次è°ç¨å¤ç¨åä¸ä¸ª promiseï¼ */ |
| | | const pullOnce = (): Promise<void> => { |
| | | if (!currentUserId) { |
| | | return Promise.resolve() |
| | | } |
| | | if (pullPromise) { |
| | | return pullPromise |
| | | } |
| | | const startEpoch = pullEpoch |
| | | // å¯å¨æ¶çç¨æ·å¿«ç
§ï¼pullByType æ¯æ¹ await 忝坹å½åç»å½ç¨æ·ï¼è´¦å·åäºç«å»ä¸¢å¼ |
| | | const startUserId = currentUserId |
| | | const abortController = new AbortController() |
| | | pullAbortController = abortController |
| | | // æ¬è½® pull ä»å±äºå½å sessionï¼epoch æªæ¼ + ç¨æ·æªåï¼ä»»ä½å¨æ° store ç¶æçå¯ä½ç¨é½è¦å
è¿è¿éå
³ |
| | | const isCurrentPull = () => |
| | | !abortController.signal.aborted && |
| | | pullEpoch === startEpoch && |
| | | getCurrentUserId() === startUserId |
| | | pullPromise = (async () => { |
| | | try { |
| | | // æ§ puller å¨ cancelPull æªè§¦åçå¼å¸¸è·¯å¾ä¸åè¿æ¥æ¶ï¼å
äºä»»ä½å¯ä½ç¨éåºï¼é¿å
æ±¡ææ° session ç loading |
| | | if (!isCurrentPull()) { |
| | | return |
| | | } |
| | | conversationStore.loading = true |
| | | let messagePullSucceeded = false |
| | | try { |
| | | // å¹¶åæåç§è + 群è + é¢éæ¶æ¯ï¼éä½åå§å è½½èæ¶ |
| | | await Promise.all([ |
| | | pullByType( |
| | | ImConversationType.PRIVATE, |
| | | messageStore.privateMessageMaxId, |
| | | startEpoch, |
| | | startUserId, |
| | | abortController.signal |
| | | ), |
| | | pullByType( |
| | | ImConversationType.GROUP, |
| | | messageStore.groupMessageMaxId, |
| | | startEpoch, |
| | | startUserId, |
| | | abortController.signal |
| | | ), |
| | | pullByType( |
| | | ImConversationType.CHANNEL, |
| | | messageStore.channelMessageMaxId, |
| | | startEpoch, |
| | | startUserId, |
| | | abortController.signal |
| | | ) |
| | | ]) |
| | | messagePullSucceeded = true |
| | | } catch (error) { |
| | | if (isAbortError(error)) { |
| | | return |
| | | } |
| | | console.error('[IM] æåç¦»çº¿æ¶æ¯å¤±è´¥:', error) |
| | | } finally { |
| | | // ä»å±æ¬è½®æå¤ä½ loadingï¼æ§è½®è¢« cancel / åè´¦å·æ¶ç±æ°ä¸è½®èªç®¡ï¼é¿å
è¦çæ° session ç true |
| | | if (isCurrentPull()) { |
| | | conversationStore.loading = false |
| | | } |
| | | } |
| | | |
| | | // åæ¶ / åè´¦å·åè·³è¿ flushBuffer / æåº / 已读ä½ç½®è¡¥é½ |
| | | if (!isCurrentPull()) { |
| | | return |
| | | } |
| | | if (!messagePullSucceeded) { |
| | | return |
| | | } |
| | | |
| | | // åæ¾ WebSocket å¨ loading æé´æ¶å°çç¼å²æ¶æ¯ |
| | | const buffered = wsStore.flushBuffer() |
| | | const replayPersistPromises: Promise<void>[] = [] |
| | | for (const item of buffered) { |
| | | if (item.conversationType === ImConversationType.PRIVATE) { |
| | | replayPersistPromises.push(wsStore.handlePrivateMessage(item.payload)) |
| | | } else if (item.conversationType === ImConversationType.CHANNEL) { |
| | | replayPersistPromises.push(wsStore.handleChannelMessage(item.payload)) |
| | | } else { |
| | | replayPersistPromises.push(wsStore.handleGroupMessage(item.payload)) |
| | | } |
| | | } |
| | | await Promise.all(replayPersistPromises) |
| | | |
| | | // pull + replay é½å®æååæåºï¼é¿å
åæ¾æ¶æ¯æä¹±é¡ºåº |
| | | conversationStore.sortConversationList() |
| | | |
| | | // éè¿ / å·å¯å¨åè¡¥é½å½åæ¿æ´»ç§èä¼è¯çã对æ¹å·²è¯»ä½ç½®ã |
| | | // 离线æé´éè¿ç RECEIPT æ¨éä¼è¢«è¿éè¡¥åï¼å
¶ä»ç§èä¼è¯çç¨æ·ç¹å¼æ¶ç± index.vue ç watch 触å |
| | | // ç§è已读å
³éæ¶è·³è¿ï¼é¿å
æå°å·²ç¦ç¨æ¥å£è§¦åé误æ¥å¿ |
| | | const active = conversationStore.activeConversation |
| | | if (MESSAGE_PRIVATE_READ_ENABLED && active && active.type === ImConversationType.PRIVATE) { |
| | | try { |
| | | const maxReadId = await apiGetPrivateMaxReadMessageId( |
| | | active.targetId, |
| | | abortController.signal |
| | | ) |
| | | if (!isCurrentPull()) { |
| | | return |
| | | } |
| | | messageStore.updatePrivateReadMaxId(active.targetId, maxReadId) |
| | | if (maxReadId) { |
| | | messageStore.applyMessageReadReceipt({ |
| | | conversationType: ImConversationType.PRIVATE, |
| | | targetId: active.targetId, |
| | | privateReadMaxId: maxReadId |
| | | }) |
| | | } |
| | | } catch (error) { |
| | | if (isAbortError(error)) { |
| | | return |
| | | } |
| | | console.warn('[IM] æå对æ¹å·²è¯»ä½ç½®å¤±è´¥', error) |
| | | } |
| | | } |
| | | } finally { |
| | | // ä»å±æ¬è½®ï¼æ£å¸¸å®æé¦æï¼epoch çä½ userId åäºï¼æ¸
pullPromise é²å¡æ»ã䏿 馿ï¼epoch æ¼ï¼cancelPull å·²æ¸
ï¼no-op |
| | | if (isCurrentPull()) { |
| | | pullPromise = null |
| | | initialPulled = true |
| | | if (pullAbortController === abortController) { |
| | | pullAbortController = null |
| | | } |
| | | } else if (pullEpoch === startEpoch) { |
| | | pullPromise = null |
| | | if (pullAbortController === abortController) { |
| | | pullAbortController = null |
| | | } |
| | | } |
| | | } |
| | | })() |
| | | return pullPromise |
| | | } |
| | | |
| | | /** |
| | | * æç½æé´ WS æ¶ä¸å°æ¨éï¼éè¿åæ¢è¦æ minId è¡¥é½æ¶æ¯ï¼ä¹è¦æ update_time + id è¡¥é½å¥½å / 群 / 群ç³è¯·ç¶æã |
| | | * 馿¬¡è¿æ¥ç± index.vue æ¾å¼é©±å¨ï¼pullOnce ææ¶æ¯ + å store 馿ï¼ï¼è¿éä»
è¦çä¹åçéè¿ã |
| | | * éè¿æ¶ store 已就ä½ï¼pullStateEvents ä¸ pullOnce å¹¶åå³å¯ï¼æ éãå
å°±ä½åææ¶æ¯ãçé¦ç»é¡ºåºçº¦æã |
| | | */ |
| | | watch( |
| | | () => wsStore.isConnected, |
| | | (isConnected) => { |
| | | if (isConnected && initialPulled) { |
| | | void pullOnce() |
| | | void pullStateEvents() |
| | | } |
| | | } |
| | | ) |
| | | |
| | | return { pullOnce, cancelPull, convertPrivateMessage, convertGroupMessage } |
| | | } |