temp before holiday

Signed-off-by: Timo K <toger5@hotmail.de>
This commit is contained in:
Timo K
2025-08-28 18:41:13 +02:00
parent d46fe55a67
commit 386dc6c84d
3 changed files with 57 additions and 55 deletions

View File

@@ -132,7 +132,7 @@ export const GroupCallView: FC<Props> = ({
// This should use `useEffectEvent` (only available in experimental versions) // This should use `useEffectEvent` (only available in experimental versions)
useEffect(() => { useEffect(() => {
if (memberships.length >= MUTE_PARTICIPANT_COUNT) if (memberships.length >= MUTE_PARTICIPANT_COUNT)
muteStates.audio.setEnabled?.(false); muteStates.audio.setEnabled$.value?.(false);
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
}, []); }, []);
@@ -261,7 +261,7 @@ export const GroupCallView: FC<Props> = ({
if (!deviceId) { if (!deviceId) {
logger.warn("Unknown audio input: " + audioInput); logger.warn("Unknown audio input: " + audioInput);
// override the default mute state // override the default mute state
latestMuteStates.current!.audio.setEnabled?.(false); latestMuteStates.current!.audio.setEnabled$.value?.(false);
} else { } else {
logger.debug( logger.debug(
`Found audio input ID ${deviceId} for name ${audioInput}`, `Found audio input ID ${deviceId} for name ${audioInput}`,
@@ -275,7 +275,7 @@ export const GroupCallView: FC<Props> = ({
if (!deviceId) { if (!deviceId) {
logger.warn("Unknown video input: " + videoInput); logger.warn("Unknown video input: " + videoInput);
// override the default mute state // override the default mute state
latestMuteStates.current!.video.setEnabled?.(false); latestMuteStates.current!.video.setEnabled$.value?.(false);
} else { } else {
logger.debug( logger.debug(
`Found video input ID ${deviceId} for name ${videoInput}`, `Found video input ID ${deviceId} for name ${videoInput}`,

View File

@@ -18,13 +18,13 @@ import {
import E2EEWorker from "livekit-client/e2ee-worker?worker"; import E2EEWorker from "livekit-client/e2ee-worker?worker";
import { import {
ClientEvent, ClientEvent,
type EventTimelineSetHandlerMap,
EventType,
RoomEvent,
type RoomMember, type RoomMember,
RoomStateEvent, RoomStateEvent,
SyncState, SyncState,
type Room as MatrixRoom, type Room as MatrixRoom,
type EventTimelineSetHandlerMap,
EventType,
RoomEvent,
} from "matrix-js-sdk"; } from "matrix-js-sdk";
import { import {
BehaviorSubject, BehaviorSubject,
@@ -61,8 +61,6 @@ import { logger } from "matrix-js-sdk/lib/logger";
import { import {
type CallMembership, type CallMembership,
isLivekitFocus, isLivekitFocus,
isLivekitFocusConfig,
type LivekitFocusConfig,
type MatrixRTCSession, type MatrixRTCSession,
MatrixRTCSessionEvent, MatrixRTCSessionEvent,
type MatrixRTCSessionEventHandlerMap, type MatrixRTCSessionEventHandlerMap,
@@ -112,11 +110,11 @@ import { observeSpeaker$ } from "./observeSpeaker";
import { shallowEquals } from "../utils/array"; import { shallowEquals } from "../utils/array";
import { calculateDisplayName, shouldDisambiguate } from "../utils/displayname"; import { calculateDisplayName, shouldDisambiguate } from "../utils/displayname";
import { type MediaDevices } from "./MediaDevices"; import { type MediaDevices } from "./MediaDevices";
import { constant, type Behavior } from "./Behavior"; import { type Behavior } from "./Behavior";
import { import {
enterRTCSession, enterRTCSession,
getLivekitAlias, getLivekitAlias,
leaveRTCSession,
makeFocus, makeFocus,
} from "../rtcSessionHelpers"; } from "../rtcSessionHelpers";
import { E2eeType } from "../e2ee/e2eeType"; import { E2eeType } from "../e2ee/e2eeType";
@@ -453,16 +451,6 @@ export class CallViewModel extends ViewModel {
), ),
); );
private readonly memberships$ = this.scope.behavior(
fromEvent(
this.matrixRTCSession,
MatrixRTCSessionEvent.MembershipsChanged,
).pipe(
startWith(null),
map(() => this.matrixRTCSession.memberships),
),
);
private readonly membershipsAndFocusMap$ = this.scope.behavior( private readonly membershipsAndFocusMap$ = this.scope.behavior(
this.memberships$.pipe( this.memberships$.pipe(
map((memberships) => map((memberships) =>
@@ -524,17 +512,19 @@ export class CallViewModel extends ViewModel {
), ),
); );
private readonly joined$ = new Subject<void>(); private readonly join$ = new Subject<void>();
public join(): void { public join(): void {
this.joined$.next(); this.join$.next();
} }
private readonly leave$ = new Subject<void>();
public leave(): void { public leave(): void {
// TODO this.leave$.next();
} }
private readonly connectionInstructions$ = this.joined$.pipe( private readonly connectionInstructions$ = this.join$.pipe(
switchMap(() => this.remoteConnections$), switchMap(() => this.remoteConnections$),
startWith(new Map<string, Connection>()), startWith(new Map<string, Connection>()),
pairwise(), pairwise(),
@@ -622,6 +612,17 @@ export class CallViewModel extends ViewModel {
// in a split-brained state. // in a split-brained state.
private readonly pretendToBeDisconnected$ = this.reconnecting$; private readonly pretendToBeDisconnected$ = this.reconnecting$;
private readonly memberships$ = this.scope.behavior(
fromEvent(
this.matrixRTCSession,
MatrixRTCSessionEvent.MembershipsChanged,
).pipe(
startWith(null),
pauseWhen(this.pretendToBeDisconnected$),
map(() => this.matrixRTCSession.memberships),
),
);
private readonly participants$ = this.scope private readonly participants$ = this.scope
.behavior< .behavior<
{ {
@@ -671,17 +672,6 @@ export class CallViewModel extends ViewModel {
) )
.pipe(startWith([]), pauseWhen(this.pretendToBeDisconnected$)); .pipe(startWith([]), pauseWhen(this.pretendToBeDisconnected$));
private readonly memberships$ = this.scope.behavior(
fromEvent(
this.matrixRTCSession,
MatrixRTCSessionEvent.MembershipsChanged,
).pipe(
startWith(null),
pauseWhen(this.pretendToBeDisconnected$),
map(() => this.matrixRTCSession.memberships),
),
);
/** /**
* Displaynames for each member of the call. This will disambiguate * Displaynames for each member of the call. This will disambiguate
* any displaynames that clashes with another member. Only members * any displaynames that clashes with another member. Only members
@@ -1790,7 +1780,7 @@ export class CallViewModel extends ViewModel {
for (const connection of start) void connection.start(); for (const connection of start) void connection.start();
for (const connection of stop) connection.stop(); for (const connection of stop) connection.stop();
}); });
combineLatest([this.localFocus, this.joined$]) combineLatest([this.localFocus, this.join$])
.pipe(this.scope.bind()) .pipe(this.scope.bind())
.subscribe(([localFocus]) => { .subscribe(([localFocus]) => {
void enterRTCSession( void enterRTCSession(
@@ -1799,6 +1789,17 @@ export class CallViewModel extends ViewModel {
this.options.encryptionSystem.kind !== E2eeType.PER_PARTICIPANT, this.options.encryptionSystem.kind !== E2eeType.PER_PARTICIPANT,
); );
}); });
this.join$.pipe(this.scope.bind()).subscribe(() => {
leaveRTCSession(
this.matrixRTCSession,
"user", // TODO-MULTI-SFU ?
// Wait for the sound in widget mode (it's not long)
Promise.resolve(), // TODO-MULTI-SFU
//Promise.all([audioPromise, posthogRequest]),
).catch((e) => {
logger.error("Error leaving RTC session", e);
});
});
// Pause upstream of all local media tracks when we're disconnected from // Pause upstream of all local media tracks when we're disconnected from
// MatrixRTC, because it can be an unpleasant surprise for the app to say // MatrixRTC, because it can be an unpleasant surprise for the app to say

View File

@@ -26,6 +26,7 @@ import { Config } from "../config/Config";
import { getUrlParams } from "../UrlParams"; import { getUrlParams } from "../UrlParams";
import { type ObservableScope } from "./ObservableScope"; import { type ObservableScope } from "./ObservableScope";
import { accumulate } from "../utils/observable"; import { accumulate } from "../utils/observable";
import { type Behavior } from "./Behavior";
interface MuteStateData { interface MuteStateData {
enabled$: Observable<boolean>; enabled$: Observable<boolean>;
@@ -33,13 +34,13 @@ interface MuteStateData {
toggle: (() => void) | null; toggle: (() => void) | null;
} }
class MuteState { class MuteState<Label, Selected> {
private readonly enabledByDefault$ = private readonly enabledByDefault$ =
this.enabledByConfig && !getUrlParams().skipLobby this.enabledByConfig && !getUrlParams().skipLobby
? this.isJoined$.pipe(map((isJoined) => !isJoined)) ? this.joined$.pipe(map((isJoined) => !isJoined))
: of(false); : of(false);
private readonly data$: Observable<MuteStateData> = private readonly data$ = this.scope.behavior<MuteStateData>(
this.device.available$.pipe( this.device.available$.pipe(
map((available) => available.size > 0), map((available) => available.size > 0),
distinctUntilChanged(), distinctUntilChanged(),
@@ -52,8 +53,8 @@ class MuteState {
const set$ = new Subject<boolean>(); const set$ = new Subject<boolean>();
const toggle$ = new Subject<void>(); const toggle$ = new Subject<void>();
return { return {
set: (enabled: boolean) => set$.next(enabled), set: (enabled: boolean): void => set$.next(enabled),
toggle: () => toggle$.next(), toggle: (): void => toggle$.next(),
// Assume the default value only once devices are actually connected // Assume the default value only once devices are actually connected
enabled$: merge( enabled$: merge(
set$, set$,
@@ -66,24 +67,24 @@ class MuteState {
}; };
}, },
), ),
this.scope.state(), ),
);
public readonly enabled$: Observable<boolean> = this.data$.pipe(
switchMap(({ enabled$ }) => enabled$),
); );
public readonly setEnabled$: Observable<((enabled: boolean) => void) | null> = public readonly enabled$: Behavior<boolean> = this.scope.behavior(
this.data$.pipe(map(({ set }) => set)); this.data$.pipe(switchMap(({ enabled$ }) => enabled$)),
);
public readonly toggle$: Observable<(() => void) | null> = this.data$.pipe( public readonly setEnabled$: Behavior<((enabled: boolean) => void) | null> =
map(({ toggle }) => toggle), this.scope.behavior(this.data$.pipe(map(({ set }) => set)));
public readonly toggle$: Behavior<(() => void) | null> = this.scope.behavior(
this.data$.pipe(map(({ toggle }) => toggle)),
); );
public constructor( public constructor(
private readonly scope: ObservableScope, private readonly scope: ObservableScope,
private readonly device: MediaDevice, private readonly device: MediaDevice<Label, Selected>,
private readonly isJoined$: Observable<boolean>, private readonly joined$: Observable<boolean>,
private readonly enabledByConfig: boolean, private readonly enabledByConfig: boolean,
) {} ) {}
} }
@@ -92,20 +93,20 @@ export class MuteStates {
public readonly audio = new MuteState( public readonly audio = new MuteState(
this.scope, this.scope,
this.mediaDevices.audioInput, this.mediaDevices.audioInput,
this.isJoined$, this.joined$,
Config.get().media_devices.enable_video, Config.get().media_devices.enable_video,
); );
public readonly video = new MuteState( public readonly video = new MuteState(
this.scope, this.scope,
this.mediaDevices.videoInput, this.mediaDevices.videoInput,
this.isJoined$, this.joined$,
Config.get().media_devices.enable_video, Config.get().media_devices.enable_video,
); );
public constructor( public constructor(
private readonly scope: ObservableScope, private readonly scope: ObservableScope,
private readonly mediaDevices: MediaDevices, private readonly mediaDevices: MediaDevices,
private readonly isJoined$: Observable<boolean>, private readonly joined$: Observable<boolean>,
) { ) {
if (widget !== null) { if (widget !== null) {
// Sync our mute states with the hosting client // Sync our mute states with the hosting client