topfans/frontend/pages/support-activity/composables/useContributionRealtime.js

90 lines
3.0 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { onMounted, onUnmounted } from 'vue'
import { useContributionPolling } from './useContributionPolling.js'
import { getActivitySocket } from '@/utils/socket/ActivitySocket.js'
/**
* 贡献实时推送 composableWS 优先,断线降级为轮询)
* @param {import('vue').Ref<string|number>} activityId
* @param {import('vue').Ref<boolean>} isPageActive
*/
export function useContributionRealtime(activityId, isPageActive) {
const MAX_RECORDS = 5
const {
records,
visible,
loading,
error,
start: startPolling,
stop: stopPolling,
reset: resetPolling,
highestIdRef,
} = useContributionPolling(activityId, isPageActive)
const socket = getActivitySocket()
let usingWS = false
function onWsMessage(payload) {
if (!payload || Number(payload.activity_id) !== Number(activityId.value)) return
if (!payload.record) return
const record = payload.record
if (record.id > highestIdRef()) {
// 追加到列表末尾(与轮询的方向不同),保留最近 MAX_RECORDS 条
records.value = [...records.value, record].slice(-MAX_RECORDS)
}
}
function onWsConnect() {
if (usingWS) return
usingWS = true
stopPolling() // 停掉可能的轮询
socket.subscribe(activityId.value, ['contributions'])
}
function onWsDisconnect() {
if (!usingWS) return
usingWS = false
startPolling() // 降级为轮询
}
socket.onContributionsResponse(onWsMessage)
socket.on('connect', onWsConnect)
socket.on('disconnect', onWsDisconnect)
onMounted(() => {
// 总是调用 connect():SocketManager.connect() 内部会判断 token 是否变化
// (详见 useMessageRealtime.js 注释)。这样能确保用户切换登录后,
// contribution channel 也能用新 token 重新订阅,而不是复用上一个用户的 WS。
const token = uni.getStorageSync('access_token')
if (token) {
socket.connect(token).catch(err => console.warn('[useContributionRealtime] connect error:', err))
}
// 同步分支:如果 WS 已连接(单例复用导致 'connect' 事件不会再次触发),
// 必须直接调 onWsConnect 停轮询,否则 polling 会一直跑。
// 异步分支:WS 还没连上时,先起 polling 兜底;
// 等 'connect' 事件触发 onWsConnect 后会停掉轮询。
if (socket.isConnected) {
onWsConnect()
} else {
startPolling()
}
})
onUnmounted(() => {
if (usingWS) socket.unsubscribe(activityId.value, ['contributions'])
socket.off('connect', onWsConnect)
socket.off('disconnect', onWsDisconnect)
socket.offContributionsResponse(onWsMessage)
stopPolling()
resetPolling()
})
return {
records,
visible,
loading,
error,
isUsingWS: () => usingWS,
}
}