import { h, Component } from 'preact'; import PropTypes from 'prop-types'; import { getTwilioToken } from './actions'; /** * TODO: Instead of calling this function in render, use jsx (). */ function VideoControlButton({ btnClassName, btnClickCallback, btnLabel }) { return ( ); } VideoControlButton.propTypes = { btnClassName: PropTypes.string.isRequired, btnClickCallback: PropTypes.func.isRequired, btnLabel: PropTypes.string.isRequired, }; export default class Video extends Component { static propTypes = { activeChannelId: PropTypes.string.isRequired, onToggleSound: PropTypes.func.isRequired, onToggleVideo: PropTypes.func.isRequired, onExit: PropTypes.func.isRequired, soundOn: PropTypes.bool.isRequired, videoOn: PropTypes.bool.isRequired, }; constructor(props) { super(props); let leftPx = 40; let topPx = 70; if (window.innerWidth > 1500) { leftPx = window.innerWidth / 5 + 200; topPx = window.innerHeight / 10; } else if (window.innerWidth > 641) { leftPx = window.innerWidth / 6; topPx = window.innerHeight / 10; } this.state = { leftPx, topPx, pageX: null, pageY: null, room: null, participants: [], }; } componentDidMount() { const { activeChannelId } = this.props; getTwilioToken( `private-video-channel-${activeChannelId}`, this.setupCallChannel, ); } componentWillUnmount() { const { room } = this.state; room.disconnect(); } setupCallChannel = response => { const component = this; const { activeChannelId } = this.props; import('twilio-video').then(({ connect, createLocalVideoTrack }) => { connect( response.token, { name: `private-video-channel-${activeChannelId}`, audio: true, type: 'peer-to-peer', video: { width: 640 }, }, ).then( function onConnectSuccess(room) { component.setState({ token: response.token, room }); createLocalVideoTrack().then(track => { const localMediaContainer = document.getElementById( 'videolocalscreen', ); localMediaContainer.appendChild(track.attach()); }); const roomParticipants = []; room.participants.forEach(participant => { component.triggerRemoteJoin(participant); roomParticipants.push(participant); }); component.setState({ participants: roomParticipants }); room.on('participantConnected', function onParticipantConnected( participant, ) { component.props.onParticipantChange(room.participants); component.triggerRemoteJoin(participant); room.participants.forEach(p => { roomParticipants.push(p); }); component.setState({ participants: roomParticipants }); room.on( 'participantDisconnected', function onParticipantDisconnected() { component.props.onParticipantChange(room.participants); }, ); participant.on('dominantSpeakerChanged', dominantSpeaker => { // eslint-disable-next-line no-console console.log( 'The new dominant speaker in the Room is:', dominantSpeaker, ); }); }); }, function onConnectFailure(error) { document.getElementById('videoremotescreen').innerHTML = ''; // eslint-disable-next-line no-console console.error(`Unable to connect to Room: ${error.message}`); }, ); }); }; triggerRemoteJoin = participant => { participant.on('trackAdded', track => { if (!document.getElementById(`${track.kind}-${track.id}`)) { const trackDiv = document.createElement('div'); trackDiv.className = `chat__videocalltrackdiv--${track.kind}`; trackDiv.appendChild(track.attach()); document.getElementById('videoremotescreen').appendChild(trackDiv); document.getElementById( 'videoremotescreen', ).lastChild.id = `${track.kind}-${track.id}`; } }); participant.on('trackRemoved', track => { if (document.getElementById(track.id)) { document.getElementById(track.id).outerHTML = ''; } }); // participant.on('trackDisabled', track => { // console.log('disabled') // console.log('TODO: Show track status on video') // console.log(track.mediaStreamTrack.id) // }); // participant.on('trackEnabled', track => { // console.log('enabled') // console.log('TODO: Show track status on video') // console.log(track.mediaStreamTrack.id) // }); }; handleDrag = e => { const { pageX, offsetDiffX, pageY, offsetDiffY } = this.state; if (!pageX) { this.setState({ pageX: e.pageX, pageY: e.pageY, offsetDiffX: e.pageX - e.target.offsetLeft, offsetDiffY: e.pageY - e.target.offsetTop, }); } else if (e.pageX !== 0) { this.setState({ leftPx: pageX + e.pageX - pageX - offsetDiffX, topPx: pageY + e.pageY - pageY - offsetDiffY, }); } else if (e.pageX === 0) { this.setState({ pageX: null, pageY: null, offsetDiffX: null, offsetDiffY: null, }); } }; toggleSound = () => { const { room } = this.state; const { onToggleSound } = this.props; if (room) { room.localParticipant.audioTracks.forEach(track => { if (track.isEnabled) { track.disable(); } else { track.enable(); } }); } onToggleSound(); }; toggleVideo = () => { const { room } = this.state; const { onToggleVideo } = this.props; if (room) { room.localParticipant.videoTracks.forEach(track => { if (track.isEnabled) { track.disable(); } else { track.enable(); } }); } onToggleVideo(); }; render() { const { topPx, leftPx, participants } = this.state; const { onExit, soundOn, videoOn } = this.props; return (
{VideoControlButton({ btnClassName: 'chat__videocallexitbutton', btnClickCallback: onExit, btnLabel: '×', })} {VideoControlButton({ btnClassName: 'chat__videocallcontrolbutton', btnClickCallback: this.toggleSound, btnLabel: soundOn ? 'Mute' : 'UnMute', })} {VideoControlButton({ btnClassName: 'chat__videocallcontrolbutton chat__videocallcontrolbutton--videoonoff', btnClickCallback: this.toggleVideo, btnLabel: videoOn ? 'Turn Off Video' : 'Turn On Video', })}
); } }