2016-05-01 18:35:18 +00:00
|
|
|
/* global $, config, interfaceConfig, APP, JitsiMeetJS */
|
2017-07-14 19:22:27 +00:00
|
|
|
|
|
|
|
/* eslint-disable no-unused-vars */
|
|
|
|
import React, { Component } from 'react';
|
|
|
|
import ReactDOM from 'react-dom';
|
|
|
|
import { Provider } from 'react-redux';
|
|
|
|
|
|
|
|
import { VideoTrack } from '../../../react/features/base/media';
|
|
|
|
/* eslint-enable no-unused-vars */
|
|
|
|
|
2016-11-11 15:00:54 +00:00
|
|
|
const logger = require("jitsi-meet-logger").getLogger(__filename);
|
|
|
|
|
2015-12-14 12:26:50 +00:00
|
|
|
import UIEvents from "../../../service/UI/UIEvents";
|
|
|
|
import SmallVideo from "./SmallVideo";
|
|
|
|
|
2016-01-06 22:39:13 +00:00
|
|
|
const TrackEvents = JitsiMeetJS.events.track;
|
|
|
|
|
2015-12-01 12:53:01 +00:00
|
|
|
function LocalVideo(VideoLayout, emitter) {
|
2015-06-23 08:00:46 +00:00
|
|
|
this.videoSpanId = "localVideoContainer";
|
2017-06-30 17:40:55 +00:00
|
|
|
|
|
|
|
this.container = this.createContainer();
|
2017-06-30 23:07:37 +00:00
|
|
|
this.$container = $(this.container);
|
2017-06-30 17:40:55 +00:00
|
|
|
$("#filmstripLocalVideo").append(this.container);
|
|
|
|
|
2016-05-07 01:50:37 +00:00
|
|
|
this.localVideoId = null;
|
2016-10-26 20:45:51 +00:00
|
|
|
this.bindHoverHandler();
|
2016-05-09 17:39:42 +00:00
|
|
|
if(config.enableLocalVideoFlip)
|
|
|
|
this._buildContextMenu();
|
2015-07-15 10:14:34 +00:00
|
|
|
this.isLocal = true;
|
2015-12-01 12:53:01 +00:00
|
|
|
this.emitter = emitter;
|
2017-08-14 15:02:58 +00:00
|
|
|
this.statsPopoverLocation = interfaceConfig.VERTICAL_FILMSTRIP
|
|
|
|
? 'left bottom' : 'top center';
|
|
|
|
|
2016-02-09 10:19:43 +00:00
|
|
|
Object.defineProperty(this, 'id', {
|
|
|
|
get: function () {
|
2016-07-08 01:44:04 +00:00
|
|
|
return APP.conference.getMyUserId();
|
2016-02-09 10:19:43 +00:00
|
|
|
}
|
|
|
|
});
|
2016-08-08 22:03:00 +00:00
|
|
|
this.initBrowserSpecificProperties();
|
|
|
|
|
2016-03-15 20:42:53 +00:00
|
|
|
SmallVideo.call(this, VideoLayout);
|
2016-08-31 19:18:09 +00:00
|
|
|
|
|
|
|
// Set default display name.
|
|
|
|
this.setDisplayName();
|
|
|
|
|
2016-09-28 21:31:40 +00:00
|
|
|
this.addAudioLevelIndicator();
|
2017-07-05 18:17:30 +00:00
|
|
|
this.updateIndicators();
|
2015-06-23 08:00:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
LocalVideo.prototype = Object.create(SmallVideo.prototype);
|
|
|
|
LocalVideo.prototype.constructor = LocalVideo;
|
|
|
|
|
2017-06-30 17:40:55 +00:00
|
|
|
LocalVideo.prototype.createContainer = function () {
|
|
|
|
const containerSpan = document.createElement('span');
|
|
|
|
containerSpan.classList.add('videocontainer');
|
|
|
|
containerSpan.id = this.videoSpanId;
|
|
|
|
|
|
|
|
containerSpan.innerHTML = `
|
|
|
|
<div class = 'videocontainer__background'></div>
|
|
|
|
<span id = 'localVideoWrapper'></span>
|
|
|
|
<div class = 'videocontainer__toolbar'></div>
|
|
|
|
<div class = 'videocontainer__toptoolbar'></div>
|
|
|
|
<div class = 'videocontainer__hoverOverlay'></div>
|
|
|
|
<div class = 'displayNameContainer'></div>
|
|
|
|
<div class = 'avatar-container'></div>`;
|
|
|
|
|
|
|
|
return containerSpan;
|
|
|
|
};
|
|
|
|
|
2015-06-23 08:00:46 +00:00
|
|
|
/**
|
|
|
|
* Sets the display name for the given video span id.
|
|
|
|
*/
|
2016-10-03 16:12:04 +00:00
|
|
|
LocalVideo.prototype.setDisplayName = function(displayName) {
|
2015-06-23 08:00:46 +00:00
|
|
|
if (!this.container) {
|
2016-11-11 15:00:54 +00:00
|
|
|
logger.warn(
|
2015-07-28 21:52:32 +00:00
|
|
|
"Unable to set displayName - " + this.videoSpanId +
|
|
|
|
" does not exist");
|
2015-06-23 08:00:46 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-06-29 03:35:43 +00:00
|
|
|
this.updateDisplayName({
|
|
|
|
allowEditing: true,
|
|
|
|
displayName: displayName,
|
|
|
|
displayNameSuffix: interfaceConfig.DEFAULT_LOCAL_DISPLAY_NAME,
|
|
|
|
elementID: 'localDisplayName',
|
|
|
|
participantID: this.id
|
|
|
|
});
|
2015-07-28 21:52:32 +00:00
|
|
|
};
|
2015-06-23 08:00:46 +00:00
|
|
|
|
2015-12-14 12:26:50 +00:00
|
|
|
LocalVideo.prototype.changeVideo = function (stream) {
|
2016-01-29 00:33:27 +00:00
|
|
|
this.videoStream = stream;
|
2015-06-23 08:00:46 +00:00
|
|
|
|
2015-12-14 12:26:50 +00:00
|
|
|
let localVideoClick = (event) => {
|
2017-08-14 15:02:58 +00:00
|
|
|
// TODO Checking the classes is a workround to allow events to bubble
|
2017-06-29 03:35:43 +00:00
|
|
|
// into the DisplayName component if it was clicked. React's synthetic
|
|
|
|
// events will fire after jQuery handlers execute, so stop propogation
|
|
|
|
// at this point will prevent DisplayName from getting click events.
|
|
|
|
// This workaround should be removeable once LocalVideo is a React
|
|
|
|
// Component because then the components share the same eventing system.
|
2017-08-14 15:02:58 +00:00
|
|
|
const $source = $(event.target || event.srcElement);
|
2017-06-29 03:35:43 +00:00
|
|
|
const { classList } = event.target;
|
2017-08-14 15:02:58 +00:00
|
|
|
|
|
|
|
const clickedOnDisplayName
|
|
|
|
= $source.parents('.displayNameContainer').length > 0;
|
|
|
|
const clickedOnPopover
|
|
|
|
= $source.parents('.connection-info').length > 0;
|
|
|
|
const clickedOnPopoverTrigger
|
|
|
|
= $source.parents('.popover-trigger').length > 0
|
|
|
|
|| classList.contains('popover-trigger');
|
|
|
|
|
|
|
|
const ignoreClick = clickedOnDisplayName
|
|
|
|
|| clickedOnPopoverTrigger
|
|
|
|
|| clickedOnPopover;
|
2017-06-29 03:35:43 +00:00
|
|
|
|
2015-07-10 09:57:20 +00:00
|
|
|
// FIXME: with Temasys plugin event arg is not an event, but
|
|
|
|
// the clicked object itself, so we have to skip this call
|
2017-08-14 15:02:58 +00:00
|
|
|
if (event.stopPropagation && !ignoreClick) {
|
2015-07-10 09:57:20 +00:00
|
|
|
event.stopPropagation();
|
|
|
|
}
|
2017-06-29 03:35:43 +00:00
|
|
|
|
2017-08-14 15:02:58 +00:00
|
|
|
if (!ignoreClick) {
|
2017-06-29 03:35:43 +00:00
|
|
|
this.VideoLayout.handleVideoThumbClicked(this.id);
|
|
|
|
}
|
2015-12-14 12:26:50 +00:00
|
|
|
};
|
2015-06-23 08:00:46 +00:00
|
|
|
|
2017-06-30 23:07:37 +00:00
|
|
|
this.$container.off('click');
|
|
|
|
this.$container.on('click', localVideoClick);
|
2015-06-23 08:00:46 +00:00
|
|
|
|
2017-07-14 19:22:27 +00:00
|
|
|
this.localVideoId = 'localVideo_' + stream.getId();
|
2015-06-23 08:00:46 +00:00
|
|
|
|
|
|
|
var localVideoContainer = document.getElementById('localVideoWrapper');
|
|
|
|
|
2017-07-14 19:22:27 +00:00
|
|
|
/* jshint ignore:start */
|
|
|
|
ReactDOM.render(
|
|
|
|
<Provider store = { APP.store }>
|
|
|
|
<VideoTrack
|
|
|
|
id = { this.localVideoId }
|
|
|
|
videoTrack = {{ jitsiTrack: stream }} />
|
|
|
|
</Provider>,
|
|
|
|
localVideoContainer
|
|
|
|
);
|
|
|
|
/* jshint ignore:end */
|
2015-06-23 08:00:46 +00:00
|
|
|
|
2016-05-07 01:50:37 +00:00
|
|
|
let isVideo = stream.videoType != "desktop";
|
|
|
|
this._enableDisableContextMenu(isVideo);
|
|
|
|
this.setFlipX(isVideo? APP.settings.getLocalFlipX() : false);
|
2015-06-23 08:00:46 +00:00
|
|
|
|
2016-01-06 22:39:13 +00:00
|
|
|
let endedHandler = () => {
|
2017-07-14 19:22:27 +00:00
|
|
|
// Only remove if there is no video and not a transition state.
|
|
|
|
// Previous non-react logic created a new video element with each track
|
|
|
|
// removal whereas react reuses the video component so it could be the
|
|
|
|
// stream ended but a new one is being used.
|
|
|
|
if (this.videoStream.isEnded()) {
|
|
|
|
ReactDOM.unmountComponentAtNode(localVideoContainer);
|
|
|
|
}
|
|
|
|
|
2016-03-23 22:45:27 +00:00
|
|
|
// when removing only the video element and we are on stage
|
|
|
|
// update the stage
|
2017-06-15 15:01:32 +00:00
|
|
|
if (this.isCurrentlyOnLargeVideo()) {
|
2017-07-10 10:06:48 +00:00
|
|
|
this.VideoLayout.updateLargeVideo(this.id);
|
2017-06-15 15:01:32 +00:00
|
|
|
}
|
2016-03-03 12:53:36 +00:00
|
|
|
stream.off(TrackEvents.LOCAL_TRACK_STOPPED, endedHandler);
|
2016-01-06 22:39:13 +00:00
|
|
|
};
|
2016-03-03 12:53:36 +00:00
|
|
|
stream.on(TrackEvents.LOCAL_TRACK_STOPPED, endedHandler);
|
2015-07-20 17:32:04 +00:00
|
|
|
};
|
2015-06-23 08:00:46 +00:00
|
|
|
|
2016-05-01 18:35:18 +00:00
|
|
|
/**
|
|
|
|
* Shows or hides the local video container.
|
|
|
|
* @param {boolean} true to make the local video container visible, false
|
|
|
|
* otherwise
|
|
|
|
*/
|
|
|
|
LocalVideo.prototype.setVisible = function(visible) {
|
|
|
|
|
|
|
|
// We toggle the hidden class as an indication to other interested parties
|
|
|
|
// that this container has been hidden on purpose.
|
2017-06-30 23:07:37 +00:00
|
|
|
this.$container.toggleClass("hidden");
|
2016-05-01 18:35:18 +00:00
|
|
|
|
|
|
|
// We still show/hide it as we need to overwrite the style property if we
|
|
|
|
// want our action to take effect. Toggling the display property through
|
|
|
|
// the above css class didn't succeed in overwriting the style.
|
|
|
|
if (visible) {
|
2017-06-30 23:07:37 +00:00
|
|
|
this.$container.show();
|
2016-05-01 18:35:18 +00:00
|
|
|
}
|
|
|
|
else {
|
2017-06-30 23:07:37 +00:00
|
|
|
this.$container.hide();
|
2016-05-01 18:35:18 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2016-05-07 01:50:37 +00:00
|
|
|
/**
|
|
|
|
* Sets the flipX state of the video.
|
|
|
|
* @param val {boolean} true for flipped otherwise false;
|
|
|
|
*/
|
|
|
|
LocalVideo.prototype.setFlipX = function (val) {
|
|
|
|
this.emitter.emit(UIEvents.LOCAL_FLIPX_CHANGED, val);
|
|
|
|
if(!this.localVideoId)
|
|
|
|
return;
|
|
|
|
if(val) {
|
|
|
|
this.selectVideoElement().addClass("flipVideoX");
|
|
|
|
} else {
|
|
|
|
this.selectVideoElement().removeClass("flipVideoX");
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Builds the context menu for the local video.
|
|
|
|
*/
|
|
|
|
LocalVideo.prototype._buildContextMenu = function () {
|
|
|
|
$.contextMenu({
|
|
|
|
selector: '#' + this.videoSpanId,
|
|
|
|
zIndex: 10000,
|
|
|
|
items: {
|
|
|
|
flip: {
|
|
|
|
name: "Flip",
|
|
|
|
callback: () => {
|
|
|
|
let val = !APP.settings.getLocalFlipX();
|
|
|
|
this.setFlipX(val);
|
|
|
|
APP.settings.setLocalFlipX(val);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
events: {
|
|
|
|
show : function(options){
|
|
|
|
options.items.flip.name =
|
2016-10-21 17:11:22 +00:00
|
|
|
APP.translation.generateTranslationHTML(
|
|
|
|
"videothumbnail.flip");
|
2016-05-07 01:50:37 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Enables or disables the context menu for the local video.
|
|
|
|
* @param enable {boolean} true for enable, false for disable
|
|
|
|
*/
|
|
|
|
LocalVideo.prototype._enableDisableContextMenu = function (enable) {
|
2017-06-30 23:07:37 +00:00
|
|
|
if(this.$container.contextMenu)
|
|
|
|
this.$container.contextMenu(enable);
|
2016-05-07 01:50:37 +00:00
|
|
|
};
|
|
|
|
|
2015-12-14 12:26:50 +00:00
|
|
|
export default LocalVideo;
|