Compare commits
6
Commits
c28b9ac565
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f077d902b4 | ||
|
|
75750cf226 | ||
|
|
cd3ecc4507 | ||
|
|
3b19f58e26 | ||
|
|
9769a80381 | ||
|
|
d2c48c0c9a |
@@ -691,7 +691,7 @@ function FlowchartEditorInner({ projectId, projectName, userId, userDisplayName,
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const handleVisibilityChange = () => {
|
const handleVisibilityChange = () => {
|
||||||
if (!document.hidden) {
|
if (!document.hidden) {
|
||||||
realtimeRef.current?.notifyActivity()
|
realtimeRef.current?.notifyVisibilityResumed()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ const RECONNECT_BASE_DELAY_MS = 1000
|
|||||||
const RECONNECT_MAX_DELAY_MS = 30_000
|
const RECONNECT_MAX_DELAY_MS = 30_000
|
||||||
const INACTIVITY_TIMEOUT_MS = 5 * 60_000 // 5 minutes of inactivity before pausing
|
const INACTIVITY_TIMEOUT_MS = 5 * 60_000 // 5 minutes of inactivity before pausing
|
||||||
const CONNECTION_TIMEOUT_MS = 15_000 // 15s timeout for initial connection
|
const CONNECTION_TIMEOUT_MS = 15_000 // 15s timeout for initial connection
|
||||||
const STALE_THRESHOLD_MS = 60_000 // 60s of inactivity = force fresh reconnect on return
|
const STALE_THRESHOLD_MS = 3 * 60_000 // 3 minutes hidden before forcing fresh reconnect
|
||||||
|
|
||||||
export class RealtimeConnection {
|
export class RealtimeConnection {
|
||||||
private channel: RealtimeChannel | null = null
|
private channel: RealtimeChannel | null = null
|
||||||
@@ -77,6 +77,21 @@ export class RealtimeConnection {
|
|||||||
this.resetInactivityTimer()
|
this.resetInactivityTimer()
|
||||||
this.clearConnectionTimer()
|
this.clearConnectionTimer()
|
||||||
|
|
||||||
|
// Ensure the Supabase client has a valid auth session before connecting.
|
||||||
|
// On initial page load, the session may still be loading from cookies.
|
||||||
|
try {
|
||||||
|
const { data: { session } } = await this.supabase.auth.getSession()
|
||||||
|
if (!session && !this.isDestroyed) {
|
||||||
|
// No session yet - wait briefly and retry
|
||||||
|
this.scheduleReconnect()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
// Session check failed - proceed anyway, channel will handle auth errors
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.isDestroyed || this.isPaused) return
|
||||||
|
|
||||||
// Set a timeout: if we don't connect within CONNECTION_TIMEOUT_MS, retry
|
// Set a timeout: if we don't connect within CONNECTION_TIMEOUT_MS, retry
|
||||||
this.connectionTimer = setTimeout(() => {
|
this.connectionTimer = setTimeout(() => {
|
||||||
if (this.isDestroyed || this.isPaused) return
|
if (this.isDestroyed || this.isPaused) return
|
||||||
@@ -247,21 +262,31 @@ export class RealtimeConnection {
|
|||||||
/**
|
/**
|
||||||
* Notify that user activity has occurred, resetting the inactivity timer.
|
* Notify that user activity has occurred, resetting the inactivity timer.
|
||||||
* If the connection was paused due to inactivity, it will resume.
|
* If the connection was paused due to inactivity, it will resume.
|
||||||
* If the connection was idle for a significant period, force a fresh reconnect
|
|
||||||
* to ensure the WebSocket isn't stale.
|
|
||||||
*/
|
*/
|
||||||
notifyActivity(): void {
|
notifyActivity(): void {
|
||||||
|
if (this.isDestroyed) return
|
||||||
|
this.lastActivityTime = Date.now()
|
||||||
|
this.resetInactivityTimer()
|
||||||
|
|
||||||
|
if (this.isPaused) {
|
||||||
|
this.resume()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when the tab becomes visible again. If the tab was hidden for a
|
||||||
|
* significant period, force a fresh reconnect to handle stale WebSockets.
|
||||||
|
*/
|
||||||
|
notifyVisibilityResumed(): void {
|
||||||
if (this.isDestroyed) return
|
if (this.isDestroyed) return
|
||||||
const now = Date.now()
|
const now = Date.now()
|
||||||
const idleDuration = now - this.lastActivityTime
|
const hiddenDuration = now - this.lastActivityTime
|
||||||
this.lastActivityTime = now
|
this.lastActivityTime = now
|
||||||
this.resetInactivityTimer()
|
this.resetInactivityTimer()
|
||||||
|
|
||||||
if (this.isPaused) {
|
if (this.isPaused) {
|
||||||
this.resume()
|
this.resume()
|
||||||
} else if (idleDuration > STALE_THRESHOLD_MS && this.channel) {
|
} else if (hiddenDuration > STALE_THRESHOLD_MS && this.channel) {
|
||||||
// Connection may appear alive but WebSocket could be stale after idle.
|
|
||||||
// Force a fresh reconnect to ensure broadcasts actually work.
|
|
||||||
this.forceReconnect()
|
this.forceReconnect()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user