您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

functions.js 5.8KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213
  1. // @flow
  2. import { findIndex } from 'lodash';
  3. import { CONNECTION_TYPE } from './constants';
  4. const LOSS_AUDIO_THRESHOLDS = [ 0.33, 0.05 ];
  5. const LOSS_VIDEO_THRESHOLDS = [ 0.33, 0.1, 0.05 ];
  6. const THROUGHPUT_AUDIO_THRESHOLDS = [ 8, 20 ];
  7. const THROUGHPUT_VIDEO_THRESHOLDS = [ 60, 750 ];
  8. /**
  9. * The avatar size to container size ration.
  10. */
  11. const ratio = 1 / 3;
  12. /**
  13. * The max avatar size.
  14. */
  15. const maxSize = 190;
  16. /**
  17. * The window limit hight over which the avatar should have the default dimension.
  18. */
  19. const upperHeightLimit = 760;
  20. /**
  21. * The window limit hight under which the avatar should not be resized anymore.
  22. */
  23. const lowerHeightLimit = 460;
  24. /**
  25. * The default top margin of the avatar.
  26. */
  27. const defaultMarginTop = '10%';
  28. /**
  29. * The top margin of the avatar when its dimension is small.
  30. */
  31. const smallMarginTop = '5%';
  32. /**
  33. * Calculates avatar dimensions based on window height and position.
  34. *
  35. * @param {number} height - The window height.
  36. * @returns {{
  37. * marginTop: string,
  38. * size: number
  39. * }}
  40. */
  41. export function calculateAvatarDimensions(height: number) {
  42. if (height > upperHeightLimit) {
  43. return {
  44. size: maxSize,
  45. marginTop: defaultMarginTop
  46. };
  47. }
  48. if (height > lowerHeightLimit) {
  49. const diff = height - lowerHeightLimit;
  50. const percent = diff * ratio;
  51. const size = Math.floor(maxSize * percent / 100);
  52. let marginTop = defaultMarginTop;
  53. if (height < 600) {
  54. marginTop = smallMarginTop;
  55. }
  56. return {
  57. size,
  58. marginTop
  59. };
  60. }
  61. return {
  62. size: 0,
  63. marginTop: '0'
  64. };
  65. }
  66. /**
  67. * Returns the level based on a list of thresholds.
  68. *
  69. * @param {number[]} thresholds - The thresholds array.
  70. * @param {number} value - The value against which the level is calculated.
  71. * @param {boolean} descending - The order based on which the level is calculated.
  72. *
  73. * @returns {number}
  74. */
  75. function _getLevel(thresholds, value, descending = true) {
  76. let predicate;
  77. if (descending) {
  78. predicate = function(threshold) {
  79. return value > threshold;
  80. };
  81. } else {
  82. predicate = function(threshold) {
  83. return value < threshold;
  84. };
  85. }
  86. const i = findIndex(thresholds, predicate);
  87. if (i === -1) {
  88. return thresholds.length;
  89. }
  90. return i;
  91. }
  92. /**
  93. * Returns the connection details from the test results.
  94. *
  95. * @param {{
  96. * fractionalLoss: number,
  97. * throughput: number
  98. * }} testResults - The state of the app.
  99. *
  100. * @returns {{
  101. * connectionType: string,
  102. * connectionDetails: string[]
  103. * }}
  104. */
  105. function _getConnectionDataFromTestResults({ fractionalLoss: l, throughput: t }) {
  106. const loss = {
  107. audioQuality: _getLevel(LOSS_AUDIO_THRESHOLDS, l),
  108. videoQuality: _getLevel(LOSS_VIDEO_THRESHOLDS, l)
  109. };
  110. const throughput = {
  111. audioQuality: _getLevel(THROUGHPUT_AUDIO_THRESHOLDS, t, false),
  112. videoQuality: _getLevel(THROUGHPUT_VIDEO_THRESHOLDS, t, false)
  113. };
  114. let connectionType = CONNECTION_TYPE.NONE;
  115. const connectionDetails = [];
  116. if (throughput.audioQuality === 0 || loss.audioQuality === 0) {
  117. // Calls are impossible.
  118. connectionType = CONNECTION_TYPE.POOR;
  119. connectionDetails.push('prejoin.connectionDetails.veryPoorConnection');
  120. } else if (
  121. throughput.audioQuality === 2
  122. && throughput.videoQuality === 2
  123. && loss.audioQuality === 2
  124. && loss.videoQuality === 3
  125. ) {
  126. // Ideal conditions for both audio and video. Show only one message.
  127. connectionType = CONNECTION_TYPE.GOOD;
  128. connectionDetails.push('prejoin.connectionDetails.goodQuality');
  129. } else {
  130. connectionType = CONNECTION_TYPE.NON_OPTIMAL;
  131. if (throughput.audioQuality === 1) {
  132. // Minimum requirements for a call are met.
  133. connectionDetails.push('prejoin.connectionDetails.audioLowNoVideo');
  134. } else {
  135. // There are two paragraphs: one saying something about audio and the other about video.
  136. if (loss.audioQuality === 1) {
  137. connectionDetails.push('prejoin.connectionDetails.audioClipping');
  138. } else {
  139. connectionDetails.push('prejoin.connectionDetails.audioHighQuality');
  140. }
  141. if (throughput.videoQuality === 0 || loss.videoQuality === 0) {
  142. connectionDetails.push('prejoin.connectionDetails.noVideo');
  143. } else if (throughput.videoQuality === 1) {
  144. connectionDetails.push('prejoin.connectionDetails.videoLowQuality');
  145. } else if (loss.videoQuality === 1) {
  146. connectionDetails.push('prejoin.connectionDetails.videoFreezing');
  147. } else if (loss.videoQuality === 2) {
  148. connectionDetails.push('prejoin.connectionDetails.videoTearing');
  149. } else {
  150. connectionDetails.push('prejoin.connectionDetails.videoHighQuality');
  151. }
  152. }
  153. connectionDetails.push('prejoin.connectionDetails.undetectable');
  154. }
  155. return {
  156. connectionType,
  157. connectionDetails
  158. };
  159. }
  160. /**
  161. * Selector for determining the connection type & details.
  162. *
  163. * @param {Object} state - The state of the app.
  164. * @returns {{
  165. * connectionType: string,
  166. * connectionDetails: string[]
  167. * }}
  168. */
  169. export function getConnectionData(state: Object) {
  170. const { precallTestResults } = state['features/prejoin'];
  171. if (precallTestResults) {
  172. if (precallTestResults.mediaConnectivity) {
  173. return _getConnectionDataFromTestResults(precallTestResults);
  174. }
  175. return {
  176. connectionType: CONNECTION_TYPE.POOR,
  177. connectionDetails: [ 'prejoin.connectionDetails.noMediaConnectivity' ]
  178. };
  179. }
  180. return {
  181. connectionType: CONNECTION_TYPE.NONE,
  182. connectionDetails: []
  183. };
  184. }