You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

LocalVideo.js 6.6KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206
  1. /* global $, config, interfaceConfig, APP, JitsiMeetJS */
  2. /* eslint-disable no-unused-vars */
  3. import React, { Component } from 'react';
  4. import ReactDOM from 'react-dom';
  5. import { Provider } from 'react-redux';
  6. import { VideoTrack } from '../../../react/features/base/media';
  7. /* eslint-enable no-unused-vars */
  8. const logger = require("jitsi-meet-logger").getLogger(__filename);
  9. import UIEvents from "../../../service/UI/UIEvents";
  10. import SmallVideo from "./SmallVideo";
  11. const TrackEvents = JitsiMeetJS.events.track;
  12. function LocalVideo(VideoLayout, emitter) {
  13. this.videoSpanId = "localVideoContainer";
  14. this.container = $("#localVideoContainer").get(0);
  15. this.localVideoId = null;
  16. this.bindHoverHandler();
  17. if(config.enableLocalVideoFlip)
  18. this._buildContextMenu();
  19. this.isLocal = true;
  20. this.emitter = emitter;
  21. Object.defineProperty(this, 'id', {
  22. get: function () {
  23. return APP.conference.getMyUserId();
  24. }
  25. });
  26. this.initBrowserSpecificProperties();
  27. SmallVideo.call(this, VideoLayout);
  28. // Set default display name.
  29. this.setDisplayName();
  30. this.addAudioLevelIndicator();
  31. this.updateIndicators();
  32. }
  33. LocalVideo.prototype = Object.create(SmallVideo.prototype);
  34. LocalVideo.prototype.constructor = LocalVideo;
  35. /**
  36. * Sets the display name for the given video span id.
  37. */
  38. LocalVideo.prototype.setDisplayName = function(displayName) {
  39. if (!this.container) {
  40. logger.warn(
  41. "Unable to set displayName - " + this.videoSpanId +
  42. " does not exist");
  43. return;
  44. }
  45. this.updateDisplayName({
  46. allowEditing: true,
  47. displayName: displayName,
  48. displayNameSuffix: interfaceConfig.DEFAULT_LOCAL_DISPLAY_NAME,
  49. elementID: 'localDisplayName',
  50. participantID: this.id
  51. });
  52. };
  53. LocalVideo.prototype.changeVideo = function (stream) {
  54. this.videoStream = stream;
  55. let localVideoClick = (event) => {
  56. // TODO Checking the classList is a workround to allow events to bubble
  57. // into the DisplayName component if it was clicked. React's synthetic
  58. // events will fire after jQuery handlers execute, so stop propogation
  59. // at this point will prevent DisplayName from getting click events.
  60. // This workaround should be removeable once LocalVideo is a React
  61. // Component because then the components share the same eventing system.
  62. const { classList } = event.target;
  63. const clickedOnDisplayName = classList.contains('displayname')
  64. || classList.contains('editdisplayname');
  65. // FIXME: with Temasys plugin event arg is not an event, but
  66. // the clicked object itself, so we have to skip this call
  67. if (event.stopPropagation && !clickedOnDisplayName) {
  68. event.stopPropagation();
  69. }
  70. if (!clickedOnDisplayName) {
  71. this.VideoLayout.handleVideoThumbClicked(this.id);
  72. }
  73. };
  74. let localVideoContainerSelector = $('#localVideoContainer');
  75. localVideoContainerSelector.off('click');
  76. localVideoContainerSelector.on('click', localVideoClick);
  77. this.localVideoId = 'localVideo_' + stream.getId();
  78. var localVideoContainer = document.getElementById('localVideoWrapper');
  79. /* jshint ignore:start */
  80. ReactDOM.render(
  81. <Provider store = { APP.store }>
  82. <VideoTrack
  83. id = { this.localVideoId }
  84. videoTrack = {{ jitsiTrack: stream }} />
  85. </Provider>,
  86. localVideoContainer
  87. );
  88. /* jshint ignore:end */
  89. let isVideo = stream.videoType != "desktop";
  90. this._enableDisableContextMenu(isVideo);
  91. this.setFlipX(isVideo? APP.settings.getLocalFlipX() : false);
  92. let endedHandler = () => {
  93. // Only remove if there is no video and not a transition state.
  94. // Previous non-react logic created a new video element with each track
  95. // removal whereas react reuses the video component so it could be the
  96. // stream ended but a new one is being used.
  97. if (this.videoStream.isEnded()) {
  98. ReactDOM.unmountComponentAtNode(localVideoContainer);
  99. }
  100. // when removing only the video element and we are on stage
  101. // update the stage
  102. if (this.isCurrentlyOnLargeVideo()) {
  103. this.VideoLayout.updateLargeVideo(this.id);
  104. }
  105. stream.off(TrackEvents.LOCAL_TRACK_STOPPED, endedHandler);
  106. };
  107. stream.on(TrackEvents.LOCAL_TRACK_STOPPED, endedHandler);
  108. };
  109. /**
  110. * Shows or hides the local video container.
  111. * @param {boolean} true to make the local video container visible, false
  112. * otherwise
  113. */
  114. LocalVideo.prototype.setVisible = function(visible) {
  115. // We toggle the hidden class as an indication to other interested parties
  116. // that this container has been hidden on purpose.
  117. $("#localVideoContainer").toggleClass("hidden");
  118. // We still show/hide it as we need to overwrite the style property if we
  119. // want our action to take effect. Toggling the display property through
  120. // the above css class didn't succeed in overwriting the style.
  121. if (visible) {
  122. $("#localVideoContainer").show();
  123. }
  124. else {
  125. $("#localVideoContainer").hide();
  126. }
  127. };
  128. /**
  129. * Sets the flipX state of the video.
  130. * @param val {boolean} true for flipped otherwise false;
  131. */
  132. LocalVideo.prototype.setFlipX = function (val) {
  133. this.emitter.emit(UIEvents.LOCAL_FLIPX_CHANGED, val);
  134. if(!this.localVideoId)
  135. return;
  136. if(val) {
  137. this.selectVideoElement().addClass("flipVideoX");
  138. } else {
  139. this.selectVideoElement().removeClass("flipVideoX");
  140. }
  141. };
  142. /**
  143. * Builds the context menu for the local video.
  144. */
  145. LocalVideo.prototype._buildContextMenu = function () {
  146. $.contextMenu({
  147. selector: '#' + this.videoSpanId,
  148. zIndex: 10000,
  149. items: {
  150. flip: {
  151. name: "Flip",
  152. callback: () => {
  153. let val = !APP.settings.getLocalFlipX();
  154. this.setFlipX(val);
  155. APP.settings.setLocalFlipX(val);
  156. }
  157. }
  158. },
  159. events: {
  160. show : function(options){
  161. options.items.flip.name =
  162. APP.translation.generateTranslationHTML(
  163. "videothumbnail.flip");
  164. }
  165. }
  166. });
  167. };
  168. /**
  169. * Enables or disables the context menu for the local video.
  170. * @param enable {boolean} true for enable, false for disable
  171. */
  172. LocalVideo.prototype._enableDisableContextMenu = function (enable) {
  173. if($('#' + this.videoSpanId).contextMenu)
  174. $('#' + this.videoSpanId).contextMenu(enable);
  175. };
  176. export default LocalVideo;