diff --git a/templates/web/src/client.ts.twig b/templates/web/src/client.ts.twig index 4d0cac6df..0e0035149 100644 --- a/templates/web/src/client.ts.twig +++ b/templates/web/src/client.ts.twig @@ -19,9 +19,9 @@ type Headers = { */ type RealtimeResponse = { /** - * Type of the response: 'error', 'event', 'connected', or 'response'. + * Type of the response: 'error', 'event', 'connected', 'pong', or 'response'. */ - type: 'error' | 'event' | 'connected' | 'response'; + type: 'error' | 'event' | 'connected' | 'response' | 'pong'; /** * Data associated with the response based on the response type. @@ -129,6 +129,8 @@ type RealtimeRequestAuthenticate = { session: string; } +type TimeoutHandle = ReturnType | number; + /** * Realtime interface representing the structure of a realtime communication object. */ @@ -139,9 +141,14 @@ type Realtime = { socket?: WebSocket; /** - * Timeout duration for communication operations. + * Timeout for reconnect operations. */ - timeout?: number; + timeout?: TimeoutHandle; + + /** + * Heartbeat interval for the realtime connection. + */ + heartbeat?: TimeoutHandle; /** * URL for establishing the WebSocket connection. @@ -196,6 +203,11 @@ type Realtime = { */ createSocket: () => void; + /** + * Function to create a new heartbeat interval. + */ + createHeartbeat: () => void; + /** * Function to clean up resources associated with specified channels. * @@ -359,6 +371,7 @@ class Client { private realtime: Realtime = { socket: undefined, timeout: undefined, + heartbeat: undefined, url: '', channels: new Set(), subscriptions: new Map(), @@ -384,6 +397,17 @@ class Client { return 60_000; } }, + createHeartbeat: () => { + if (this.realtime.heartbeat) { + clearTimeout(this.realtime.heartbeat); + } + + this.realtime.heartbeat = window?.setInterval(() => { + this.realtime.socket?.send(JSON.stringify({ + type: 'ping' + })); + }, 20_000); + }, createSocket: () => { if (this.realtime.channels.size < 1) { this.realtime.reconnect = false; @@ -417,6 +441,7 @@ class Client { this.realtime.socket.addEventListener('message', this.realtime.onMessage); this.realtime.socket.addEventListener('open', _event => { this.realtime.reconnectAttempts = 0; + this.realtime.createHeartbeat(); }); this.realtime.socket.addEventListener('close', event => { if (