diff --git a/css/modals/invite/_invite.scss b/css/modals/invite/_invite.scss index 855b7b9bb..d2931016d 100644 --- a/css/modals/invite/_invite.scss +++ b/css/modals/invite/_invite.scss @@ -8,6 +8,19 @@ .invite-dialog { .dial-in-numbers { + .dial-in-numbers-copy { + opacity: 0; + pointer-events: none; + position: fixed; + user-select: text; + -webkit-user-select: text; + } + + .dial-in-numbers-conference-id { + color: orange; + margin-left: 3px; + } + .dial-in-numbers-trigger { position: relative; width: 100%; diff --git a/lang/main.json b/lang/main.json index 4ce44012b..e0b516bbe 100644 --- a/lang/main.json +++ b/lang/main.json @@ -437,14 +437,13 @@ }, "invite": { "addPassword": "Add password", - "dialInNumbers": "Dial-in telephone numbers", - "errorFetchingNumbers": "Failed to obtain dial-in numbers", + "callNumber": "Call __number__", + "enterId": "Enter Meeting ID: __meetingId__ following by # to dial in from a phone", + "howToDialIn": "To dial in, use one of the following numbers and meeting ID", "hidePassword": "Hide password", "inviteTo": "Invite people to __conferenceName__", - "loadingNumbers": "Loading...", + "invitedYouTo": "__userName__ has invited you to the __meetingUrl__ conference", "locked": "This call is locked. New callers must have the link and enter the password to join.", - "noNumbers": "No numbers available", - "numbersDisabled": "Dialing in has been disabled", "showPassword": "Show password", "unlocked": "This call is unlocked. Any new caller with the link may join the call." } diff --git a/react/features/invite/actionTypes.js b/react/features/invite/actionTypes.js index 84ead153a..f0bdeaf8e 100644 --- a/react/features/invite/actionTypes.js +++ b/react/features/invite/actionTypes.js @@ -12,17 +12,6 @@ import { Symbol } from '../base/react'; export const UPDATE_DIAL_IN_NUMBERS_FAILED = Symbol('UPDATE_DIAL_IN_NUMBERS_FAILED'); -/** - * The type of the action which signals a request for dial-in numbers has been - * started. - * - * { - * type: UPDATE_DIAL_IN_NUMBERS_REQUEST - * } - */ -export const UPDATE_DIAL_IN_NUMBERS_REQUEST - = Symbol('UPDATE_DIAL_IN_NUMBERS_REQUEST'); - /** * The type of the action which signals a request for dial-in numbers has * succeeded. diff --git a/react/features/invite/actions.js b/react/features/invite/actions.js index 64fec5dce..6e892b6e7 100644 --- a/react/features/invite/actions.js +++ b/react/features/invite/actions.js @@ -2,7 +2,6 @@ import { openDialog } from '../../features/base/dialog'; import { UPDATE_DIAL_IN_NUMBERS_FAILED, - UPDATE_DIAL_IN_NUMBERS_REQUEST, UPDATE_DIAL_IN_NUMBERS_SUCCESS } from './actionTypes'; import { InviteDialog } from './components'; @@ -11,6 +10,10 @@ declare var $: Function; declare var APP: Object; declare var config: Object; +const CONFERENCE_ID_ENDPOINT = config.conferenceMapperUrl; +const DIAL_IN_NUMBERS_ENDPOINT = config.dialInNumbersUrl; +const MUC_URL = config && config.hosts && config.hosts.muc; + /** * Opens the Invite Dialog. * @@ -18,34 +21,46 @@ declare var config: Object; */ export function openInviteDialog() { return openDialog(InviteDialog, { - conferenceUrl: encodeURI(APP.ConferenceUrl.getInviteUrl()), - dialInNumbersUrl: config.dialInNumbersUrl + conferenceUrl: encodeURI(APP.ConferenceUrl.getInviteUrl()) }); } /** - * Sends an ajax request for dial-in numbers. + * Sends an ajax requests for dial-in numbers and conference id. * - * @param {string} dialInNumbersUrl - The endpoint for retrieving json that - * includes numbers for dialing in to a conference. * @returns {Function} */ -export function updateDialInNumbers(dialInNumbersUrl) { - return dispatch => { - dispatch({ - type: UPDATE_DIAL_IN_NUMBERS_REQUEST +export function updateDialInNumbers() { + return (dispatch, getState) => { + + if (!CONFERENCE_ID_ENDPOINT || !DIAL_IN_NUMBERS_ENDPOINT || !MUC_URL) { + return; + } + + const { room } = getState()['features/base/conference']; + const conferenceIdUrl + = `${CONFERENCE_ID_ENDPOINT}?conference=${room}@${MUC_URL}`; + + Promise.all([ + $.getJSON(DIAL_IN_NUMBERS_ENDPOINT), + $.getJSON(conferenceIdUrl) + ]).then(([ numbersResponse, idResponse ]) => { + if (!idResponse.conference || !idResponse.id) { + return Promise.reject(idResponse.message); + } + + dispatch({ + type: UPDATE_DIAL_IN_NUMBERS_SUCCESS, + conferenceId: idResponse, + dialInNumbers: numbersResponse + }); + }) + .catch(error => { + dispatch({ + type: UPDATE_DIAL_IN_NUMBERS_FAILED, + error + }); }); - $.getJSON(dialInNumbersUrl) - .success(response => - dispatch({ - type: UPDATE_DIAL_IN_NUMBERS_SUCCESS, - response - })) - .error(error => - dispatch({ - type: UPDATE_DIAL_IN_NUMBERS_FAILED, - error - })); }; } diff --git a/react/features/invite/components/DialInNumbersForm.js b/react/features/invite/components/DialInNumbersForm.js index 0b002faec..47e8503c6 100644 --- a/react/features/invite/components/DialInNumbersForm.js +++ b/react/features/invite/components/DialInNumbersForm.js @@ -4,6 +4,7 @@ import React, { Component } from 'react'; import { connect } from 'react-redux'; import { translate } from '../../base/i18n'; +import { getLocalParticipant } from '../../base/participants'; import { updateDialInNumbers } from '../actions'; @@ -25,15 +26,20 @@ class DialInNumbersForm extends Component { * @static */ static propTypes = { + /** + * The name of the current user. + */ + _currentUserName: React.PropTypes.string, + /** * The redux state representing the dial-in numbers feature. */ _dialIn: React.PropTypes.object, /** - * The url for retrieving dial-in numbers. + * The url for the JitsiConference. */ - dialInNumbersUrl: React.PropTypes.string, + conferenceUrl: React.PropTypes.string, /** * Invoked to send an ajax request for dial-in numbers. @@ -77,35 +83,32 @@ class DialInNumbersForm extends Component { /** * The internal reference to the DOM/HTML element backing the React - * {@code Component} input. It is necessary for the implementation of - * copying to the clipboard. + * {@code Component} text area. It is necessary for the implementation + * of copying to the clipboard. * * @private - * @type {HTMLInputElement} + * @type {HTMLTextAreaElement} */ - this._inputElement = null; + this._copyElement = null; // Bind event handlers so they are only bound once for every instance. - this._onClick = this._onClick.bind(this); + this._onCopyClick = this._onCopyClick.bind(this); this._onOpenChange = this._onOpenChange.bind(this); this._onSelect = this._onSelect.bind(this); - this._setInput = this._setInput.bind(this); + this._setCopyElement = this._setCopyElement.bind(this); } /** - * Dispatches a request for numbers if not already present in the redux - * store. If numbers are present, sets a default number to display in the - * dropdown trigger. + * Sets a default number to display in the dropdown trigger. * * @inheritdoc * returns {void} */ - componentDidMount() { + componentWillMount() { if (this.props._dialIn.numbers) { this._setDefaultNumber(this.props._dialIn.numbers); } else { - this.props.dispatch( - updateDialInNumbers(this.props.dialInNumbersUrl)); + this.props.dispatch(updateDialInNumbers()); } } @@ -123,52 +126,46 @@ class DialInNumbersForm extends Component { } /** - * Implements React's {@link Component#render()}. + * Implements React's {@link Component#render()}. Returns null if the + * component is not ready for display. * * @inheritdoc - * @returns {ReactElement} + * @returns {ReactElement|null} */ render() { - const { t, _dialIn } = this.props; + const { _dialIn, t } = this.props; + const { conferenceId, numbers, numbersEnabled } = _dialIn; + const { selectedNumber } = this.state; - const numbers = _dialIn.numbers; - const items = numbers ? this._formatNumbers(numbers) : []; - - const isEnabled = this._isDropdownEnabled(); - const inputWrapperClassNames - = `form-control__container ${isEnabled ? '' : 'is-disabled'} - ${_dialIn.loading ? 'is-loading' : ''}`; - - let triggerText = ''; - - if (!_dialIn.numbersEnabled) { - triggerText = t('invite.numbersDisabled'); - } else if (this.state.selectedNumber - && this.state.selectedNumber.content) { - triggerText = this.state.selectedNumber.content; - } else if (!numbers && _dialIn.loading) { - triggerText = t('invite.loadingNumbers'); - } else if (_dialIn.error) { - triggerText = t('invite.errorFetchingNumbers'); - } else { - triggerText = t('invite.noNumbers'); + if (!conferenceId || !numbers || !numbersEnabled || !selectedNumber) { + return null; } + const items = numbers ? this._formatNumbers(numbers) : []; + return (