Skip to content

Conversation

@iparaskev
Copy link
Contributor

@iparaskev iparaskev commented Jan 28, 2026

Summary by CodeRabbit

  • New Features
    • Added a reconnecting banner that appears during connection restoration attempts.
    • Implemented automatic reconnection when the connection is unexpectedly lost.
    • Enhanced connection state monitoring for improved stability.

✏️ Tip: You can customize this high-level summary in your review settings.

@iparaskev iparaskev requested a review from konsalex as a code owner January 28, 2026 08:42
@netlify
Copy link

netlify bot commented Jan 28, 2026

Deploy Preview for hoppdocs ready!

Name Link
🔨 Latest commit 829b0ce
🔍 Latest deploy log https://app.netlify.com/projects/hoppdocs/deploys/6979cbef8ffe8b0008bb3dc8
😎 Deploy Preview https://deploy-preview-240--hoppdocs.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@coderabbitai
Copy link

coderabbitai bot commented Jan 28, 2026

📝 Walkthrough

Walkthrough

This PR introduces automatic reconnection functionality for LiveKit-based video calls. It adds connection state monitoring to detect disconnections and trigger reconnection attempts, introduces an isReconnecting flag to track reconnection status, migrates livekitUrl management to a centralized store, and displays a reconnecting banner in the UI.

Changes

Cohort / File(s) Summary
Store State Management
tauri/src/store/store.ts
Added isReconnecting boolean flag to CallState and livekitUrl string to global State. Introduced setLivekitUrl setter action to enable centralized LiveKit URL management.
App Component Refactoring
tauri/src/windows/main-window/app.tsx
Migrated livekitUrl from local component state to centralized store. Now reads and updates livekitUrl via store methods. Updated LiveKitRoom invocation to handle potentially undefined URL with fallback to empty string.
Call Center UI & Connection Handling
tauri/src/components/ui/call-center.tsx
Added reconnecting banner display when callTokens.isReconnecting is true. Implemented RoomEvent.ConnectionStateChanged listener to detect disconnections and trigger automatic reconnection attempts using stored livekitUrl and audioToken. Updates isReconnecting flag on connection success, failure, or reconnection completion.

Sequence Diagram(s)

sequenceDiagram
    participant App as App Component
    participant Store as Store
    participant MediaDevices as MediaDevicesSettings
    participant Room as LiveKit Room
    participant UI as Call Center UI

    App->>Store: setLivekitUrl(url)
    Store->>MediaDevices: provides livekitUrl & updateCallTokens
    Room-->>MediaDevices: ConnectionStateChanged event (Disconnected)
    
    alt Has callTokens
        MediaDevices->>Room: room.connect(livekitUrl, audioToken)
        MediaDevices->>Store: updateCallTokens({isReconnecting: true})
        Store->>UI: re-render (display reconnecting banner)
        
        alt Reconnection Succeeds
            Room-->>MediaDevices: ConnectionStateChanged event (Connected)
            MediaDevices->>Store: updateCallTokens({isReconnecting: false})
            Store->>UI: re-render (hide reconnecting banner)
        else Reconnection Fails
            MediaDevices->>Store: updateCallTokens({isReconnecting: false})
            Store->>UI: re-render
        end
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐰 Hop, skip, and reconnect in flight!
When networks stumble, we hold tight,
A banner bounces, flags unfold,
LiveKit calls come back to hold,
No dropped frames in our rabbit's sight! 🌐✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'fix: livekit disconnect issue' directly aligns with the main changeset objective of handling LiveKit disconnections and implementing reconnection logic.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@tauri/src/components/ui/call-center.tsx`:
- Around line 628-659: The reconnection handler (handleConnectionStateChange)
captures stale callTokens/livekitUrl and retries immediately; change it to read
the latest values at reconnection time (e.g., store callTokens and livekitUrl in
refs like callTokensRef/livekitUrlRef or call a getter to retrieve current
state) and guard before attempting reconnect by re-checking room.state and
callTokensRef.current; implement a retry loop with exponential backoff and a max
attempts counter when calling room.connect, and before each attempt verify token
validity (call an existing token refresh function or add refreshCallTokens() to
obtain a fresh callTokens.audioToken) so room.connect uses a non-expired token;
keep updateCallTokens({ isReconnecting }) usage to reflect state transitions.
🧹 Nitpick comments (1)
tauri/src/windows/main-window/app.tsx (1)

392-394: Consider guarding against missing livekitUrl before rendering LiveKitRoom.

If callTokens is set before livekitUrl is populated (e.g., during initial load or after a store sync), LiveKitRoom will receive an empty string for serverUrl, which may cause connection failures or errors.

Consider either:

  1. Adding livekitUrl to the condition check in ConditionalWrap
  2. Showing a loading state while livekitUrl is not yet available
Proposed fix
       <ConditionalWrap
-        condition={!!callTokens}
+        condition={!!callTokens && !!livekitUrl}
         wrap={(children) => (
           <LiveKitRoom key={callTokens?.audioToken} token={callTokens?.audioToken} serverUrl={livekitUrl || ""}>

Comment on lines +628 to +659
// Listen to connection state changes and handle reconnection
useEffect(() => {
const handleConnectionStateChange = async (state: ConnectionState) => {
console.log("Connection state changed:", state);

if (state === ConnectionState.Disconnected && callTokens) {
// Room disconnected but we still have callTokens - try to reconnect
console.log("Room disconnected, attempting to reconnect...");
updateCallTokens({ isReconnecting: true });

try {
if (!livekitUrl) {
throw new Error("LiveKit URL not available");
}
await room.connect(livekitUrl, callTokens.audioToken);
} catch (error) {
console.error("Reconnection failed:", error);
updateCallTokens({ isReconnecting: false });
}
} else if (state === ConnectionState.Connected && callTokens?.isReconnecting) {
// Successfully reconnected
console.log("Successfully reconnected!");
updateCallTokens({ isReconnecting: false });
}
};

room.on(RoomEvent.ConnectionStateChanged, handleConnectionStateChange);

return () => {
room.off(RoomEvent.ConnectionStateChanged, handleConnectionStateChange);
};
}, [room, callTokens, updateCallTokens, livekitUrl]);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Reconnection logic has potential issues with stale closures and lacks retry backoff.

Several concerns with the current implementation:

  1. Stale closure: The async handler captures callTokens and livekitUrl from the closure. If state changes during the async room.connect() call, the values used may be stale. For example, if the user ends the call while reconnecting, callTokens in the closure would still be truthy.

  2. No retry delay/backoff: Immediate reconnection without delay could cause rapid retry loops if the server is temporarily unavailable.

  3. Token expiration: After a network issue, callTokens.audioToken might have expired, causing auth failures without any token refresh mechanism.

Proposed fix with stale closure guard and retry delay
   useEffect(() => {
     const handleConnectionStateChange = async (state: ConnectionState) => {
       console.log("Connection state changed:", state);

-      if (state === ConnectionState.Disconnected && callTokens) {
+      // Capture current state for the async operation
+      const currentCallTokens = useStore.getState().callTokens;
+      const currentLivekitUrl = useStore.getState().livekitUrl;
+
+      if (state === ConnectionState.Disconnected && currentCallTokens) {
         // Room disconnected but we still have callTokens - try to reconnect
         console.log("Room disconnected, attempting to reconnect...");
         updateCallTokens({ isReconnecting: true });

         try {
-          if (!livekitUrl) {
+          if (!currentLivekitUrl) {
             throw new Error("LiveKit URL not available");
           }
-          await room.connect(livekitUrl, callTokens.audioToken);
+          // Add a small delay before reconnecting
+          await new Promise((resolve) => setTimeout(resolve, 1000));
+          // Re-check that we still want to reconnect
+          if (!useStore.getState().callTokens) {
+            console.log("Call ended during reconnection delay, aborting");
+            return;
+          }
+          await room.connect(currentLivekitUrl, currentCallTokens.audioToken);
         } catch (error) {
           console.error("Reconnection failed:", error);
           updateCallTokens({ isReconnecting: false });
         }
-      } else if (state === ConnectionState.Connected && callTokens?.isReconnecting) {
+      } else if (state === ConnectionState.Connected && useStore.getState().callTokens?.isReconnecting) {
         // Successfully reconnected
         console.log("Successfully reconnected!");
         updateCallTokens({ isReconnecting: false });
       }
     };

     room.on(RoomEvent.ConnectionStateChanged, handleConnectionStateChange);

     return () => {
       room.off(RoomEvent.ConnectionStateChanged, handleConnectionStateChange);
     };
-  }, [room, callTokens, updateCallTokens, livekitUrl]);
+  }, [room, updateCallTokens]);
🤖 Prompt for AI Agents
In `@tauri/src/components/ui/call-center.tsx` around lines 628 - 659, The
reconnection handler (handleConnectionStateChange) captures stale
callTokens/livekitUrl and retries immediately; change it to read the latest
values at reconnection time (e.g., store callTokens and livekitUrl in refs like
callTokensRef/livekitUrlRef or call a getter to retrieve current state) and
guard before attempting reconnect by re-checking room.state and
callTokensRef.current; implement a retry loop with exponential backoff and a max
attempts counter when calling room.connect, and before each attempt verify token
validity (call an existing token refresh function or add refreshCallTokens() to
obtain a fresh callTokens.audioToken) so room.connect uses a non-expired token;
keep updateCallTokens({ isReconnecting }) usage to reflect state transitions.

@iparaskev iparaskev merged commit e59cbe3 into main Jan 28, 2026
15 checks passed
@iparaskev iparaskev deleted the fix_reconnect branch January 28, 2026 09:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants