| ¶Ô±ÈÐÂÎļþ |
| | |
| | | import type { ImGroupRequestApi } from '#/api/im/group/request' |
| | | |
| | | import { acceptHMRUpdate, defineStore } from 'pinia' |
| | | |
| | | import { agreeGroupRequest as apiAgreeGroupRequest, getMyGroupRequest as apiGetMyGroupRequest, getUnhandledRequestList as apiGetUnhandledRequestList, pullMyGroupRequestList as apiPullMyGroupRequestList, refuseGroupRequest as apiRefuseGroupRequest } from '#/api/im/group/request' |
| | | import { getCurrentUserId } from '#/views/im/utils/auth' |
| | | import { ImGroupRequestHandleResult } from '#/views/im/utils/constants' |
| | | |
| | | import { getDb, StorageKeys } from '../../utils/db' |
| | | import { runIncrementalPull } from '../../utils/pull' |
| | | |
| | | type PendingRequest = { epoch: number; promise: Promise<void>; userId: number; } |
| | | |
| | | /** clear() æ¶éå¢ï¼æ§è´¦å· in-flight ç pullGroupRequests ç»æ resolve 忝坹ä¸è´æå storeï¼é²è·¨è´¦å·çº¢ç¹æ±¡æï¼ä¸ friendStore åå£å¾ï¼ */ |
| | | let storeEpoch = 0 |
| | | let pendingUnhandledFetch: null | PendingRequest = null |
| | | |
| | | /** |
| | | * IM å 群ç³è¯· Store |
| | | * |
| | | * ä»
ç»´æ¤ãæç®¡ççææç¾¤ã䏿ªå¤ççç³è¯·å表ï¼unhandledListï¼ï¼ |
| | | * 横å¹
/ Drawer é½ä»è¿éæ´¾ç count ååç»å表ï¼é¿å
ç» ImGroupApi.GroupRespVO æ pendingRequestCount åæ®µ |
| | | * |
| | | * æ°æ®çå½å¨æï¼ |
| | | * - è¿ IM åè°ä¸æ¬¡ fetchUnhandledGroupRequestList æé¦é¡µå
¨é |
| | | * - WebSocket 1503 â è° addGroupRequestById(requestId) æåæ¡ + push å° unhandledList å¤´é¨ |
| | | * - WebSocket 1505 / 1506 â æ requestId ä» unhandledList ç§»é¤ |
| | | * - WebSocket 1517 GROUP_ADMIN_ADDï¼èªå·±è¢«å 为 adminï¼â éæ°è° fetchUnhandledGroupRequestList |
| | | * - æ¬ç«¯ agree / refuse å¤çå â æ¬å°æ requestId ç§»é¤ |
| | | */ |
| | | export const useGroupRequestStore = defineStore('imGroupRequestStore', { |
| | | state: () => ({ |
| | | /** æç®¡ççææç¾¤ä¸æªå¤çç³è¯·åè¡¨ï¼æ id ååºï¼ */ |
| | | unhandledList: [] as ImGroupRequestApi.GroupRequestRespVO[], |
| | | /** fetchUnhandledGroupRequestList æ¯å¦æåæ§è¡è¿ï¼é¿å
横å¹
æ¾ç¤º 0 ç¶åè·³æ°åçéªç */ |
| | | loaded: false |
| | | }), |
| | | |
| | | getters: { |
| | | /** |
| | | * åç¾¤ä¸æªå¤çç³è¯·æ°ç Mapï¼O(N) æ«ä¸æ¬¡ç¼åä¾ ConversationItem ç N å¤å¤ç¨ï¼é¿å
NÃM éå¤ filter |
| | | */ |
| | | getUnhandledGroupRequestCountMap(state): Map<number, number> { |
| | | const map = new Map<number, number>() |
| | | for (const request of state.unhandledList) { |
| | | map.set(request.groupId, (map.get(request.groupId) ?? 0) + 1) |
| | | } |
| | | return map |
| | | }, |
| | | /** æå®ç¾¤ä¸çæªå¤çç³è¯·æ° */ |
| | | getUnhandledGroupRequestCount(): (groupId: number) => number { |
| | | return (groupId: number) => this.getUnhandledGroupRequestCountMap.get(groupId) ?? 0 |
| | | }, |
| | | /** æå®ç¾¤ä¸çæªå¤çç³è¯·å表 */ |
| | | getUnhandledGroupRequestListByGroupId: |
| | | (state) => |
| | | (groupId: number): ImGroupRequestApi.GroupRequestRespVO[] => |
| | | state.unhandledList.filter((r) => r.groupId === groupId) |
| | | }, |
| | | |
| | | actions: { |
| | | /** ä» IndexedDB æ¢å¤å 群ç³è¯· */ |
| | | async loadGroupRequestList(): Promise<boolean> { |
| | | try { |
| | | const cached = await getDb().getAll<ImGroupRequestApi.GroupRequestRespVO>('groupRequests') |
| | | if (!cached || cached.length === 0) { |
| | | return false |
| | | } |
| | | this.unhandledList = cached |
| | | .filter((request) => request.handleResult === ImGroupRequestHandleResult.UNHANDLED) |
| | | .toSorted((requestA, requestB) => requestB.id - requestA.id) |
| | | return true |
| | | } catch (error) { |
| | | console.warn('[IM groupRequestStore] æ¬å°å 群ç³è¯·ç¼å读å失败', error) |
| | | return false |
| | | } |
| | | }, |
| | | |
| | | /** ä¿åå 群ç³è¯·å表 */ |
| | | saveGroupRequestList(): void { |
| | | void getDb() |
| | | .transaction(['groupRequests'], 'readwrite', async (tx) => { |
| | | const db = getDb() |
| | | await db.clearStore('groupRequests', tx) |
| | | for (const request of this.unhandledList) { |
| | | await db.put('groupRequests', request, tx) |
| | | } |
| | | }) |
| | | .catch((error) => console.warn('[IM groupRequestStore] æ¬å°å 群ç³è¯·ç¼ååå
¥å¤±è´¥', error)) |
| | | }, |
| | | |
| | | /** ä¿ååæ¡å 群ç³è¯· */ |
| | | async saveGroupRequestRecord(request: ImGroupRequestApi.GroupRequestRespVO): Promise<void> { |
| | | await getDb().put('groupRequests', request) |
| | | }, |
| | | |
| | | /** ä¿ååæ¡å 群ç³è¯· */ |
| | | saveGroupRequest(request: ImGroupRequestApi.GroupRequestRespVO): void { |
| | | void this.saveGroupRequestRecord(request).catch((error) => |
| | | console.warn('[IM groupRequestStore] æ¬å°å 群ç³è¯·åå
¥å¤±è´¥', error) |
| | | ) |
| | | }, |
| | | |
| | | /** æåæç®¡ççææç¾¤ä¸æªå¤çç³è¯·ï¼è¿ IM å / å级 admin å / WS æ¨éæå²çªæ¶è°ç¨ */ |
| | | async fetchUnhandledGroupRequestList() { |
| | | const requestEpoch = storeEpoch |
| | | const requestUserId = getCurrentUserId() |
| | | if ( |
| | | pendingUnhandledFetch?.epoch === requestEpoch && |
| | | pendingUnhandledFetch.userId === requestUserId |
| | | ) { |
| | | return pendingUnhandledFetch.promise |
| | | } |
| | | const promise = (async () => { |
| | | const list = await apiGetUnhandledRequestList() |
| | | if (requestEpoch !== storeEpoch || getCurrentUserId() !== requestUserId) { |
| | | return |
| | | } |
| | | this.unhandledList = list || [] |
| | | this.loaded = true |
| | | this.saveGroupRequestList() |
| | | })().finally(() => { |
| | | if ( |
| | | pendingUnhandledFetch?.epoch === requestEpoch && |
| | | pendingUnhandledFetch.userId === requestUserId |
| | | ) { |
| | | pendingUnhandledFetch = null |
| | | } |
| | | }) |
| | | pendingUnhandledFetch = { epoch: requestEpoch, userId: requestUserId, promise } |
| | | return promise |
| | | }, |
| | | |
| | | /** |
| | | * WS æ¶å° 1503ï¼æææ°å
容并置顶 |
| | | * |
| | | * åä¸å¯¹ group_id, user_id å¤ç¨è®°å½æ¶ requestId ä¸åä½ applyContent / inviterUserId ä¼å·æ°ï¼æä»¥æ æ¡ä»¶ fetch + æå°å¤´é¨ |
| | | */ |
| | | async addGroupRequestById(requestId: number) { |
| | | const requestEpoch = storeEpoch |
| | | const requestUserId = getCurrentUserId() |
| | | const request = await apiGetMyGroupRequest(requestId) |
| | | if (!request) { |
| | | return |
| | | } |
| | | if (requestEpoch !== storeEpoch || getCurrentUserId() !== requestUserId) { |
| | | return |
| | | } |
| | | this.upsertGroupRequest(request) |
| | | }, |
| | | |
| | | /** |
| | | * æ¬å°åå¹¶ / æ°å¢åæ¡å 群ç³è¯·ï¼WS æ¨é & å¢éæåå
±ç¨ï¼ |
| | | * |
| | | * æªå¤ççæ id å»éå置顶ï¼å·²å¤çç仿ªå¤çå表移é¤ï¼é¿å
è¡¥å¿æ¶æå·²åæ / æç»çè®°å½å¡åçº¢ç¹ |
| | | */ |
| | | upsertGroupRequest(request: ImGroupRequestApi.GroupRequestRespVO) { |
| | | void this.upsertGroupRequestForPull(request).catch((error) => |
| | | console.warn('[IM groupRequestStore] æ¬å°å 群ç³è¯·åå
¥å¤±è´¥', error) |
| | | ) |
| | | }, |
| | | |
| | | /** æ¬å°åå¹¶ / æ°å¢åæ¡å 群ç³è¯· */ |
| | | async upsertGroupRequestForPull(request: ImGroupRequestApi.GroupRequestRespVO): Promise<void> { |
| | | if (request.handleResult !== ImGroupRequestHandleResult.UNHANDLED) { |
| | | await this.removeGroupRequestByIdForPull(request.id) |
| | | return |
| | | } |
| | | this.unhandledList = [request, ...this.unhandledList.filter((r) => r.id !== request.id)] |
| | | await this.saveGroupRequestRecord(request) |
| | | }, |
| | | |
| | | /** |
| | | * å¢éæåå 群ç³è¯·åæ´å¹¶åå¹¶ï¼å«å·²å¤ççæ handleResult èµ°ç§»é¤ï¼å·²å¤ç â ä»çº¢ç¹å表åé¤ï¼ |
| | | * |
| | | * åªåéè¿ / åç»ç¦»çº¿è¡¥å¿ï¼ä¸è´è´£é¦ç»ï¼é¦ç»çº¢ç¹èµ° fetchUnhandledGroupRequestListï¼æå¡ç«¯ç´æ¥è¿æ»¤æªå¤çï¼è¯ä¹æ´ç²¾åãå¯å¨æ´è½»ï¼ã |
| | | * æ
馿¬¡éè¿æ¶æ¸¸æ 为空 = 䏿¬¡æ§å
¨éèµ°ä¸éï¼å·²å¤çè®°å½å½ä¸ removeGroupRequestById 为 no-opï¼çº¢ç¹ä¸åå½±åï¼ï¼ä¹åå¢éã |
| | | */ |
| | | async pullGroupRequests() { |
| | | // å¿«ç
§ epochï¼è´¦å·å¨æåéä¸åæ¢ï¼clear() â epoch++ï¼æ¶ä¸¢å¼æ§è´¦å·é£å é¡µç»æï¼é²è·¨è´¦å·çº¢ç¹æ±¡æ |
| | | const requestEpoch = storeEpoch |
| | | const requestUserId = getCurrentUserId() |
| | | const isActive = () => requestEpoch === storeEpoch && getCurrentUserId() === requestUserId |
| | | await runIncrementalPull( |
| | | StorageKeys.settings.groupRequestPullCursor, |
| | | apiPullMyGroupRequestList, |
| | | async (records) => { |
| | | if (!isActive()) { |
| | | return false |
| | | } |
| | | await Promise.all(records.map((vo) => this.upsertGroupRequestForPull(vo))) |
| | | return true |
| | | }, |
| | | isActive |
| | | ) |
| | | if (isActive()) { |
| | | this.loaded = true |
| | | } |
| | | }, |
| | | |
| | | /** WS æ¶å° 1505 / 1506 ææ¬ç«¯å¤çå®ä¸æ¡ï¼æ requestId ä»åè¡¨ç§»é¤ */ |
| | | removeGroupRequestById(requestId: number) { |
| | | this.unhandledList = this.unhandledList.filter((r) => r.id !== requestId) |
| | | void getDb() |
| | | .delete('groupRequests', requestId) |
| | | .catch((error) => console.warn('[IM groupRequestStore] æ¬å°å 群ç³è¯·å é¤å¤±è´¥', error)) |
| | | }, |
| | | |
| | | /** å é¤åæ¡å 群ç³è¯· */ |
| | | async removeGroupRequestByIdForPull(requestId: number): Promise<void> { |
| | | this.unhandledList = this.unhandledList.filter((r) => r.id !== requestId) |
| | | await getDb().delete('groupRequests', requestId) |
| | | }, |
| | | |
| | | /** åæç³è¯·ï¼æ¬ç«¯å¤çåç«å³ä»å表移é¤ï¼é¿å
被åå¤ç¹å» */ |
| | | async agreeGroupRequest(requestId: number) { |
| | | await apiAgreeGroupRequest(requestId) |
| | | this.removeGroupRequestById(requestId) |
| | | }, |
| | | |
| | | /** æç»ç³è¯· */ |
| | | async refuseGroupRequest(requestId: number, handleContent?: string) { |
| | | await apiRefuseGroupRequest(requestId, handleContent) |
| | | this.removeGroupRequestById(requestId) |
| | | }, |
| | | |
| | | /** æ¸
空å 群ç³è¯·å
å */ |
| | | clear() { |
| | | this.unhandledList = [] |
| | | this.loaded = false |
| | | // è´¦å·åæ¢ï¼éå¢ epoch åºå¼æ§è´¦å· in-flight ç pullGroupRequests ç»æï¼é¿å
åè¿æ°è´¦å·çº¢ç¹å表 |
| | | storeEpoch++ |
| | | pendingUnhandledFetch = null |
| | | } |
| | | } |
| | | }) |
| | | |
| | | export const useGroupRequestStoreWithOut = () => useGroupRequestStore() |
| | | |
| | | if (import.meta.hot) { |
| | | import.meta.hot.accept(acceptHMRUpdate(useGroupRequestStore, import.meta.hot)) |
| | | } |