diff --git a/src/settings/DeveloperSettingsTab.tsx b/src/settings/DeveloperSettingsTab.tsx index 19af03373..76e0c03ec 100644 --- a/src/settings/DeveloperSettingsTab.tsx +++ b/src/settings/DeveloperSettingsTab.tsx @@ -69,6 +69,23 @@ import { Slider } from "../Slider"; import { useUrlParams } from "../UrlParams"; import { type CallViewModel } from "../state/CallViewModel/CallViewModel.ts"; import { getSFUConfigWithOpenID } from "../livekit/openIDSFU"; +import { useBehavior } from "../useBehavior"; + +/** + * Shows whether the call is large enough that MatrixRTC has stopped rotating the media key. + */ +const KeyRotationStatus: FC<{ vm: CallViewModel }> = ({ vm }) => { + const suppressed = useBehavior(vm.keyRotationSuppressed$); + const participantCount = useBehavior(vm.participantCount$); + return ( +

+ Media key rotation:{" "} + {suppressed + ? `suppressed, participant limit reached (${participantCount} participants)` + : `active (${participantCount} participants)`} +

+ ); +}; interface Props { client: MatrixClient; @@ -368,6 +385,7 @@ export const DeveloperSettingsTab: FC = ({ id: client.getDeviceId() || "unknown", })}

+ {vm && } ; + /** + * Whether the call has grown large enough that MatrixRTC has stopped rotating the media + * encryption key. While this is true the key in use is still shared with new joiners, but no new + * key is generated when someone joins or leaves. + */ + keyRotationSuppressed$: Behavior; allConnections$: Behavior; /** Participants sorted by livekit room so they can be used in the audio rendering */ livekitRoomItems$: Behavior; @@ -856,6 +863,11 @@ export function createCallViewModel$( matrixLivekitMembers$.pipe(map((ms) => ms.length)), ); + const keyRotationSuppressed$ = createKeyRotationSuppressed$( + scope, + matrixRTCSession, + ); + const leaveSoundEffect$ = userMedia$.pipe( pairwise(), filter( @@ -1782,6 +1794,7 @@ export function createCallViewModel$( ), allConnections$, participantCount$: participantCount$, + keyRotationSuppressed$: keyRotationSuppressed$, handsRaised$: handsRaised$, reactions$: reactions$, joinSoundEffect$: joinSoundEffect$, diff --git a/src/state/SessionBehaviors.ts b/src/state/SessionBehaviors.ts index 652e43b5f..784e33666 100644 --- a/src/state/SessionBehaviors.ts +++ b/src/state/SessionBehaviors.ts @@ -88,3 +88,22 @@ export const createMemberships$ = ( new Epoch(matrixRTCSession.memberships), ); }; + +/** + * Whether the session has grown large enough that MatrixRTC has stopped rotating the media + * encryption key. While this is true the key in use is still shared with new joiners, but no new + * key is generated when someone joins or leaves. + */ +export const createKeyRotationSuppressed$ = ( + scope: ObservableScope, + matrixRTCSession: MatrixRTCSession, +): Behavior => { + return scope.behavior( + fromEvent( + matrixRTCSession, + MatrixRTCSessionEvent.KeyRotationSuppressedChanged, + (suppressed: boolean) => suppressed, + ), + matrixRTCSession.isKeyRotationSuppressed, + ); +}; diff --git a/src/utils/test.ts b/src/utils/test.ts index 206db88f5..38a1bb4a5 100644 --- a/src/utils/test.ts +++ b/src/utils/test.ts @@ -490,6 +490,8 @@ export class MockRTCSession extends TypedEventEmitter< return this.joined; } + public isKeyRotationSuppressed = false; + public withMemberships( rtcMembers$: Behavior[]>, ): MockRTCSession {