import React, {useState, useRef, useEffect} from ‘react’
import {
View,
Alert,
StyleSheet,
BackHandler,
TouchableOpacity,
ActivityIndicator,
EmitterSubscription,
Text,
} from ‘react-native’
import {
useZoom,
ZoomVideoSdkUser,
EventType,
VideoAspect,
ZoomView,
} from ‘@zoom/react-native-videosdk’
import Icon from ‘react-native-vector-icons/MaterialIcons’
import {useNavigation, useFocusEffect} from ‘@react-navigation/native’
import {useDispatch, useSelector} from ‘react-redux’
import {resetZoomSession} from ‘…/…/store/actions/homeActions’
import {navigate} from ‘…/…/navigation/navigation-helpers’
import {
selectedZoomSession,
selectHomeError,
} from ‘…/…/store/selector/homeSelectors’
import {showErrorMessage} from ‘…/…/app-base/utils’
const CustomZoomSessionHandler = ({route, onSessionJoin, onSessionLeave}) => {
const {appointment} = route.params
const dispatch = useDispatch()
const zoom = useZoom()
const listeners = useRef<EmitterSubscription>()
const [sessionEnded, setSessionEnded] = useState(false)
const [isLoading, setIsLoading] = useState(true)
const [isInSession, setIsInSession] = useState(false)
const [isAudioMuted, setIsAudioMuted] = useState(true)
const [isVideoMuted, setIsVideoMuted] = useState(true)
const [users, setUsersInSession] = useState()
const zoomSessionData = useSelector(selectedZoomSession)
const error = useSelector(selectHomeError)
const clearListeners = () => {
listeners.current.forEach((listener) => listener.remove())
listeners.current =
}
useFocusEffect(
React.useCallback(() => {
const joinZoomSession = async () => {
if (zoomSessionData && !error && !isInSession && !sessionEnded) {
try {
setIsLoading(true)
const {session, token} = zoomSessionData
await zoom.joinSession({
sessionName: session,
sessionPassword: ‘’,
token,
userName: appointment.patientName,
audioOptions: {
connect: true,
mute: true,
autoAdjustSpeakerVolume: false,
},
videoOptions: {localVideoOn: true},
sessionIdleTimeoutMins: 0,
})
} catch (joinError) {
setIsLoading(false)
dispatch(resetZoomSession())
}
// Listener for session join
listeners.current.push(
zoom.addListener(EventType.onSessionJoin, async () => {
const localUser = new ZoomVideoSdkUser(
await zoom.session.getMySelf(),
)
const remoteUsers = await zoom.session.getRemoteUsers()
setUsersInSession([
localUser,
...remoteUsers.map((user) => new ZoomVideoSdkUser(user)),
])
setIsInSession(true)
setIsLoading(false) // Stop loading when session is joined
onSessionJoin && onSessionJoin([localUser, ...remoteUsers])
const isMuted = await localUser.audioStatus.isMuted()
const videoOn = await localUser.videoStatus.isOn()
setIsAudioMuted(isMuted)
setIsVideoMuted(!videoOn)
}),
)
// Listener for user join
listeners.current.push(
zoom.addListener(EventType.onUserJoin, async (event) => {
const {joinedUsers, remoteUsers} = event
const allUsers = [...joinedUsers, ...remoteUsers]
const uniqueUsers = Array.from(
new Map(
allUsers.map((user) => [
user.userId,
new ZoomVideoSdkUser(user),
]),
).values(),
)
setUsersInSession(uniqueUsers)
if (!isInSession) {
setIsInSession(true)
setIsLoading(false)
onSessionJoin && onSessionJoin(uniqueUsers)
}
}),
)
// Listener for session leave
listeners.current.push(
zoom.addListener(EventType.onSessionLeave, () => {
leaveSession(false)
}),
)
}
}
// Join session if session data and no error
if (zoomSessionData && !error && !isInSession) {
joinZoomSession()
} else if (error) {
showErrorMessage(error ?? '')
dispatch(resetZoomSession())
navigate('Tab', {screen: 'Home'})
}
if (isInSession) {
setIsLoading(false)
}
const backHandler = BackHandler.addEventListener(
'hardwareBackPress',
handleBackPress,
)
return () => {
backHandler.remove()
clearListeners() // Ensure listeners are cleared when unmounting
}
}, [zoomSessionData, error, sessionEnded, isInSession, zoom]),
)
const handleBackPress = () => {
Alert.alert(
‘Leave Session’,
‘Are you sure you want to leave the session?’,
[
{text: ‘Cancel’, style: ‘cancel’},
{text: ‘Yes’, onPress: () => leaveSession()},
],
{cancelable: true},
)
return true
}
const leaveSession = async (notifyLeave = true) => {
if (!isInSession) return
try {
await zoom.leaveSession(false)
clearListeners()
setIsInSession(false)
setUsersInSession([])
setSessionEnded(true)
if (notifyLeave) {
onSessionLeave && onSessionLeave()
}
dispatch(resetZoomSession())
navigate('Tab', {screen: 'Home'})
} catch (error) {
console.error('Error leaving session:', error)
}
}
const toggleAudio = async () => {
const mySelf = await zoom.session.getMySelf()
const muted = await mySelf.audioStatus.isMuted()
if (muted) {
await zoom.audioHelper.unmuteAudio(mySelf.userId)
setIsAudioMuted(false)
} else {
await zoom.audioHelper.muteAudio(mySelf.userId)
setIsAudioMuted(true)
}
}
const toggleVideo = async () => {
const mySelf = await zoom.session.getMySelf()
const videoOn = await mySelf.videoStatus.isOn()
if (videoOn) {
await zoom.videoHelper.stopVideo()
setIsVideoMuted(true)
} else {
await zoom.videoHelper.startVideo()
setIsVideoMuted(false)
}
}
return (
{isLoading ? (
) : isInSession ? (
{/* Render co-host (Full screen) */}
{users
.filter((user) => user.isHost)
.map((user) => (
{user.userName}
))}
{/* Render host (Small bottom-right corner) */}
{users
.filter((user) => !user.isHost)
.map((user) => (
<View key={user.userId} style={styles.hostContainer}>
<View style={styles.userNameContainer}>
<Text style={styles.userNameText}>{user.userName}</Text>
</View>
<ZoomView
style={styles.hostView}
userId={user.userId}
videoAspect={VideoAspect.PanAndScan}
/>
</View>
))}
{/* Control icons */}
<ControlIcons
isAudioMuted={isAudioMuted}
isVideoMuted={isVideoMuted}
onToggleAudio={toggleAudio}
onToggleVideo={toggleVideo}
onLeaveSession={leaveSession}
/>
</View>
) : null}
</View>
)
}
const ControlIcons = ({
isAudioMuted,
isVideoMuted,
onToggleAudio,
onToggleVideo,
onLeaveSession,
}) => {
const handleLeaveSession = () => {
Alert.alert(
‘Leave Session’,
‘Are you certain you want to end the call? If so, the teleconsultation between you and the doctor will be terminated immediately and may not be able to be recalled.’,
[
{text: ‘Cancel’, style: ‘cancel’},
{text: ‘Confirm’, onPress: () => onLeaveSession()},
],
{cancelable: true},
)
}
return (
<Icon name={isAudioMuted ? ‘mic-off’ : ‘mic’} size={30} color=“#fff” />
<Icon
name={isVideoMuted ? ‘videocam-off’ : ‘videocam’}
size={30}
color=“#fff”
/>
)
}
export default CustomZoomSessionHandler
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: ‘center’,
backgroundColor: ‘#000’,
},
loadingContainer: {
…StyleSheet.absoluteFillObject,
alignItems: ‘center’,
justifyContent: ‘center’,
backgroundColor: ‘#000’,
},
hostContainer: {
position: ‘absolute’,
width: ‘30%’, // Adjust the width of the host view
height: ‘30%’, // Adjust the height of the host view
bottom: 20, // Add some spacing from the bottom
right: 20, // Add some spacing from the right
borderRadius: 10,
overflow: ‘hidden’,
backgroundColor: ‘rgba(0, 0, 0, 0.5)’,
},
coHostContainer: {
flex: 1, // Co-host view takes the entire screen
backgroundColor: ‘#000’,
},
hostView: {
flex: 1, // Host view content scales within the allocated container
},
coHostView: {
width: ‘100%’,
height: ‘100%’, // Co-host view covers the entire screen
},
userNameContainer: {
position: ‘absolute’,
top: 0, // Position the name at the top of the view
width: ‘100%’,
padding: 5,
backgroundColor: ‘rgba(0, 0, 0, 0.7)’,
alignItems: ‘center’,
zIndex: 1, // Ensure it appears above the video feed
},
userNameText: {
color: ‘#fff’,
fontSize: 14,
fontWeight: ‘bold’,
},
iconContainer: {
flexDirection: ‘row’,
justifyContent: ‘space-evenly’,
position: ‘absolute’,
bottom: 20,
width: ‘100%’,
backgroundColor: ‘rgba(0, 0, 0, 0.6)’,
paddingVertical: 10,
},
})
use this code and help me where i use wrong code. @ekaansh.zoom