Skip to content

Commit 8593973

Browse files
committed
Add event stream reconnection and visibility handling
This helps when tabbing back-and-forth between browser tabs, as browsers kill background SSE connections.
1 parent b5debbf commit 8593973

1 file changed

Lines changed: 52 additions & 14 deletions

File tree

packages/app/src/context/global-sdk.tsx

Lines changed: 52 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -12,26 +12,64 @@ export const { use: useGlobalSDK, provider: GlobalSDKProvider } = createSimpleCo
1212
const server = useServer()
1313
const abort = new AbortController()
1414

15-
const eventSdk = createOpencodeClient({
16-
baseUrl: server.url,
17-
signal: abort.signal,
18-
fetch: platform.fetch,
19-
})
2015
const emitter = createGlobalEmitter<{
2116
[key: string]: Event
2217
}>()
2318

24-
void (async () => {
25-
const events = await eventSdk.global.event()
26-
for await (const event of events.stream) {
27-
emitter.emit(event.directory ?? "global", event.payload)
19+
let currentStreamAbort: AbortController | null = null
20+
21+
async function connectEventStream() {
22+
if (currentStreamAbort) {
23+
currentStreamAbort.abort()
24+
}
25+
26+
currentStreamAbort = new AbortController()
27+
const streamAbort = currentStreamAbort
28+
29+
const eventSdk = createOpencodeClient({
30+
baseUrl: server.url,
31+
signal: streamAbort.signal,
32+
fetch: platform.fetch,
33+
})
34+
35+
try {
36+
const events = await eventSdk.global.event()
37+
for await (const event of events.stream) {
38+
if (streamAbort.signal.aborted) break
39+
emitter.emit(event.directory ?? "global", event.payload)
40+
}
41+
} catch (error: any) {
42+
if (error.name === "AbortError" || streamAbort.signal.aborted) return
43+
console.error("Event stream error:", error)
2844
}
29-
})().catch((error) => {
30-
if (error.name === "AbortError") return
31-
console.error("Event stream error:", error)
32-
})
3345

34-
onCleanup(() => abort.abort())
46+
if (!abort.signal.aborted && !streamAbort.signal.aborted) {
47+
setTimeout(() => {
48+
if (!abort.signal.aborted) {
49+
connectEventStream()
50+
}
51+
}, 1000)
52+
}
53+
}
54+
55+
connectEventStream()
56+
57+
// Reconnect when tab regains visibility - browsers kill background SSE connections
58+
function handleVisibilityChange() {
59+
if (document.visibilityState === "visible" && !abort.signal.aborted) {
60+
connectEventStream()
61+
}
62+
}
63+
64+
document.addEventListener("visibilitychange", handleVisibilityChange)
65+
66+
onCleanup(() => {
67+
abort.abort()
68+
if (currentStreamAbort) {
69+
currentStreamAbort.abort()
70+
}
71+
document.removeEventListener("visibilitychange", handleVisibilityChange)
72+
})
3573

3674
const sdk = createOpencodeClient({
3775
baseUrl: server.url,

0 commit comments

Comments
 (0)