Skip to content

Commit d22d746

Browse files
committed
Another larger refactor to fix sfu switches and in general proper
cleanup.
1 parent 3491a68 commit d22d746

File tree

12 files changed

+479
-325
lines changed

12 files changed

+479
-325
lines changed

locales/en/app.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,11 +108,14 @@
108108
"connection_lost_description": "You were disconnected from the call.",
109109
"e2ee_unsupported": "Incompatible browser",
110110
"e2ee_unsupported_description": "Your web browser does not support encrypted calls. Supported browsers include Chrome, Safari, and Firefox 117+.",
111+
"failed_to_start_livekit": "Failed to start Livekit",
111112
"generic": "Something went wrong",
112113
"generic_description": "Submitting debug logs will help us track down the problem.",
113114
"insufficient_capacity": "Insufficient capacity",
114115
"insufficient_capacity_description": "The server has reached its maximum capacity and you cannot join the call at this time. Try again later, or contact your server admin if the problem persists.",
115116
"matrix_rtc_transport_missing": "The server is not configured to work with {{brand}}. Please contact your server admin (Domain: {{domain}}, Error Code: {{ errorCode }}).",
117+
"membership_manager": "Membership Manager Error",
118+
"membership_manager_description": "The Membership Manager had to shut down. This is caused by many consequtive failed network requests.",
116119
"open_elsewhere": "Opened in another tab",
117120
"open_elsewhere_description": "{{brand}} has been opened in another tab. If that doesn't sound right, try reloading the page.",
118121
"room_creation_restricted": "Failed to create call",

src/room/InCallView.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,8 @@ export const ActiveCall: FC<ActiveCallProps> = (props) => {
146146
reactionsReader.reactions$,
147147
scope.behavior(trackProcessorState$),
148148
);
149+
// TODO move this somewhere else once we use the callViewModel in the lobby as well!
150+
vm.join();
149151
setVm(vm);
150152

151153
vm.leave$.pipe(scope.bind()).subscribe(props.onLeft);

src/state/CallViewModel/CallViewModel.ts

Lines changed: 3 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,6 @@ import {
102102
createLocalMembership$,
103103
enterRTCSession,
104104
LivekitState,
105-
type LocalMemberConnectionState,
106105
} from "./localMember/LocalMembership.ts";
107106
import { createLocalTransport$ } from "./localMember/LocalTransport.ts";
108107
import {
@@ -202,7 +201,7 @@ export interface CallViewModel {
202201
hangup: () => void;
203202

204203
// joining
205-
join: () => LocalMemberConnectionState;
204+
join: () => void;
206205

207206
// screen sharing
208207
/**
@@ -572,15 +571,6 @@ export function createCallViewModel$(
572571
),
573572
);
574573

575-
// CODESMELL?
576-
// This is functionally the same Observable as leave$, except here it's
577-
// hoisted to the top of the class. This enables the cyclic dependency between
578-
// leave$ -> autoLeave$ -> callPickupState$ -> livekitConnectionState$ ->
579-
// localConnection$ -> transports$ -> joined$ -> leave$.
580-
const leaveHoisted$ = new Subject<
581-
"user" | "timeout" | "decline" | "allOthersLeft"
582-
>();
583-
584574
/**
585575
* Whether various media/event sources should pretend to be disconnected from
586576
* all network input, even if their connection still technically works.
@@ -840,10 +830,7 @@ export function createCallViewModel$(
840830
merge(
841831
autoLeave$,
842832
merge(userHangup$, widgetHangup$).pipe(map(() => "user" as const)),
843-
).pipe(
844-
scope.share,
845-
tap((reason) => leaveHoisted$.next(reason)),
846-
);
833+
).pipe(scope.share);
847834

848835
const spotlightSpeaker$ = scope.behavior<UserMediaViewModel | null>(
849836
userMedia$.pipe(
@@ -1448,16 +1435,13 @@ export function createCallViewModel$(
14481435
// reassigned here to make it publicly accessible
14491436
const toggleScreenSharing = localMembership.toggleScreenSharing;
14501437

1451-
const join = localMembership.requestConnect;
1452-
// TODO-MULTI-SFU: Use this view model for the lobby as well, and only call this once 'join' is clicked?
1453-
join();
14541438
return {
14551439
autoLeave$: autoLeave$,
14561440
callPickupState$: callPickupState$,
14571441
ringOverlay$: ringOverlay$,
14581442
leave$: leave$,
14591443
hangup: (): void => userHangup$.next(),
1460-
join: join,
1444+
join: localMembership.requestConnect,
14611445
toggleScreenSharing: toggleScreenSharing,
14621446
sharingScreen$: sharingScreen$,
14631447

src/state/CallViewModel/localMember/LocalMembership.test.ts

Lines changed: 72 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,15 @@ import {
1212
} from "matrix-js-sdk/lib/matrixrtc";
1313
import { describe, expect, it, vi } from "vitest";
1414
import { AutoDiscovery } from "matrix-js-sdk/lib/autodiscovery";
15-
import { map } from "rxjs";
15+
import { BehaviorSubject, map, of } from "rxjs";
1616
import { logger } from "matrix-js-sdk/lib/logger";
17+
import { type LocalParticipant } from "livekit-client";
1718

1819
import { MatrixRTCMode } from "../../../settings/settings";
1920
import {
21+
flushPromises,
2022
mockConfig,
23+
mockLivekitRoom,
2124
mockMuteStates,
2225
withTestScheduler,
2326
} from "../../../utils/test";
@@ -27,14 +30,19 @@ import {
2730
LivekitState,
2831
} from "./LocalMembership";
2932
import { MatrixRTCTransportMissingError } from "../../../utils/errors";
30-
import { Epoch } from "../../ObservableScope";
33+
import { Epoch, ObservableScope } from "../../ObservableScope";
3134
import { constant } from "../../Behavior";
3235
import { ConnectionManagerData } from "../remoteMembers/ConnectionManager";
33-
import { type Publisher } from "./Publisher";
36+
import { type Connection } from "../remoteMembers/Connection";
3437

3538
const MATRIX_RTC_MODE = MatrixRTCMode.Legacy;
3639
const getUrlParams = vi.hoisted(() => vi.fn(() => ({})));
3740
vi.mock("../../../UrlParams", () => ({ getUrlParams }));
41+
vi.mock("@livekit/components-core", () => ({
42+
observeParticipantEvents: vi
43+
.fn()
44+
.mockReturnValue(of({ isScreenShareEnabled: false })),
45+
}));
3846

3947
describe("LocalMembership", () => {
4048
describe("enterRTCSession", () => {
@@ -183,7 +191,7 @@ describe("LocalMembership", () => {
183191
processor: undefined,
184192
}),
185193
logger: logger,
186-
createPublisherFactory: (): Publisher => ({}) as unknown as Publisher,
194+
createPublisherFactory: vi.fn(),
187195
joinMatrixRTC: async (): Promise<void> => {},
188196
homeserverConnected$: constant(true),
189197
};
@@ -216,7 +224,7 @@ describe("LocalMembership", () => {
216224
});
217225

218226
expectObservable(localMembership.connectionState.livekit$).toBe("ne", {
219-
n: { state: LivekitState.Uninitialized },
227+
n: { state: LivekitState.Connecting },
220228
e: {
221229
state: LivekitState.Error,
222230
error: expect.toSatisfy(
@@ -226,4 +234,63 @@ describe("LocalMembership", () => {
226234
});
227235
});
228236
});
237+
238+
it("recreates publisher if new connection is used", async () => {
239+
const scope = new ObservableScope();
240+
const aTransport = {
241+
livekit_service_url: "a",
242+
} as LivekitTransport;
243+
const bTransport = {
244+
livekit_service_url: "b",
245+
} as LivekitTransport;
246+
247+
const localTransport$ = new BehaviorSubject(aTransport);
248+
249+
const connectionManagerData = new ConnectionManagerData();
250+
251+
connectionManagerData.add(
252+
{
253+
livekitRoom: mockLivekitRoom({
254+
localParticipant: {
255+
isScreenShareEnabled: false,
256+
trackPublications: [],
257+
} as unknown as LocalParticipant,
258+
}),
259+
state$: constant({
260+
state: "ConnectedToLkRoom",
261+
}),
262+
transport: aTransport,
263+
} as unknown as Connection,
264+
[],
265+
);
266+
connectionManagerData.add(
267+
{
268+
state$: constant({
269+
state: "ConnectedToLkRoom",
270+
}),
271+
transport: bTransport,
272+
} as unknown as Connection,
273+
[],
274+
);
275+
276+
const publisherFactory =
277+
defaultCreateLocalMemberValues.createPublisherFactory as ReturnType<
278+
typeof vi.fn
279+
>;
280+
281+
createLocalMembership$({
282+
scope,
283+
...defaultCreateLocalMemberValues,
284+
connectionManager: {
285+
connectionManagerData$: constant(new Epoch(connectionManagerData)),
286+
},
287+
localTransport$,
288+
});
289+
await flushPromises();
290+
localTransport$.next(bTransport);
291+
await flushPromises();
292+
expect(publisherFactory).toHaveBeenCalledTimes(2);
293+
expect(publisherFactory.mock.calls[0][0].transport).toBe(aTransport);
294+
expect(publisherFactory.mock.calls[1][0].transport).toBe(bTransport);
295+
});
229296
});

0 commit comments

Comments
 (0)