devtool: quick display of focus URL in stats tile

This commit is contained in:
Valere
2025-10-14 14:06:54 +02:00
parent 58d60b35fd
commit 93d763f58f
9 changed files with 53 additions and 3 deletions

View File

@@ -19,10 +19,26 @@ import mediaViewStyles from "../src/tile/MediaView.module.css";
interface Props { interface Props {
audio?: RTCInboundRtpStreamStats | RTCOutboundRtpStreamStats; audio?: RTCInboundRtpStreamStats | RTCOutboundRtpStreamStats;
video?: RTCInboundRtpStreamStats | RTCOutboundRtpStreamStats; video?: RTCInboundRtpStreamStats | RTCOutboundRtpStreamStats;
focusUrl?: string;
} }
const extractDomain = (url: string): string => {
try {
const parsedUrl = new URL(url);
return parsedUrl.hostname; // Returns "kdk.cpm"
} catch (error) {
console.error("Invalid URL:", error);
return url;
}
};
// This is only used in developer mode for debugging purposes, so we don't need full localization // This is only used in developer mode for debugging purposes, so we don't need full localization
export const RTCConnectionStats: FC<Props> = ({ audio, video, ...rest }) => { export const RTCConnectionStats: FC<Props> = ({
audio,
video,
focusUrl,
...rest
}) => {
const [showModal, setShowModal] = useState(false); const [showModal, setShowModal] = useState(false);
const [modalContents, setModalContents] = useState< const [modalContents, setModalContents] = useState<
"video" | "audio" | "none" "video" | "audio" | "none"
@@ -55,6 +71,13 @@ export const RTCConnectionStats: FC<Props> = ({ audio, video, ...rest }) => {
</pre> </pre>
</div> </div>
</Modal> </Modal>
{focusUrl && (
<div>
<Text as="span" size="xs" title="focusURL">
&nbsp;{extractDomain(focusUrl)}
</Text>
</div>
)}
{audio && ( {audio && (
<div> <div>
<Button <Button

View File

@@ -750,7 +750,11 @@ export class CallViewModel extends ViewModel {
scan((prevItems, [participantsByRoom, duplicateTiles]) => { scan((prevItems, [participantsByRoom, duplicateTiles]) => {
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,
url,
} of participantsByRoom) {
for (const { id, participant, member } of participants) { for (const { id, participant, member } of participants) {
for (let i = 0; i < 1 + duplicateTiles; i++) { for (let i = 0; i < 1 + duplicateTiles; i++) {
const mediaId = `${id}:${i}`; const mediaId = `${id}:${i}`;
@@ -770,6 +774,7 @@ export class CallViewModel extends ViewModel {
participant, participant,
this.options.encryptionSystem, this.options.encryptionSystem,
livekitRoom, livekitRoom,
url,
this.mediaDevices, this.mediaDevices,
this.pretendToBeDisconnected$, this.pretendToBeDisconnected$,
this.memberDisplaynames$.pipe( this.memberDisplaynames$.pipe(
@@ -791,6 +796,7 @@ export class CallViewModel extends ViewModel {
participant, participant,
this.options.encryptionSystem, this.options.encryptionSystem,
livekitRoom, livekitRoom,
url,
this.pretendToBeDisconnected$, this.pretendToBeDisconnected$,
this.memberDisplaynames$.pipe( this.memberDisplaynames$.pipe(
map((m) => m.get(id) ?? "[👻]"), map((m) => m.get(id) ?? "[👻]"),

View File

@@ -266,6 +266,7 @@ abstract class BaseMediaViewModel extends ViewModel {
audioSource: AudioSource, audioSource: AudioSource,
videoSource: VideoSource, videoSource: VideoSource,
livekitRoom: LivekitRoom, livekitRoom: LivekitRoom,
public readonly focusURL: string,
public readonly displayName$: Behavior<string>, public readonly displayName$: Behavior<string>,
) { ) {
super(); super();
@@ -407,6 +408,7 @@ abstract class BaseUserMediaViewModel extends BaseMediaViewModel {
participant$: Observable<LocalParticipant | RemoteParticipant | undefined>, participant$: Observable<LocalParticipant | RemoteParticipant | undefined>,
encryptionSystem: EncryptionSystem, encryptionSystem: EncryptionSystem,
livekitRoom: LivekitRoom, livekitRoom: LivekitRoom,
focusUrl: string,
displayName$: Behavior<string>, displayName$: Behavior<string>,
public readonly handRaised$: Behavior<Date | null>, public readonly handRaised$: Behavior<Date | null>,
public readonly reaction$: Behavior<ReactionOption | null>, public readonly reaction$: Behavior<ReactionOption | null>,
@@ -419,6 +421,7 @@ abstract class BaseUserMediaViewModel extends BaseMediaViewModel {
Track.Source.Microphone, Track.Source.Microphone,
Track.Source.Camera, Track.Source.Camera,
livekitRoom, livekitRoom,
focusUrl,
displayName$, displayName$,
); );
@@ -539,6 +542,7 @@ export class LocalUserMediaViewModel extends BaseUserMediaViewModel {
participant$: Behavior<LocalParticipant | undefined>, participant$: Behavior<LocalParticipant | undefined>,
encryptionSystem: EncryptionSystem, encryptionSystem: EncryptionSystem,
livekitRoom: LivekitRoom, livekitRoom: LivekitRoom,
focusURL: string,
private readonly mediaDevices: MediaDevices, private readonly mediaDevices: MediaDevices,
displayName$: Behavior<string>, displayName$: Behavior<string>,
handRaised$: Behavior<Date | null>, handRaised$: Behavior<Date | null>,
@@ -550,6 +554,7 @@ export class LocalUserMediaViewModel extends BaseUserMediaViewModel {
participant$, participant$,
encryptionSystem, encryptionSystem,
livekitRoom, livekitRoom,
focusURL,
displayName$, displayName$,
handRaised$, handRaised$,
reaction$, reaction$,
@@ -645,6 +650,7 @@ export class RemoteUserMediaViewModel extends BaseUserMediaViewModel {
participant$: Observable<RemoteParticipant | undefined>, participant$: Observable<RemoteParticipant | undefined>,
encryptionSystem: EncryptionSystem, encryptionSystem: EncryptionSystem,
livekitRoom: LivekitRoom, livekitRoom: LivekitRoom,
focusUrl: string,
private readonly pretendToBeDisconnected$: Behavior<boolean>, private readonly pretendToBeDisconnected$: Behavior<boolean>,
displayname$: Behavior<string>, displayname$: Behavior<string>,
handRaised$: Behavior<Date | null>, handRaised$: Behavior<Date | null>,
@@ -656,6 +662,7 @@ export class RemoteUserMediaViewModel extends BaseUserMediaViewModel {
participant$, participant$,
encryptionSystem, encryptionSystem,
livekitRoom, livekitRoom,
focusUrl,
displayname$, displayname$,
handRaised$, handRaised$,
reaction$, reaction$,
@@ -740,6 +747,7 @@ export class ScreenShareViewModel extends BaseMediaViewModel {
participant$: Observable<LocalParticipant | RemoteParticipant>, participant$: Observable<LocalParticipant | RemoteParticipant>,
encryptionSystem: EncryptionSystem, encryptionSystem: EncryptionSystem,
livekitRoom: LivekitRoom, livekitRoom: LivekitRoom,
focusUrl: string,
private readonly pretendToBeDisconnected$: Behavior<boolean>, private readonly pretendToBeDisconnected$: Behavior<boolean>,
displayname$: Behavior<string>, displayname$: Behavior<string>,
public readonly local: boolean, public readonly local: boolean,
@@ -752,6 +760,7 @@ export class ScreenShareViewModel extends BaseMediaViewModel {
Track.Source.ScreenShareAudio, Track.Source.ScreenShareAudio,
Track.Source.ScreenShare, Track.Source.ScreenShare,
livekitRoom, livekitRoom,
focusUrl,
displayname$, displayname$,
); );
} }

View File

@@ -31,6 +31,7 @@ export class ScreenShare {
participant: LocalParticipant | RemoteParticipant, participant: LocalParticipant | RemoteParticipant,
encryptionSystem: EncryptionSystem, encryptionSystem: EncryptionSystem,
livekitRoom: LivekitRoom, livekitRoom: LivekitRoom,
focusUrl: string,
pretendToBeDisconnected$: Behavior<boolean>, pretendToBeDisconnected$: Behavior<boolean>,
displayName$: Observable<string>, displayName$: Observable<string>,
) { ) {
@@ -42,6 +43,7 @@ export class ScreenShare {
this.participant$.asObservable(), this.participant$.asObservable(),
encryptionSystem, encryptionSystem,
livekitRoom, livekitRoom,
focusUrl,
pretendToBeDisconnected$, pretendToBeDisconnected$,
this.scope.behavior(displayName$), this.scope.behavior(displayName$),
participant.isLocal, participant.isLocal,

View File

@@ -47,6 +47,7 @@ export class UserMedia {
participant: LocalParticipant | RemoteParticipant | undefined, participant: LocalParticipant | RemoteParticipant | undefined,
encryptionSystem: EncryptionSystem, encryptionSystem: EncryptionSystem,
livekitRoom: LivekitRoom, livekitRoom: LivekitRoom,
focusURL: string,
mediaDevices: MediaDevices, mediaDevices: MediaDevices,
pretendToBeDisconnected$: Behavior<boolean>, pretendToBeDisconnected$: Behavior<boolean>,
displayname$: Observable<string>, displayname$: Observable<string>,
@@ -62,6 +63,7 @@ export class UserMedia {
this.participant$ as Behavior<LocalParticipant>, this.participant$ as Behavior<LocalParticipant>,
encryptionSystem, encryptionSystem,
livekitRoom, livekitRoom,
focusURL,
mediaDevices, mediaDevices,
this.scope.behavior(displayname$), this.scope.behavior(displayname$),
this.scope.behavior(handRaised$), this.scope.behavior(handRaised$),
@@ -76,6 +78,7 @@ export class UserMedia {
>, >,
encryptionSystem, encryptionSystem,
livekitRoom, livekitRoom,
focusURL,
pretendToBeDisconnected$, pretendToBeDisconnected$,
this.scope.behavior(displayname$), this.scope.behavior(displayname$),
this.scope.behavior(handRaised$), this.scope.behavior(handRaised$),

View File

@@ -190,6 +190,7 @@ const UserMediaTile: FC<UserMediaTileProps> = ({
currentReaction={reaction ?? undefined} currentReaction={reaction ?? undefined}
raisedHandOnClick={raisedHandOnClick} raisedHandOnClick={raisedHandOnClick}
localParticipant={vm.local} localParticipant={vm.local}
focusUrl={vm.focusURL}
audioStreamStats={audioStreamStats} audioStreamStats={audioStreamStats}
videoStreamStats={videoStreamStats} videoStreamStats={videoStreamStats}
{...props} {...props}

View File

@@ -46,6 +46,8 @@ interface Props extends ComponentProps<typeof animated.div> {
localParticipant: boolean; localParticipant: boolean;
audioStreamStats?: RTCInboundRtpStreamStats | RTCOutboundRtpStreamStats; audioStreamStats?: RTCInboundRtpStreamStats | RTCOutboundRtpStreamStats;
videoStreamStats?: RTCInboundRtpStreamStats | RTCOutboundRtpStreamStats; videoStreamStats?: RTCInboundRtpStreamStats | RTCOutboundRtpStreamStats;
// The focus url, mainly for debugging purposes
focusUrl?: string;
} }
export const MediaView: FC<Props> = ({ export const MediaView: FC<Props> = ({
@@ -71,6 +73,7 @@ export const MediaView: FC<Props> = ({
localParticipant, localParticipant,
audioStreamStats, audioStreamStats,
videoStreamStats, videoStreamStats,
focusUrl,
...props ...props
}) => { }) => {
const { t } = useTranslation(); const { t } = useTranslation();
@@ -134,6 +137,7 @@ export const MediaView: FC<Props> = ({
<RTCConnectionStats <RTCConnectionStats
audio={audioStreamStats} audio={audioStreamStats}
video={videoStreamStats} video={videoStreamStats}
focusUrl={focusUrl}
/> />
)} )}
{/* TODO: Bring this back once encryption status is less broken */} {/* TODO: Bring this back once encryption status is less broken */}

View File

@@ -78,7 +78,7 @@ const SpotlightLocalUserMediaItem: FC<SpotlightLocalUserMediaItemProps> = ({
...props ...props
}) => { }) => {
const mirror = useBehavior(vm.mirror$); const mirror = useBehavior(vm.mirror$);
return <MediaView mirror={mirror} {...props} />; return <MediaView mirror={mirror} focusUrl={vm.focusURL} {...props} />;
}; };
SpotlightLocalUserMediaItem.displayName = "SpotlightLocalUserMediaItem"; SpotlightLocalUserMediaItem.displayName = "SpotlightLocalUserMediaItem";

View File

@@ -274,6 +274,7 @@ export async function withLocalMedia(
kind: E2eeType.PER_PARTICIPANT, kind: E2eeType.PER_PARTICIPANT,
}, },
mockLivekitRoom({ localParticipant }), mockLivekitRoom({ localParticipant }),
"https://rtc-example.org",
mediaDevices, mediaDevices,
constant(roomMember.rawDisplayName ?? "nodisplayname"), constant(roomMember.rawDisplayName ?? "nodisplayname"),
constant(null), constant(null),
@@ -314,6 +315,7 @@ export async function withRemoteMedia(
kind: E2eeType.PER_PARTICIPANT, kind: E2eeType.PER_PARTICIPANT,
}, },
mockLivekitRoom({}, { remoteParticipants$: of([remoteParticipant]) }), mockLivekitRoom({}, { remoteParticipants$: of([remoteParticipant]) }),
"https://rtc-example.org",
constant(false), constant(false),
constant(roomMember.rawDisplayName ?? "nodisplayname"), constant(roomMember.rawDisplayName ?? "nodisplayname"),
constant(null), constant(null),