import * as React from 'react'; import type { Participant } from 'livekit-client'; import { Track } from 'livekit-client'; import type { ParticipantClickEvent, TrackReferenceOrPlaceholder } from '@livekit/components-core'; import { isTrackReference, isTrackReferencePinned } from '@livekit/components-core'; import { ConnectionQualityIndicator } from './ConnectionQualityIndicator'; import { ParticipantName } from './ParticipantName'; import { TrackMutedIndicator } from './TrackMutedIndicator'; import { ParticipantContext, TrackRefContext, useEnsureTrackRef, useFeatureContext, useMaybeLayoutContext, useMaybeParticipantContext, useMaybeTrackRefContext, } from '../../context'; import { FocusToggle } from '../controls/FocusToggle'; import { ParticipantPlaceholder } from '../../assets/images'; import { LockLockedIcon, ScreenShareIcon } from '../../assets/icons'; import { VideoTrack } from './VideoTrack'; import { AudioTrack } from './AudioTrack'; import { useParticipantTile } from '../../hooks'; import { useIsEncrypted } from '../../hooks/useIsEncrypted'; /** * The `ParticipantContextIfNeeded` component only creates a `ParticipantContext` * if there is no `ParticipantContext` already. * @example * ```tsx * * ... * * ``` * @public */ export function ParticipantContextIfNeeded( props: React.PropsWithChildren<{ participant?: Participant; }>, ) { const hasContext = !!useMaybeParticipantContext(); return props.participant && !hasContext ? ( {props.children} ) : ( <>{props.children} ); } /** * Only create a `TrackRefContext` if there is no `TrackRefContext` already. * @internal */ export function TrackRefContextIfNeeded( props: React.PropsWithChildren<{ trackRef?: TrackReferenceOrPlaceholder; }>, ) { const hasContext = !!useMaybeTrackRefContext(); return props.trackRef && !hasContext ? ( {props.children} ) : ( <>{props.children} ); } /** @public */ export interface ParticipantTileProps extends React.HTMLAttributes { /** The track reference to display. */ trackRef?: TrackReferenceOrPlaceholder; disableSpeakingIndicator?: boolean; onParticipantClick?: (event: ParticipantClickEvent) => void; } /** * The `ParticipantTile` component is the base utility wrapper for displaying a visual representation of a participant. * This component can be used as a child of the `TrackLoop` component or by passing a track reference as property. * * @example Using the `ParticipantTile` component with a track reference: * ```tsx * * ``` * @example Using the `ParticipantTile` component as a child of the `TrackLoop` component: * ```tsx * * * * ``` * @public */ export const ParticipantTile: ( props: ParticipantTileProps & React.RefAttributes, ) => React.ReactNode = /* @__PURE__ */ React.forwardRef( function ParticipantTile( { trackRef, children, onParticipantClick, disableSpeakingIndicator, ...htmlProps }: ParticipantTileProps, ref, ) { const trackReference = useEnsureTrackRef(trackRef); const { elementProps } = useParticipantTile({ htmlProps, disableSpeakingIndicator, onParticipantClick, trackRef: trackReference, }); const isEncrypted = useIsEncrypted(trackReference.participant); const layoutContext = useMaybeLayoutContext(); const autoManageSubscription = useFeatureContext()?.autoSubscription; const handleSubscribe = React.useCallback( (subscribed: boolean) => { if ( trackReference.source && !subscribed && layoutContext && layoutContext.pin.dispatch && isTrackReferencePinned(trackReference, layoutContext.pin.state) ) { layoutContext.pin.dispatch({ msg: 'clear_pin' }); } }, [trackReference, layoutContext], ); return (
{children ?? ( <> {isTrackReference(trackReference) && (trackReference.publication?.kind === 'video' || trackReference.source === Track.Source.Camera || trackReference.source === Track.Source.ScreenShare) ? ( ) : ( isTrackReference(trackReference) && ( ) )}
{trackReference.source === Track.Source.Camera ? ( <> {isEncrypted && } ) : ( <> 's screen )}
)}
); }, );