Don't show the reconnecting overlay for signal-only reconnects

A LiveKit signalReconnecting state means the signalling WebSocket dropped
but the peer connection (and so the media) is still up. Treating it as
disconnected muted every remote participant, hid their video and showed the
non-dismissable reconnecting overlay for what is usually a 2s blip.
Only a full Reconnecting (or homeserver disconnect) does that now.
This commit is contained in:
Matthew Hodgson
2026-09-09 11:29:50 +01:00
parent b90ed5bdf7
commit 871458a773
2 changed files with 56 additions and 1 deletions
@@ -930,6 +930,55 @@ describe("LocalMembership", () => {
scope.end();
});
it("ignores signal-only reconnects", async () => {
const scope = new ObservableScope();
const trackSpy = vi.spyOn(
PosthogAnalytics.instance.eventCallReconnecting,
"track",
);
const connectionState$ = new BehaviorSubject<ConnectionState>(
ConnectionState.LivekitConnected,
);
const mutableConnection = {
...connectionTransportAConnected,
state$: connectionState$,
} as unknown as Connection;
const connectionManagerData = new ConnectionManagerData();
connectionManagerData.add(mutableConnection, []);
const localMembership = createLocalMembership$({
scope,
...defaultCreateLocalMemberValues,
homeserverConnected: {
combined$: constant<[boolean, HomeserverDisconnectReason | null]>([
true,
null,
]),
rtsSession$: constant(RTCMemberStatus.Connected),
},
connectionManager: {
connectionManagerData$: constant(new Epoch(connectionManagerData)),
},
localTransport: {
advertised$: constant(aTransport),
active$: constant(aTransportWithSFUConfig),
},
});
await flushPromises();
connectionState$.next(ConnectionState.LivekitSignalReconnecting);
expect(localMembership.reconnecting$.value).toBe(false);
expect(localMembership.connected$.value).toBe(true);
connectionState$.next(ConnectionState.LivekitConnected);
expect(trackSpy).not.toHaveBeenCalled();
scope.end();
});
it("fires one event per completed reconnection cycle", async () => {
const scope = new ObservableScope();
const trackSpy = vi.spyOn(
@@ -582,7 +582,13 @@ export const createLocalMembership$ = ({
combineLatest([
homeserverConnected.combined$,
localConnectionState$.pipe(
map((state) => state === ConnectionState.LivekitConnected),
// A signal-only reconnect keeps the peer connection (and so the
// media) up, so we don't tell the user we're reconnecting for it.
map(
(state) =>
state === ConnectionState.LivekitConnected ||
state === ConnectionState.LivekitSignalReconnecting,
),
),
]).pipe(
map(([[hsConnected, hsReason], livekitConnected]) => {