From e313cf04a6fe2c4c953ec6e86ad70e41f412ad61 Mon Sep 17 00:00:00 2001 From: Robin Date: Tue, 21 Oct 2025 13:01:38 -0400 Subject: [PATCH] Always consider multi-SFU mode enabled when using sticky events CallViewModel would pass the wrong transport to enterRtcSession when the user enabled sticky events but didn't manually enable multi-SFU mode as well. This likely would've added some confusion to our attempts to test these modes. --- src/rtcSessionHelpers.ts | 21 +++++++++----------- src/state/CallViewModel.ts | 40 +++++++++++++++++++++++++++++--------- 2 files changed, 40 insertions(+), 21 deletions(-) diff --git a/src/rtcSessionHelpers.ts b/src/rtcSessionHelpers.ts index 8753105e..ecbff79f 100644 --- a/src/rtcSessionHelpers.ts +++ b/src/rtcSessionHelpers.ts @@ -20,7 +20,6 @@ import { ElementWidgetActions, widget, type WidgetHelpers } from "./widget"; import { MatrixRTCTransportMissingError } from "./utils/errors"; import { getUrlParams } from "./UrlParams"; import { getSFUConfigWithOpenID } from "./livekit/openIDSFU.ts"; -import { preferStickyEvents } from "./settings/settings.ts"; const FOCI_WK_KEY = "org.matrix.msc4143.rtc_foci"; @@ -100,12 +99,11 @@ export async function makeTransport( export interface EnterRTCSessionOptions { encryptMedia: boolean; - // TODO: remove this flag, the new membership manager is stable enough - useNewMembershipManager?: boolean; // TODO: remove this flag, to-device transport is stable enough now useExperimentalToDeviceTransport?: boolean; /** EXPERIMENTAL: If true, will use the multi-sfu codepath where each member connects to its SFU instead of everyone connecting to an elected on. */ - useMultiSfu?: boolean; + useMultiSfu: boolean; + preferStickyEvents: boolean; } /** @@ -117,14 +115,13 @@ export interface EnterRTCSessionOptions { export async function enterRTCSession( rtcSession: MatrixRTCSession, transport: LivekitTransport, - options: EnterRTCSessionOptions = { - encryptMedia: true, - useExperimentalToDeviceTransport: false, - useMultiSfu: true, - }, + { + encryptMedia, + useExperimentalToDeviceTransport = false, + useMultiSfu, + preferStickyEvents, + }: EnterRTCSessionOptions, ): Promise { - const { encryptMedia, useExperimentalToDeviceTransport = false } = options; - const useMultiSfu = preferStickyEvents.getValue() || options.useMultiSfu; PosthogAnalytics.instance.eventCallEnded.cacheStartCall(new Date()); PosthogAnalytics.instance.eventCallStarted.track(rtcSession.room.roomId); @@ -158,7 +155,7 @@ export async function enterRTCSession( membershipEventExpiryMs: matrixRtcSessionConfig?.membership_event_expiry_ms, useExperimentalToDeviceTransport, - unstableSendStickyEvents: preferStickyEvents.getValue(), + unstableSendStickyEvents: preferStickyEvents, }, ); if (widget) { diff --git a/src/state/CallViewModel.ts b/src/state/CallViewModel.ts index 6b046b28..5236820e 100644 --- a/src/state/CallViewModel.ts +++ b/src/state/CallViewModel.ts @@ -91,6 +91,7 @@ import { duplicateTiles, multiSfu, playReactionsSound, + preferStickyEvents, showReactions, } from "../settings/settings"; import { isFirefox } from "../Platform"; @@ -262,23 +263,32 @@ export class CallViewModel extends ViewModel { /** * Lists the transports used by ourselves, plus all other MatrixRTC session * members. For completeness this also lists the preferred transport and - * whether we are in multi-SFU mode (because advertisedTransport$ wants to - * read them at the same time, and bundling data together when it might change - * together is what you have to do in RxJS to avoid reading inconsistent state - * or observing too many changes.) + * whether we are in multi-SFU mode or sticky events mode (because + * advertisedTransport$ wants to read them at the same time, and bundling data + * together when it might change together is what you have to do in RxJS to + * avoid reading inconsistent state or observing too many changes.) */ private readonly transports$: Behavior<{ local: Async; remote: { membership: CallMembership; transport: LivekitTransport }[]; preferred: Async; multiSfu: boolean; + preferStickyEvents: boolean; } | null> = this.scope.behavior( this.joined$.pipe( switchMap((joined) => joined ? combineLatest( - [this.preferredTransport$, this.memberships$, multiSfu.value$], - (preferred, memberships, multiSfu) => { + [ + this.preferredTransport$, + this.memberships$, + multiSfu.value$, + preferStickyEvents.value$, + ], + (preferred, memberships, preferMultiSfu, preferStickyEvents) => { + // Multi-SFU must be implicitly enabled when using sticky events + const multiSfu = preferStickyEvents || preferMultiSfu; + const oldestMembership = this.matrixRTCSession.getOldestMembership(); const remote = memberships.flatMap((m) => { @@ -289,6 +299,7 @@ export class CallViewModel extends ViewModel { ? [{ membership: m, transport: t }] : []; }); + let local = preferred; if (!multiSfu) { const oldest = this.matrixRTCSession.getOldestMembership(); @@ -299,6 +310,7 @@ export class CallViewModel extends ViewModel { local = ready(selection); } } + if (local.state === "error") { this._configError$.next( local.value instanceof ElementCallError @@ -306,7 +318,14 @@ export class CallViewModel extends ViewModel { : new UnknownCallError(local.value), ); } - return { local, remote, preferred, multiSfu }; + + return { + local, + remote, + preferred, + multiSfu, + preferStickyEvents, + }; }, ) : of(null), @@ -336,10 +355,11 @@ export class CallViewModel extends ViewModel { /** * The transport we should advertise in our MatrixRTC membership (plus whether - * it is a multi-SFU transport). + * it is a multi-SFU transport and whether we should use sticky events). */ private readonly advertisedTransport$: Behavior<{ multiSfu: boolean; + preferStickyEvents: boolean; transport: LivekitTransport; } | null> = this.scope.behavior( this.transports$.pipe( @@ -348,6 +368,7 @@ export class CallViewModel extends ViewModel { transports.preferred.state === "ready" ? { multiSfu: transports.multiSfu, + preferStickyEvents: transports.preferStickyEvents, // In non-multi-SFU mode we should always advertise the preferred // SFU to minimize the number of membership updates transport: transports.multiSfu @@ -358,6 +379,7 @@ export class CallViewModel extends ViewModel { ), distinctUntilChanged<{ multiSfu: boolean; + preferStickyEvents: boolean; transport: LivekitTransport; } | null>(deepCompare), ), @@ -1800,8 +1822,8 @@ export class CallViewModel extends ViewModel { await enterRTCSession(this.matrixRTCSession, advertised.transport, { encryptMedia: this.options.encryptionSystem.kind !== E2eeType.NONE, useExperimentalToDeviceTransport: true, - useNewMembershipManager: true, useMultiSfu: advertised.multiSfu, + preferStickyEvents: advertised.preferStickyEvents, }); } catch (e) { logger.error("Error entering RTC session", e);