|
@@ -1,25 +1,30 @@
|
1
|
|
-// @flow
|
2
|
|
-
|
3
|
1
|
import InlineDialog from '@atlaskit/inline-dialog';
|
4
|
2
|
import React, { Component } from 'react';
|
|
3
|
+import { WithTranslation } from 'react-i18next';
|
5
|
4
|
|
|
5
|
+import { IReduxState } from '../../../app/types';
|
|
6
|
+// eslint-disable-next-line lines-around-comment
|
|
7
|
+// @ts-ignore
|
6
|
8
|
import { Avatar } from '../../../base/avatar';
|
7
|
|
-import { isNameReadOnly } from '../../../base/config';
|
8
|
|
-import { translate } from '../../../base/i18n';
|
9
|
|
-import { IconArrowDown, IconArrowUp, IconPhoneRinging, IconVolumeOff } from '../../../base/icons';
|
10
|
|
-import { isVideoMutedByUser } from '../../../base/media';
|
11
|
|
-import { getLocalParticipant } from '../../../base/participants';
|
12
|
|
-import { ActionButton, InputField, PreMeetingScreen } from '../../../base/premeeting';
|
13
|
|
-import { connect } from '../../../base/redux';
|
14
|
|
-import { getDisplayName, updateSettings } from '../../../base/settings';
|
15
|
|
-import { getLocalJitsiVideoTrack } from '../../../base/tracks';
|
|
9
|
+import { isNameReadOnly } from '../../../base/config/functions.web';
|
|
10
|
+import { translate } from '../../../base/i18n/functions';
|
|
11
|
+import { IconArrowDown, IconArrowUp, IconPhoneRinging, IconVolumeOff } from '../../../base/icons/svg';
|
|
12
|
+import { isVideoMutedByUser } from '../../../base/media/functions';
|
|
13
|
+import { getLocalParticipant } from '../../../base/participants/functions';
|
|
14
|
+import ActionButton from '../../../base/premeeting/components/web/ActionButton';
|
|
15
|
+import PreMeetingScreen from '../../../base/premeeting/components/web/PreMeetingScreen';
|
|
16
|
+import { connect } from '../../../base/redux/functions';
|
|
17
|
+import { updateSettings } from '../../../base/settings/actions';
|
|
18
|
+import { getDisplayName } from '../../../base/settings/functions.web';
|
|
19
|
+import { getLocalJitsiVideoTrack } from '../../../base/tracks/functions.web';
|
16
|
20
|
import Button from '../../../base/ui/components/web/Button';
|
|
21
|
+import Input from '../../../base/ui/components/web/Input';
|
17
|
22
|
import { BUTTON_TYPES } from '../../../base/ui/constants.any';
|
18
|
23
|
import {
|
19
|
24
|
joinConference as joinConferenceAction,
|
20
|
25
|
joinConferenceWithoutAudio as joinConferenceWithoutAudioAction,
|
21
|
26
|
setJoinByPhoneDialogVisiblity as setJoinByPhoneDialogVisiblityAction
|
22
|
|
-} from '../../actions';
|
|
27
|
+} from '../../actions.web';
|
23
|
28
|
import {
|
24
|
29
|
isDeviceStatusVisible,
|
25
|
30
|
isDisplayNameRequired,
|
|
@@ -28,114 +33,112 @@ import {
|
28
|
33
|
isPrejoinDisplayNameVisible
|
29
|
34
|
} from '../../functions';
|
30
|
35
|
|
|
36
|
+// @ts-ignore
|
31
|
37
|
import JoinByPhoneDialog from './dialogs/JoinByPhoneDialog';
|
32
|
38
|
|
33
|
|
-type Props = {
|
|
39
|
+interface IProps extends WithTranslation {
|
34
|
40
|
|
35
|
41
|
/**
|
36
|
42
|
* Indicates whether the display name is editable.
|
37
|
43
|
*/
|
38
|
|
- canEditDisplayName: boolean,
|
|
44
|
+ canEditDisplayName: boolean;
|
39
|
45
|
|
40
|
46
|
/**
|
41
|
47
|
* Flag signaling if the device status is visible or not.
|
42
|
48
|
*/
|
43
|
|
- deviceStatusVisible: boolean,
|
|
49
|
+ deviceStatusVisible: boolean;
|
44
|
50
|
|
45
|
51
|
/**
|
46
|
52
|
* If join by phone button should be visible.
|
47
|
53
|
*/
|
48
|
|
- hasJoinByPhoneButton: boolean,
|
|
54
|
+ hasJoinByPhoneButton: boolean;
|
49
|
55
|
|
50
|
56
|
/**
|
51
|
57
|
* Joins the current meeting.
|
52
|
58
|
*/
|
53
|
|
- joinConference: Function,
|
|
59
|
+ joinConference: Function;
|
54
|
60
|
|
55
|
61
|
/**
|
56
|
62
|
* Joins the current meeting without audio.
|
57
|
63
|
*/
|
58
|
|
- joinConferenceWithoutAudio: Function,
|
|
64
|
+ joinConferenceWithoutAudio: Function;
|
59
|
65
|
|
60
|
66
|
/**
|
61
|
67
|
* Whether conference join is in progress.
|
62
|
68
|
*/
|
63
|
|
- joiningInProgress: boolean,
|
|
69
|
+ joiningInProgress: boolean;
|
64
|
70
|
|
65
|
71
|
/**
|
66
|
72
|
* The name of the user that is about to join.
|
67
|
73
|
*/
|
68
|
|
- name: string,
|
69
|
|
-
|
70
|
|
- /**
|
71
|
|
- * Updates settings.
|
72
|
|
- */
|
73
|
|
- updateSettings: Function,
|
|
74
|
+ name: string;
|
74
|
75
|
|
75
|
76
|
/**
|
76
|
77
|
* Local participant id.
|
77
|
78
|
*/
|
78
|
|
- participantId: string,
|
|
79
|
+ participantId: string;
|
79
|
80
|
|
80
|
81
|
/**
|
81
|
82
|
* The prejoin config.
|
82
|
83
|
*/
|
83
|
|
- prejoinConfig?: Object,
|
|
84
|
+ prejoinConfig?: any;
|
84
|
85
|
|
85
|
86
|
/**
|
86
|
87
|
* Whether the name input should be read only or not.
|
87
|
88
|
*/
|
88
|
|
- readOnlyName: boolean,
|
|
89
|
+ readOnlyName: boolean;
|
89
|
90
|
|
90
|
91
|
/**
|
91
|
92
|
* Sets visibility of the 'JoinByPhoneDialog'.
|
92
|
93
|
*/
|
93
|
|
- setJoinByPhoneDialogVisiblity: Function,
|
|
94
|
+ setJoinByPhoneDialogVisiblity: Function;
|
94
|
95
|
|
95
|
96
|
/**
|
96
|
97
|
* Flag signaling the visibility of camera preview.
|
97
|
98
|
*/
|
98
|
|
- showCameraPreview: boolean,
|
|
99
|
+ showCameraPreview: boolean;
|
99
|
100
|
|
100
|
101
|
/**
|
101
|
|
- * If should show an error when joining without a name.
|
|
102
|
+ * If 'JoinByPhoneDialog' is visible or not.
|
102
|
103
|
*/
|
103
|
|
- showErrorOnJoin: boolean,
|
|
104
|
+ showDialog: boolean;
|
104
|
105
|
|
105
|
106
|
/**
|
106
|
|
- * If 'JoinByPhoneDialog' is visible or not.
|
|
107
|
+ * If should show an error when joining without a name.
|
107
|
108
|
*/
|
108
|
|
- showDialog: boolean,
|
|
109
|
+ showErrorOnJoin: boolean;
|
109
|
110
|
|
110
|
111
|
/**
|
111
|
|
- * Used for translation.
|
|
112
|
+ * Updates settings.
|
112
|
113
|
*/
|
113
|
|
- t: Function,
|
|
114
|
+ updateSettings: Function;
|
114
|
115
|
|
115
|
116
|
/**
|
116
|
117
|
* The JitsiLocalTrack to display.
|
117
|
118
|
*/
|
118
|
|
- videoTrack: ?Object
|
119
|
|
-};
|
|
119
|
+ videoTrack?: Object;
|
|
120
|
+}
|
120
|
121
|
|
121
|
|
-type State = {
|
|
122
|
+interface IState {
|
122
|
123
|
|
123
|
124
|
/**
|
124
|
125
|
* Flag controlling the visibility of the 'join by phone' buttons.
|
125
|
126
|
*/
|
126
|
|
- showJoinByPhoneButtons: boolean
|
|
127
|
+ showJoinByPhoneButtons: boolean;
|
127
|
128
|
}
|
128
|
129
|
|
129
|
130
|
/**
|
130
|
131
|
* This component is displayed before joining a meeting.
|
131
|
132
|
*/
|
132
|
|
-class Prejoin extends Component<Props, State> {
|
|
133
|
+class Prejoin extends Component<IProps, IState> {
|
|
134
|
+ showDisplayNameField: boolean;
|
|
135
|
+
|
133
|
136
|
/**
|
134
|
137
|
* Initializes a new {@code Prejoin} instance.
|
135
|
138
|
*
|
136
|
139
|
* @inheritdoc
|
137
|
140
|
*/
|
138
|
|
- constructor(props) {
|
|
141
|
+ constructor(props: IProps) {
|
139
|
142
|
super(props);
|
140
|
143
|
|
141
|
144
|
this.state = {
|
|
@@ -150,12 +153,11 @@ class Prejoin extends Component<Props, State> {
|
150
|
153
|
this._setName = this._setName.bind(this);
|
151
|
154
|
this._onJoinConferenceWithoutAudioKeyPress = this._onJoinConferenceWithoutAudioKeyPress.bind(this);
|
152
|
155
|
this._showDialogKeyPress = this._showDialogKeyPress.bind(this);
|
153
|
|
- this._onJoinKeyPress = this._onJoinKeyPress.bind(this);
|
154
|
156
|
this._getExtraJoinButtons = this._getExtraJoinButtons.bind(this);
|
|
157
|
+ this._onInputKeyPress = this._onInputKeyPress.bind(this);
|
155
|
158
|
|
156
|
159
|
this.showDisplayNameField = props.canEditDisplayName || props.showErrorOnJoin;
|
157
|
160
|
}
|
158
|
|
- _onJoinButtonClick: () => void;
|
159
|
161
|
|
160
|
162
|
/**
|
161
|
163
|
* Handler for the join button.
|
|
@@ -170,24 +172,6 @@ class Prejoin extends Component<Props, State> {
|
170
|
172
|
this.props.joinConference();
|
171
|
173
|
}
|
172
|
174
|
|
173
|
|
- _onJoinKeyPress: (Object) => void;
|
174
|
|
-
|
175
|
|
- /**
|
176
|
|
- * KeyPress handler for accessibility.
|
177
|
|
- *
|
178
|
|
- * @param {Object} e - The key event to handle.
|
179
|
|
- *
|
180
|
|
- * @returns {void}
|
181
|
|
- */
|
182
|
|
- _onJoinKeyPress(e) {
|
183
|
|
- if (e.key === ' ' || e.key === 'Enter') {
|
184
|
|
- e.preventDefault();
|
185
|
|
- this._onJoinButtonClick();
|
186
|
|
- }
|
187
|
|
- }
|
188
|
|
-
|
189
|
|
- _onDropdownClose: () => void;
|
190
|
|
-
|
191
|
175
|
/**
|
192
|
176
|
* Closes the dropdown.
|
193
|
177
|
*
|
|
@@ -199,38 +183,32 @@ class Prejoin extends Component<Props, State> {
|
199
|
183
|
});
|
200
|
184
|
}
|
201
|
185
|
|
202
|
|
- _onOptionsClick: () => void;
|
203
|
|
-
|
204
|
186
|
/**
|
205
|
187
|
* Displays the join by phone buttons dropdown.
|
206
|
188
|
*
|
207
|
189
|
* @param {Object} e - The synthetic event.
|
208
|
190
|
* @returns {void}
|
209
|
191
|
*/
|
210
|
|
- _onOptionsClick(e) {
|
211
|
|
- e.stopPropagation();
|
|
192
|
+ _onOptionsClick(e?: React.KeyboardEvent | React.MouseEvent | undefined) {
|
|
193
|
+ e?.stopPropagation();
|
212
|
194
|
|
213
|
195
|
this.setState({
|
214
|
196
|
showJoinByPhoneButtons: !this.state.showJoinByPhoneButtons
|
215
|
197
|
});
|
216
|
198
|
}
|
217
|
199
|
|
218
|
|
- _setName: () => void;
|
219
|
|
-
|
220
|
200
|
/**
|
221
|
201
|
* Sets the guest participant name.
|
222
|
202
|
*
|
223
|
203
|
* @param {string} displayName - Participant name.
|
224
|
204
|
* @returns {void}
|
225
|
205
|
*/
|
226
|
|
- _setName(displayName) {
|
|
206
|
+ _setName(displayName: string) {
|
227
|
207
|
this.props.updateSettings({
|
228
|
208
|
displayName
|
229
|
209
|
});
|
230
|
210
|
}
|
231
|
211
|
|
232
|
|
- _closeDialog: () => void;
|
233
|
|
-
|
234
|
212
|
/**
|
235
|
213
|
* Closes the join by phone dialog.
|
236
|
214
|
*
|
|
@@ -240,8 +218,6 @@ class Prejoin extends Component<Props, State> {
|
240
|
218
|
this.props.setJoinByPhoneDialogVisiblity(false);
|
241
|
219
|
}
|
242
|
220
|
|
243
|
|
- _showDialog: () => void;
|
244
|
|
-
|
245
|
221
|
/**
|
246
|
222
|
* Displays the dialog for joining a meeting by phone.
|
247
|
223
|
*
|
|
@@ -252,8 +228,6 @@ class Prejoin extends Component<Props, State> {
|
252
|
228
|
this._onDropdownClose();
|
253
|
229
|
}
|
254
|
230
|
|
255
|
|
- _showDialogKeyPress: (Object) => void;
|
256
|
|
-
|
257
|
231
|
/**
|
258
|
232
|
* KeyPress handler for accessibility.
|
259
|
233
|
*
|
|
@@ -261,15 +235,13 @@ class Prejoin extends Component<Props, State> {
|
261
|
235
|
*
|
262
|
236
|
* @returns {void}
|
263
|
237
|
*/
|
264
|
|
- _showDialogKeyPress(e) {
|
|
238
|
+ _showDialogKeyPress(e: React.KeyboardEvent) {
|
265
|
239
|
if (e.key === ' ' || e.key === 'Enter') {
|
266
|
240
|
e.preventDefault();
|
267
|
241
|
this._showDialog();
|
268
|
242
|
}
|
269
|
243
|
}
|
270
|
244
|
|
271
|
|
- _onJoinConferenceWithoutAudioKeyPress: (Object) => void;
|
272
|
|
-
|
273
|
245
|
/**
|
274
|
246
|
* KeyPress handler for accessibility.
|
275
|
247
|
*
|
|
@@ -277,7 +249,7 @@ class Prejoin extends Component<Props, State> {
|
277
|
249
|
*
|
278
|
250
|
* @returns {void}
|
279
|
251
|
*/
|
280
|
|
- _onJoinConferenceWithoutAudioKeyPress(e) {
|
|
252
|
+ _onJoinConferenceWithoutAudioKeyPress(e: React.KeyboardEvent) {
|
281
|
253
|
if (this.props.joinConferenceWithoutAudio
|
282
|
254
|
&& (e.key === ' '
|
283
|
255
|
|| e.key === 'Enter')) {
|
|
@@ -286,8 +258,6 @@ class Prejoin extends Component<Props, State> {
|
286
|
258
|
}
|
287
|
259
|
}
|
288
|
260
|
|
289
|
|
- _getExtraJoinButtons: () => Object;
|
290
|
|
-
|
291
|
261
|
/**
|
292
|
262
|
* Gets the list of extra join buttons.
|
293
|
263
|
*
|
|
@@ -320,6 +290,20 @@ class Prejoin extends Component<Props, State> {
|
320
|
290
|
};
|
321
|
291
|
}
|
322
|
292
|
|
|
293
|
+ /**
|
|
294
|
+ * Handle keypress on input.
|
|
295
|
+ *
|
|
296
|
+ * @param {KeyboardEvent} e - Keyboard event.
|
|
297
|
+ * @returns {void}
|
|
298
|
+ */
|
|
299
|
+ _onInputKeyPress(e: React.KeyboardEvent) {
|
|
300
|
+ const { joinConference } = this.props;
|
|
301
|
+
|
|
302
|
+ if (e.key === 'Enter') {
|
|
303
|
+ joinConference();
|
|
304
|
+ }
|
|
305
|
+ }
|
|
306
|
+
|
323
|
307
|
/**
|
324
|
308
|
* Implements React's {@link Component#render()}.
|
325
|
309
|
*
|
|
@@ -330,7 +314,6 @@ class Prejoin extends Component<Props, State> {
|
330
|
314
|
const {
|
331
|
315
|
deviceStatusVisible,
|
332
|
316
|
hasJoinByPhoneButton,
|
333
|
|
- joinConference,
|
334
|
317
|
joinConferenceWithoutAudio,
|
335
|
318
|
joiningInProgress,
|
336
|
319
|
name,
|
|
@@ -343,16 +326,16 @@ class Prejoin extends Component<Props, State> {
|
343
|
326
|
t,
|
344
|
327
|
videoTrack
|
345
|
328
|
} = this.props;
|
346
|
|
- const { _closeDialog, _onDropdownClose, _onJoinButtonClick, _onJoinKeyPress,
|
347
|
|
- _onOptionsClick, _setName } = this;
|
|
329
|
+ const { _closeDialog, _onDropdownClose, _onJoinButtonClick,
|
|
330
|
+ _onOptionsClick, _setName, _onInputKeyPress } = this;
|
348
|
331
|
|
349
|
332
|
const extraJoinButtons = this._getExtraJoinButtons();
|
350
|
|
- let extraButtonsToRender = Object.values(extraJoinButtons).filter((val: Object) =>
|
|
333
|
+ let extraButtonsToRender = Object.values(extraJoinButtons).filter((val: any) =>
|
351
|
334
|
!(prejoinConfig?.hideExtraJoinButtons || []).includes(val.key)
|
352
|
335
|
);
|
353
|
336
|
|
354
|
337
|
if (!hasJoinByPhoneButton) {
|
355
|
|
- extraButtonsToRender = extraButtonsToRender.filter((btn: Object) => btn.key !== 'by-phone');
|
|
338
|
+ extraButtonsToRender = extraButtonsToRender.filter((btn: any) => btn.key !== 'by-phone');
|
356
|
339
|
}
|
357
|
340
|
const hasExtraJoinButtons = Boolean(extraButtonsToRender.length);
|
358
|
341
|
const { showJoinByPhoneButtons } = this.state;
|
|
@@ -366,14 +349,14 @@ class Prejoin extends Component<Props, State> {
|
366
|
349
|
<div
|
367
|
350
|
className = 'prejoin-input-area'
|
368
|
351
|
data-testid = 'prejoin.screen'>
|
369
|
|
- {this.showDisplayNameField ? (<InputField
|
|
352
|
+ {this.showDisplayNameField ? (<Input
|
370
|
353
|
autoComplete = { 'name' }
|
371
|
354
|
autoFocus = { true }
|
372
|
|
- className = { showErrorOnJoin ? 'error' : '' }
|
373
|
|
- hasError = { showErrorOnJoin }
|
|
355
|
+ className = 'prejoin-input'
|
|
356
|
+ error = { showErrorOnJoin }
|
374
|
357
|
onChange = { _setName }
|
375
|
|
- onSubmit = { joinConference }
|
376
|
|
- placeHolder = { t('dialog.enterDisplayName') }
|
|
358
|
+ onKeyPress = { _onInputKeyPress }
|
|
359
|
+ placeholder = { t('dialog.enterDisplayName') }
|
377
|
360
|
readOnly = { readOnlyName }
|
378
|
361
|
value = { name } />
|
379
|
362
|
) : (
|
|
@@ -394,7 +377,7 @@ class Prejoin extends Component<Props, State> {
|
394
|
377
|
<div className = 'prejoin-preview-dropdown-container'>
|
395
|
378
|
<InlineDialog
|
396
|
379
|
content = { hasExtraJoinButtons && <div className = 'prejoin-preview-dropdown-btns'>
|
397
|
|
- {extraButtonsToRender.map(({ key, ...rest }: Object) => (
|
|
380
|
+ {extraButtonsToRender.map(({ key, ...rest }) => (
|
398
|
381
|
<Button
|
399
|
382
|
disabled = { joiningInProgress }
|
400
|
383
|
fullWidth = { true }
|
|
@@ -413,7 +396,6 @@ class Prejoin extends Component<Props, State> {
|
413
|
396
|
disabled = { joiningInProgress }
|
414
|
397
|
hasOptions = { hasExtraJoinButtons }
|
415
|
398
|
onClick = { _onJoinButtonClick }
|
416
|
|
- onKeyPress = { _onJoinKeyPress }
|
417
|
399
|
onOptionsClick = { _onOptionsClick }
|
418
|
400
|
role = 'button'
|
419
|
401
|
tabIndex = { 0 }
|
|
@@ -440,10 +422,10 @@ class Prejoin extends Component<Props, State> {
|
440
|
422
|
* @param {Object} state - The redux state.
|
441
|
423
|
* @returns {Object}
|
442
|
424
|
*/
|
443
|
|
-function mapStateToProps(state): Object {
|
|
425
|
+function mapStateToProps(state: IReduxState) {
|
444
|
426
|
const name = getDisplayName(state);
|
445
|
427
|
const showErrorOnJoin = isDisplayNameRequired(state) && !name;
|
446
|
|
- const { id: participantId } = getLocalParticipant(state);
|
|
428
|
+ const { id: participantId } = getLocalParticipant(state) ?? {};
|
447
|
429
|
const { joiningInProgress } = state['features/prejoin'];
|
448
|
430
|
|
449
|
431
|
return {
|