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 4.9KB

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