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

InviteButton.web.js 7.0KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228
  1. /* global interfaceConfig */
  2. import PropTypes from 'prop-types';
  3. import React, { Component } from 'react';
  4. import { connect } from 'react-redux';
  5. import Button from '@atlaskit/button';
  6. import DropdownMenu from '@atlaskit/dropdown-menu';
  7. import { translate } from '../../base/i18n';
  8. import { getLocalParticipant, PARTICIPANT_ROLE } from '../../base/participants';
  9. import { openDialog } from '../../base/dialog';
  10. import { AddPeopleDialog } from '.';
  11. import { DialOutDialog } from '../../dial-out';
  12. import { isInviteOptionEnabled } from '../functions';
  13. const DIAL_OUT_OPTION = 'dialout';
  14. const ADD_TO_CALL_OPTION = 'addtocall';
  15. /**
  16. * The button that provides different invite options.
  17. */
  18. class InviteButton extends Component {
  19. /**
  20. * {@code InviteButton}'s property types.
  21. *
  22. * @static
  23. */
  24. static propTypes = {
  25. /**
  26. * Indicates if the "Add to call" feature is available.
  27. */
  28. _isAddToCallAvailable: PropTypes.bool,
  29. /**
  30. * Indicates if the "Dial out" feature is available.
  31. */
  32. _isDialOutAvailable: PropTypes.bool,
  33. /**
  34. * The function opening the dialog.
  35. */
  36. openDialog: PropTypes.func,
  37. /**
  38. * Invoked to obtain translated strings.
  39. */
  40. t: PropTypes.func
  41. };
  42. /**
  43. * Initializes a new {@code InviteButton} instance.
  44. *
  45. * @param {Object} props - The read-only properties with which the new
  46. * instance is to be initialized.
  47. */
  48. constructor(props) {
  49. super(props);
  50. this._onInviteOptionSelected = this._onInviteOptionSelected.bind(this);
  51. this._updateInviteItems = this._updateInviteItems.bind(this);
  52. this._updateInviteItems(this.props);
  53. }
  54. /**
  55. * Implements React's {@link Component#componentWillReceiveProps()}.
  56. *
  57. * @inheritdoc
  58. * @param {Object} nextProps - The read-only props which this Component will
  59. * receive.
  60. * @returns {void}
  61. */
  62. componentWillReceiveProps(nextProps) {
  63. if (this.props._isDialOutAvailable !== nextProps._isDialOutAvailable
  64. || this.props._isAddToCallAvailable
  65. !== nextProps._isAddToCallAvailable) {
  66. this._updateInviteItems(nextProps);
  67. }
  68. }
  69. /**
  70. * Renders the content of this component.
  71. *
  72. * @returns {ReactElement}
  73. */
  74. render() {
  75. // HACK ALERT: Normally children should not be controlling their own
  76. // visibility; parents should control that. However, this component is
  77. // in a transitionary state while the Invite Dialog is being redone.
  78. // This hack will go away when the Invite Dialog is back.
  79. if (!this.state.buttonOption) {
  80. return null;
  81. }
  82. const { VERTICAL_FILMSTRIP } = interfaceConfig;
  83. return (
  84. <div className = 'filmstrip__invite'>
  85. <div className = 'invite-button-group'>
  86. <Button
  87. // eslint-disable-next-line react/jsx-handler-names
  88. onClick = { this.state.buttonOption.action }
  89. shouldFitContainer = { true }>
  90. { this.state.buttonOption.content }
  91. </Button>
  92. { this.state.inviteOptions[0].items.length
  93. ? <DropdownMenu
  94. items = { this.state.inviteOptions }
  95. onItemActivated = { this._onInviteOptionSelected }
  96. position = { VERTICAL_FILMSTRIP
  97. ? 'bottom right'
  98. : 'top right' }
  99. shouldFlip = { true }
  100. triggerType = 'button' />
  101. : null }
  102. </div>
  103. </div>
  104. );
  105. }
  106. /**
  107. * Handles selection of the invite options.
  108. *
  109. * @param { Object } option - The invite option that has been selected from
  110. * the dropdown menu.
  111. * @private
  112. * @returns {void}
  113. */
  114. _onInviteOptionSelected(option) {
  115. this.state.inviteOptions[0].items.forEach(item => {
  116. if (item.content === option.item.content) {
  117. item.action();
  118. }
  119. });
  120. }
  121. /**
  122. * Updates the invite items list depending on the availability of the
  123. * features.
  124. *
  125. * @param {Object} props - The read-only properties of the component.
  126. * @private
  127. * @returns {void}
  128. */
  129. _updateInviteItems(props) {
  130. const { INVITE_OPTIONS = [] } = interfaceConfig;
  131. const validOptions = INVITE_OPTIONS.filter(option =>
  132. (option === DIAL_OUT_OPTION && props._isDialOutAvailable)
  133. || (option === ADD_TO_CALL_OPTION && props._isAddToCallAvailable));
  134. /* eslint-disable array-callback-return */
  135. const inviteItems = validOptions.map(option => {
  136. switch (option) {
  137. case DIAL_OUT_OPTION:
  138. return {
  139. content: this.props.t('dialOut.dialOut'),
  140. action: () => this.props.openDialog(DialOutDialog)
  141. };
  142. case ADD_TO_CALL_OPTION:
  143. return {
  144. content: interfaceConfig.ADD_PEOPLE_APP_NAME,
  145. action: () => this.props.openDialog(AddPeopleDialog)
  146. };
  147. }
  148. });
  149. /* eslint-enable array-callback-return */
  150. const buttonOption = inviteItems[0];
  151. const dropdownOptions = inviteItems.splice(1, inviteItems.length);
  152. const nextState = {
  153. /**
  154. * The configuration for how the invite button should display and
  155. * behave on click.
  156. */
  157. buttonOption,
  158. /**
  159. * The list of invite options in the dropdown.
  160. */
  161. inviteOptions: [
  162. {
  163. items: dropdownOptions
  164. }
  165. ]
  166. };
  167. if (this.state) {
  168. this.setState(nextState);
  169. } else {
  170. // eslint-disable-next-line react/no-direct-mutation-state
  171. this.state = nextState;
  172. }
  173. }
  174. }
  175. /**
  176. * Maps (parts of) the Redux state to the associated {@code InviteButton}'s
  177. * props.
  178. *
  179. * @param {Object} state - The Redux state.
  180. * @private
  181. * @returns {{
  182. * _isAddToCallAvailable: boolean,
  183. * _isDialOutAvailable: boolean
  184. * }}
  185. */
  186. function _mapStateToProps(state) {
  187. const { conference } = state['features/base/conference'];
  188. const { enableUserRolesBasedOnToken } = state['features/base/config'];
  189. const { isGuest } = state['features/base/jwt'];
  190. return {
  191. _isAddToCallAvailable:
  192. !isGuest && isInviteOptionEnabled(ADD_TO_CALL_OPTION),
  193. _isDialOutAvailable:
  194. getLocalParticipant(state).role === PARTICIPANT_ROLE.MODERATOR
  195. && conference && conference.isSIPCallingSupported()
  196. && isInviteOptionEnabled(DIAL_OUT_OPTION)
  197. && (!enableUserRolesBasedOnToken || !isGuest)
  198. };
  199. }
  200. export default translate(connect(_mapStateToProps, { openDialog })(
  201. InviteButton));