@@ -202,6 +202,9 @@ export function CapturePreview({
202202 stopHosting,
203203 publishStream : hostPublishStream ,
204204 unpublishStream : hostUnpublishStream ,
205+ grantControl,
206+ revokeControl,
207+ kickViewer,
205208 muteViewer,
206209 micEnabled : hostMicEnabled ,
207210 hasMic : hostHasMic ,
@@ -446,6 +449,24 @@ export function CapturePreview({
446449 return headers ;
447450 } , [ ] ) ;
448451
452+ const resolveViewerTargetId = useCallback (
453+ ( participantId : string ) : string | null => {
454+ const participant = participants . find ( ( p ) => p . id === participantId ) ;
455+ if ( ! participant ) return null ;
456+
457+ const candidates = [ participant . user_id , participant . id ] . filter (
458+ ( value ) : value is string => typeof value === 'string' && value . length > 0
459+ ) ;
460+
461+ for ( const candidate of candidates ) {
462+ if ( hostedViewers . has ( candidate ) ) return candidate ;
463+ }
464+
465+ return candidates [ 0 ] ?? null ;
466+ } ,
467+ [ participants , hostedViewers ]
468+ ) ;
469+
449470 const handleGrantControl = useCallback (
450471 async ( participantId : string ) => {
451472 if ( ! session ) return ;
@@ -460,12 +481,22 @@ export function CapturePreview({
460481 ) ;
461482 if ( ! response . ok ) {
462483 console . error ( 'Failed to grant control' ) ;
484+ return ;
485+ }
486+
487+ const viewerId = resolveViewerTargetId ( participantId ) ;
488+ if ( viewerId ) {
489+ grantControl ( viewerId ) ;
490+ } else {
491+ console . warn ( '[CapturePreview] Could not resolve viewer target for grant control' , {
492+ participantId,
493+ } ) ;
463494 }
464495 } catch ( err ) {
465496 console . error ( 'Error granting control:' , err ) ;
466497 }
467498 } ,
468- [ session , getAuthHeaders ]
499+ [ session , getAuthHeaders , resolveViewerTargetId , grantControl ]
469500 ) ;
470501
471502 const handleRevokeControl = useCallback (
@@ -482,12 +513,22 @@ export function CapturePreview({
482513 ) ;
483514 if ( ! response . ok ) {
484515 console . error ( 'Failed to revoke control' ) ;
516+ return ;
517+ }
518+
519+ const viewerId = resolveViewerTargetId ( participantId ) ;
520+ if ( viewerId ) {
521+ revokeControl ( viewerId ) ;
522+ } else {
523+ console . warn ( '[CapturePreview] Could not resolve viewer target for revoke control' , {
524+ participantId,
525+ } ) ;
485526 }
486527 } catch ( err ) {
487528 console . error ( 'Error revoking control:' , err ) ;
488529 }
489530 } ,
490- [ session , getAuthHeaders ]
531+ [ session , getAuthHeaders , resolveViewerTargetId , revokeControl ]
491532 ) ;
492533
493534 const handleKickParticipant = useCallback (
@@ -503,12 +544,22 @@ export function CapturePreview({
503544 ) ;
504545 if ( ! response . ok ) {
505546 console . error ( 'Failed to kick participant' ) ;
547+ return ;
548+ }
549+
550+ const viewerId = resolveViewerTargetId ( participantId ) ;
551+ if ( viewerId ) {
552+ kickViewer ( viewerId ) ;
553+ } else {
554+ console . warn ( '[CapturePreview] Could not resolve viewer target for kick' , {
555+ participantId,
556+ } ) ;
506557 }
507558 } catch ( err ) {
508559 console . error ( 'Error kicking participant:' , err ) ;
509560 }
510561 } ,
511- [ session , getAuthHeaders ]
562+ [ session , getAuthHeaders , resolveViewerTargetId , kickViewer ]
512563 ) ;
513564
514565 const handleStartRecording = useCallback ( async ( ) => {
@@ -556,14 +607,14 @@ export function CapturePreview({
556607 } , [ isPaused , pauseRecording , resumeRecording ] ) ;
557608
558609 const handleMuteParticipant = useCallback (
559- ( participantUserId : string , muted : boolean ) => {
560- muteViewer ( participantUserId , muted ) ;
610+ ( participantIdentity : string , muted : boolean ) => {
611+ muteViewer ( participantIdentity , muted ) ;
561612 setMutedParticipants ( ( prev ) => {
562613 const next = new Set ( prev ) ;
563614 if ( muted ) {
564- next . add ( participantUserId ) ;
615+ next . add ( participantIdentity ) ;
565616 } else {
566- next . delete ( participantUserId ) ;
617+ next . delete ( participantIdentity ) ;
567618 }
568619 return next ;
569620 } ) ;
@@ -959,6 +1010,22 @@ export function CapturePreview({
9591010 < ChatPanel
9601011 sessionId = { session . id }
9611012 currentUserId = { currentUserId }
1013+ isHost = { true }
1014+ mutedParticipants = { mutedParticipants }
1015+ onGrantControl = { ( participant ) => {
1016+ void handleGrantControl ( participant . id ) ;
1017+ } }
1018+ onRevokeControl = { ( participant ) => {
1019+ void handleRevokeControl ( participant . id ) ;
1020+ } }
1021+ onKickParticipant = { ( participant ) => {
1022+ void handleKickParticipant ( participant . id ) ;
1023+ } }
1024+ onMuteParticipant = { ( participant , muted ) => {
1025+ const targetId =
1026+ resolveViewerTargetId ( participant . id ) ?? participant . user_id ?? participant . id ;
1027+ handleMuteParticipant ( targetId , muted ) ;
1028+ } }
9621029 isCollapsed = { ! showChat }
9631030 onToggleCollapse = { ( ) => {
9641031 setShowChat ( ! showChat ) ;
0 commit comments