import React, { useEffect, useState } from 'react'; import { AudioTrack, useTracks, VideoTrack, useTrackRefContext, useEnsureTrackRef, TrackRefContextIfNeeded, } from '@livekit/components-react'; import { Track, Participant } from 'livekit-client'; import { isTrackReference } from '@livekit/components-core'; function getAvatarColor(identity: string): string { const colors = [ '#4CAF50', '#8BC34A', '#CDDC39', '#FFC107', '#FF9800', '#FF5722', '#F44336', '#E91E63', '#9C27B0', '#673AB7', '#3F51B5', '#2196F3', '#03A9F4', '#00BCD4', '#009688', ]; let hash = 0; for (let i = 0; i < identity.length; i++) { hash = identity.charCodeAt(i) + ((hash << 5) - hash); } const index = Math.abs(hash) % colors.length; return colors[index]; } function getInitials(name: string): string { if (!name) return '?'; const parts = name.split(' '); if (parts.length === 1) { return parts[0].charAt(0).toUpperCase(); } return (parts[0].charAt(0) + parts[parts.length - 1].charAt(0)).toUpperCase(); } export interface ParticipantTileProps { participant?: Participant; } export const ParticipantTile: React.FC = ({ participant: propParticipant, }) => { const trackRef = useTrackRefContext(); const trackReference = useEnsureTrackRef(trackRef); const participant = propParticipant || trackRef?.participant; if (!participant) return null; const [profilePictureUrl, setProfilePictureUrl] = useState(null); const microphoneTrack = useTracks([Track.Source.Microphone], { onlySubscribed: false }).filter( (track) => track.participant.identity === participant.identity, )[0]; const isSpeaking = participant.isSpeaking; useEffect(() => { if (participant.metadata) { try { const metadata = JSON.parse(participant.metadata); if (metadata.profilePictureUrl) { setProfilePictureUrl(metadata.profilePictureUrl); } } catch (e) { console.error('Failed to parse participant metadata', e); } } }, [participant.metadata]); const isCameraEnabled = (trackReference.source === Track.Source.Camera && !trackReference.publication?.isMuted) || trackReference.source === Track.Source.ScreenShare; const hasMicrophone = !!microphoneTrack; const isMicrophoneEnabled = hasMicrophone && !microphoneTrack.publication?.isMuted; const avatarColor = getAvatarColor(participant.identity); const initials = getInitials(participant.name || participant.identity); return (
{isTrackReference(trackReference) && isCameraEnabled ? (
) : (
{profilePictureUrl ? ( {participant.name} ) : ( {initials} )}
)}
{isMicrophoneEnabled ? ( <> {isSpeaking ? ( graphic_eq ) : ( mic )} ) : ( mic_off )} {participant.name || participant.identity}
{hasMicrophone && microphoneTrack && }
); }; export default ParticipantTile;