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.

ConnectionIndicator.js 8.0KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273
  1. /* global $, interfaceConfig, JitsiMeetJS */
  2. /* jshint -W101 */
  3. /* eslint-disable no-unused-vars */
  4. import React from 'react';
  5. import ReactDOM from 'react-dom';
  6. import { ConnectionStatsTable } from '../../../react/features/connection-stats';
  7. /* eslint-enable no-unused-vars */
  8. import JitsiPopover from "../util/JitsiPopover";
  9. import UIUtil from "../util/UIUtil";
  10. const ParticipantConnectionStatus
  11. = JitsiMeetJS.constants.participantConnectionStatus;
  12. /**
  13. * Maps a connection quality value (in percent) to the width of the "full" icon.
  14. */
  15. const qualityToWidth = [
  16. // Full (5 bars)
  17. {percent: 80, width: "100%"},
  18. // 4 bars
  19. {percent: 60, width: "80%"},
  20. // 3 bars
  21. {percent: 40, width: "55%"},
  22. // 2 bars
  23. {percent: 20, width: "40%"},
  24. // 1 bar
  25. {percent: 0, width: "20%"}
  26. // Note: we never show 0 bars.
  27. ];
  28. /**
  29. * Constructs new connection indicator.
  30. * @param videoContainer the video container associated with the indicator.
  31. * @param videoId the identifier of the video
  32. * @constructor
  33. */
  34. function ConnectionIndicator(videoContainer, videoId) {
  35. this.videoContainer = videoContainer;
  36. this.bandwidth = null;
  37. this.packetLoss = null;
  38. this.bitrate = null;
  39. this.showMoreValue = false;
  40. this.resolution = null;
  41. this.transport = [];
  42. this.framerate = null;
  43. this.popover = null;
  44. this.id = videoId;
  45. this.create();
  46. this.isLocalVideo
  47. = this.videoContainer.videoSpanId === 'localVideoContainer';
  48. this.showMore = this.showMore.bind(this);
  49. }
  50. /**
  51. * Generates the html content.
  52. * @returns {string} the html content.
  53. */
  54. ConnectionIndicator.prototype.generateText = function () {
  55. /* jshint ignore:start */
  56. return (
  57. <ConnectionStatsTable
  58. bandwidth = { this.bandwidth }
  59. bitrate = { this.bitrate }
  60. isLocalVideo = { this.isLocalVideo }
  61. framerate = { this.framerate }
  62. onShowMore = { this.showMore }
  63. packetLoss = { this.packetLoss}
  64. resolution = { this.resolution }
  65. shouldShowMore = { this.showMoreValue }
  66. transport = { this.transport } />
  67. );
  68. /* jshint ignore:end */
  69. };
  70. /**
  71. * Shows or hide the additional information.
  72. */
  73. ConnectionIndicator.prototype.showMore = function () {
  74. this.showMoreValue = !this.showMoreValue;
  75. this.updatePopoverData();
  76. };
  77. function createIcon(classes, iconClass) {
  78. var icon = document.createElement("span");
  79. for(var i in classes) {
  80. icon.classList.add(classes[i]);
  81. }
  82. icon.appendChild(
  83. document.createElement("i")).classList.add(iconClass);
  84. return icon;
  85. }
  86. /**
  87. * Creates the indicator
  88. */
  89. ConnectionIndicator.prototype.create = function () {
  90. let indicatorId = 'connectionindicator';
  91. let element = UIUtil.getVideoThumbnailIndicatorSpan({
  92. videoSpanId: this.videoContainer.videoSpanId,
  93. indicatorId
  94. });
  95. element.classList.add('show');
  96. this.connectionIndicatorContainer = element;
  97. let popoverContent = (
  98. `<div class="connection-info" data-i18n="${indicatorId}.na"></div>`
  99. );
  100. this.popover = new JitsiPopover($(element), {
  101. content: popoverContent,
  102. skin: "black",
  103. position: interfaceConfig.VERTICAL_FILMSTRIP ? 'left' : 'top'
  104. });
  105. // override popover show method to make sure we will update the content
  106. // before showing the popover
  107. var origShowFunc = this.popover.show;
  108. this.popover.show = function () {
  109. // update content by forcing it, to finish even if popover
  110. // is not visible
  111. this.updatePopoverData(true);
  112. // call the original show, passing its actual this
  113. origShowFunc.call(this.popover);
  114. }.bind(this);
  115. let connectionIconContainer = document.createElement('div');
  116. connectionIconContainer.className = 'connection indicatoricon';
  117. this.emptyIcon = connectionIconContainer.appendChild(
  118. createIcon(["connection_empty"], "icon-connection"));
  119. this.fullIcon = connectionIconContainer.appendChild(
  120. createIcon(["connection_full"], "icon-connection"));
  121. this.interruptedIndicator = connectionIconContainer.appendChild(
  122. createIcon(["connection_lost"],"icon-connection-lost"));
  123. this.ninjaIndicator = connectionIconContainer.appendChild(
  124. createIcon(["connection_ninja"],"icon-ninja"));
  125. $(this.interruptedIndicator).hide();
  126. $(this.ninjaIndicator).hide();
  127. this.connectionIndicatorContainer.appendChild(connectionIconContainer);
  128. };
  129. /**
  130. * Removes the indicator
  131. */
  132. ConnectionIndicator.prototype.remove = function() {
  133. if (this.connectionIndicatorContainer.parentNode) {
  134. this.connectionIndicatorContainer.parentNode.removeChild(
  135. this.connectionIndicatorContainer);
  136. }
  137. this.popover.forceHide();
  138. };
  139. /**
  140. * Updates the UI which displays or not a warning about user's connectivity
  141. * problems.
  142. *
  143. * @param {ParticipantConnectionStatus} connectionStatus
  144. */
  145. ConnectionIndicator.prototype.updateConnectionStatusIndicator
  146. = function (connectionStatus) {
  147. this.connectionStatus = connectionStatus;
  148. if (connectionStatus === ParticipantConnectionStatus.INTERRUPTED) {
  149. $(this.interruptedIndicator).show();
  150. $(this.emptyIcon).hide();
  151. $(this.fullIcon).hide();
  152. $(this.ninjaIndicator).hide();
  153. } else if (connectionStatus === ParticipantConnectionStatus.INACTIVE) {
  154. $(this.interruptedIndicator).hide();
  155. $(this.emptyIcon).hide();
  156. $(this.fullIcon).hide();
  157. $(this.ninjaIndicator).show();
  158. } else {
  159. $(this.interruptedIndicator).hide();
  160. $(this.emptyIcon).show();
  161. $(this.fullIcon).show();
  162. $(this.ninjaIndicator).hide();
  163. }
  164. };
  165. /**
  166. * Updates the data of the indicator
  167. * @param percent the percent of connection quality
  168. * @param object the statistics data.
  169. */
  170. ConnectionIndicator.prototype.updateConnectionQuality =
  171. function (percent, object) {
  172. if (!percent) {
  173. this.connectionIndicatorContainer.style.display = "none";
  174. this.popover.forceHide();
  175. return;
  176. } else {
  177. if(this.connectionIndicatorContainer.style.display == "none") {
  178. this.connectionIndicatorContainer.style.display = "block";
  179. }
  180. }
  181. if (object) {
  182. this.bandwidth = object.bandwidth;
  183. this.bitrate = object.bitrate;
  184. this.packetLoss = object.packetLoss;
  185. this.transport = object.transport;
  186. if (object.resolution) {
  187. this.resolution = object.resolution;
  188. }
  189. if (object.framerate)
  190. this.framerate = object.framerate;
  191. }
  192. let width = qualityToWidth.find(x => percent >= x.percent);
  193. this.fullIcon.style.width = width.width;
  194. this.updatePopoverData();
  195. };
  196. /**
  197. * Updates the resolution
  198. * @param resolution the new resolution
  199. */
  200. ConnectionIndicator.prototype.updateResolution = function (resolution) {
  201. this.resolution = resolution;
  202. this.updatePopoverData();
  203. };
  204. /**
  205. * Updates the framerate
  206. * @param framerate the new resolution
  207. */
  208. ConnectionIndicator.prototype.updateFramerate = function (framerate) {
  209. this.framerate = framerate;
  210. this.updatePopoverData();
  211. };
  212. /**
  213. * Updates the content of the popover if its visible
  214. * @param force to work even if popover is not visible
  215. */
  216. ConnectionIndicator.prototype.updatePopoverData = function (force) {
  217. // generate content, translate it and add it to document only if
  218. // popover is visible or we force to do so.
  219. if(this.popover.popoverShown || force) {
  220. this.popover.updateContent(this.generateText());
  221. }
  222. };
  223. /**
  224. * Hides the popover
  225. */
  226. ConnectionIndicator.prototype.hide = function () {
  227. this.popover.forceHide();
  228. };
  229. /**
  230. * Hides the indicator
  231. */
  232. ConnectionIndicator.prototype.hideIndicator = function () {
  233. this.connectionIndicatorContainer.style.display = "none";
  234. if(this.popover)
  235. this.popover.forceHide();
  236. };
  237. /**
  238. * Adds a hover listener to the popover.
  239. */
  240. ConnectionIndicator.prototype.addPopoverHoverListener = function (listener) {
  241. this.popover.addOnHoverPopover(listener);
  242. };
  243. export default ConnectionIndicator;