import React, { useState, useEffect } from 'react'; import { Row } from 'react-bootstrap'; import { OverlayScrollbarsComponent } from 'overlayscrollbars-react'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { faExpandAlt, faCompressAlt, faWindowMinimize, faWindowMaximize, } from '@fortawesome/free-solid-svg-icons'; import Player from 'components/video-player/video-player'; import './video-chat.css'; function getName(peerId) { const { sync } = window; if (peerId in sync.profiles) { return sync.profiles[peerId].username; } return ''; } const VideoChat = () => { const [myPeerId, setMyPeerId] = useState( window.sync?.peerId || null ); const [streams, setStreams] = useState(window.sync?.streams || {}); const [localStream, setLocalStream] = useState(null); const [focusStream, setFocusStream] = useState(null); const [focusId, setFocusId] = useState(null); const [uiCompressed, setUiCompressed] = useState(false); const [uiMinimized, setUiMinimized] = useState(false); // Media stream helper const getMediaStream = () => { window.sync .getUserMedia() .then(stream => { // don't want our audio echo setLocalStream(new MediaStream(stream.getVideoTracks())); }) .catch(console.warn); }; // Sync listeners const addStream = ({ peerId, stream }) => { setStreams({ ...streams, [peerId]: stream, }); }; const updatePeers = () => { setStreams(window.sync.streams); }; useEffect(() => { const { sync } = window; // sync object event listeners sync.on('ready', updatePeers); sync.on('peers', updatePeers); sync.on('stream', ({ peerId, stream }) => { addStream({ peerId, stream }); setFocusStream(stream); setFocusId(peerId); }); sync.on('peerId', setMyPeerId); // get user media getMediaStream(); return () => { try { window.sync.releaseUserMedia(); } catch (err) { console.warn('error while releasing user stream', err); } }; }, []); if (uiMinimized && !uiCompressed) setUiCompressed(true); if (myPeerId) streams[myPeerId] = localStream; return ( <div className={`video-chat-window ${(uiCompressed && 'compressed') || ''} ${(uiMinimized && 'minimized') || ''}`} > <div className="title-bar align-right"> <FontAwesomeIcon icon={uiCompressed ? faExpandAlt : faCompressAlt} pull="right" className="mr-1" onClick={() => { setUiCompressed(!uiCompressed); setUiMinimized(false); }} /> <FontAwesomeIcon icon={uiMinimized ? faWindowMaximize : faWindowMinimize} pull="right" size="xs" onClick={() => { setUiMinimized(!uiMinimized); }} /> </div> <div className="video-chat-container"> <OverlayScrollbarsComponent options={{ autoUpdate: true, resize: uiCompressed ? 'vertical' : 'both', scrollbars: { autoHide: 'leave' }, className: 'os-theme-light', }} > <Row className="focus-stream-row d-flex justify-content-center"> <Player className="focus-stream" showControls={ focusStream === null ? false : focusStream !== localStream } stream={focusStream || localStream} name={getName(focusId)} /> </Row> <Row className="peer-stream-row d-flex justify-content-center" noGutters > {Object.keys(streams).map(id => { if ( focusStream === streams[id] || (!focusStream && streams[id] === localStream) ) return ''; return ( <Player key={id} showControls={streams[id] !== localStream} width="" className="peer-stream" stream={streams[id]} name={getName(id)} onClick={() => { setFocusStream(streams[id]); setFocusId(id); }} /> ); })} </Row> </OverlayScrollbarsComponent> </div> </div> ); }; export default VideoChat;