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.

functions.js 5.6KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189
  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. * @returns {MediaState}
  64. */
  65. export function getParticipantAudioMediaState(participant: Object, muted: Boolean, state: Object) {
  66. const dominantSpeaker = getDominantSpeakerParticipant(state);
  67. if (muted) {
  68. if (isForceMuted(participant, MEDIA_TYPE.AUDIO, state)) {
  69. return MEDIA_STATE.FORCE_MUTED;
  70. }
  71. return MEDIA_STATE.MUTED;
  72. }
  73. if (participant === dominantSpeaker) {
  74. return MEDIA_STATE.DOMINANT_SPEAKER;
  75. }
  76. return MEDIA_STATE.UNMUTED;
  77. }
  78. /**
  79. * Determines the video media state (the mic icon) for a participant.
  80. *
  81. * @param {Object} participant - The participant.
  82. * @param {boolean} muted - The mute state of the participant.
  83. * @param {Object} state - The redux state.
  84. * @returns {MediaState}
  85. */
  86. export function getParticipantVideoMediaState(participant: Object, muted: Boolean, state: Object) {
  87. if (muted) {
  88. if (isForceMuted(participant, MEDIA_TYPE.VIDEO, state)) {
  89. return MEDIA_STATE.FORCE_MUTED;
  90. }
  91. return MEDIA_STATE.MUTED;
  92. }
  93. return MEDIA_STATE.UNMUTED;
  94. }
  95. /**
  96. * Get a style property from a style declaration as a float.
  97. *
  98. * @param {CSSStyleDeclaration} styles - Style declaration.
  99. * @param {string} name - Property name.
  100. * @returns {number} Float value.
  101. */
  102. export const getFloatStyleProperty = (styles: Object, name: string) =>
  103. parseFloat(styles.getPropertyValue(name));
  104. /**
  105. * Gets the outer height of an element, including margins.
  106. *
  107. * @param {Element} element - Target element.
  108. * @returns {number} Computed height.
  109. */
  110. export const getComputedOuterHeight = (element: HTMLElement) => {
  111. const computedStyle = getComputedStyle(element);
  112. return element.offsetHeight
  113. + getFloatStyleProperty(computedStyle, 'margin-top')
  114. + getFloatStyleProperty(computedStyle, 'margin-bottom');
  115. };
  116. /**
  117. * Returns this feature's root state.
  118. *
  119. * @param {Object} state - Global state.
  120. * @returns {Object} Feature state.
  121. */
  122. const getState = (state: Object) => state[REDUCER_KEY];
  123. /**
  124. * Is the participants pane open.
  125. *
  126. * @param {Object} state - Global state.
  127. * @returns {boolean} Is the participants pane open.
  128. */
  129. export const getParticipantsPaneOpen = (state: Object) => Boolean(getState(state)?.isOpen);
  130. /**
  131. * Returns the type of quick action button to be displayed for a participant.
  132. * The button is displayed when hovering a participant from the participant list.
  133. *
  134. * @param {Object} participant - The participant.
  135. * @param {boolean} isAudioMuted - If audio is muted for the participant.
  136. * @param {Object} state - The redux state.
  137. * @returns {string} - The type of the quick action button.
  138. */
  139. export function getQuickActionButtonType(participant: Object, isAudioMuted: Boolean, state: Object) {
  140. // handled only by moderators
  141. if (isLocalParticipantModerator(state)) {
  142. if (isForceMuted(participant, MEDIA_TYPE.AUDIO, state) || isForceMuted(participant, MEDIA_TYPE.VIDEO, state)) {
  143. return QUICK_ACTION_BUTTON.ASK_TO_UNMUTE;
  144. }
  145. if (!isAudioMuted) {
  146. return QUICK_ACTION_BUTTON.MUTE;
  147. }
  148. }
  149. return QUICK_ACTION_BUTTON.NONE;
  150. }
  151. /**
  152. * Returns true if the invite button should be rendered.
  153. *
  154. * @param {Object} state - Global state.
  155. * @returns {boolean}
  156. */
  157. export const shouldRenderInviteButton = (state: Object) => {
  158. const { disableInviteFunctions } = toState(state)['features/base/config'];
  159. const flagEnabled = getFeatureFlag(state, INVITE_ENABLED, true);
  160. return flagEnabled && !disableInviteFunctions;
  161. };