|
@@ -4,6 +4,7 @@ import React, { Component } from 'react';
|
4
|
4
|
import { connect } from 'react-redux';
|
5
|
5
|
|
6
|
6
|
import { translate } from '../../base/i18n';
|
|
7
|
+import { getLocalParticipant } from '../../base/participants';
|
7
|
8
|
|
8
|
9
|
import { updateDialInNumbers } from '../actions';
|
9
|
10
|
|
|
@@ -25,15 +26,20 @@ class DialInNumbersForm extends Component {
|
25
|
26
|
* @static
|
26
|
27
|
*/
|
27
|
28
|
static propTypes = {
|
|
29
|
+ /**
|
|
30
|
+ * The name of the current user.
|
|
31
|
+ */
|
|
32
|
+ _currentUserName: React.PropTypes.string,
|
|
33
|
+
|
28
|
34
|
/**
|
29
|
35
|
* The redux state representing the dial-in numbers feature.
|
30
|
36
|
*/
|
31
|
37
|
_dialIn: React.PropTypes.object,
|
32
|
38
|
|
33
|
39
|
/**
|
34
|
|
- * The url for retrieving dial-in numbers.
|
|
40
|
+ * The url for the JitsiConference.
|
35
|
41
|
*/
|
36
|
|
- dialInNumbersUrl: React.PropTypes.string,
|
|
42
|
+ conferenceUrl: React.PropTypes.string,
|
37
|
43
|
|
38
|
44
|
/**
|
39
|
45
|
* Invoked to send an ajax request for dial-in numbers.
|
|
@@ -77,35 +83,32 @@ class DialInNumbersForm extends Component {
|
77
|
83
|
|
78
|
84
|
/**
|
79
|
85
|
* The internal reference to the DOM/HTML element backing the React
|
80
|
|
- * {@code Component} input. It is necessary for the implementation of
|
81
|
|
- * copying to the clipboard.
|
|
86
|
+ * {@code Component} text area. It is necessary for the implementation
|
|
87
|
+ * of copying to the clipboard.
|
82
|
88
|
*
|
83
|
89
|
* @private
|
84
|
|
- * @type {HTMLInputElement}
|
|
90
|
+ * @type {HTMLTextAreaElement}
|
85
|
91
|
*/
|
86
|
|
- this._inputElement = null;
|
|
92
|
+ this._copyElement = null;
|
87
|
93
|
|
88
|
94
|
// Bind event handlers so they are only bound once for every instance.
|
89
|
|
- this._onClick = this._onClick.bind(this);
|
|
95
|
+ this._onCopyClick = this._onCopyClick.bind(this);
|
90
|
96
|
this._onOpenChange = this._onOpenChange.bind(this);
|
91
|
97
|
this._onSelect = this._onSelect.bind(this);
|
92
|
|
- this._setInput = this._setInput.bind(this);
|
|
98
|
+ this._setCopyElement = this._setCopyElement.bind(this);
|
93
|
99
|
}
|
94
|
100
|
|
95
|
101
|
/**
|
96
|
|
- * Dispatches a request for numbers if not already present in the redux
|
97
|
|
- * store. If numbers are present, sets a default number to display in the
|
98
|
|
- * dropdown trigger.
|
|
102
|
+ * Sets a default number to display in the dropdown trigger.
|
99
|
103
|
*
|
100
|
104
|
* @inheritdoc
|
101
|
105
|
* returns {void}
|
102
|
106
|
*/
|
103
|
|
- componentDidMount() {
|
|
107
|
+ componentWillMount() {
|
104
|
108
|
if (this.props._dialIn.numbers) {
|
105
|
109
|
this._setDefaultNumber(this.props._dialIn.numbers);
|
106
|
110
|
} else {
|
107
|
|
- this.props.dispatch(
|
108
|
|
- updateDialInNumbers(this.props.dialInNumbersUrl));
|
|
111
|
+ this.props.dispatch(updateDialInNumbers());
|
109
|
112
|
}
|
110
|
113
|
}
|
111
|
114
|
|
|
@@ -123,52 +126,46 @@ class DialInNumbersForm extends Component {
|
123
|
126
|
}
|
124
|
127
|
|
125
|
128
|
/**
|
126
|
|
- * Implements React's {@link Component#render()}.
|
|
129
|
+ * Implements React's {@link Component#render()}. Returns null if the
|
|
130
|
+ * component is not ready for display.
|
127
|
131
|
*
|
128
|
132
|
* @inheritdoc
|
129
|
|
- * @returns {ReactElement}
|
|
133
|
+ * @returns {ReactElement|null}
|
130
|
134
|
*/
|
131
|
135
|
render() {
|
132
|
|
- const { t, _dialIn } = this.props;
|
133
|
|
-
|
134
|
|
- const numbers = _dialIn.numbers;
|
135
|
|
- const items = numbers ? this._formatNumbers(numbers) : [];
|
|
136
|
+ const { _dialIn, t } = this.props;
|
|
137
|
+ const { conferenceId, numbers, numbersEnabled } = _dialIn;
|
|
138
|
+ const { selectedNumber } = this.state;
|
136
|
139
|
|
137
|
|
- const isEnabled = this._isDropdownEnabled();
|
138
|
|
- const inputWrapperClassNames
|
139
|
|
- = `form-control__container ${isEnabled ? '' : 'is-disabled'}
|
140
|
|
- ${_dialIn.loading ? 'is-loading' : ''}`;
|
141
|
|
-
|
142
|
|
- let triggerText = '';
|
143
|
|
-
|
144
|
|
- if (!_dialIn.numbersEnabled) {
|
145
|
|
- triggerText = t('invite.numbersDisabled');
|
146
|
|
- } else if (this.state.selectedNumber
|
147
|
|
- && this.state.selectedNumber.content) {
|
148
|
|
- triggerText = this.state.selectedNumber.content;
|
149
|
|
- } else if (!numbers && _dialIn.loading) {
|
150
|
|
- triggerText = t('invite.loadingNumbers');
|
151
|
|
- } else if (_dialIn.error) {
|
152
|
|
- triggerText = t('invite.errorFetchingNumbers');
|
153
|
|
- } else {
|
154
|
|
- triggerText = t('invite.noNumbers');
|
|
140
|
+ if (!conferenceId || !numbers || !numbersEnabled || !selectedNumber) {
|
|
141
|
+ return null;
|
155
|
142
|
}
|
156
|
143
|
|
|
144
|
+ const items = numbers ? this._formatNumbers(numbers) : [];
|
|
145
|
+
|
157
|
146
|
return (
|
158
|
147
|
<div className = 'form-control dial-in-numbers'>
|
159
|
148
|
<label className = 'form-control__label'>
|
160
|
|
- { t('invite.dialInNumbers') }
|
|
149
|
+ { t('invite.howToDialIn') }
|
|
150
|
+ <span className = 'dial-in-numbers-conference-id'>
|
|
151
|
+ { conferenceId }
|
|
152
|
+ </span>
|
161
|
153
|
</label>
|
162
|
|
- <div className = { inputWrapperClassNames }>
|
163
|
|
- { this._createDropdownMenu(items, triggerText) }
|
|
154
|
+ <div className = 'form-control__container'>
|
|
155
|
+ { this._createDropdownMenu(items, selectedNumber.content) }
|
164
|
156
|
<button
|
165
|
157
|
className = 'button-control button-control_light'
|
166
|
|
- disabled = { !isEnabled }
|
167
|
|
- onClick = { this._onClick }
|
|
158
|
+ onClick = { this._onCopyClick }
|
168
|
159
|
type = 'button'>
|
169
|
160
|
Copy
|
170
|
161
|
</button>
|
171
|
162
|
</div>
|
|
163
|
+ <textarea
|
|
164
|
+ className = 'dial-in-numbers-copy'
|
|
165
|
+ readOnly = { true }
|
|
166
|
+ ref = { this._setCopyElement }
|
|
167
|
+ tabIndex = '-1'
|
|
168
|
+ value = { this._generateCopyText() } />
|
172
|
169
|
</div>
|
173
|
170
|
);
|
174
|
171
|
}
|
|
@@ -209,7 +206,6 @@ class DialInNumbersForm extends Component {
|
209
|
206
|
<input
|
210
|
207
|
className = 'input-control'
|
211
|
208
|
readOnly = { true }
|
212
|
|
- ref = { this._setInput }
|
213
|
209
|
type = 'text'
|
214
|
210
|
value = { triggerText || '' } />
|
215
|
211
|
<span className = 'dial-in-numbers-trigger-icon'>
|
|
@@ -288,19 +284,26 @@ class DialInNumbersForm extends Component {
|
288
|
284
|
}
|
289
|
285
|
|
290
|
286
|
/**
|
291
|
|
- * Determines if the dropdown can be opened.
|
|
287
|
+ * Creates a message describing how to dial in to the conference.
|
292
|
288
|
*
|
293
|
289
|
* @private
|
294
|
|
- * @returns {boolean} True if the dropdown can be opened.
|
|
290
|
+ * @returns {string}
|
295
|
291
|
*/
|
296
|
|
- _isDropdownEnabled() {
|
297
|
|
- const { selectedNumber } = this.state;
|
|
292
|
+ _generateCopyText() {
|
|
293
|
+ const welcome = this.props.t('invite.invitedYouTo', {
|
|
294
|
+ meetingUrl: this.props.conferenceUrl,
|
|
295
|
+ userName: this.props._currentUserName
|
|
296
|
+ });
|
298
|
297
|
|
299
|
|
- return Boolean(
|
300
|
|
- this.props._dialIn.numbersEnabled
|
301
|
|
- && selectedNumber
|
302
|
|
- && selectedNumber.content
|
303
|
|
- );
|
|
298
|
+ const callNumber = this.props.t('invite.callNumber',
|
|
299
|
+ { number: this.state.selectedNumber.number });
|
|
300
|
+ const stepOne = `1) ${callNumber}`;
|
|
301
|
+
|
|
302
|
+ const enterId = this.props.t('invite.enterId',
|
|
303
|
+ { meetingId: this.props._dialIn.conferenceId });
|
|
304
|
+ const stepTwo = `2) ${enterId}`;
|
|
305
|
+
|
|
306
|
+ return `${welcome}\n${stepOne}\n${stepTwo}`;
|
304
|
307
|
}
|
305
|
308
|
|
306
|
309
|
/**
|
|
@@ -311,16 +314,11 @@ class DialInNumbersForm extends Component {
|
311
|
314
|
* @private
|
312
|
315
|
* @returns {void}
|
313
|
316
|
*/
|
314
|
|
- _onClick() {
|
315
|
|
- const displayedValue = this.state.selectedNumber.content;
|
316
|
|
- const desiredNumber = this.state.selectedNumber.number;
|
317
|
|
- const startIndex = displayedValue.indexOf(desiredNumber);
|
318
|
|
-
|
|
317
|
+ _onCopyClick() {
|
319
|
318
|
try {
|
320
|
|
- this._input.focus();
|
321
|
|
- this._input.setSelectionRange(startIndex, displayedValue.length);
|
|
319
|
+ this._copyElement.select();
|
322
|
320
|
document.execCommand('copy');
|
323
|
|
- this._input.blur();
|
|
321
|
+ this._copyElement.blur();
|
324
|
322
|
} catch (err) {
|
325
|
323
|
logger.error('error when copying the text', err);
|
326
|
324
|
}
|
|
@@ -337,7 +335,7 @@ class DialInNumbersForm extends Component {
|
337
|
335
|
*/
|
338
|
336
|
_onOpenChange(dropdownEvent) {
|
339
|
337
|
this.setState({
|
340
|
|
- isDropdownOpen: this._isDropdownEnabled() && dropdownEvent.isOpen
|
|
338
|
+ isDropdownOpen: dropdownEvent.isOpen
|
341
|
339
|
});
|
342
|
340
|
}
|
343
|
341
|
|
|
@@ -355,6 +353,19 @@ class DialInNumbersForm extends Component {
|
355
|
353
|
});
|
356
|
354
|
}
|
357
|
355
|
|
|
356
|
+ /**
|
|
357
|
+ * Sets the internal reference to the DOM/HTML element backing the React
|
|
358
|
+ * {@code Component} text area.
|
|
359
|
+ *
|
|
360
|
+ * @param {HTMLTextAreaElement} element - The DOM/HTML element for this
|
|
361
|
+ * {@code Component}'s text area.
|
|
362
|
+ * @private
|
|
363
|
+ * @returns {void}
|
|
364
|
+ */
|
|
365
|
+ _setCopyElement(element) {
|
|
366
|
+ this._copyElement = element;
|
|
367
|
+ }
|
|
368
|
+
|
358
|
369
|
/**
|
359
|
370
|
* Updates the internal state of the currently selected number by defaulting
|
360
|
371
|
* to the first available number.
|
|
@@ -370,19 +381,6 @@ class DialInNumbersForm extends Component {
|
370
|
381
|
selectedNumber: numbers[0]
|
371
|
382
|
});
|
372
|
383
|
}
|
373
|
|
-
|
374
|
|
- /**
|
375
|
|
- * Sets the internal reference to the DOM/HTML element backing the React
|
376
|
|
- * {@code Component} input.
|
377
|
|
- *
|
378
|
|
- * @param {HTMLInputElement} element - The DOM/HTML element for this
|
379
|
|
- * {@code Component}'s input.
|
380
|
|
- * @private
|
381
|
|
- * @returns {void}
|
382
|
|
- */
|
383
|
|
- _setInput(element) {
|
384
|
|
- this._input = element;
|
385
|
|
- }
|
386
|
384
|
}
|
387
|
385
|
|
388
|
386
|
/**
|
|
@@ -392,11 +390,16 @@ class DialInNumbersForm extends Component {
|
392
|
390
|
* @param {Object} state - The Redux state.
|
393
|
391
|
* @private
|
394
|
392
|
* @returns {{
|
|
393
|
+ * _currentUserName: React.PropTypes.string,
|
395
|
394
|
* _dialIn: React.PropTypes.object
|
396
|
395
|
* }}
|
397
|
396
|
*/
|
398
|
397
|
function _mapStateToProps(state) {
|
|
398
|
+ const { name }
|
|
399
|
+ = getLocalParticipant(state['features/base/participants']);
|
|
400
|
+
|
399
|
401
|
return {
|
|
402
|
+ _currentUserName: name,
|
400
|
403
|
_dialIn: state['features/invite/dial-in']
|
401
|
404
|
};
|
402
|
405
|
}
|