bendwidth / frontend / src / videoComponents / VideoButton.js
VideoButton.js
Raw
import { useDispatch, useSelector } from "react-redux";
import { updateCallStatus } from "../redux-elements/callStatus";
import pair from "../redux-elements/pair";
import { useEffect, useState } from "react";
import CaretDropdown from "./CaretDropdown";
import getDevices from "./getDevices";

const VideoButton = ({smallFeedEl}) => {
    const dispatch = useDispatch();
    const callStatus = useSelector(state => state.callStatus);

    // keeps track of wether the user media is still pending.
    const [mediaStillPending, setMediaStillPending] = useState(false);

    const [caretOpen, setCaretOpen] = useState(false);

    const [videoDeviceList, setVideoDeviceList] = useState([]);

    const changeVideoDevice = async (e) => {
        const deviceId = e.target.value;
        const newConstraints = {
            audio: callStatus.audioDevice === "default" ? true : { exact: callStatus.audioDevice},
            video: {deviceId: {exact : deviceId}},
        }
        const stream = await navigator.mediaDevices.getUserMedia(newConstraints);

        dispatch(updateCallStatus(pair('videoDevice', deviceId)));
        smallFeedEl.current.srcObject = stream;
        dispatch(updateCallStatus(pair('localStream', stream)));
        dispatch(updateCallStatus(pair('video', 'enabled')))
        const [videoTrracks] = stream.getVideoTracks()
    }
    
    // function to start/stop video
    const toggleVideo = () => {
        if (callStatus.video === "enabled") {
            // stop the video.
            const tracks = callStatus.localStream.getVideoTracks();
            tracks.forEach(t => t.enabled = false);

            // update redux state
            dispatch(updateCallStatus(pair('video', 'disabled')));
        } else if (callStatus.video === "disabled") {
            const tracks = callStatus.localStream.getVideoTracks();
            tracks.forEach(t => t.enabled = true);
            dispatch(updateCallStatus(pair('video', 'enabled')));
        } else if (callStatus.hasMedia) {
            smallFeedEl.current.srcObject = callStatus.localStream;

            // add local tracks to the peer connection.
            callStatus.localStream.getVideoTracks().forEach(t => {
                callStatus.peerConnection.addTrack(t, callStatus.localStream);
            })

            // update redux state
            dispatch(updateCallStatus(pair('video', 'enabled')));
        } else {
            // the video is off and we dont have user media yet, wait for user media.
            setMediaStillPending(true)
        }
    }

    // get all the users video devices.
    useEffect(() => {
        const getDevicesAsync = async () => {
             if (caretOpen) {
                 const devices = await getDevices(); 
                 setVideoDeviceList(devices.videoDevices);
            }
        }
        getDevicesAsync();
       
    }, [caretOpen])

    useEffect(() => {
        if (mediaStillPending && callStatus.hasMedia) {
            console.log("media is now available") 
            setMediaStillPending(false);
            toggleVideo();
        }

    // eslint-disable-next-line react-hooks/exhaustive-deps
    }, [mediaStillPending, callStatus.hasMedia])

    return (<div className="button-wrapper no-border-left" onClick={toggleVideo}>
        <i className="fa fa-caret-down choose-video" onClick={(e) => {
            e.stopPropagation();
            setCaretOpen(!caretOpen)
        }
        }></i>
       <i className={`fa fa-video${callStatus.video === 'enabled' ? '': '-slash'}`}></i>
        <div className="btn-text">{callStatus.video === 'enabled' ? "Stop" : "Start"} Video</div>
        {caretOpen ? <CaretDropdown defaultValue={callStatus.videoDevice}
            changeHandler={changeVideoDevice}
            deviceList={videoDeviceList}
            type="video"
        /> : <></>}
            </div>)
}
export default VideoButton;