diff --git a/tauri/src/components/ui/call-center.tsx b/tauri/src/components/ui/call-center.tsx
index 8a20da4..a183328 100644
--- a/tauri/src/components/ui/call-center.tsx
+++ b/tauri/src/components/ui/call-center.tsx
@@ -53,6 +53,14 @@ export function CallCenter() {
return (
+ {/* Reconnecting Banner */}
+ {callTokens.isReconnecting && (
+
+ )}
+
{/* Call Timer */}
{callTokens && (
@@ -594,7 +602,7 @@ function CameraIcon() {
}
function MediaDevicesSettings() {
- const { callTokens, setCallTokens } = useStore();
+ const { callTokens, setCallTokens, updateCallTokens, livekitUrl } = useStore();
const { state: roomState } = useRoomContext();
const { localParticipant } = useLocalParticipant();
const { isNoiseFilterPending, setNoiseFilterEnabled, isNoiseFilterEnabled } = useKrispNoiseFilter({
@@ -610,12 +618,46 @@ function MediaDevicesSettings() {
const room = useRoomContext();
const [roomConnected, setRoomConnected] = useState(false);
+
useEffect(() => {
room.on(RoomEvent.Connected, () => {
setRoomConnected(true);
});
}, [room]);
+ // 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]);
+
useEffect(() => {
if (!callTokens) return;
diff --git a/tauri/src/store/store.ts b/tauri/src/store/store.ts
index cbe9971..4dd9a7b 100644
--- a/tauri/src/store/store.ts
+++ b/tauri/src/store/store.ts
@@ -32,6 +32,8 @@ export type CallState = {
krispToggle?: boolean;
controllerSupportsAv1?: boolean;
av1Enabled?: boolean;
+ // Reconnection state
+ isReconnecting?: boolean;
} & TCallTokensMessage["payload"];
type State = {
@@ -47,6 +49,7 @@ type State = {
// Call tokens for LiveKit
callTokens: CallState | null;
customServerUrl: string | null;
+ livekitUrl: string | null;
};
type Actions = {
@@ -63,6 +66,7 @@ type Actions = {
setCallTokens: (tokens: CallState | null) => void;
updateCallTokens: (tokens: Partial) => void;
setCustomServerUrl: (url: string | null) => void;
+ setLivekitUrl: (url: string | null) => void;
};
const initialState: State = {
@@ -76,6 +80,7 @@ const initialState: State = {
calling: null,
callTokens: null,
customServerUrl: null,
+ livekitUrl: null,
};
/**
@@ -130,6 +135,10 @@ const useStore = create()(
set((state) => {
state.customServerUrl = url;
}),
+ setLivekitUrl: (url) =>
+ set((state) => {
+ state.livekitUrl = url;
+ }),
setTab: (tab) =>
set((state) => {
state.tab = tab;
diff --git a/tauri/src/windows/main-window/app.tsx b/tauri/src/windows/main-window/app.tsx
index 20d4337..af263d3 100644
--- a/tauri/src/windows/main-window/app.tsx
+++ b/tauri/src/windows/main-window/app.tsx
@@ -23,7 +23,6 @@ import { listen } from "@tauri-apps/api/event";
import Invite from "./invite";
import { sounds } from "@/constants/sounds";
import { useDisableNativeContextMenu } from "@/lib/hooks";
-import { validateAndSetAuthToken } from "@/lib/authUtils";
import { processDeepLinkUrl } from "@/lib/deepLinkUtils";
import { Rooms } from "./tabs/Rooms";
import { LiveKitRoom } from "@livekit/components-react";
@@ -44,6 +43,7 @@ function App() {
setTab,
setTeammates,
setAuthToken,
+ setLivekitUrl,
} = useStore();
const coreProcessCrashedRef = useRef(false);
@@ -52,8 +52,8 @@ function App() {
const { useQuery } = useAPI();
const [incomingCallerId, setIncomingCallerId] = useState(null);
- const [livekitUrl, setLivekitUrl] = useState("");
const sentryMetadataRef = useRef(false);
+ const livekitUrl = useStore((s) => s.livekitUrl);
const { error: userError } = useQuery("get", "/api/auth/user", undefined, {
enabled: !!authToken,
@@ -90,7 +90,7 @@ function App() {
queryHash: `livekit-url-${authToken}`,
});
- // Send LiveKit URL to Tauri backend when it's fetched
+ // Send LiveKit URL to Tauri backend and store when fetched
useEffect(() => {
const sendLivekitUrlToBackend = async () => {
if (livekitUrlData?.url) {
@@ -106,7 +106,7 @@ function App() {
};
sendLivekitUrlToBackend();
- }, [livekitUrlData]);
+ }, [livekitUrlData, setLivekitUrl]);
// Load stored token and custom server URL on app start
useEffect(() => {
@@ -389,7 +389,7 @@ function App() {
(
-
+
{children}
)}