This commit is contained in:
Hugh Nimmo-Smith 2024-11-14 13:34:49 +00:00
parent 87c3793f69
commit fa3c9fde14

View File

@ -223,7 +223,7 @@ interface LayoutScanState {
class UserMedia { class UserMedia {
private readonly scope = new ObservableScope(); private readonly scope = new ObservableScope();
public readonly vm: UserMediaViewModel; public readonly vm: UserMediaViewModel;
public readonly participant: BehaviorSubject< private readonly participant: BehaviorSubject<
LocalParticipant | RemoteParticipant | undefined LocalParticipant | RemoteParticipant | undefined
>; >;
@ -294,6 +294,15 @@ class UserMedia {
); );
} }
public updateParticipant(
newParticipant: LocalParticipant | RemoteParticipant | undefined,
): void {
if (this.participant.value !== newParticipant) {
// Update the BehaviourSubject in the UserMedia.
this.participant.next(newParticipant);
}
}
public destroy(): void { public destroy(): void {
this.scope.end(); this.scope.end();
this.vm.destroy(); this.vm.destroy();
@ -367,12 +376,17 @@ export class CallViewModel extends ViewModel {
}), }),
); );
private readonly rawRemoteParticipants = connectedParticipantsObserver( /**
this.livekitRoom, * The raw list of RemoteParticipants as reported by LiveKit
).pipe(this.scope.state()); */
// Lists of participants to "hold" on display, even if LiveKit claims that private readonly rawRemoteParticipants: Observable<RemoteParticipant[]> =
// they've left connectedParticipantsObserver(this.livekitRoom).pipe(this.scope.state());
/**
* Lists of RemoteParticipants to "hold" on display, even if LiveKit claims that
* they've left
*/
private readonly remoteParticipantHolds: Observable<RemoteParticipant[][]> = private readonly remoteParticipantHolds: Observable<RemoteParticipant[][]> =
this.connectionState.pipe( this.connectionState.pipe(
withLatestFrom(this.rawRemoteParticipants), withLatestFrom(this.rawRemoteParticipants),
@ -407,6 +421,9 @@ export class CallViewModel extends ViewModel {
), ),
); );
/**
* The RemoteParticipants including those that are being "held" on the screen
*/
private readonly remoteParticipants: Observable<RemoteParticipant[]> = private readonly remoteParticipants: Observable<RemoteParticipant[]> =
combineLatest( combineLatest(
[this.rawRemoteParticipants, this.remoteParticipantHolds], [this.rawRemoteParticipants, this.remoteParticipantHolds],
@ -428,8 +445,9 @@ export class CallViewModel extends ViewModel {
}, },
); );
public readonly nonMemberItemCount = new BehaviorSubject<number>(0); /**
* List of MediaItems that we want to display
*/
private readonly mediaItems: Observable<MediaItem[]> = combineLatest([ private readonly mediaItems: Observable<MediaItem[]> = combineLatest([
this.remoteParticipants, this.remoteParticipants,
observeParticipantMedia(this.livekitRoom.localParticipant), observeParticipantMedia(this.livekitRoom.localParticipant),
@ -452,36 +470,42 @@ export class CallViewModel extends ViewModel {
) => { ) => {
const newItems = new Map( const newItems = new Map(
function* (this: CallViewModel): Iterable<[string, MediaItem]> { function* (this: CallViewModel): Iterable<[string, MediaItem]> {
// m.rtc.members are the basis for calculating what is visible in the call
for (const rtcMember of this.matrixRTCSession.memberships) { for (const rtcMember of this.matrixRTCSession.memberships) {
const room = this.matrixRTCSession.room; const room = this.matrixRTCSession.room;
// WARN! This is not exactly the sender but the user defined in the state key. // WARN! This is not exactly the sender but the user defined in the state key.
// This will be available once we change to the new "member as object" format in the MatrixRTC object. // This will be available once we change to the new "member as object" format in the MatrixRTC object.
let mediaId = rtcMember.sender + ":" + rtcMember.deviceId; let livekitParticipantId =
let participant = undefined; rtcMember.sender + ":" + rtcMember.deviceId;
let participant:
| LocalParticipant
| RemoteParticipant
| undefined = undefined;
if ( if (
rtcMember.sender === room.client.getUserId()! && rtcMember.sender === room.client.getUserId()! &&
rtcMember.deviceId === room.client.getDeviceId() rtcMember.deviceId === room.client.getDeviceId()
) { ) {
mediaId = "local"; livekitParticipantId = "local";
participant = localParticipant; participant = localParticipant;
} else { } else {
participant = remoteParticipants.find( participant = remoteParticipants.find(
(p) => p.identity === mediaId, (p) => p.identity === livekitParticipantId,
); );
} }
const member = findMatrixRoomMember(room, mediaId); const member = findMatrixRoomMember(room, livekitParticipantId);
if (!member) { if (!member) {
logger.error("Could not find member for media id: ", mediaId); logger.error(
"Could not find member for media id: ",
livekitParticipantId,
);
} }
for (let i = 0; i < 1 + duplicateTiles; i++) { for (let i = 0; i < 1 + duplicateTiles; i++) {
const indexedMediaId = `${mediaId}:${i}`; const indexedMediaId = `${livekitParticipantId}:${i}`;
const prevMedia = prevItems.get(indexedMediaId); const prevMedia = prevItems.get(indexedMediaId);
if (prevMedia && prevMedia instanceof UserMedia) { if (prevMedia && prevMedia instanceof UserMedia) {
if (prevMedia.participant.value !== participant) { prevMedia.updateParticipant(participant);
// Update the BahviourSubject in the UserMedia.
prevMedia.participant.next(participant);
}
} }
yield [ yield [
indexedMediaId, indexedMediaId,
@ -495,7 +519,7 @@ export class CallViewModel extends ViewModel {
participant, participant,
this.encryptionSystem, this.encryptionSystem,
this.livekitRoom, this.livekitRoom,
mediaId === "local", livekitParticipantId === "local",
), ),
]; ];
@ -529,12 +553,18 @@ export class CallViewModel extends ViewModel {
this.scope.state(), this.scope.state(),
); );
/**
* List of MediaItems that we want to display, that are of type UserMedia
*/
private readonly userMedia: Observable<UserMedia[]> = this.mediaItems.pipe( private readonly userMedia: Observable<UserMedia[]> = this.mediaItems.pipe(
map((mediaItems) => map((mediaItems) =>
mediaItems.filter((m): m is UserMedia => m instanceof UserMedia), mediaItems.filter((m): m is UserMedia => m instanceof UserMedia),
), ),
); );
/**
* List of MediaItems that we want to display, that are of type ScreenShare
*/
private readonly screenShares: Observable<ScreenShare[]> = private readonly screenShares: Observable<ScreenShare[]> =
this.mediaItems.pipe( this.mediaItems.pipe(
map((mediaItems) => map((mediaItems) =>
@ -853,7 +883,6 @@ export class CallViewModel extends ViewModel {
}), }),
this.scope.state(), this.scope.state(),
); );
/** /**
* The layout of tiles in the call interface. * The layout of tiles in the call interface.
*/ */
@ -1006,7 +1035,7 @@ export class CallViewModel extends ViewModel {
this.scope.state(), this.scope.state(),
); );
public readonly showFooter = this.windowMode.pipe( public readonly showFooter: Observable<boolean> = this.windowMode.pipe(
switchMap((mode) => { switchMap((mode) => {
switch (mode) { switch (mode) {
case "pip": case "pip":