浏览代码

feat(welcome-page): be able to open settings dialog (#3327)

* feat(welcome-page): be able to open settings dialog

- Create a getter for getting a settings tab's props so the device
  selection tab can get updated available devices.
- Be able to call a function from a tab after it has mounted. This is
  used for device selection to essentially call enumerateDevices on
  the welcome page so the device selectors are populated.
- Remove event UIEvents.AUDIO_OUTPUT_DEVICE_CHANGED. Instead directly call
  setAudioOutputDeviceId where possible.
- Fix initialization of the audioOutputDeviceId in settings by defaulting
  the audio output device to the one set in settings.

* squash: updateAvailableDevices -> getAvailableDevices, add comment for propsUpdateFunction
master
virtuacoplenny 7 年前
父节点
当前提交
cac8888b37

+ 26
- 35
conference.js 查看文件

45
     setDesktopSharingEnabled
45
     setDesktopSharingEnabled
46
 } from './react/features/base/conference';
46
 } from './react/features/base/conference';
47
 import {
47
 import {
48
+    getAvailableDevices,
48
     setAudioOutputDeviceId,
49
     setAudioOutputDeviceId,
49
     updateDeviceList
50
     updateDeviceList
50
 } from './react/features/base/devices';
51
 } from './react/features/base/devices';
2129
             }
2130
             }
2130
         );
2131
         );
2131
 
2132
 
2132
-        APP.UI.addListener(
2133
-            UIEvents.AUDIO_OUTPUT_DEVICE_CHANGED,
2134
-            audioOutputDeviceId => {
2135
-                sendAnalytics(createDeviceChangedEvent('audio', 'output'));
2136
-                setAudioOutputDeviceId(audioOutputDeviceId, APP.store.dispatch)
2137
-                    .then(() => logger.log('changed audio output device'))
2138
-                    .catch(err => {
2139
-                        logger.warn('Failed to change audio output device. '
2140
-                            + 'Default or previously set audio output device '
2141
-                            + 'will be used instead.', err);
2142
-                    });
2143
-            }
2144
-        );
2145
-
2146
         APP.UI.addListener(UIEvents.TOGGLE_AUDIO_ONLY, audioOnly => {
2133
         APP.UI.addListener(UIEvents.TOGGLE_AUDIO_ONLY, audioOnly => {
2147
 
2134
 
2148
             // FIXME On web video track is stored both in redux and in
2135
             // FIXME On web video track is stored both in redux and in
2314
     /**
2301
     /**
2315
      * Inits list of current devices and event listener for device change.
2302
      * Inits list of current devices and event listener for device change.
2316
      * @private
2303
      * @private
2304
+     * @returns {Promise}
2317
      */
2305
      */
2318
     _initDeviceList() {
2306
     _initDeviceList() {
2319
         const { mediaDevices } = JitsiMeetJS;
2307
         const { mediaDevices } = JitsiMeetJS;
2320
 
2308
 
2321
         if (mediaDevices.isDeviceListAvailable()
2309
         if (mediaDevices.isDeviceListAvailable()
2322
                 && mediaDevices.isDeviceChangeAvailable()) {
2310
                 && mediaDevices.isDeviceChangeAvailable()) {
2323
-            mediaDevices.enumerateDevices(devices => {
2324
-                // Ugly way to synchronize real device IDs with local storage
2325
-                // and settings menu. This is a workaround until
2326
-                // getConstraints() method will be implemented in browsers.
2327
-                const { dispatch } = APP.store;
2328
-
2329
-                if (this.localAudio) {
2330
-                    dispatch(updateSettings({
2331
-                        micDeviceId: this.localAudio.getDeviceId()
2332
-                    }));
2333
-                }
2334
-                if (this.localVideo) {
2335
-                    dispatch(updateSettings({
2336
-                        cameraDeviceId: this.localVideo.getDeviceId()
2337
-                    }));
2338
-                }
2339
-
2340
-                APP.store.dispatch(updateDeviceList(devices));
2341
-                APP.UI.onAvailableDevicesChanged(devices);
2342
-            });
2343
-
2344
             this.deviceChangeListener = devices =>
2311
             this.deviceChangeListener = devices =>
2345
                 window.setTimeout(() => this._onDeviceListChanged(devices), 0);
2312
                 window.setTimeout(() => this._onDeviceListChanged(devices), 0);
2346
             mediaDevices.addEventListener(
2313
             mediaDevices.addEventListener(
2347
                 JitsiMediaDevicesEvents.DEVICE_LIST_CHANGED,
2314
                 JitsiMediaDevicesEvents.DEVICE_LIST_CHANGED,
2348
                 this.deviceChangeListener);
2315
                 this.deviceChangeListener);
2316
+
2317
+            const { dispatch } = APP.store;
2318
+
2319
+            return dispatch(getAvailableDevices())
2320
+                .then(devices => {
2321
+                    // Ugly way to synchronize real device IDs with local
2322
+                    // storage and settings menu. This is a workaround until
2323
+                    // getConstraints() method will be implemented in browsers.
2324
+                    if (this.localAudio) {
2325
+                        dispatch(updateSettings({
2326
+                            micDeviceId: this.localAudio.getDeviceId()
2327
+                        }));
2328
+                    }
2329
+
2330
+                    if (this.localVideo) {
2331
+                        dispatch(updateSettings({
2332
+                            cameraDeviceId: this.localVideo.getDeviceId()
2333
+                        }));
2334
+                    }
2335
+
2336
+                    APP.UI.onAvailableDevicesChanged(devices);
2337
+                });
2349
         }
2338
         }
2339
+
2340
+        return Promise.resolve();
2350
     },
2341
     },
2351
 
2342
 
2352
     /**
2343
     /**

+ 0
- 11
react/features/base/devices/actionTypes.js 查看文件

9
  */
9
  */
10
 export const SET_AUDIO_INPUT_DEVICE = Symbol('SET_AUDIO_INPUT_DEVICE');
10
 export const SET_AUDIO_INPUT_DEVICE = Symbol('SET_AUDIO_INPUT_DEVICE');
11
 
11
 
12
-/**
13
- * The type of Redux action which signals that the currently used audio
14
- * output device should be changed.
15
- *
16
- * {
17
- *     type: SET_AUDIO_OUTPUT_DEVICE,
18
- *     deviceId: string,
19
- * }
20
- */
21
-export const SET_AUDIO_OUTPUT_DEVICE = Symbol('SET_AUDIO_OUTPUT_DEVICE');
22
-
23
 /**
12
 /**
24
  * The type of Redux action which signals that the currently used video
13
  * The type of Redux action which signals that the currently used video
25
  * input device should be changed.
14
  * input device should be changed.

+ 25
- 17
react/features/base/devices/actions.js 查看文件

1
+import JitsiMeetJS from '../lib-jitsi-meet';
2
+
1
 import {
3
 import {
2
     SET_AUDIO_INPUT_DEVICE,
4
     SET_AUDIO_INPUT_DEVICE,
3
-    SET_AUDIO_OUTPUT_DEVICE,
4
     SET_VIDEO_INPUT_DEVICE,
5
     SET_VIDEO_INPUT_DEVICE,
5
     UPDATE_DEVICE_LIST
6
     UPDATE_DEVICE_LIST
6
 } from './actionTypes';
7
 } from './actionTypes';
7
 
8
 
8
 /**
9
 /**
9
- * Signals to update the currently used audio input device.
10
+ * Queries for connected A/V input and output devices and updates the redux
11
+ * state of known devices.
10
  *
12
  *
11
- * @param {string} deviceId - The id of the new audio input device.
12
- * @returns {{
13
- *      type: SET_AUDIO_INPUT_DEVICE,
14
- *      deviceId: string
15
- * }}
13
+ * @returns {Function}
16
  */
14
  */
17
-export function setAudioInputDevice(deviceId) {
18
-    return {
19
-        type: SET_AUDIO_INPUT_DEVICE,
20
-        deviceId
21
-    };
15
+export function getAvailableDevices() {
16
+    return dispatch => new Promise(resolve => {
17
+        const { mediaDevices } = JitsiMeetJS;
18
+
19
+        if (mediaDevices.isDeviceListAvailable()
20
+                && mediaDevices.isDeviceChangeAvailable()) {
21
+            mediaDevices.enumerateDevices(devices => {
22
+                dispatch(updateDeviceList(devices));
23
+
24
+                resolve(devices);
25
+            });
26
+        } else {
27
+            resolve([]);
28
+        }
29
+    });
22
 }
30
 }
23
 
31
 
24
 /**
32
 /**
25
- * Signals to update the currently used audio output device.
33
+ * Signals to update the currently used audio input device.
26
  *
34
  *
27
- * @param {string} deviceId - The id of the new audio ouput device.
35
+ * @param {string} deviceId - The id of the new audio input device.
28
  * @returns {{
36
  * @returns {{
29
- *      type: SET_AUDIO_OUTPUT_DEVICE,
37
+ *      type: SET_AUDIO_INPUT_DEVICE,
30
  *      deviceId: string
38
  *      deviceId: string
31
  * }}
39
  * }}
32
  */
40
  */
33
-export function setAudioOutputDevice(deviceId) {
41
+export function setAudioInputDevice(deviceId) {
34
     return {
42
     return {
35
-        type: SET_AUDIO_OUTPUT_DEVICE,
43
+        type: SET_AUDIO_INPUT_DEVICE,
36
         deviceId
44
         deviceId
37
     };
45
     };
38
 }
46
 }

+ 0
- 4
react/features/base/devices/middleware.js 查看文件

6
 
6
 
7
 import {
7
 import {
8
     SET_AUDIO_INPUT_DEVICE,
8
     SET_AUDIO_INPUT_DEVICE,
9
-    SET_AUDIO_OUTPUT_DEVICE,
10
     SET_VIDEO_INPUT_DEVICE
9
     SET_VIDEO_INPUT_DEVICE
11
 } from './actionTypes';
10
 } from './actionTypes';
12
 
11
 
22
     case SET_AUDIO_INPUT_DEVICE:
21
     case SET_AUDIO_INPUT_DEVICE:
23
         APP.UI.emitEvent(UIEvents.AUDIO_DEVICE_CHANGED, action.deviceId);
22
         APP.UI.emitEvent(UIEvents.AUDIO_DEVICE_CHANGED, action.deviceId);
24
         break;
23
         break;
25
-    case SET_AUDIO_OUTPUT_DEVICE:
26
-        APP.UI.emitEvent(UIEvents.AUDIO_OUTPUT_DEVICE_CHANGED, action.deviceId);
27
-        break;
28
     case SET_VIDEO_INPUT_DEVICE:
24
     case SET_VIDEO_INPUT_DEVICE:
29
         APP.UI.emitEvent(UIEvents.VIDEO_DEVICE_CHANGED, action.deviceId);
25
         APP.UI.emitEvent(UIEvents.VIDEO_DEVICE_CHANGED, action.deviceId);
30
         break;
26
         break;

+ 0
- 2
react/features/base/devices/reducer.js 查看文件

1
 import {
1
 import {
2
     SET_AUDIO_INPUT_DEVICE,
2
     SET_AUDIO_INPUT_DEVICE,
3
-    SET_AUDIO_OUTPUT_DEVICE,
4
     SET_VIDEO_INPUT_DEVICE,
3
     SET_VIDEO_INPUT_DEVICE,
5
     UPDATE_DEVICE_LIST
4
     UPDATE_DEVICE_LIST
6
 } from './actionTypes';
5
 } from './actionTypes';
40
         // now.
39
         // now.
41
         case SET_AUDIO_INPUT_DEVICE:
40
         case SET_AUDIO_INPUT_DEVICE:
42
         case SET_VIDEO_INPUT_DEVICE:
41
         case SET_VIDEO_INPUT_DEVICE:
43
-        case SET_AUDIO_OUTPUT_DEVICE:
44
         default:
42
         default:
45
             return state;
43
             return state;
46
         }
44
         }

+ 24
- 1
react/features/base/dialog/components/DialogWithTabs.web.js 查看文件

103
         );
103
         );
104
     }
104
     }
105
 
105
 
106
+    /**
107
+     * Gets the props to pass into the tab component.
108
+     *
109
+     * @param {number} tabId - The index of the tab configuration within
110
+     * {@link this.state.tabStates}.
111
+     * @returns {Object}
112
+     */
113
+    _getTabProps(tabId) {
114
+        const { tabs } = this.props;
115
+        const { tabStates } = this.state;
116
+        const tabConfiguration = tabs[tabId];
117
+        const currentTabState = tabStates[tabId];
118
+
119
+        if (tabConfiguration.propsUpdateFunction) {
120
+            return tabConfiguration.propsUpdateFunction(
121
+                currentTabState,
122
+                tabConfiguration.props);
123
+        }
124
+
125
+        return { ...currentTabState };
126
+    }
127
+
106
     /**
128
     /**
107
      * Renders the tabs from the tab information passed on props.
129
      * Renders the tabs from the tab information passed on props.
108
      *
130
      *
155
             <div className = { styles }>
177
             <div className = { styles }>
156
                 <TabComponent
178
                 <TabComponent
157
                     closeDialog = { closeDialog }
179
                     closeDialog = { closeDialog }
180
+                    mountCallback = { this.props.tabs[tabId].onMount }
158
                     onTabStateChange
181
                     onTabStateChange
159
                         = { this._onTabStateChange }
182
                         = { this._onTabStateChange }
160
                     tabId = { tabId }
183
                     tabId = { tabId }
161
-                    { ...this.state.tabStates[tabId] } />
184
+                    { ...this._getTabProps(tabId) } />
162
             </div>);
185
             </div>);
163
     }
186
     }
164
 
187
 

+ 1
- 1
react/features/base/settings/reducer.js 查看文件

138
         if (settings.audioOutputDeviceId
138
         if (settings.audioOutputDeviceId
139
             !== JitsiMeetJS.mediaDevices.getAudioOutputDevice()) {
139
             !== JitsiMeetJS.mediaDevices.getAudioOutputDevice()) {
140
             JitsiMeetJS.mediaDevices.setAudioOutputDevice(
140
             JitsiMeetJS.mediaDevices.setAudioOutputDevice(
141
-                audioOutputDeviceId
141
+                settings.audioOutputDeviceId
142
             ).catch(ex => {
142
             ).catch(ex => {
143
                 logger.warn('Failed to set audio output device from local '
143
                 logger.warn('Failed to set audio output device from local '
144
                     + 'storage. Default audio output device will be used'
144
                     + 'storage. Default audio output device will be used'

+ 30
- 8
react/features/device-selection/actions.js 查看文件

4
     Transport
4
     Transport
5
 } from '../../../modules/transport';
5
 } from '../../../modules/transport';
6
 
6
 
7
+import { createDeviceChangedEvent, sendAnalytics } from '../analytics';
7
 import {
8
 import {
8
     getAudioOutputDeviceId,
9
     getAudioOutputDeviceId,
9
     setAudioInputDevice,
10
     setAudioInputDevice,
10
-    setAudioOutputDevice,
11
+    setAudioOutputDeviceId,
11
     setVideoInputDevice
12
     setVideoInputDevice
12
 } from '../base/devices';
13
 } from '../base/devices';
13
 import { i18next } from '../base/i18n';
14
 import { i18next } from '../base/i18n';
14
 import JitsiMeetJS from '../base/lib-jitsi-meet';
15
 import JitsiMeetJS from '../base/lib-jitsi-meet';
16
+import { updateSettings } from '../base/settings';
15
 
17
 
16
 import { SET_DEVICE_SELECTION_POPUP_DATA } from './actionTypes';
18
 import { SET_DEVICE_SELECTION_POPUP_DATA } from './actionTypes';
17
 import { getDeviceSelectionDialogProps } from './functions';
19
 import { getDeviceSelectionDialogProps } from './functions';
18
 
20
 
21
+const logger = require('jitsi-meet-logger').getLogger(__filename);
22
+
19
 /**
23
 /**
20
  * Opens a popup window with the device selection dialog in it.
24
  * Opens a popup window with the device selection dialog in it.
21
  *
25
  *
115
             responseCallback(getState()['features/base/devices']);
119
             responseCallback(getState()['features/base/devices']);
116
             break;
120
             break;
117
         case 'setDevice': {
121
         case 'setDevice': {
118
-            let action;
119
             const { device } = request;
122
             const { device } = request;
120
 
123
 
121
             switch (device.kind) {
124
             switch (device.kind) {
122
             case 'audioinput':
125
             case 'audioinput':
123
-                action = setAudioInputDevice;
126
+                dispatch(setAudioInputDevice(device.id));
124
                 break;
127
                 break;
125
             case 'audiooutput':
128
             case 'audiooutput':
126
-                action = setAudioOutputDevice;
129
+                setAudioOutputDeviceId(device.id, dispatch);
127
                 break;
130
                 break;
128
             case 'videoinput':
131
             case 'videoinput':
129
-                action = setVideoInputDevice;
132
+                dispatch(setVideoInputDevice(device.id));
130
                 break;
133
                 break;
131
             default:
134
             default:
132
 
135
 
133
             }
136
             }
134
-            dispatch(action(device.id));
137
+
135
             responseCallback(true);
138
             responseCallback(true);
136
             break;
139
             break;
137
         }
140
         }
179
         if (newState.selectedVideoInputId
182
         if (newState.selectedVideoInputId
180
             && newState.selectedVideoInputId
183
             && newState.selectedVideoInputId
181
                 !== currentState.selectedVideoInputId) {
184
                 !== currentState.selectedVideoInputId) {
185
+            dispatch(updateSettings({
186
+                cameraDeviceId: newState.selectedVideoInputId
187
+            }));
188
+
182
             dispatch(
189
             dispatch(
183
                 setVideoInputDevice(newState.selectedVideoInputId));
190
                 setVideoInputDevice(newState.selectedVideoInputId));
184
         }
191
         }
186
         if (newState.selectedAudioInputId
193
         if (newState.selectedAudioInputId
187
                 && newState.selectedAudioInputId
194
                 && newState.selectedAudioInputId
188
                   !== currentState.selectedAudioInputId) {
195
                   !== currentState.selectedAudioInputId) {
196
+            dispatch(updateSettings({
197
+                micDeviceId: newState.selectedAudioInputId
198
+            }));
199
+
189
             dispatch(
200
             dispatch(
190
                 setAudioInputDevice(newState.selectedAudioInputId));
201
                 setAudioInputDevice(newState.selectedAudioInputId));
191
         }
202
         }
193
         if (newState.selectedAudioOutputId
204
         if (newState.selectedAudioOutputId
194
                 && newState.selectedAudioOutputId
205
                 && newState.selectedAudioOutputId
195
                     !== currentState.selectedAudioOutputId) {
206
                     !== currentState.selectedAudioOutputId) {
196
-            dispatch(
197
-                setAudioOutputDevice(newState.selectedAudioOutputId));
207
+            sendAnalytics(createDeviceChangedEvent('audio', 'output'));
208
+
209
+            setAudioOutputDeviceId(
210
+                newState.selectedAudioOutputId,
211
+                dispatch)
212
+                .then(() => logger.log('changed audio output device'))
213
+                .catch(err => {
214
+                    logger.warn(
215
+                        'Failed to change audio output device.',
216
+                        'Default or previously set audio output device will',
217
+                        ' be used instead.',
218
+                        err);
219
+                });
198
         }
220
         }
199
     };
221
     };
200
 }
222
 }

+ 16
- 4
react/features/device-selection/components/DeviceSelection.js 查看文件

12
 import DeviceSelector from './DeviceSelector';
12
 import DeviceSelector from './DeviceSelector';
13
 import VideoInputPreview from './VideoInputPreview';
13
 import VideoInputPreview from './VideoInputPreview';
14
 
14
 
15
+const logger = require('jitsi-meet-logger').getLogger(__filename);
16
+
15
 /**
17
 /**
16
  * The type of the React {@code Component} props of {@link DeviceSelection}.
18
  * The type of the React {@code Component} props of {@link DeviceSelection}.
17
  */
19
  */
64
      */
66
      */
65
     hideAudioOutputSelect: boolean,
67
     hideAudioOutputSelect: boolean,
66
 
68
 
69
+    /**
70
+     * An optional callback to invoke after the component has completed its
71
+     * mount logic.
72
+     */
73
+    mountCallback?: Function,
74
+
67
     /**
75
     /**
68
      * The id of the audio input device to preview.
76
      * The id of the audio input device to preview.
69
      */
77
      */
134
      * @inheritdoc
142
      * @inheritdoc
135
      */
143
      */
136
     componentDidMount() {
144
     componentDidMount() {
137
-        this._createAudioInputTrack(this.props.selectedAudioInputId);
138
-        this._createVideoInputTrack(this.props.selectedVideoInputId);
145
+        Promise.all([
146
+            this._createAudioInputTrack(this.props.selectedAudioInputId),
147
+            this._createVideoInputTrack(this.props.selectedVideoInputId)
148
+        ])
149
+        .catch(err => logger.warn('Failed to initialize preview tracks', err))
150
+        .then(() => this.props.mountCallback && this.props.mountCallback());
139
     }
151
     }
140
 
152
 
141
     /**
153
     /**
212
      * @returns {void}
224
      * @returns {void}
213
      */
225
      */
214
     _createAudioInputTrack(deviceId) {
226
     _createAudioInputTrack(deviceId) {
215
-        this._disposeAudioInputPreview()
227
+        return this._disposeAudioInputPreview()
216
             .then(() => createLocalTrack('audio', deviceId))
228
             .then(() => createLocalTrack('audio', deviceId))
217
             .then(jitsiLocalTrack => {
229
             .then(jitsiLocalTrack => {
218
                 this.setState({
230
                 this.setState({
234
      * @returns {void}
246
      * @returns {void}
235
      */
247
      */
236
     _createVideoInputTrack(deviceId) {
248
     _createVideoInputTrack(deviceId) {
237
-        this._disposeVideoInputPreview()
249
+        return this._disposeVideoInputPreview()
238
             .then(() => createLocalTrack('video', deviceId))
250
             .then(() => createLocalTrack('video', deviceId))
239
             .then(jitsiLocalTrack => {
251
             .then(jitsiLocalTrack => {
240
                 if (!jitsiLocalTrack) {
252
                 if (!jitsiLocalTrack) {

+ 19
- 0
react/features/settings/components/web/SettingsDialog.js 查看文件

3
 import React, { Component } from 'react';
3
 import React, { Component } from 'react';
4
 import { connect } from 'react-redux';
4
 import { connect } from 'react-redux';
5
 
5
 
6
+import { getAvailableDevices } from '../../../base/devices';
6
 import { DialogWithTabs, hideDialog } from '../../../base/dialog';
7
 import { DialogWithTabs, hideDialog } from '../../../base/dialog';
7
 import {
8
 import {
8
     DeviceSelection,
9
     DeviceSelection,
77
         const tabs = _tabs.map(tab => {
78
         const tabs = _tabs.map(tab => {
78
             return {
79
             return {
79
                 ...tab,
80
                 ...tab,
81
+                onMount: tab.onMount
82
+                    ? (...args) => dispatch(tab.onMount(...args))
83
+                    : undefined,
80
                 submit: (...args) => dispatch(tab.submit(...args))
84
                 submit: (...args) => dispatch(tab.submit(...args))
81
             };
85
             };
82
         });
86
         });
133
             name: SETTINGS_TABS.DEVICES,
137
             name: SETTINGS_TABS.DEVICES,
134
             component: DeviceSelection,
138
             component: DeviceSelection,
135
             label: 'settings.devices',
139
             label: 'settings.devices',
140
+            onMount: getAvailableDevices,
136
             props: getDeviceSelectionDialogProps(state),
141
             props: getDeviceSelectionDialogProps(state),
142
+            propsUpdateFunction: (tabState, newProps) => {
143
+                // Ensure the device selection tab gets updated when new devices
144
+                // are found by taking the new props and only preserving the
145
+                // current user selected devices. If this were not done, the
146
+                // tab would keep using a copy of the initial props it received,
147
+                // leaving the device list to become stale.
148
+
149
+                return {
150
+                    ...newProps,
151
+                    selectedAudioInputId: tabState.selectedAudioInputId,
152
+                    selectedAudioOutputId: tabState.selectedAudioOutputId,
153
+                    selectedVideoInputId: tabState.selectedVideoInputId
154
+                };
155
+            },
137
             styles: 'settings-pane devices-pane',
156
             styles: 'settings-pane devices-pane',
138
             submit: submitDeviceSelectionTab
157
             submit: submitDeviceSelectionTab
139
         });
158
         });

+ 20
- 10
react/features/settings/functions.js 查看文件

74
 export function getMoreTabProps(stateful: Object | Function) {
74
 export function getMoreTabProps(stateful: Object | Function) {
75
     const state = toState(stateful);
75
     const state = toState(stateful);
76
     const language = i18next.language || DEFAULT_LANGUAGE;
76
     const language = i18next.language || DEFAULT_LANGUAGE;
77
-    const conference = state['features/base/conference'];
77
+    const {
78
+        conference,
79
+        followMeEnabled,
80
+        startAudioMutedPolicy,
81
+        startVideoMutedPolicy
82
+    } = state['features/base/conference'];
78
     const configuredTabs = interfaceConfig.SETTINGS_SECTIONS || [];
83
     const configuredTabs = interfaceConfig.SETTINGS_SECTIONS || [];
79
     const localParticipant = getLocalParticipant(state);
84
     const localParticipant = getLocalParticipant(state);
80
 
85
 
81
 
86
 
82
     // The settings sections to display.
87
     // The settings sections to display.
83
-    const showModeratorSettings
84
-        = configuredTabs.includes('moderator')
85
-            && localParticipant.role === PARTICIPANT_ROLE.MODERATOR;
88
+    const showModeratorSettings = Boolean(
89
+        conference
90
+            && configuredTabs.includes('moderator')
91
+            && localParticipant.role === PARTICIPANT_ROLE.MODERATOR);
86
 
92
 
87
     return {
93
     return {
88
         currentLanguage: language,
94
         currentLanguage: language,
89
-        followMeEnabled: Boolean(conference.followMeEnabled),
95
+        followMeEnabled: Boolean(conference && followMeEnabled),
90
         languages: LANGUAGES,
96
         languages: LANGUAGES,
91
         showLanguageSettings: configuredTabs.includes('language'),
97
         showLanguageSettings: configuredTabs.includes('language'),
92
         showModeratorSettings,
98
         showModeratorSettings,
93
-        startAudioMuted: Boolean(conference.startAudioMutedPolicy),
94
-        startVideoMuted: Boolean(conference.startVideoMutedPolicy)
99
+        startAudioMuted: Boolean(conference && startAudioMutedPolicy),
100
+        startVideoMuted: Boolean(conference && startVideoMutedPolicy)
95
     };
101
     };
96
 }
102
 }
97
 
103
 
106
  */
112
  */
107
 export function getProfileTabProps(stateful: Object | Function) {
113
 export function getProfileTabProps(stateful: Object | Function) {
108
     const state = toState(stateful);
114
     const state = toState(stateful);
109
-    const conference = state['features/base/conference'];
115
+    const {
116
+        authEnabled,
117
+        authLogin,
118
+        conference
119
+    } = state['features/base/conference'];
110
     const localParticipant = getLocalParticipant(state);
120
     const localParticipant = getLocalParticipant(state);
111
 
121
 
112
     return {
122
     return {
113
-        authEnabled: conference.authEnabled,
114
-        authLogin: conference.authLogin,
123
+        authEnabled: Boolean(conference && authEnabled),
124
+        authLogin: Boolean(conference && authLogin),
115
         displayName: localParticipant.name,
125
         displayName: localParticipant.name,
116
         email: localParticipant.email
126
         email: localParticipant.email
117
     };
127
     };

+ 25
- 0
react/features/welcome/components/WelcomePage.web.js 查看文件

6
 import React from 'react';
6
 import React from 'react';
7
 import { connect } from 'react-redux';
7
 import { connect } from 'react-redux';
8
 
8
 
9
+import { DialogContainer } from '../../base/dialog';
9
 import { translate } from '../../base/i18n';
10
 import { translate } from '../../base/i18n';
10
 import { Watermarks } from '../../base/react';
11
 import { Watermarks } from '../../base/react';
11
 import { RecentList } from '../../recent-list';
12
 import { RecentList } from '../../recent-list';
13
+import { openSettingsDialog } from '../../settings';
12
 
14
 
13
 import { AbstractWelcomePage, _mapStateToProps } from './AbstractWelcomePage';
15
 import { AbstractWelcomePage, _mapStateToProps } from './AbstractWelcomePage';
14
 
16
 
18
  * @extends AbstractWelcomePage
20
  * @extends AbstractWelcomePage
19
  */
21
  */
20
 class WelcomePage extends AbstractWelcomePage {
22
 class WelcomePage extends AbstractWelcomePage {
23
+    /**
24
+     * Default values for {@code WelcomePage} component's properties.
25
+     *
26
+     * @static
27
+     */
28
+    static defaultProps = {
29
+        _room: ''
30
+    };
31
+
21
     /**
32
     /**
22
      * Initializes a new WelcomePage instance.
33
      * Initializes a new WelcomePage instance.
23
      *
34
      *
55
 
66
 
56
         // Bind event handlers so they are only bound once per instance.
67
         // Bind event handlers so they are only bound once per instance.
57
         this._onFormSubmit = this._onFormSubmit.bind(this);
68
         this._onFormSubmit = this._onFormSubmit.bind(this);
69
+        this._onOpenSettings = this._onOpenSettings.bind(this);
58
         this._onRoomChange = this._onRoomChange.bind(this);
70
         this._onRoomChange = this._onRoomChange.bind(this);
59
         this._setAdditionalContentRef
71
         this._setAdditionalContentRef
60
             = this._setAdditionalContentRef.bind(this);
72
             = this._setAdditionalContentRef.bind(this);
155
                             ref = { this._setAdditionalContentRef } />
167
                             ref = { this._setAdditionalContentRef } />
156
                         : null }
168
                         : null }
157
                 </div>
169
                 </div>
170
+                <AtlasKitThemeProvider mode = 'dark'>
171
+                    <DialogContainer />
172
+                </AtlasKitThemeProvider>
158
             </AtlasKitThemeProvider>
173
             </AtlasKitThemeProvider>
159
         );
174
         );
160
     }
175
     }
172
         this._onJoin();
187
         this._onJoin();
173
     }
188
     }
174
 
189
 
190
+    /**
191
+     * Opens {@code SettingsDialog}.
192
+     *
193
+     * @private
194
+     * @returns {void}
195
+     */
196
+    _onOpenSettings() {
197
+        this.props.dispatch(openSettingsDialog());
198
+    }
199
+
175
     /**
200
     /**
176
      * Overrides the super to account for the differences in the argument types
201
      * Overrides the super to account for the differences in the argument types
177
      * provided by HTML and React Native text inputs.
202
      * provided by HTML and React Native text inputs.

+ 0
- 1
service/UI/UIEvents.js 查看文件

63
     LOGOUT: 'UI.logout',
63
     LOGOUT: 'UI.logout',
64
     VIDEO_DEVICE_CHANGED: 'UI.video_device_changed',
64
     VIDEO_DEVICE_CHANGED: 'UI.video_device_changed',
65
     AUDIO_DEVICE_CHANGED: 'UI.audio_device_changed',
65
     AUDIO_DEVICE_CHANGED: 'UI.audio_device_changed',
66
-    AUDIO_OUTPUT_DEVICE_CHANGED: 'UI.audio_output_device_changed',
67
 
66
 
68
     /**
67
     /**
69
      * Notifies interested listeners that the follow-me feature is enabled or
68
      * Notifies interested listeners that the follow-me feature is enabled or

正在加载...
取消
保存