Repository navigation
Conversation
Adds a Pusher-protocol client for Laravel Reverb (private channel auth, ping/pong keepalive, reconnect) and wires ai_chat_cubit to consume turn events live instead of polling.
Code Review SummaryAdds a hand-rolled Pusher/Reverb websocket client ( 🚀 Key Improvements
💡 Minor Suggestions
|
| ? _reconnectAttempt | ||
| : _reconnectBackoff.length - 1; | ||
| _reconnectAttempt++; | ||
| _reconnectTimer = Timer(_reconnectBackoff[i], () { |
There was a problem hiding this comment.
If subscribeToSession reconnects during the backoff window, _channel is already non-null when this timer fires, so _connectSocket opens a second socket and orphans the first subscription, which is never cancelled or closed. Bail out when a channel already exists.
| _reconnectTimer = Timer(_reconnectBackoff[i], () { | |
| _reconnectTimer = Timer(_reconnectBackoff[i], () { | |
| if (!_disposed && _channel == null) unawaited(_connectSocket()); | |
| }); |
| return; | ||
| } | ||
| // The target may have changed while the auth request was in flight. | ||
| if (_targetSessionId != sessionId) return; |
There was a problem hiding this comment.
If the socket drops while the auth POST is in flight, _scheduleReconnect nulls _channel/_socketId, but this code then sets _subscribedChannel anyway. On the next connection_established, _subscribeToTarget sees channel == _subscribedChannel and returns without subscribing, so no live updates arrive until the session is reopened. Re-check the connection before writing.
| if (_targetSessionId != sessionId) return; | |
| + if (_targetSessionId != sessionId) return; | |
| + if (_channel == null || _socketId != socketId) return; | |
| + _channel!.sink.add(encodeSubscribe(channel: channel, auth: auth)); | |
| + _subscribedChannel = channel; |
| ? _reconnectAttempt | ||
| : _reconnectBackoff.length - 1; | ||
| _reconnectAttempt++; | ||
| _reconnectTimer = Timer(_reconnectBackoff[i], () { |
There was a problem hiding this comment.
The reconnect timer only checks _disposed, so a timer scheduled before unsubscribe() still fires and reconnects a socket that no longer has a target session — an idle websocket kept open with no subscription. Include _targetSessionId != null in the guard.
| _reconnectTimer = Timer(_reconnectBackoff[i], () { | |
| _reconnectTimer = Timer(_reconnectBackoff[i], () { | |
| if (!_disposed && _targetSessionId != null) unawaited(_connectSocket()); | |
| }); |
Description
Streams AI chat progress live over Laravel Reverb instead of relying on polling alone.
lib/core/realtime/): private channel auth through/broadcasting/auth, ping/pong keepalive and reconnect.AiChatCubitsubscribes tochat-session.{id}and handles the server'sturnevents:progressadds the step label to the reply in flight,settledrefetches the message right away.--dart-define:REVERB_APP_KEY,REVERB_HOST,REVERB_PORT,REVERB_SCHEME. Without a key the socket stays off and chat runs on polling alone.The server side is already on webservice
dev(ChatTurnEvent, trakli/webservice@32db7ba), and the web app already uses it.Draft until:
REVERB_*values for staging and production. None do today, so builds would run on polling only..env.exampledefaults toBROADCAST_DRIVER=log.Type of Change