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

functions.js 5.8KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191
  1. // @flow
  2. import {
  3. isParticipantApproved,
  4. isEnabledFromState,
  5. isLocalParticipantApprovedFromState
  6. } from '../av-moderation/functions';
  7. import { getFeatureFlag, INVITE_ENABLED } from '../base/flags';
  8. import { MEDIA_TYPE, type MediaType } from '../base/media/constants';
  9. import {
  10. getDominantSpeakerParticipant,
  11. isLocalParticipantModerator,
  12. isParticipantModerator
  13. } from '../base/participants/functions';
  14. import { toState } from '../base/redux';
  15. import { QUICK_ACTION_BUTTON, REDUCER_KEY, MEDIA_STATE } from './constants';
  16. /**
  17. * Generates a class attribute value.
  18. *
  19. * @param {Iterable<string>} args - String iterable.
  20. * @returns {string} Class attribute value.
  21. */
  22. export const classList = (...args: Array<string | boolean>) => args.filter(Boolean).join(' ');
  23. /**
  24. * Find the first styled ancestor component of an element.
  25. *
  26. * @param {Element} target - Element to look up.
  27. * @param {StyledComponentClass} component - Styled component reference.
  28. * @returns {Element|null} Ancestor.
  29. */
  30. export const findStyledAncestor = (target: Object, component: any) => {
  31. if (!target || target.matches(`.${component.styledComponentId}`)) {
  32. return target;
  33. }
  34. return findStyledAncestor(target.parentElement, component);
  35. };
  36. /**
  37. * Checks if a participant is force muted.
  38. *
  39. * @param {Object} participant - The participant.
  40. * @param {MediaType} mediaType - The media type.
  41. * @param {Object} state - The redux state.
  42. * @returns {MediaState}
  43. */
  44. export function isForceMuted(participant: Object, mediaType: MediaType, state: Object) {
  45. if (isEnabledFromState(mediaType, state)) {
  46. if (participant.local) {
  47. return !isLocalParticipantApprovedFromState(mediaType, state);
  48. }
  49. // moderators cannot be force muted
  50. if (isParticipantModerator(participant)) {
  51. return false;
  52. }
  53. return !isParticipantApproved(participant.id, mediaType)(state);
  54. }
  55. return false;
  56. }
  57. /**
  58. * Determines the audio media state (the mic icon) for a participant.
  59. *
  60. * @param {Object} participant - The participant.
  61. * @param {boolean} muted - The mute state of the participant.
  62. * @param {Object} state - The redux state.
  63. * @param {boolean} ignoreDominantSpeaker - Whether to ignore the dominant speaker state.
  64. * @returns {MediaState}
  65. */
  66. export function getParticipantAudioMediaState(participant: Object, muted: Boolean, state: Object) {
  67. const dominantSpeaker = getDominantSpeakerParticipant(state);
  68. if (muted) {
  69. if (isForceMuted(participant, MEDIA_TYPE.AUDIO, state)) {
  70. return MEDIA_STATE.FORCE_MUTED;
  71. }
  72. return MEDIA_STATE.MUTED;
  73. }
  74. if (participant === dominantSpeaker) {
  75. return MEDIA_STATE.DOMINANT_SPEAKER;
  76. }
  77. return MEDIA_STATE.UNMUTED;
  78. }
  79. /**
  80. * Determines the video media state (the mic icon) for a participant.
  81. *
  82. * @param {Object} participant - The participant.
  83. * @param {boolean} muted - The mute state of the participant.
  84. * @param {Object} state - The redux state.
  85. * @param {boolean} ignoreDominantSpeaker - Whether to ignore the dominant speaker state.
  86. * @returns {MediaState}
  87. */
  88. export function getParticipantVideoMediaState(participant: Object, muted: Boolean, state: Object) {
  89. if (muted) {
  90. if (isForceMuted(participant, MEDIA_TYPE.VIDEO, state)) {
  91. return MEDIA_STATE.FORCE_MUTED;
  92. }
  93. return MEDIA_STATE.MUTED;
  94. }
  95. return MEDIA_STATE.UNMUTED;
  96. }
  97. /**
  98. * Get a style property from a style declaration as a float.
  99. *
  100. * @param {CSSStyleDeclaration} styles - Style declaration.
  101. * @param {string} name - Property name.
  102. * @returns {number} Float value.
  103. */
  104. export const getFloatStyleProperty = (styles: Object, name: string) =>
  105. parseFloat(styles.getPropertyValue(name));
  106. /**
  107. * Gets the outer height of an element, including margins.
  108. *
  109. * @param {Element} element - Target element.
  110. * @returns {number} Computed height.
  111. */
  112. export const getComputedOuterHeight = (element: HTMLElement) => {
  113. const computedStyle = getComputedStyle(element);
  114. return element.offsetHeight
  115. + getFloatStyleProperty(computedStyle, 'margin-top')
  116. + getFloatStyleProperty(computedStyle, 'margin-bottom');
  117. };
  118. /**
  119. * Returns this feature's root state.
  120. *
  121. * @param {Object} state - Global state.
  122. * @returns {Object} Feature state.
  123. */
  124. const getState = (state: Object) => state[REDUCER_KEY];
  125. /**
  126. * Is the participants pane open.
  127. *
  128. * @param {Object} state - Global state.
  129. * @returns {boolean} Is the participants pane open.
  130. */
  131. export const getParticipantsPaneOpen = (state: Object) => Boolean(getState(state)?.isOpen);
  132. /**
  133. * Returns the type of quick action button to be displayed for a participant.
  134. * The button is displayed when hovering a participant from the participant list.
  135. *
  136. * @param {Object} participant - The participant.
  137. * @param {boolean} isAudioMuted - If audio is muted for the participant.
  138. * @param {Object} state - The redux state.
  139. * @returns {string} - The type of the quick action button.
  140. */
  141. export function getQuickActionButtonType(participant: Object, isAudioMuted: Boolean, state: Object) {
  142. // handled only by moderators
  143. if (isLocalParticipantModerator(state)) {
  144. if (isForceMuted(participant, MEDIA_TYPE.AUDIO, state) || isForceMuted(participant, MEDIA_TYPE.VIDEO, state)) {
  145. return QUICK_ACTION_BUTTON.ASK_TO_UNMUTE;
  146. }
  147. if (!isAudioMuted) {
  148. return QUICK_ACTION_BUTTON.MUTE;
  149. }
  150. }
  151. return QUICK_ACTION_BUTTON.NONE;
  152. }
  153. /**
  154. * Returns true if the invite button should be rendered.
  155. *
  156. * @param {Object} state - Global state.
  157. * @returns {boolean}
  158. */
  159. export const shouldRenderInviteButton = (state: Object) => {
  160. const { disableInviteFunctions } = toState(state)['features/base/config'];
  161. const flagEnabled = getFeatureFlag(state, INVITE_ENABLED, true);
  162. return flagEnabled && !disableInviteFunctions;
  163. };