Create media items for session members not joined to LiveKit

This commit is contained in:
Robin
2025-10-03 19:14:48 -04:00
parent 86fb026be8
commit 1820cac3f6
5 changed files with 32 additions and 45 deletions

View File

@@ -304,7 +304,7 @@ class UserMedia {
public readonly presenter$: Behavior<boolean>; public readonly presenter$: Behavior<boolean>;
public constructor( public constructor(
public readonly id: string, public readonly id: string,
member: RoomMember | undefined, member: RoomMember,
participant: LocalParticipant | RemoteParticipant | undefined, participant: LocalParticipant | RemoteParticipant | undefined,
encryptionSystem: EncryptionSystem, encryptionSystem: EncryptionSystem,
livekitRoom: LivekitRoom, livekitRoom: LivekitRoom,
@@ -377,7 +377,7 @@ class ScreenShare {
public constructor( public constructor(
id: string, id: string,
member: RoomMember | undefined, member: RoomMember,
participant: LocalParticipant | RemoteParticipant, participant: LocalParticipant | RemoteParticipant,
encryptionSystem: EncryptionSystem, encryptionSystem: EncryptionSystem,
livekitRoom: LivekitRoom, livekitRoom: LivekitRoom,
@@ -799,7 +799,8 @@ export class CallViewModel extends ViewModel {
livekitRoom: LivekitRoom; livekitRoom: LivekitRoom;
url: string; url: string;
participants: { participants: {
participant: LocalParticipant | RemoteParticipant; id: string;
participant: LocalParticipant | RemoteParticipant | undefined;
member: RoomMember; member: RoomMember;
}[]; }[];
}[] }[]
@@ -814,6 +815,7 @@ export class CallViewModel extends ViewModel {
throw new Error("No room member for call membership"); throw new Error("No room member for call membership");
}; };
const localParticipant = { const localParticipant = {
id: "local",
participant: localConnection.livekitRoom.localParticipant, participant: localConnection.livekitRoom.localParticipant,
member: member:
this.matrixRoom.getMember(this.userId ?? "") ?? memberError(), this.matrixRoom.getMember(this.userId ?? "") ?? memberError(),
@@ -826,9 +828,14 @@ export class CallViewModel extends ViewModel {
c.publishingParticipants$.pipe( c.publishingParticipants$.pipe(
map((ps) => { map((ps) => {
const participants: { const participants: {
participant: LocalParticipant | RemoteParticipant; id: string;
participant:
| LocalParticipant
| RemoteParticipant
| undefined;
member: RoomMember; member: RoomMember;
}[] = ps.map(({ participant, membership }) => ({ }[] = ps.map(({ participant, membership }) => ({
id: `${membership.sender}:${membership.deviceId}`,
participant, participant,
member: member:
getRoomMemberFromRtcMember( getRoomMemberFromRtcMember(
@@ -929,26 +936,12 @@ export class CallViewModel extends ViewModel {
const newItems: Map<string, UserMedia | ScreenShare> = new Map( const newItems: Map<string, UserMedia | ScreenShare> = new Map(
function* (this: CallViewModel): Iterable<[string, MediaItem]> { function* (this: CallViewModel): Iterable<[string, MediaItem]> {
for (const { livekitRoom, participants } of participantsByRoom) { for (const { livekitRoom, participants } of participantsByRoom) {
for (const { participant, member } of participants) { for (const { id, participant, member } of participants) {
const matrixId = participant.isLocal
? "local"
: participant.identity;
for (let i = 0; i < 1 + duplicateTiles; i++) { for (let i = 0; i < 1 + duplicateTiles; i++) {
const mediaId = `${matrixId}:${i}`; const mediaId = `${id}:${i}`;
let prevMedia = prevItems.get(mediaId); const prevMedia = prevItems.get(mediaId);
if (prevMedia && prevMedia instanceof UserMedia) { if (prevMedia instanceof UserMedia)
prevMedia.updateParticipant(participant); prevMedia.updateParticipant(participant);
if (prevMedia.vm.member === undefined) {
// TODO-MULTI-SFU: This is outdated.
// We have a previous media created because of the `debugShowNonMember` flag.
// In this case we actually replace the media item.
// This "hack" never occurs if we do not use the `debugShowNonMember` debugging
// option and if we always find a room member for each rtc member (which also
// only fails if we have a fundamental problem)
prevMedia = undefined;
}
}
yield [ yield [
mediaId, mediaId,
@@ -965,14 +958,10 @@ export class CallViewModel extends ViewModel {
this.mediaDevices, this.mediaDevices,
this.pretendToBeDisconnected$, this.pretendToBeDisconnected$,
this.memberDisplaynames$.pipe( this.memberDisplaynames$.pipe(
map((m) => m.get(matrixId) ?? "[👻]"), map((m) => m.get(id) ?? "[👻]"),
),
this.handsRaised$.pipe(
map((v) => v[matrixId]?.time ?? null),
),
this.reactions$.pipe(
map((v) => v[matrixId] ?? undefined),
), ),
this.handsRaised$.pipe(map((v) => v[id]?.time ?? null)),
this.reactions$.pipe(map((v) => v[id] ?? undefined)),
), ),
]; ];
@@ -989,7 +978,7 @@ export class CallViewModel extends ViewModel {
livekitRoom, livekitRoom,
this.pretendToBeDisconnected$, this.pretendToBeDisconnected$,
this.memberDisplaynames$.pipe( this.memberDisplaynames$.pipe(
map((m) => m.get(matrixId) ?? "[👻]"), map((m) => m.get(id) ?? "[👻]"),
), ),
), ),
]; ];

View File

@@ -66,7 +66,7 @@ export class Connection {
this.livekitAlias, this.livekitAlias,
); );
public readonly participantsIncludingSubscribers$; private readonly participantsIncludingSubscribers$;
public readonly publishingParticipants$; public readonly publishingParticipants$;
public readonly livekitRoom: LivekitRoom; public readonly livekitRoom: LivekitRoom;
@@ -105,13 +105,11 @@ export class Connection {
? [membership] ? [membership]
: [], : [],
) )
// Find all associated publishing livekit participant objects // Pair with their associated LiveKit participant (if any)
.flatMap((membership) => { .map((membership) => {
const participant = participants.find( const id = `${membership.sender}:${membership.deviceId}`;
(p) => const participant = participants.find((p) => p.identity === id);
p.identity === `${membership.sender}:${membership.deviceId}`, return { participant, membership };
);
return participant ? [{ participant, membership }] : [];
}), }),
), ),
[], [],

View File

@@ -255,7 +255,7 @@ abstract class BaseMediaViewModel extends ViewModel {
*/ */
// TODO: Fully separate the data layer from the UI layer by keeping the // TODO: Fully separate the data layer from the UI layer by keeping the
// member object internal // member object internal
public readonly member: RoomMember | undefined, public readonly member: RoomMember,
// We don't necessarily have a participant if a user connects via MatrixRTC but not (yet) through // We don't necessarily have a participant if a user connects via MatrixRTC but not (yet) through
// livekit. // livekit.
protected readonly participant$: Observable< protected readonly participant$: Observable<
@@ -403,7 +403,7 @@ abstract class BaseUserMediaViewModel extends BaseMediaViewModel {
public constructor( public constructor(
id: string, id: string,
member: RoomMember | undefined, member: RoomMember,
participant$: Observable<LocalParticipant | RemoteParticipant | undefined>, participant$: Observable<LocalParticipant | RemoteParticipant | undefined>,
encryptionSystem: EncryptionSystem, encryptionSystem: EncryptionSystem,
livekitRoom: LivekitRoom, livekitRoom: LivekitRoom,
@@ -535,7 +535,7 @@ export class LocalUserMediaViewModel extends BaseUserMediaViewModel {
public constructor( public constructor(
id: string, id: string,
member: RoomMember | undefined, member: RoomMember,
participant$: Behavior<LocalParticipant | undefined>, participant$: Behavior<LocalParticipant | undefined>,
encryptionSystem: EncryptionSystem, encryptionSystem: EncryptionSystem,
livekitRoom: LivekitRoom, livekitRoom: LivekitRoom,
@@ -641,7 +641,7 @@ export class RemoteUserMediaViewModel extends BaseUserMediaViewModel {
public constructor( public constructor(
id: string, id: string,
member: RoomMember | undefined, member: RoomMember,
participant$: Observable<RemoteParticipant | undefined>, participant$: Observable<RemoteParticipant | undefined>,
encryptionSystem: EncryptionSystem, encryptionSystem: EncryptionSystem,
livekitRoom: LivekitRoom, livekitRoom: LivekitRoom,
@@ -736,7 +736,7 @@ export class ScreenShareViewModel extends BaseMediaViewModel {
public constructor( public constructor(
id: string, id: string,
member: RoomMember | undefined, member: RoomMember,
participant$: Observable<LocalParticipant | RemoteParticipant>, participant$: Observable<LocalParticipant | RemoteParticipant>,
encryptionSystem: EncryptionSystem, encryptionSystem: EncryptionSystem,
livekitRoom: LivekitRoom, livekitRoom: LivekitRoom,

View File

@@ -32,7 +32,7 @@ interface Props extends ComponentProps<typeof animated.div> {
video: TrackReferenceOrPlaceholder | undefined; video: TrackReferenceOrPlaceholder | undefined;
videoFit: "cover" | "contain"; videoFit: "cover" | "contain";
mirror: boolean; mirror: boolean;
member: RoomMember | undefined; member: RoomMember;
videoEnabled: boolean; videoEnabled: boolean;
unencryptedWarning: boolean; unencryptedWarning: boolean;
encryptionStatus: EncryptionStatus; encryptionStatus: EncryptionStatus;

View File

@@ -55,7 +55,7 @@ interface SpotlightItemBaseProps {
targetHeight: number; targetHeight: number;
video: TrackReferenceOrPlaceholder | undefined; video: TrackReferenceOrPlaceholder | undefined;
videoEnabled: boolean; videoEnabled: boolean;
member: RoomMember | undefined; member: RoomMember;
unencryptedWarning: boolean; unencryptedWarning: boolean;
encryptionStatus: EncryptionStatus; encryptionStatus: EncryptionStatus;
displayName: string; displayName: string;