diff --git a/src/state/CallViewModel.ts b/src/state/CallViewModel.ts index 5e2a9982..6b046b28 100644 --- a/src/state/CallViewModel.ts +++ b/src/state/CallViewModel.ts @@ -140,6 +140,7 @@ import { type SpotlightPortraitLayoutMedia, } from "./layout-types.ts"; import { ElementCallError, UnknownCallError } from "../utils/errors.ts"; +import { ObservableScope } from "./ObservableScope.ts"; export interface CallViewModelOptions { encryptionSystem: EncryptionSystem; @@ -1805,18 +1806,19 @@ export class CallViewModel extends ViewModel { } catch (e) { logger.error("Error entering RTC session", e); } + // Update our member event when our mute state changes. - const muteSubscription = this.muteStates.video.enabled$.subscribe( - (videoEnabled) => - // TODO: Ensure that these calls are serialized in case of - // fast video toggling - void this.matrixRTCSession.updateCallIntent( + const intentScope = new ObservableScope(); + intentScope.reconcile( + this.muteStates.video.enabled$, + async (videoEnabled) => + this.matrixRTCSession.updateCallIntent( videoEnabled ? "video" : "audio", ), ); return async (): Promise => { - muteSubscription.unsubscribe(); + intentScope.end(); // Only sends Matrix leave event. The LiveKit session will disconnect // as soon as either the stopConnection$ handler above gets to it or // the view model is destroyed. diff --git a/src/state/ObservableScope.ts b/src/state/ObservableScope.ts index d6fd8e32..879445e6 100644 --- a/src/state/ObservableScope.ts +++ b/src/state/ObservableScope.ts @@ -114,11 +114,11 @@ export class ObservableScope { */ public reconcile( value$: Behavior, - callback: (value: T) => Promise<(() => Promise) | undefined>, + callback: (value: T) => Promise<(() => Promise) | void>, ): void { let latestValue: T | typeof nothing = nothing; let reconciledValue: T | typeof nothing = nothing; - let cleanUp: (() => Promise) | undefined = undefined; + let cleanUp: (() => Promise) | void = undefined; value$ .pipe( catchError(() => EMPTY), // Ignore errors