import React, { Component } from 'react'; import { styles } from '../styles'; import { defineMessages, injectIntl } from 'react-intl'; import { log } from '/imports/ui/services/api'; import { notify } from '/imports/ui/services/notification'; import { toast } from 'react-toastify'; import { styles as mediaStyles } from '/imports/ui/components/media/styles'; import Toast from '/imports/ui/components/toast/component'; import _ from 'lodash'; import VideoElement from '../video-element/component'; const INITIAL_SHARE_WAIT_TIME = 2000; const intlMessages = defineMessages({ chromeExtensionError: { id: 'app.video.chromeExtensionError', description: 'Error message for Chrome Extension not installed', }, chromeExtensionErrorLink: { id: 'app.video.chromeExtensionErrorLink', description: 'Error message for Chrome Extension not installed', }, }); class VideoDock extends Component { constructor(props) { super(props); this.state = { videos: {}, sharingWebcamBefore: false, userNames: {}, }; } componentDidMount() { const { users, userId } = this.props; users.forEach((user) => { if (user.has_stream && user.userId !== userId) { // FIX: Really ugly hack, but sometimes the ICE candidates aren't // generated properly when we send videos right after componentDidMount setTimeout(() => { this.start(user.userId); }, INITIAL_SHARE_WAIT_TIME); } }); document.addEventListener('installChromeExtension', this.installChromeExtension.bind(this)); window.addEventListener('resize', this.adjustVideos); window.addEventListener('orientationchange', this.adjustVideos); } componentWillUnmount() { window.removeEventListener('resize', this.adjustVideos); window.removeEventListener('orientationchange', this.adjustVideos); document.removeEventListener('installChromeExtension', this.installChromeExtension.bind(this)); } componentDidUpdate() { this.adjustVideos(); } notifyError(message) { notify(message, 'error', 'video'); } installChromeExtension() { console.log(intlMessages); const { intl } = this.props; const CHROME_EXTENSION_LINK = Meteor.settings.public.kurento.chromeExtensionLink; this.notifyError(
{intl.formatMessage(intlMessages.chromeExtensionError)}{' '} {intl.formatMessage(intlMessages.chromeExtensionErrorLink)}
); } // TODO // Find a better place to put this piece of code adjustVideos() { setTimeout(() => { window.adjustVideos('webcamArea', true, mediaStyles.moreThan4Videos, mediaStyles.container, mediaStyles.overlayWrapper, 'presentationAreaData', 'screenshareVideo'); }, 0); } createVideoTag(id) { let newState = {...this.state}; newState.videos[id] = true; this.setState(newState); } destroyVideoTag(id) { const { videos } = this.state; if (id !== this.props.userId) { this.setState({ videos: _.omit(videos, id) }); } console.log(_.omit(videos, id)); } start(id) { const { users } = this.props; log('info', `Starting video call for video: ${id}`); this.createVideoTag(id); } stop(id) { this.destroyVideoTag(id); this.props.onStop(id); } stopMany(ids) { const { videos } = this.state; ids.forEach((id) => { if (id === this.props.userId) { this.setState({ sharingWebcamBefore: true }); delete ids[id]; } }); this.setState({ videos: _.omit(videos, ids) }); ids.forEach((id) => { this.props.onStop(id); }); } getNameFromId(id) { const { users } = this.props; let name = users.find(u => u.userId === id).name; return name; } render() { return (
{Object.keys(this.state.videos).map(id => ( ))} {this.props.sharedWebcam && }
); } shouldComponentUpdate(nextProps, nextState) { const { userId, sharedWebcam, socketOpen } = this.props; const currentUsers = this.props.users || {}; const nextUsers = nextProps.users; const users = {}; const present = {}; sharedWebcam = sharedWebcam || false; // If disconnected, stop all webcams if (socketOpen && !nextProps.socketOpen) { this.stopMany(nextUsers.filter(u => u.has_stream).map(u => u.userId)); } // When reconnecting, restart all webcams. if (!socketOpen && nextProps.socketOpen) { nextUsers.forEach((user) => { if (user.has_stream && user.userId != userId) { this.start(user.userId); } }); if (this.state.sharingWebcamBefore){ this.props.onShareWebcam(this); this.setState({ sharingWebcamBefore: false }); } } // If the user un-shared a webcam we'll stop it if (sharedWebcam !== nextProps.sharedWebcam && !nextProps.sharedWebcam) { this.stop(userId); } if (!currentUsers) { return false; } // Map user objects to an object in the form {userId: has_stream} currentUsers.forEach((user) => { users[user.userId] = user.has_stream; }); // Keep instances where the flag has changed or next user adds it nextUsers.forEach((user) => { const id = user.userId; // The case when a user exists and stream status has not changed if (users[id] === user.has_stream) { delete users[id]; } else { // Case when a user has been added to the list users[id] = user.has_stream; } // Mark the ids which are present in nextUsers present[id] = true; }); const userIds = Object.keys(users); for (let i = 0; i < userIds.length; i++) { const id = userIds[i]; // If a userId is not present in nextUsers let's stop it if (!present[id]) { this.stop(id); continue; } console.log(`User ${users[id] ? '' : 'un'}shared webcam ${id}`); // If a user stream is true, changed and was shared by other // user we'll start it. If it is false and changed we stop it if (users[id]) { if (userId !== id) { this.start(id); } } else { this.stop(id); } } return true; } } export default injectIntl(VideoDock);