Files
dumpsterChat/web/src/components/AudioRenderers.tsx
T

74 lines
2.1 KiB
TypeScript

import { useEffect, useRef } from 'react';
import { useVoiceStore } from '../stores/voice.ts';
import { Track } from 'livekit-client';
import type { Participant, TrackPublication, Room } from 'livekit-client';
function RemoteAudioTrack({ participant, room }: { participant: Participant; room: Room }) {
const audioRef = useRef<HTMLAudioElement>(null);
useEffect(() => {
const el = audioRef.current;
if (!el) return;
let pub: TrackPublication | undefined;
const attachTrack = () => {
pub = participant.getTrackPublication(Track.Source.Microphone);
if (pub?.track && el) {
pub.track.attach(el);
el.play().catch(() => {});
}
};
const detachTrack = () => {
if (pub?.track && el) {
pub.track.detach(el);
}
};
attachTrack();
const handlePublished = (publication: TrackPublication) => {
if (publication.source === Track.Source.Microphone && publication.track) {
publication.track.attach(el);
el.play().catch(() => {});
}
};
room.on('trackPublished' as any, handlePublished);
// If a track is subscribed later
const handleSubscribed = (track: Track, publication: TrackPublication, trackParticipant: Participant) => {
if (trackParticipant.identity === participant.identity && publication.source === Track.Source.Microphone) {
track.attach(el);
el.play().catch(() => {});
}
};
room.on('trackSubscribed' as any, handleSubscribed);
return () => {
detachTrack();
room.off('trackPublished' as any, handlePublished);
room.off('trackSubscribed' as any, handleSubscribed);
};
}, [participant, room]);
return <audio ref={audioRef} autoPlay />;
}
export function AudioRenderers() {
const room = useVoiceStore((s) => s._room);
if (!room) return null;
const remoteParticipants = Array.from(room.remoteParticipants.values());
return (
<div className="hidden">
{remoteParticipants.map((p) => (
<RemoteAudioTrack key={p.identity} participant={p} room={room} />
))}
</div>
);
}