90 lines
3.0 KiB
JavaScript
90 lines
3.0 KiB
JavaScript
import { onMounted, onUnmounted } from 'vue'
|
||
import { useContributionPolling } from './useContributionPolling.js'
|
||
import { getActivitySocket } from '@/utils/socket/ActivitySocket.js'
|
||
|
||
/**
|
||
* 贡献实时推送 composable(WS 优先,断线降级为轮询)
|
||
* @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,
|
||
}
|
||
}
|