CoGuestState module, purpose-built to handle the entire workflow of audience co-hosting in live streaming scenarios. You can easily enable robust audio/video interactive features between hosts and audience members with just a few method calls—no need to manage complex state synchronization or signaling logic.CoGuestState supports the two most common co-hosting scenarios:applyForSeat method.import { useCoGuestState } from 'react-native-tuikit-atomic-x/lib/module/atomic-x/state/CoGuestState';const liveID = 'xxx'; // The liveID of the current live room// Get the CoGuestState instance using liveIDconst { applyForSeat } = useCoGuestState(liveID);// User taps "Request to Co-host"const handleRequestToConnect = () => {applyForSeat({liveID,seatIndex: -1, // Use -1 for random seat assignmenttimeout: 30, // Timeout in seconds (e.g., 30s)});};
onGuestApplicationResponded event using addCoGuestGuestListener to get the host’s decision.import { useEffect } from 'react';import { useCoGuestState } from 'react-native-tuikit-atomic-x/lib/module/atomic-x/state/CoGuestState';import { useDeviceState } from 'react-native-tuikit-atomic-x/lib/module/atomic-x/state/DeviceState';const liveID = 'xxx'; // The liveID of the current live roomconst { addCoGuestGuestListener, removeCoGuestGuestListener } = useCoGuestState(liveID);const { openLocalMicrophone, openLocalCamera } = useDeviceState(liveID);// Subscribe to event on page initializationconst handleGuestApplicationResponded = (event) => {if (event.isAccept) {console.log('Co-host request accepted');openLocalMicrophone();openLocalCamera({ isFront: true });// Update UI to reflect co-hosting status} else {console.log('Co-host request rejected');// Display a dialog notifying the user of rejection}};useEffect(() => {addCoGuestGuestListener('onGuestApplicationResponded', handleGuestApplicationResponded);return () => {removeCoGuestGuestListener('onGuestApplicationResponded', handleGuestApplicationResponded);};}, []);
disconnect method to return to normal audience status.import { useCoGuestState } from 'react-native-tuikit-atomic-x/lib/module/atomic-x/state/CoGuestState';const liveID = 'xxx'; // The liveID of the current live roomconst { disconnect } = useCoGuestState(liveID);// User taps "Leave Co-host Seat"const handleDisconnect = () => {disconnect({liveID,onSuccess: () => { console.log('Left co-host seat successfully'); },onError: (error) => { console.log('Failed to leave co-host seat', error); },});};
cancelApplication.import { useCoGuestState } from 'react-native-tuikit-atomic-x/lib/module/atomic-x/state/CoGuestState';const liveID = 'xxx'; // The liveID of the current live roomconst { cancelApplication } = useCoGuestState(liveID);// User taps "Cancel Request" while waitingconst handleCancelRequest = () => {cancelApplication({liveID,onSuccess: () => { console.log('Request cancelled successfully'); },onError: (error) => { console.log('Failed to cancel request', error); },});};
onGuestApplicationReceived event with CoGuestHostListener to get notified when a new audience request arrives.import { useEffect } from 'react';import { useCoGuestState } from 'react-native-tuikit-atomic-x/lib/module/atomic-x/state/CoGuestState';const liveID = 'xxx'; // The liveID of your current live roomconst { addCoGuestHostListener, removeCoGuestHostListener } = useCoGuestState(liveID);// Subscribe to event on page initializationconst handleGuestApplicationReceived = (event) => {console.log('Received audience co-host request', event);// Update UI, e.g., show notification indicator on request list button};useEffect(() => {addCoGuestHostListener('onGuestApplicationReceived', handleGuestApplicationReceived);return () => {removeCoGuestHostListener('onGuestApplicationReceived', handleGuestApplicationReceived);};}, []);
CoGuestState provides a real-time list of applicants, which can be used directly in your UI.import { useCoGuestState } from 'react-native-tuikit-atomic-x/lib/module/atomic-x/state/CoGuestState';const liveID = 'xxx'; // The liveID of your current live roomconst { applicants } = useCoGuestState(liveID);// Render your applicant list UI<FlatListdata={applicants || []}keyExtractor={(item) => item.userID}renderItem={({ item }) => (<View><Text>{item.userName}</Text><Text>{item.userID}</Text></View>)}/>
import { useCoGuestState } from 'react-native-tuikit-atomic-x/lib/module/atomic-x/state/CoGuestState';const liveID = 'xxx'; // The liveID of your current live roomconst { acceptApplication, rejectApplication } = useCoGuestState(liveID);// Host clicks "Accept" button for a userconst handleAccept = (userID) => {acceptApplication({liveID,userID,onSuccess: () => { console.log('Accepted co-host request successfully'); },onError: (error) => { console.log('Failed to accept co-host request', error); },});};// Host clicks "Reject" button for a userconst handleReject = (userID) => {rejectApplication({liveID,userID,onSuccess: () => { console.log('Rejected co-host request successfully'); },onError: (error) => { console.log('Failed to reject co-host request', error); },});};
inviteToSeat method.import { useCoGuestState } from 'react-native-tuikit-atomic-x/lib/module/atomic-x/state/CoGuestState';const liveID = 'xxx'; // The liveID of your current live roomconst { inviteToSeat } = useCoGuestState(liveID);// Host selects an audience member and sends an inviteconst handleInviteToSeat = (inviteeID) => {inviteToSeat({liveID,inviteeID,seatIndex: -1, // Use -1 for random seat assignmenttimeout: 30, // Timeout duration in secondsonSuccess: () => { console.log('Sent co-host invitation to audience'); },onError: (error) => { console.log('Failed to send co-host invitation', error); },});};
onHostInvitationResponded event using CoGuestHostListener.import { useEffect } from 'react';import { useCoGuestState } from 'react-native-tuikit-atomic-x/lib/module/atomic-x/state/CoGuestState';const liveID = 'xxx'; // The liveID of your current live roomconst { addCoGuestHostListener, removeCoGuestHostListener } = useCoGuestState(liveID);// Subscribe to event on page initializationconst handleHostInvitationResponded = (event) => {if (event.isAccept) {console.log('Audience accepted your invitation');} else {console.log('Audience rejected your invitation');}};useEffect(() => {addCoGuestHostListener('onHostInvitationResponded', handleHostInvitationResponded);return () => {removeCoGuestHostListener('onHostInvitationResponded', handleHostInvitationResponded);};}, []);
onHostInvitationReceived event using addCoGuestGuestListener.import { useEffect } from 'react';import { useCoGuestState } from 'react-native-tuikit-atomic-x/lib/module/atomic-x/state/CoGuestState';import { useLiveListState } from 'react-native-tuikit-atomic-x/lib/module/atomic-x/state/LiveListState';const liveID = 'xxx'; // The liveID of the current live roomconst { addCoGuestGuestListener, removeCoGuestGuestListener } = useCoGuestState(liveID);const { currentLive } = useLiveListState();// Subscribe to event on page initializationconst handleHostInvitationReceived = (event) => {console.log('Received host invitation to co-host', event);const inviterID = currentLive?.liveOwner?.userID || '';// Display a dialog for the user to accept or reject};useEffect(() => {addCoGuestGuestListener('onHostInvitationReceived', handleHostInvitationReceived);return () => {removeCoGuestGuestListener('onHostInvitationReceived', handleHostInvitationReceived);};}, [currentLive]);
import { useCoGuestState } from 'react-native-tuikit-atomic-x/lib/module/atomic-x/state/CoGuestState';import { useDeviceState } from 'react-native-tuikit-atomic-x/lib/module/atomic-x/state/DeviceState';import { useLiveListState } from 'react-native-tuikit-atomic-x/lib/module/atomic-x/state/LiveListState';const liveID = 'xxx'; // The liveID of the current live roomconst { acceptInvitation, rejectInvitation } = useCoGuestState(liveID);const { openLocalMicrophone, openLocalCamera } = useDeviceState(liveID);const { currentLive } = useLiveListState();// Get inviterID from currentLiveconst inviterID = currentLive?.liveOwner?.userID || '';// User clicks "Accept"const handleAcceptInvitation = () => {acceptInvitation({liveID,inviterID,onSuccess: () => {console.log('Accepted invitation successfully');openLocalMicrophone();openLocalCamera({ isFront: true });},onError: (error) => { console.log('Failed to accept invitation', error); },});};// User clicks "Reject"const handleRejectInvitation = () => {rejectInvitation({liveID,inviterID,onSuccess: () => { console.log('Rejected invitation successfully'); },onError: (error) => { console.log('Failed to reject invitation', error); },});};


import React, { useMemo } from 'react';import { View, Text, Image, StyleSheet, Dimensions } from 'react-native';const DEFAULT_AVATAR = 'https://liteav-test-1252463788.cos.ap-guangzhou.myqcloud.com/voice_room/voice_room_cover1.png';const { width: SCREEN_WIDTH } = Dimensions.get('window');// Receives seatList and canvas from parent componentexport default function ParticipantOverlay({ seatList, canvas }) {if (!seatList || seatList.length === 0) return null;// Calculate scaling ratio based on canvasconst scale = useMemo(() => {if (!canvas?.w || !canvas?.h) return { scaleX: 1, scaleY: 1 };const displayWidth = SCREEN_WIDTH;const displayHeight = SCREEN_WIDTH * (canvas.h / canvas.w);return {scaleX: displayWidth / canvas.w,scaleY: displayHeight / canvas.h,};}, [canvas]);// Calculate precise position and size for each participant's UI containerconst getParticipantStyle = (participant) => {if (!participant?.region) return {};return {position: 'absolute',left: participant.region.x * scale.scaleX,top: participant.region.y * scale.scaleY,width: participant.region.w * scale.scaleX,height: participant.region.h * scale.scaleY,};};return (// overlay-container<View style={styles.overlayContainer} pointerEvents="none">{/* Iterate seatList to create a UI container for each co-host participant */}{seatList.map((participant) => {if (!participant?.userInfo?.userID) return null;const isCameraOff = participant.userInfo.cameraStatus === 'OFF';return (// participant-ui-container<View key={participant.userInfo.userID} style={getParticipantStyle(participant)}>{/* Conditional rendering: show different UI based on camera status */}{isCameraOff ? (// When camera is off, show avatar and nickname centered<View style={styles.avatarPlaceholder}><Imagestyle={styles.avatarImage}source={{ uri: participant.userInfo.userAvatar || DEFAULT_AVATAR }}/><Text style={styles.avatarName}>{participant.userInfo.userName || participant.userInfo.userID}</Text></View>) : (// When camera is on, show nickname bar at bottom left<View style={styles.nicknameBar}><Text style={styles.nicknameText}>{participant.userInfo.userName || participant.userInfo.userID}</Text></View>)}</View>);})}</View>);}const styles = StyleSheet.create({overlayContainer: {position: 'absolute',top: 0,left: 0,right: 0,bottom: 0,},avatarPlaceholder: {flex: 1,backgroundColor: '#2E323A',justifyContent: 'center',alignItems: 'center',},avatarImage: {width: 60,height: 60,borderRadius: 30,},avatarName: {marginTop: 8,fontSize: 13,color: '#fff',},nicknameBar: {position: 'absolute',left: 6,bottom: 6,backgroundColor: 'rgba(0, 0, 0, 0.5)',paddingHorizontal: 8,paddingVertical: 3,borderRadius: 10,},nicknameText: {color: '#fff',fontSize: 11,},});
import React from 'react';import { StyleSheet, View } from 'react-native';import { LiveCoreView } from 'react-native-tuikit-atomic-x/lib/module/components/LiveCoreView';import { useLiveSeatState } from 'react-native-tuikit-atomic-x/lib/module/atomic-x/state/LiveSeatState';import ParticipantOverlay from '../../components/ParticipantOverlay';export default function YourAnchorScreen({ route, navigation }) {const { liveID } = route.params || {};const { seatList, canvas } = useLiveSeatState(liveID);return (// page-container<View style={styles.pageContainer}>{/* live-container */}<View style={styles.liveContainer}>{/* Video rendering layer */}<LiveCoreViewliveID={liveID}coreViewType="pushView" // Host: pushView, Audience: playViewstyle={styles.videoLayer}/>{/* UI overlay layer */}<ParticipantOverlay seatList={seatList} canvas={canvas} /></View>{/* Additional UI elements, such as the bottom control bar */}{/* <View style={styles.bottomControls}>...</View> */}</View>);}const styles = StyleSheet.create({pageContainer: {flex: 1,backgroundColor: '#000',},liveContainer: {flex: 1,},videoLayer: {flex: 1,},});
State | Description | API Documentation |
DeviceState | Controls audio/video devices: microphone (on/off/volume), camera (on/off/switch/quality), screen sharing, and real-time device status. | |
CoGuestState | Manages audience co-hosting: requests, invitations, accept/reject actions, member permissions (microphone/camera), and state sync. | |
LiveSeatState | Manages seat information: seat list and seat order. |
ParticipantOverlay component as described in "Enhancing UI Details," the nickname bar or avatar placeholder does not align correctly with the video window, causing offset or size mismatches.participant.region.x * scale.scaleX. If the scaling ratio is wrong, the UI will be misaligned.ParticipantOverlay must receive the canvas object (server-side canvas dimensions) from useLiveSeatState(liveID). The component will calculate the scaling ratio automatically: scaleX = screen width / canvas.w.CoGuestState methods such as applyForSeat, acceptApplication, inviteToSeat, etc., no actions occur and callbacks are not received.CoGuestState features are tied to a specific live room. The most frequent issue is an incorrect, null, or mismatched liveID when initializing or invoking CoGuestState.liveID: When using useCoGuestState(liveID) and related methods (e.g., applyForSeat({ liveID, ... })), ensure the liveID matches the actual live room.liveID consistent: Use the same liveID for all CoGuestState operations throughout the page lifecycle.CoGuestState only moves the user onto the seat; it does not automatically enable media devices.isAccept is true in onGuestApplicationResponded, or after calling acceptInvitation), manually invoke DeviceState methods to turn on your media devices.フィードバック