mirror of
https://github.com/wu736139669/hapi.git
synced 2026-08-05 06:24:37 +00:00
fix(web): reconnect SSE immediately when tab becomes visible (#398)
* fix(web): reconnect SSE immediately when tab becomes visible The SSE watchdog skips heartbeat checks while the tab is hidden. If the connection dies in the background, the user sees stale messages after switching back and has to wait up to 10 s for the next watchdog tick. Add a visibilitychange listener that checks heartbeat staleness immediately when the tab becomes visible and reconnects if stale. via [HAPI](https://hapi.run) Co-Authored-By: HAPI <noreply@hapi.run> * fix(web): map visibility-recovery reason in reconnecting banner The new 'visibility-recovery' reconnect reason was not mapped in getReasonLabel(), so the raw string would appear in the UI banner. Add localized labels for both en and zh-CN. via [HAPI](https://hapi.run) Co-Authored-By: HAPI <noreply@hapi.run> --------- Co-authored-by: HAPI <noreply@hapi.run>
This commit is contained in:
@@ -5,6 +5,9 @@ function getReasonLabel(reason: string, t: (key: string) => string): string {
|
||||
if (reason === 'heartbeat-timeout') {
|
||||
return t('reconnecting.reason.heartbeatTimeout')
|
||||
}
|
||||
if (reason === 'visibility-recovery') {
|
||||
return t('reconnecting.reason.visibilityRecovery')
|
||||
}
|
||||
if (reason === 'closed') {
|
||||
return t('reconnecting.reason.closed')
|
||||
}
|
||||
|
||||
@@ -596,8 +596,22 @@ export function useSSE(options: {
|
||||
requestReconnect('heartbeat-timeout')
|
||||
}, HEARTBEAT_WATCHDOG_INTERVAL_MS)
|
||||
|
||||
// When the tab becomes visible again, check immediately whether the
|
||||
// SSE connection went stale while hidden (the watchdog skips checks
|
||||
// for hidden tabs). This avoids the user having to wait up to
|
||||
// HEARTBEAT_WATCHDOG_INTERVAL_MS after switching back.
|
||||
const onVisibilityChange = () => {
|
||||
if (getVisibilityState() !== 'visible') return
|
||||
if (eventSourceRef.current !== eventSource) return
|
||||
if (Date.now() - lastActivityAtRef.current >= HEARTBEAT_STALE_MS) {
|
||||
requestReconnect('visibility-recovery')
|
||||
}
|
||||
}
|
||||
document.addEventListener('visibilitychange', onVisibilityChange)
|
||||
|
||||
return () => {
|
||||
clearInterval(watchdogTimer)
|
||||
document.removeEventListener('visibilitychange', onVisibilityChange)
|
||||
if (invalidationTimerRef.current) {
|
||||
clearTimeout(invalidationTimerRef.current)
|
||||
invalidationTimerRef.current = null
|
||||
|
||||
@@ -237,6 +237,7 @@ export default {
|
||||
'reconnecting.reason.error': 'stream error',
|
||||
'reconnecting.reason.closed': 'stream closed',
|
||||
'reconnecting.reason.heartbeatTimeout': 'heartbeat timeout',
|
||||
'reconnecting.reason.visibilityRecovery': 'resuming after background',
|
||||
|
||||
// Send blocked
|
||||
'send.blocked.title': 'Cannot send message',
|
||||
|
||||
@@ -239,6 +239,7 @@ export default {
|
||||
'reconnecting.reason.error': '流连接错误',
|
||||
'reconnecting.reason.closed': '流连接已关闭',
|
||||
'reconnecting.reason.heartbeatTimeout': '心跳超时',
|
||||
'reconnecting.reason.visibilityRecovery': '后台恢复中',
|
||||
|
||||
// Send blocked
|
||||
'send.blocked.title': '无法发送消息',
|
||||
|
||||
Reference in New Issue
Block a user