♻️ refactor(im):用户申请列表,增加流式查询,避免一次性加载过多,或者历史无法被加载到。
parent
9fc25b7109
commit
14e3f85cb0
|
|
@ -45,7 +45,22 @@ export const refuseFriendRequest = (id: number | string, handleContent?: string)
|
|||
})
|
||||
}
|
||||
|
||||
// 查询「我相关」的好友申请列表(含我发起的、别人加我的)
|
||||
export const getMyFriendRequestList = () => {
|
||||
return request.get<ImFriendRequestRespVO[]>({ url: '/im/friend-request/list' })
|
||||
// 查询「我相关」的好友申请列表(游标分页:传 lastRequestId 加载更多)
|
||||
export const getMyFriendRequestList = (limit: number, lastRequestId?: number) => {
|
||||
const params: Record<string, number> = { limit }
|
||||
if (lastRequestId != null) {
|
||||
params.lastRequestId = lastRequestId
|
||||
}
|
||||
return request.get<ImFriendRequestRespVO[]>({
|
||||
url: '/im/friend-request/list',
|
||||
params
|
||||
})
|
||||
}
|
||||
|
||||
// 按 id 单查「我相关」的申请记录(带越权过滤;WebSocket 通知到达后用)
|
||||
export const getMyFriendRequest = (id: number) => {
|
||||
return request.get<ImFriendRequestRespVO | null>({
|
||||
url: '/im/friend-request/get',
|
||||
params: { id }
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -62,6 +62,14 @@
|
|||
>
|
||||
暂无新的朋友
|
||||
</div>
|
||||
<!-- 加载更多:按本地最旧 requestId 游标分页拉下一批;hasMore=false 不展示 -->
|
||||
<div
|
||||
v-else-if="friendStore.hasMoreFriendRequests"
|
||||
class="py-2 text-12px text-center cursor-pointer text-[var(--el-text-color-secondary)] hover:bg-[var(--el-fill-color-light)]"
|
||||
@click="handleLoadMore"
|
||||
>
|
||||
{{ loadingMore ? '加载中…' : '加载更多' }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
|
@ -108,4 +116,17 @@ const enrichedRequests = computed(() =>
|
|||
props.requests.map((request) => ({ request, peer: getPeer(request) }))
|
||||
)
|
||||
|
||||
/** 点击「加载更多」拉下一页;store 内部按 lastRequestId 游标分页 + pending 去重 */
|
||||
const loadingMore = ref(false)
|
||||
async function handleLoadMore() {
|
||||
if (loadingMore.value) {
|
||||
return
|
||||
}
|
||||
loadingMore.value = true
|
||||
try {
|
||||
await friendStore.loadMoreFriendRequests()
|
||||
} finally {
|
||||
loadingMore.value = false
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -16,11 +16,16 @@ import {
|
|||
agreeFriendRequest as apiAgreeFriendRequest,
|
||||
refuseFriendRequest as apiRefuseFriendRequest,
|
||||
getMyFriendRequestList as apiGetMyFriendRequestList,
|
||||
getMyFriendRequest as apiGetMyFriendRequest,
|
||||
type ImFriendRequestApplyReqVO,
|
||||
type ImFriendRequestRespVO
|
||||
} from '@/api/im/friend/request'
|
||||
import { useConversationStore } from './conversationStore'
|
||||
import { ImConversationType, ImFriendRequestHandleResult } from '../../utils/constants'
|
||||
import {
|
||||
FRIEND_REQUEST_PAGE_SIZE,
|
||||
ImConversationType,
|
||||
ImFriendRequestHandleResult
|
||||
} from '../../utils/constants'
|
||||
import { getCurrentUserId, imStorage, setQuietly, StorageKeys } from '../../utils/storage'
|
||||
import { getFriendDisplayName } from '../../utils/user'
|
||||
import type { Friend, FriendRequest } from '../types'
|
||||
|
|
@ -29,6 +34,8 @@ import type { Friend, FriendRequest } from '../types'
|
|||
let pendingFetchFriends: Promise<void> | null = null
|
||||
/** 当前正在进行的好友申请列表拉取;多端连续多条申请到达时复用同一 Promise,避免雪崩重拉 */
|
||||
let pendingFetchRequests: Promise<void> | null = null
|
||||
/** 当前正在进行的「加载更多申请」请求 */
|
||||
let pendingLoadMoreRequests: Promise<void> | null = null
|
||||
|
||||
/** 好友通知 payload(对齐后端 BaseFriendNotification + 子类裁减后的字段) */
|
||||
export interface FriendNotificationPayload {
|
||||
|
|
@ -61,8 +68,10 @@ export const useFriendStore = defineStore('imFriendStore', {
|
|||
friends: [] as Friend[],
|
||||
// 仅 fetchFriends 成功后置位;loadFriends(IDB)不置位,否则后台 SWR 刷新会被缓存命中跳过
|
||||
loaded: false,
|
||||
/** 我相关的好友申请列表(含我发起的 + 别人加我的;后端按 id 倒序,前端不再分页) */
|
||||
friendRequests: [] as FriendRequest[]
|
||||
/** 我相关的好友申请列表(含我发起的 + 别人加我的;后端按 id 倒序游标分页) */
|
||||
friendRequests: [] as FriendRequest[],
|
||||
/** 是否还有更早的申请记录可加载;返回不满 page size 即置 false */
|
||||
hasMoreFriendRequests: true
|
||||
}),
|
||||
|
||||
getters: {
|
||||
|
|
@ -191,9 +200,8 @@ export const useFriendStore = defineStore('imFriendStore', {
|
|||
request.handleResult = ImFriendRequestHandleResult.AGREED
|
||||
request.handleTime = Date.now()
|
||||
} else {
|
||||
// 列表过期场景兜底重拉
|
||||
// TODO @AI:是不是只拉这个人?避免拉所有?
|
||||
await this.fetchFriendRequests()
|
||||
// 本地列表没这条,按 id 单查兜底
|
||||
await this.loadFriendRequest(requestId)
|
||||
}
|
||||
},
|
||||
|
||||
|
|
@ -206,18 +214,21 @@ export const useFriendStore = defineStore('imFriendStore', {
|
|||
request.handleContent = handleContent
|
||||
request.handleTime = Date.now()
|
||||
} else {
|
||||
await this.fetchFriendRequests()
|
||||
await this.loadFriendRequest(requestId)
|
||||
}
|
||||
},
|
||||
|
||||
/** 拉取「我相关」的好友申请列表(页面打开时 / 收到 FRIEND_REQUEST_RECEIVED 时刷新);pending 期间复用同一 Promise */
|
||||
/** 拉取「我相关」的好友申请列表首页(页面打开 / 收到 FRIEND_REQUEST_RECEIVED 时刷新);pending 期间复用同一 Promise */
|
||||
async fetchFriendRequests() {
|
||||
if (pendingFetchRequests) {
|
||||
return pendingFetchRequests
|
||||
}
|
||||
pendingFetchRequests = apiGetMyFriendRequestList()
|
||||
pendingFetchRequests = apiGetMyFriendRequestList(FRIEND_REQUEST_PAGE_SIZE)
|
||||
.then((list) => {
|
||||
this.friendRequests = (list || []).map(convertFriendRequest)
|
||||
const items = (list || []).map(convertFriendRequest)
|
||||
this.friendRequests = items
|
||||
// 不足一页即没有更多;满页可能还有,等 loadMore 拉到 0 条再确定
|
||||
this.hasMoreFriendRequests = items.length >= FRIEND_REQUEST_PAGE_SIZE
|
||||
})
|
||||
.finally(() => {
|
||||
pendingFetchRequests = null
|
||||
|
|
@ -225,11 +236,47 @@ export const useFriendStore = defineStore('imFriendStore', {
|
|||
return pendingFetchRequests
|
||||
},
|
||||
|
||||
/** 加载更多申请(按本地最旧 requestId 游标分页);无更多 / pending 中直接返回 */
|
||||
async loadMoreFriendRequests() {
|
||||
if (!this.hasMoreFriendRequests || pendingLoadMoreRequests || pendingFetchRequests) {
|
||||
return
|
||||
}
|
||||
const oldest = this.friendRequests[this.friendRequests.length - 1]
|
||||
if (!oldest) {
|
||||
return this.fetchFriendRequests()
|
||||
}
|
||||
pendingLoadMoreRequests = apiGetMyFriendRequestList(FRIEND_REQUEST_PAGE_SIZE, oldest.id)
|
||||
.then((list) => {
|
||||
const items = (list || []).map(convertFriendRequest)
|
||||
this.friendRequests.push(...items)
|
||||
this.hasMoreFriendRequests = items.length >= FRIEND_REQUEST_PAGE_SIZE
|
||||
})
|
||||
.finally(() => {
|
||||
pendingLoadMoreRequests = null
|
||||
})
|
||||
return pendingLoadMoreRequests
|
||||
},
|
||||
|
||||
/** 按 id 查申请记录;列表是按 id 倒序的小列表,O(n) find 即可,不再维护 Map 索引 */
|
||||
findFriendRequest(requestId: number): FriendRequest | undefined {
|
||||
return this.friendRequests.find((request) => request.id === requestId)
|
||||
},
|
||||
|
||||
/** 按 id 从后端单查并 upsert 到本地(dispatcher 兜底用,避免全量重拉);后端带越权过滤 */
|
||||
async loadFriendRequest(requestId: number) {
|
||||
const data = await apiGetMyFriendRequest(requestId)
|
||||
if (!data) {
|
||||
return
|
||||
}
|
||||
const next = convertFriendRequest(data)
|
||||
const existing = this.findFriendRequest(requestId)
|
||||
if (existing) {
|
||||
Object.assign(existing, next)
|
||||
} else {
|
||||
this.friendRequests.unshift(next)
|
||||
}
|
||||
},
|
||||
|
||||
// ==================== 好友关系操作 ====================
|
||||
|
||||
/** 删除好友(单向软删,本端置 DISABLE;级联清理本地私聊会话) */
|
||||
|
|
@ -364,8 +411,8 @@ export const useFriendStore = defineStore('imFriendStore', {
|
|||
request.handleResult = ImFriendRequestHandleResult.AGREED
|
||||
request.handleTime = Date.now()
|
||||
} else {
|
||||
// 本地列表可能未初始化(例如刚登录还没进 contact 页),兜底重拉
|
||||
void this.fetchFriendRequests()
|
||||
// 本地列表可能没这条(例如刚登录还没进 contact 页),按 id 单查兜底
|
||||
void this.loadFriendRequest(payload.requestId!)
|
||||
}
|
||||
},
|
||||
|
||||
|
|
@ -377,7 +424,7 @@ export const useFriendStore = defineStore('imFriendStore', {
|
|||
request.handleContent = payload.handleContent
|
||||
request.handleTime = Date.now()
|
||||
} else {
|
||||
void this.fetchFriendRequests()
|
||||
void this.loadFriendRequest(payload.requestId!)
|
||||
}
|
||||
},
|
||||
|
||||
|
|
@ -442,6 +489,7 @@ export const useFriendStore = defineStore('imFriendStore', {
|
|||
this.friends = []
|
||||
this.friendRequests = []
|
||||
this.loaded = false
|
||||
this.hasMoreFriendRequests = true
|
||||
}
|
||||
}
|
||||
})
|
||||
|
|
|
|||
|
|
@ -141,6 +141,9 @@ export const PRIVATE_MESSAGE_PULL_SIZE = 100
|
|||
/** 每次拉取群聊消息的最大条数(后端上限 1000,前端取保守值 100) */
|
||||
export const GROUP_MESSAGE_PULL_SIZE = 100
|
||||
|
||||
/** 「我相关」好友申请列表的单次拉取条数(游标分页 page size,前端控制) */
|
||||
export const FRIEND_REQUEST_PAGE_SIZE = 100
|
||||
|
||||
/** 消息之间渲染「时间分隔条」的阈值:10 分钟 */
|
||||
export const TIME_TIP_GAP_MS = 10 * 60 * 1000
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue